Search code examples
reactjstypescripti18next

Extending translation function of i18next


Im struggling to extend the function propperly. Im stuck with the error Cannot redeclare block-scoped variable t. I`m not quite sure if declareing a module is the correct approach (im new to ts). I also played around with the idea of creating a wrapper for the t function to then use the wrapper and don't get the error of redeclartion. But I could not create a working example for that.

Suppresing the error // @ts-ignore results in that I get propper suggestions of valid keys from the json file but no validation if I type in a non existent one

I created a file i18n.d.ts which looks as following

import "react-i18next";
import deTranslation from 'src/translation/de-translation.json';

declare module 'i18next' {
    type Translations<T = typeof deTranslation> = T;
    type ConcatKeys<T, U> = T extends string ? `${T & string}.${U & string}` : never;

    type NestedKeys<T> =
        T extends object ? {
                [K in keyof T]: K | ConcatKeys<K, NestedKeys<T[K]>>;
            }[keyof T] :
            string;

    type TranslationKeys<T> = NestedKeys<T>;

    interface TranslationFunction<T> {
        <K extends TranslationKeys<T>>(key: K, nestedKeys?: string[]): string;
    }
    export const t: TranslationFunction<Translations>;
}

My goal is to be able to use it like this t('property.mortgage.mortgage_status')} to get suggestions for the keys and validation if the key is not existing in de-translation.json file.


Solution

  • I came up with a possible solution.

    Adding an I18n interface to the module declaration with the properties and methods used from i18n in the project. Then exporting it solves the issue. The t function now is typesafe and has the correct suggestions.

    The drawback of this solution is that you have to manually add newly used functions to the interface.

    interface I18n {
        language: string;
        changeLanguage: (lang: string) => Promise<void>;
        use: (module: any) => I18n;
        t: TranslationFunction<Translations>;
        init: (options: any) => Promise<I18n>;
        // Add other properties/methods you use from i18n
    }
    
    const i18n: I18n & {
        t: TranslationFunction<Translations>;
    };