2020-10-30 20:34:04 +00:00
|
|
|
// Copyright 2019-2020 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2019-12-17 20:25:57 +00:00
|
|
|
import * as React from 'react';
|
2022-06-13 21:39:35 +00:00
|
|
|
import type { LocalizerType, ReplacementValuesType } from '../../ts/types/Util';
|
2019-12-17 20:25:57 +00:00
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
const placeholder = () => 'NO LOCALE LOADED';
|
|
|
|
placeholder.getLocale = () => 'none';
|
2019-12-17 20:25:57 +00:00
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
const I18nContext = React.createContext<LocalizerType>(placeholder);
|
2019-12-17 20:25:57 +00:00
|
|
|
|
|
|
|
export type I18nProps = {
|
|
|
|
children: React.ReactNode;
|
2022-06-13 21:39:35 +00:00
|
|
|
locale: string;
|
2019-12-17 20:25:57 +00:00
|
|
|
messages: { [key: string]: { message: string } };
|
|
|
|
};
|
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
export const I18n = ({
|
|
|
|
messages,
|
|
|
|
locale,
|
|
|
|
children,
|
|
|
|
}: I18nProps): JSX.Element => {
|
|
|
|
const callback = (key: string, substitutions?: ReplacementValuesType) => {
|
|
|
|
if (Array.isArray(substitutions) && substitutions.length > 1) {
|
|
|
|
throw new Error(
|
|
|
|
'Array syntax is not supported with more than one placeholder'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const stringInfo = messages[key];
|
|
|
|
if (!stringInfo) {
|
|
|
|
window.SignalContext.log.warn(
|
|
|
|
`getMessage: No string found for key ${key}`
|
|
|
|
);
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
const { message } = stringInfo;
|
|
|
|
if (!substitutions) {
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
if (Array.isArray(substitutions)) {
|
|
|
|
return substitutions.reduce(
|
|
|
|
(result, substitution) =>
|
|
|
|
result.toString().replace(/\$.+?\$/, substitution.toString()),
|
|
|
|
message
|
|
|
|
) as string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const FIND_REPLACEMENTS = /\$([^$]+)\$/g;
|
|
|
|
|
|
|
|
let match = FIND_REPLACEMENTS.exec(message);
|
|
|
|
let builder = '';
|
|
|
|
let lastTextIndex = 0;
|
|
|
|
|
|
|
|
while (match) {
|
|
|
|
if (lastTextIndex < match.index) {
|
|
|
|
builder += message.slice(lastTextIndex, match.index);
|
2020-07-29 23:20:05 +00:00
|
|
|
}
|
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
const placeholderName = match[1];
|
|
|
|
const value = substitutions[placeholderName];
|
|
|
|
if (!value) {
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
console.error(
|
|
|
|
`i18n: Value not provided for placeholder ${placeholderName} in key '${key}'`
|
|
|
|
);
|
2020-07-29 23:20:05 +00:00
|
|
|
}
|
2022-06-13 21:39:35 +00:00
|
|
|
builder += value || '';
|
2020-07-29 23:20:05 +00:00
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
lastTextIndex = FIND_REPLACEMENTS.lastIndex;
|
|
|
|
match = FIND_REPLACEMENTS.exec(message);
|
|
|
|
}
|
2020-07-29 23:20:05 +00:00
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
if (lastTextIndex < message.length) {
|
|
|
|
builder += message.slice(lastTextIndex);
|
|
|
|
}
|
2020-07-29 23:20:05 +00:00
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
return builder;
|
|
|
|
};
|
|
|
|
callback.getLocale = () => locale;
|
|
|
|
|
|
|
|
const getMessage = React.useCallback<LocalizerType>(callback, [messages]);
|
2019-12-17 20:25:57 +00:00
|
|
|
|
|
|
|
return (
|
|
|
|
<I18nContext.Provider value={getMessage}>{children}</I18nContext.Provider>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-06-13 21:39:35 +00:00
|
|
|
export const useI18n = (): LocalizerType => React.useContext(I18nContext);
|