/* eslint-disable @typescript-eslint/no-unused-vars */ import type { ElementType } from 'react'; import type { Dictionary } from '../localization/locales'; import { LOCALE_DEFAULTS } from '../session/constants'; import { CustomTag, CustomTagProps } from '../components/basic/SessionCustomTagRenderer'; /** A localization dictionary key */ type Token = keyof Dictionary; /** A dynamic argument that can be used in a localized string */ type DynamicArg = string | number; /** A record of dynamic arguments for a specific key in the localization dictionary */ type ArgsRecord = Record, DynamicArg>; export type PluralKey = 'count'; export type PluralString = `{${string}, plural, one [${string}] other [${string}]}`; export type DictionaryWithoutPluralStrings = Omit; /** The dynamic arguments in a localized string */ type DynamicArgs = /** If a string follows the plural format use its plural variable name and recursively check for * dynamic args inside all plural forms */ LocalizedString extends `{${infer PluralVar}, plural, one {${infer PluralOne}} other {${infer PluralOther}}}` ? PluralVar | DynamicArgs | DynamicArgs : /** If a string segment has follows the variable form parse its variable name and recursively * check for more dynamic args */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- We dont care about _Pre TODO: see if we can remove this infer LocalizedString extends `${infer _Pre}{${infer Var}}${infer Rest}` ? Var | DynamicArgs : never; type ArgsRecordExcludingDefaults = Omit< ArgsRecord, keyof typeof LOCALE_DEFAULTS >; /** The arguments for retrieving a localized message */ export type GetMessageArgs = T extends Token ? DynamicArgs extends never ? [T] : ArgsRecordExcludingDefaults extends Record ? [T] : [T, ArgsRecordExcludingDefaults] : never; /** Basic props for all calls of the I18n component */ type I18nBaseProps = { token: T; as?: ElementType; // TODO: investigate making these required when required and not required when not required startTagProps?: CustomStartTagProps; endTagProps?: CustomEndTagProps; }; /** The props for the localization component */ export type I18nProps = T extends Token ? DynamicArgs extends never ? I18nBaseProps : ArgsRecordExcludingDefaults extends Record ? I18nBaseProps : I18nBaseProps & { args: ArgsRecordExcludingDefaults } : never; /** The props for custom tags at the start of an i18n strings */ export type CustomStartTagProps = T extends Token ? Dictionary[T] extends `<${infer Tag}/>${string}` ? Tag extends CustomTag ? CustomTagProps : never : never : never; /** * This is used to find the end tag. TypeScript navigates from outwards to inwards when doing magic * with strings. This means we need a recursive type to find the actual end tag. * * @example For the string `{name} reacted with ` * The first iteration will find `Tag` as `emoji` because it grabs the first `<` and the last `/>` * Because it doesn't contain a `<` it will return the Tag. * * @example For the string `You, {name} & 1 other reacted with ` * The first iteration will find `Tag` as `span>1 other reacted with , so we then check if Tag contains a `<`: * - If it doesn't then we have found it; * - If it does then we need to run it through the same process again to search deeper. */ type CustomEndTag = LocalizedString extends `${string}<${infer Tag}/>${string}` ? FindCustomTag : never; type FindCustomTag = S extends CustomTag ? S : S extends `${string}<${infer Tag}` ? Tag extends CustomTag ? Tag : FindCustomTag : never; /** The props for custom tags at the end of an i18n strings */ type CustomEndTagProps = CustomEndTag extends CustomTag ? CustomTagProps> : never; /** The dictionary of localized strings */ export type LocalizerDictionary = Dictionary; /** A localization dictionary key */ export type LocalizerToken = keyof LocalizerDictionary;