From b1812392cbc580ccdc96c98b0c36285e5be392bf Mon Sep 17 00:00:00 2001 From: thislight Date: Mon, 25 Nov 2024 15:22:25 +0800 Subject: [PATCH] i18n: optimize performance * createCurrentLanguage: caching result with memo * createStringResource: use useAppLocale --- src/platform/i18n.tsx | 46 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/platform/i18n.tsx b/src/platform/i18n.tsx index b64b920..69bae99 100644 --- a/src/platform/i18n.tsx +++ b/src/platform/i18n.tsx @@ -25,12 +25,22 @@ const DEFAULT_LANG = "en"; /** * Decide the using language for the user. + * + * **Performance**: This function is costy, make sure you cache the result. + * In the app, you should use {@link useAppLocale} instead. + * * @returns the selected language tag */ export function autoMatchLangTag() { return match(Array.from(navigator.languages), SUPPORTED_LANGS, DEFAULT_LANG); } +/** + * Decide the using region for the user. + * + * **Performance**: This function is costy, make sure you cache the result. + * In the app, you should use {@link useAppLocale} instead. + */ export function autoMatchRegion() { const specifiers = navigator.languages.map((x) => x.split("-")); @@ -99,7 +109,7 @@ export function useDateFnLocale(): Accessor { export function createCurrentLanguage() { const settings = useStore($settings); - return () => settings().language || autoMatchLangTag(); + return createMemo(() => settings().language || autoMatchLangTag()); } type ImportFn = (name: string) => Promise<{ default: T }>; @@ -114,10 +124,30 @@ type MergedImportedModule = T extends [] ? ImportedModule & MergedImportedModule : never; +/** + * Create a resource that combines all I18N strings into one object. + * + * The result is combined in the order of the argument functions. + * The formers will be overrided by the latter. + * + * @param importFns a series of functions imports the string modules + * based on the specified language code. + * + * **Context**: This function must be used under {@link AppLocaleProvider}. + * + * @example ````ts + * const [strings] = createStringResource( + * async (code) => await import(`./i18n/${code}.json`), // Vite can handle the bundling + * async () => import("./i18n/generic.json"), // You can also ignore the code. + * ); + * ```` + * + * @see {@link createTranslator} if you need a Translator from "@solid-primitives/i18n" + */ export function createStringResource< T extends ImportFn | undefined>>[], >(...importFns: T) { - const language = createCurrentLanguage(); + const { language } = useAppLocale(); const cache: Record> = {}; return createResource( @@ -140,6 +170,18 @@ export function createStringResource< ); } +/** + * Create the Translator from "@solid-primitives/i18n" based on + * the {@link createStringResource}. + * + * @param importFns same to {@link createStringResource} + * + * @returns the first element is the translator, the second is the result from + * {@link createStringResource}. + * + * @see {@link translator} for the translator usage + * @see {@link createStringResource} for the raw strings + */ export function createTranslator< T extends ImportFn | undefined>>[], >(...importFns: T) {