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/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@tanstack/react-query": "5.29.2",
"i18next": "^23.11.2",
"i18next-browser-languagedetector": "7.2.1",
"i18next-resources-to-backend": "^1.2.1",

View File

@@ -1,8 +1,7 @@
'use client';
import type { InitOptions, i18n } from 'i18next';
let client: i18n;
import { useSuspenseQuery } from '@tanstack/react-query';
import type { InitOptions } from 'i18next';
type Resolver = (
lang: string,
@@ -17,23 +16,32 @@ export function I18nProvider({
settings: InitOptions;
resolver: Resolver;
}>) {
// If the client is not initialized or
// the language has changed, reinitialize the client
if (!client || client.language !== settings.lng) {
throw withI18nClient(settings, resolver);
}
useI18nClient(settings, resolver);
return children;
}
async function withI18nClient(settings: InitOptions, resolver: Resolver) {
if (typeof window !== 'undefined') {
const { initializeI18nClient } = await import('./i18n.client');
/**
* @name useI18nClient
* @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);
} else {
const { initializeServerI18n } = await import('./i18n.server');
if (isBrowser) {
const { initializeI18nClient } = await import('./i18n.client');
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 { initReactI18next } from 'react-i18next';
let clientInstance: i18n | null = null;
/**
* Initialize the i18n instance on the client.
* @param settings - the i18n settings
@@ -14,10 +12,6 @@ export async function initializeI18nClient(
settings: InitOptions,
resolver: (lang: string, namespace: string) => Promise<object>,
): Promise<i18n> {
if (clientInstance) {
return Promise.resolve(clientInstance);
}
await i18next
.use(
resourcesToBackend(async (language, namespace, callback) => {
@@ -47,7 +41,5 @@ export async function initializeI18nClient(
},
);
clientInstance = i18next;
return clientInstance;
return i18next;
}

View File

@@ -14,10 +14,6 @@ export async function initializeServerI18n(
) {
const i18nInstance = createInstance();
if (i18nInstance.isInitialized) {
return i18nInstance;
}
await i18nInstance
.use(
resourcesToBackend(async (language, namespace, callback) => {
@@ -36,11 +32,20 @@ export async function initializeServerI18n(
}),
)
.use(initReactI18next)
.init(settings);
.init(settings, (error) => {
if (error) {
console.error('Error initializing i18n server', error);
}
});
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(
languageHeaderValue: string | null | undefined,
acceptedLanguages: string[],