Generic TypeScript types for strongly-typed i18n.
This solution works well in combination with i18next package. But can be easily adapted to other i18n engines.
yarn add -D https://github.com/rubyroidlabs/i18n-typesor
npm i --save-dev https://github.com/rubyroidlabs/i18n-typesYou should store your translations in the TS objects. Like this:
const en = {
appName: 'My App',
home: {
title: 'Home',
description: 'Lorem ipsum',
greeting: 'Hi, {{ username }}!',
},
menu: {
itemsCount_one: 'There is {{ count }} item',
itemsCount_other: 'There are {{ count }} items',
},
tabs: {
settings: {
name: 'Settings',
},
},
} as constPay attention to the as const at the end.
Then, create a locale object type from your default locale (e.g. english):
type DefaultLocale = typeof enAnd create the translation key type:
import { TranslationKeyForLocale } from 'i18n-types'
type TranslationKey = TranslationKeyForLocale<DefaultLocale>So, now you can use this type for typing the key argument for translation function and be sure, that the wrong key won't be passed:
const t = (key: TranslationKey, options?: any): string => {
// ...
}Create the generic type for getting options for translation key:
import { KeyOptionsForLocale } from 'i18n-types'
type KeyOptions<GKey extends TranslationKey> = KeyOptionsForLocale<DefaultLocale, GKey>Make translation function generic and use it like this:
const t = <GKey extends TranslationKey>(
key: GKey,
options: KeyOptions<GKey>,
): string => {
// ...
}So, now translation options is validated by TS and you won't miss any option/parameter.
Create locale type from your default locale object like this:
import { GeneralLocaleFromConcrete } from 'i18n-types'
type GeneralLocale = GeneralLocaleFromConcrete<TranslationObject>And use it like this:
const fr: GeneralLocale = {
...
}It checks whether locales have the same fields.
import { KeyOptionsForLocale, TranslationKeyForLocale } from 'i18n-types'
import { useTranslation as useI18nextTranslation } from 'react-i18next'
import en from './locales/en'
type DefaultLocale = typeof en.translation
type TranslationKey = TranslationKeyForLocale<DefaultLocale>
type KeyOptions<GKey extends TranslationKey> = KeyOptionsForLocale<
DefaultLocale,
GKey
>
const useTranslation = () => {
const i18next = useI18nextTranslation('translation')
const t = <GKey extends TranslationKey>(
key: GKey,
options?: KeyOptions<GKey>,
): string => {
return i18next.t(key, options)
}
return { t }
}
export { useTranslation }./locales/en.ts
export default {
translation: {
appName: 'My App',
home: {
title: 'Home',
description: 'Welcome to the home page',
greeting: 'Hi, {{ username }}!',
},
menu: {
itemsCount_one: 'There is {{ count }} item',
itemsCount_other: 'There are {{ count }} items',
},
settings: {
title: 'Settings',
language: 'Language',
},
},
} as constRoot translation key is required by i18next.
Try to copy files from /example to your project and use them.
MIT. See LICENSE.