You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.6 KiB
TypeScript
102 lines
3.6 KiB
TypeScript
/** NOTE: Because of docstring limitations changes MUST be manually synced between {@link setupI18n.getRawMessage } and {@link window.i18n.getRawMessage } */
|
|
|
|
import { en } from '../../../localization/locales';
|
|
import {
|
|
DictionaryWithoutPluralStrings,
|
|
GetMessageArgs,
|
|
LocalizerToken,
|
|
PluralKey,
|
|
PluralString,
|
|
} from '../../../types/Localizer';
|
|
import { getTranslationDictionary, getLocale, getStringForCardinalRule, i18nLog } from '../shared';
|
|
|
|
function getPluralKey<R extends PluralKey | undefined>(string: PluralString): R {
|
|
const match = /{(\w+), plural, one \[.+\] other \[.+\]}/g.exec(string);
|
|
return (match?.[1] ?? undefined) as R;
|
|
}
|
|
|
|
const isPluralForm = (localizedString: string): localizedString is PluralString =>
|
|
/{\w+, plural, one \[.+\] other \[.+\]}/g.test(localizedString);
|
|
|
|
/**
|
|
* Retrieves a localized message string, without substituting any variables. This resolves any plural forms using the given args
|
|
* @param token - The token identifying the message to retrieve.
|
|
* @param args - An optional record of substitution variables and their replacement values. This is required if the string has dynamic variables.
|
|
*
|
|
* @returns The localized message string with substitutions applied.
|
|
*
|
|
* NOTE: This is intended to be used to get the raw string then format it with {@link formatMessageWithArgs}
|
|
*
|
|
* @example
|
|
* // The string greeting is 'Hello, {name}!' in the current locale
|
|
* window.i18n.getRawMessage('greeting', { name: 'Alice' });
|
|
* // => 'Hello, {name}!'
|
|
*
|
|
* // The string search is '{count, plural, one [{found_count} of # match] other [{found_count} of # matches]}' in the current locale
|
|
* window.i18n.getRawMessage('search', { count: 1, found_count: 1 });
|
|
* // => '{found_count} of {count} match'
|
|
*/
|
|
export function getRawMessage<
|
|
T extends LocalizerToken,
|
|
R extends DictionaryWithoutPluralStrings[T],
|
|
>(...[token, args]: GetMessageArgs<T>): R | T {
|
|
try {
|
|
if (
|
|
typeof window !== 'undefined' &&
|
|
window?.sessionFeatureFlags?.replaceLocalizedStringsWithKeys
|
|
) {
|
|
return token as T;
|
|
}
|
|
|
|
const localizedDictionary = getTranslationDictionary();
|
|
|
|
let localizedString = localizedDictionary[token] as R;
|
|
|
|
if (!localizedString) {
|
|
i18nLog(`Attempted to get translation for nonexistent key: '${token}'`);
|
|
|
|
localizedString = en[token] as R;
|
|
|
|
if (!localizedString) {
|
|
i18nLog(
|
|
`Attempted to get translation for nonexistent key: '${token}' in fallback dictionary`
|
|
);
|
|
return token as T;
|
|
}
|
|
}
|
|
|
|
/** If a localized string does not have any arguments to substitute it is returned with no
|
|
* changes. We also need to check if the string contains a curly bracket as if it does
|
|
* there might be a default arg */
|
|
if (!args && !localizedString.includes('{')) {
|
|
return localizedString;
|
|
}
|
|
|
|
if (isPluralForm(localizedString)) {
|
|
const pluralKey = getPluralKey(localizedString);
|
|
|
|
if (!pluralKey) {
|
|
i18nLog(`Attempted to nonexistent pluralKey for plural form string '${localizedString}'`);
|
|
} else {
|
|
const num = args?.[pluralKey as keyof typeof args] ?? 0;
|
|
|
|
const currentLocale = getLocale();
|
|
const cardinalRule = new Intl.PluralRules(currentLocale).select(num);
|
|
|
|
const pluralString = getStringForCardinalRule(localizedString, cardinalRule);
|
|
|
|
if (!pluralString) {
|
|
i18nLog(`Plural string not found for cardinal '${cardinalRule}': '${localizedString}'`);
|
|
return token as T;
|
|
}
|
|
|
|
localizedString = pluralString.replaceAll('#', `${num}`) as R;
|
|
}
|
|
}
|
|
return localizedString;
|
|
} catch (error) {
|
|
i18nLog(error.message);
|
|
return token as T;
|
|
}
|
|
}
|