Refactor i18n client and server initialization

The refactor includes the removal of clientInstance checks as they are unnecessary. Also, i18n-provider.tsx has been updated to use react-query instead of local state for client initialization. Lastly, error handling has been added to the server initialization process and the @tanstack/react-query package has been added to the project dependencies.
This commit is contained in:
giancarlo
2024-04-21 12:48:18 +08:00
parent 562db0058e
commit c5b70d7b62
5 changed files with 39 additions and 30 deletions

View File

@@ -20,6 +20,7 @@
"@kit/shared": "workspace:^", "@kit/shared": "workspace:^",
"@kit/tailwind-config": "workspace:*", "@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*", "@kit/tsconfig": "workspace:*",
"@tanstack/react-query": "5.29.2",
"i18next": "^23.11.2", "i18next": "^23.11.2",
"i18next-browser-languagedetector": "7.2.1", "i18next-browser-languagedetector": "7.2.1",
"i18next-resources-to-backend": "^1.2.1", "i18next-resources-to-backend": "^1.2.1",

View File

@@ -1,8 +1,7 @@
'use client'; 'use client';
import type { InitOptions, i18n } from 'i18next'; import { useSuspenseQuery } from '@tanstack/react-query';
import type { InitOptions } from 'i18next';
let client: i18n;
type Resolver = ( type Resolver = (
lang: string, lang: string,
@@ -17,23 +16,32 @@ export function I18nProvider({
settings: InitOptions; settings: InitOptions;
resolver: Resolver; resolver: Resolver;
}>) { }>) {
// If the client is not initialized or useI18nClient(settings, resolver);
// the language has changed, reinitialize the client
if (!client || client.language !== settings.lng) {
throw withI18nClient(settings, resolver);
}
return children; return children;
} }
async function withI18nClient(settings: InitOptions, resolver: Resolver) { /**
if (typeof window !== 'undefined') { * @name useI18nClient
const { initializeI18nClient } = await import('./i18n.client'); * @description A hook that initializes the i18n client.
* @param settings
* @param resolver
*/
function useI18nClient(settings: InitOptions, resolver: Resolver) {
return useSuspenseQuery({
queryKey: ['i18n', settings.lng],
queryFn: async () => {
const isBrowser = typeof window !== 'undefined';
client = await initializeI18nClient(settings, resolver); if (isBrowser) {
} else { const { initializeI18nClient } = await import('./i18n.client');
const { initializeServerI18n } = await import('./i18n.server');
client = await initializeServerI18n(settings, resolver); return await initializeI18nClient(settings, resolver);
} } else {
const { initializeServerI18n } = await import('./i18n.server');
return await initializeServerI18n(settings, resolver);
}
},
});
} }

View File

@@ -3,8 +3,6 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import resourcesToBackend from 'i18next-resources-to-backend'; import resourcesToBackend from 'i18next-resources-to-backend';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
let clientInstance: i18n | null = null;
/** /**
* Initialize the i18n instance on the client. * Initialize the i18n instance on the client.
* @param settings - the i18n settings * @param settings - the i18n settings
@@ -14,10 +12,6 @@ export async function initializeI18nClient(
settings: InitOptions, settings: InitOptions,
resolver: (lang: string, namespace: string) => Promise<object>, resolver: (lang: string, namespace: string) => Promise<object>,
): Promise<i18n> { ): Promise<i18n> {
if (clientInstance) {
return Promise.resolve(clientInstance);
}
await i18next await i18next
.use( .use(
resourcesToBackend(async (language, namespace, callback) => { resourcesToBackend(async (language, namespace, callback) => {
@@ -47,7 +41,5 @@ export async function initializeI18nClient(
}, },
); );
clientInstance = i18next; return i18next;
return clientInstance;
} }

View File

@@ -14,10 +14,6 @@ export async function initializeServerI18n(
) { ) {
const i18nInstance = createInstance(); const i18nInstance = createInstance();
if (i18nInstance.isInitialized) {
return i18nInstance;
}
await i18nInstance await i18nInstance
.use( .use(
resourcesToBackend(async (language, namespace, callback) => { resourcesToBackend(async (language, namespace, callback) => {
@@ -36,11 +32,20 @@ export async function initializeServerI18n(
}), }),
) )
.use(initReactI18next) .use(initReactI18next)
.init(settings); .init(settings, (error) => {
if (error) {
console.error('Error initializing i18n server', error);
}
});
return i18nInstance; return i18nInstance;
} }
/**
* Parse the accept-language header value and return the languages that are included in the accepted languages.
* @param languageHeaderValue
* @param acceptedLanguages
*/
export function parseAcceptLanguageHeader( export function parseAcceptLanguageHeader(
languageHeaderValue: string | null | undefined, languageHeaderValue: string | null | undefined,
acceptedLanguages: string[], acceptedLanguages: string[],

3
pnpm-lock.yaml generated
View File

@@ -989,6 +989,9 @@ importers:
'@kit/tsconfig': '@kit/tsconfig':
specifier: workspace:* specifier: workspace:*
version: link:../../tooling/typescript version: link:../../tooling/typescript
'@tanstack/react-query':
specifier: 5.29.2
version: 5.29.2(react@18.2.0)
i18next: i18next:
specifier: ^23.11.2 specifier: ^23.11.2
version: 23.11.2 version: 23.11.2