Skip to content

Commit

Permalink
feat(frontend): throw i18next missing key error (#287)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastien-comeau authored Feb 27, 2025
1 parent f6ca88c commit bc807a4
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 7 deletions.
3 changes: 3 additions & 0 deletions frontend/app/errors/error-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ export const ErrorCodes = {

// dev-only error codes
TEST_ERROR_CODE: 'DEV-0001',

// i18n error codes
MISSING_TRANSLATION_KEY: 'I18N-0001',
} as const;
22 changes: 15 additions & 7 deletions frontend/app/i18n-config.server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { i18n, Namespace, TFunction } from 'i18next';
import type { i18n, KeyPrefix, Namespace, TFunction } from 'i18next';
import { createInstance } from 'i18next';
import { initReactI18next } from 'react-i18next';

Expand All @@ -14,13 +14,15 @@ import { getLanguage } from '~/utils/i18n-utils';
*
* @param languageOrRequest - The language code or Request object to get the language from.
* @param namespace - The namespace to get the translation function for.
* @param keyPrefix - The key prefix to use for the translation function.
* @returns A translation function for the given language and namespace.
* @throws {AppError} If no language is found in the `languageOrRequest`.
*/
export async function getFixedT<NS extends Namespace>(
export async function getFixedT<NS extends Namespace, TKPrefix extends KeyPrefix<NS> = undefined>(
languageOrRequest: Language | Request,
namespace: NS,
): Promise<TFunction<NS>> {
keyPrefix?: TKPrefix,
): Promise<TFunction<NS, TKPrefix>> {
const isRequest = languageOrRequest instanceof Request;

const language = isRequest //
Expand All @@ -32,7 +34,7 @@ export async function getFixedT<NS extends Namespace>(
}

const i18n = await initI18next(language);
return i18n.getFixedT(language, namespace);
return i18n.getFixedT(language, namespace, keyPrefix);
}

/**
Expand All @@ -41,20 +43,22 @@ export async function getFixedT<NS extends Namespace>(
*
* @param languageOrRequest - The language code or Request object to get the language from.
* @param namespace - The namespace to get the translation function for.
* @param keyPrefix - The key prefix to use for the translation function.
* @returns A Promise resolving to an object containing the language code (`lang`) and a translation function (`t`) for the given namespace.
* @throws {AppError} If no language is found in the `languageOrRequest`.
*/
export async function getTranslation<NS extends Namespace>(
export async function getTranslation<NS extends Namespace, TKPrefix extends KeyPrefix<NS> = undefined>(
languageOrRequest: Language | Request,
namespace: NS,
): Promise<{ lang: Language; t: TFunction<NS> }> {
keyPrefix?: TKPrefix,
): Promise<{ lang: Language; t: TFunction<NS, TKPrefix> }> {
const lang = getLanguage(languageOrRequest);

if (lang === undefined) {
throw new AppError('No language found in request', ErrorCodes.NO_LANGUAGE_FOUND);
}

return { lang, t: await getFixedT(languageOrRequest, namespace) };
return { lang, t: await getFixedT(languageOrRequest, namespace, keyPrefix) };
}

/**
Expand All @@ -78,6 +82,10 @@ export async function initI18next(language?: Language): Promise<i18n> {
ns: namespaces,
resources: i18nResources,
interpolation: { escapeValue: false },
appendNamespaceToMissingKey: true,
parseMissingKeyHandler: (key) => {
throw new AppError(`Missing translation key: ${key}`, ErrorCodes.MISSING_TRANSLATION_KEY);
},
});

return i18n;
Expand Down

0 comments on commit bc807a4

Please sign in to comment.