chore: enforce i18n functions types to match what is in window.d.ts

pull/3281/head
Audric Ackermann 3 months ago
parent 9156ec83a7
commit ce734f1e1c
No known key found for this signature in database

@ -1,10 +1,12 @@
// eslint-disable-next-line no-restricted-imports
import type { I18nMethods } from '../types/localizer';
import { CrowdinLocale } from './constants'; import { CrowdinLocale } from './constants';
import { pluralsDictionary, simpleDictionary } from './locales'; import { pluralsDictionary, simpleDictionary } from './locales';
type SimpleDictionary = typeof simpleDictionary; type SimpleDictionary = typeof simpleDictionary;
type PluralDictionary = typeof pluralsDictionary; type PluralDictionary = typeof pluralsDictionary;
type SimpleLocalizerTokens = keyof SimpleDictionary; export type SimpleLocalizerTokens = keyof SimpleDictionary;
type PluralLocalizerTokens = keyof PluralDictionary; type PluralLocalizerTokens = keyof PluralDictionary;
export type MergedLocalizerTokens = SimpleLocalizerTokens | PluralLocalizerTokens; export type MergedLocalizerTokens = SimpleLocalizerTokens | PluralLocalizerTokens;
@ -133,7 +135,7 @@ function propsToTuple<T extends MergedLocalizerTokens>(
* @param token - The token identifying the message to retrieve. * @param token - The token identifying the message to retrieve.
* @param args - An optional record of substitution variables and their replacement values. This is equired if the string has dynamic variables. * @param args - An optional record of substitution variables and their replacement values. This is equired if the string has dynamic variables.
*/ */
export function inEnglish<T extends MergedLocalizerTokens>([token, args]: GetMessageArgs<T>) { export const inEnglish: I18nMethods['inEnglish'] = token => {
if (!isSimpleToken(token)) { if (!isSimpleToken(token)) {
throw new Error('inEnglish only supports simple strings for now'); throw new Error('inEnglish only supports simple strings for now');
} }
@ -143,8 +145,8 @@ export function inEnglish<T extends MergedLocalizerTokens>([token, args]: GetMes
log(`Attempted to get forced en string for nonexistent key: '${token}' in fallback dictionary`); log(`Attempted to get forced en string for nonexistent key: '${token}' in fallback dictionary`);
return token; return token;
} }
return formatMessageWithArgs(rawMessage, args); return formatMessageWithArgs(rawMessage);
} };
/** /**
* Retrieves a localized message string, substituting variables where necessary. * Retrieves a localized message string, substituting variables where necessary.
@ -176,24 +178,21 @@ export function getMessageDefault<T extends MergedLocalizerTokens>(
* *
* @returns The localized message string with substitutions applied. Any HTML and custom tags are removed. * @returns The localized message string with substitutions applied. Any HTML and custom tags are removed.
*/ */
export const stripped: I18nMethods['stripped'] = (...[token, args]) => {
export function stripped<T extends MergedLocalizerTokens>(
...[token, args]: GetMessageArgs<T>
): string {
const sanitizedArgs = args ? sanitizeArgs(args, '\u200B') : undefined; const sanitizedArgs = args ? sanitizeArgs(args, '\u200B') : undefined;
const i18nString = getMessageDefault<T>(...([token, sanitizedArgs] as GetMessageArgs<T>)); // Note: the `as any` is needed because we don't have the <T> template argument available
// when enforcing the type of the stripped function to be the one defined by I18nMethods
const i18nString = getMessageDefault(...([token, sanitizedArgs] as GetMessageArgs<any>));
const strippedString = i18nString.replaceAll(/<[^>]*>/g, ''); const strippedString = i18nString.replaceAll(/<[^>]*>/g, '');
return deSanitizeHtmlTags(strippedString, '\u200B'); return deSanitizeHtmlTags(strippedString, '\u200B');
} };
export function strippedWithObj<T extends MergedLocalizerTokens>( export const strippedWithObj: I18nMethods['strippedWithObj'] = opts => {
opts: LocalizerComponentProps<T>
): string {
return stripped(...propsToTuple(opts)); return stripped(...propsToTuple(opts));
} };
/** /**
* Sanitizes the args to be used in the i18n function * Sanitizes the args to be used in the i18n function
@ -224,17 +223,14 @@ export function sanitizeArgs(
* @deprecated * @deprecated
* *
*/ */
export function formatMessageWithArgs<T extends MergedLocalizerTokens>( export const formatMessageWithArgs: I18nMethods['formatMessageWithArgs'] = (rawMessage, args) => {
rawMessage: string,
args?: ArgsFromToken<T>
): string {
/** Find and replace the dynamic variables in a localized string and substitute the variables with the provided values */ /** Find and replace the dynamic variables in a localized string and substitute the variables with the provided values */
return rawMessage.replace(/\{(\w+)\}/g, (match: any, arg: string) => { return rawMessage.replace(/\{(\w+)\}/g, (match: any, arg: string) => {
const matchedArg = args ? args[arg as keyof typeof args] : undefined; const matchedArg = args ? args[arg as keyof typeof args] : undefined;
return matchedArg?.toString() ?? match; return matchedArg?.toString() ?? match;
}); });
} };
/** /**
* Retrieves a localized message string, without substituting any variables. This resolves any plural forms using the given args * Retrieves a localized message string, without substituting any variables. This resolves any plural forms using the given args
@ -245,16 +241,13 @@ export function formatMessageWithArgs<T extends MergedLocalizerTokens>(
* *
* NOTE: This is intended to be used to get the raw string then format it with {@link formatMessageWithArgs} * NOTE: This is intended to be used to get the raw string then format it with {@link formatMessageWithArgs}
*/ */
export function getRawMessage<T extends MergedLocalizerTokens>( export const getRawMessage: I18nMethods['getRawMessage'] = (crowdinLocale, ...[token, args]) => {
crowdinLocale: CrowdinLocale,
...[token, args]: GetMessageArgs<T>
): string {
try { try {
if ( if (
typeof window !== 'undefined' && typeof window !== 'undefined' &&
window?.sessionFeatureFlags?.replaceLocalizedStringsWithKeys window?.sessionFeatureFlags?.replaceLocalizedStringsWithKeys
) { ) {
return token as T; return token;
} }
if (isSimpleToken(token)) { if (isSimpleToken(token)) {
@ -284,15 +277,15 @@ export function getRawMessage<T extends MergedLocalizerTokens>(
if (!pluralString) { if (!pluralString) {
log(`Plural string not found for cardinal '${cardinalRule}': '${pluralString}'`); log(`Plural string not found for cardinal '${cardinalRule}': '${pluralString}'`);
return token as T; return token;
} }
return pluralString.replaceAll('#', `${num}`); return pluralString.replaceAll('#', `${num}`);
} catch (error) { } catch (error) {
log(error.message); log(error.message);
return token as T; return token;
} }
} };
function getStringForRule({ function getStringForRule({
dictionary, dictionary,

@ -3,6 +3,7 @@ import type {
MergedLocalizerTokens, MergedLocalizerTokens,
GetMessageArgs, GetMessageArgs,
LocalizerComponentProps, LocalizerComponentProps,
SimpleLocalizerTokens,
} from '../localization/localeTools'; } from '../localization/localeTools';
import { CrowdinLocale } from '../localization/constants'; import { CrowdinLocale } from '../localization/constants';
@ -13,7 +14,7 @@ export type I18nMethods = {
opts: LocalizerComponentProps<T> opts: LocalizerComponentProps<T>
) => string | T; ) => string | T;
/** @see {@link window.i18n.inEnglish} */ /** @see {@link window.i18n.inEnglish} */
inEnglish: <T extends MergedLocalizerTokens>(...[token, args]: GetMessageArgs<T>) => string | T; inEnglish: <T extends SimpleLocalizerTokens>(token: T) => string | T;
/** @see {@link window.i18n.formatMessageWithArgs */ /** @see {@link window.i18n.formatMessageWithArgs */
getRawMessage: <T extends MergedLocalizerTokens>( getRawMessage: <T extends MergedLocalizerTokens>(
crowdinLocale: CrowdinLocale, crowdinLocale: CrowdinLocale,

Loading…
Cancel
Save