Using i18next with TypeScript: How to Support Prefixed Namespace Keys in the t Function

245 views Asked by At

I am using i18next with typescript and generating resource types from JSON with translation resources with i18next-resources-for-ts. Everything works well, but I'm facing an issue when using prefixed namespaces with the t function from the useTranslation() hook. I am not looking for useTranslation('common') or t('auth.login.text', { ns: 'common' }), which works fine and is typed correctly.

Here's a simplified version of my i18next.d.ts:

import 'i18next';
import common from '../public/locales/en/common.json';

const resources = {
  common,
} as const;

declare module 'i18next' {
  interface CustomTypeOptions {
    returnNull: false;
    defaultNS: 'common';
    resources: typeof resources;
  }
}

and set up of i18next.ts

import commonDe from '../public/locales/de/common.json';
import commonEn from '../public/locales/de/common.json';
import i18next from 'i18next';
import { initReactI18next } from 'react-i18next';

const resources = {
  de: {
    common: commonDe,
  },
  en: {
    common: commonEn,
  },
};

export const defaultNS = 'common';

i18next
  .use(initReactI18next)
  .init({
    resources,
    ns: [defaultNS],
    defaultNS,
    lng: 'en',
    interpolation: {
      escapeValue: false,
    },
    returnNull: false,
  });

export default i18next;

The problem arises when attempting to use a prefixed namespace in the t function:

const { t } = useTranslations();
t('common:auth.login.text')

Then types doesn't work.

I know that I could specify ns as options for t function or select it already in useTranslation hook. But I would also like to support the prefixed way as above in the translation key.

Any suggestions or ideas on how to achieve this would be greatly appreciated. Thank you!

2

There are 2 answers

0
onsmak On BEST ANSWER

Thanks to @hokwanhung points I got to a working solution.

As he perfectly described, my issue was in react-i18next. But in my case, I need to use useTranslations() with an array argument, simple string doesn't work.

const { t } = useTranslation('common'); // doesn't enable keys with namespace prefixes in my case

Working solution with settings in question:

const { t } = useTranslation(['common']); // use array here
t('common:auth.login.text'); // works fine now
0
hokwanhung On

It is not really about i18next-resource-for-ts, but with the way of how react-i18next works.

In i18next, this would work well:

// when you call i18next directly,
i18next.t('common:auth.login.text'); // work fine, not recommended, but what you want specifically.
i18next.t('auth.login.text', { ns: 'common' }); // work fine too.

However, with react-i18next, you must specify the namespace(s) for multiple translation files with the three methods of useTranslation(), withTranslation() and Translation (as mentioned in the documentation):

// when you call react-i18next methods,
const { t } = useTranslations();
t('common:auth.login.text'); // fail.

When the three methods are passed with no parameters, it recognised the default namespace specified in the i18next.ts document. However, whenever you want to specify namespace(s) in the t function, it is best to perform like below:

const { t } = useTranslation('common'); // and when you have multiple, put all of them in an array.
t('common:auth.login.text'); // would work fine.

Hope the above answer helps.