Scenes
Some of our projects need to use i18next to handle internationalization, but typescript needs to have type definitions, so it was previously maintained and used joplin-utils A lot of refactoring was done yesterday, and it has now been separated and released as a public npm package. If anyone is interested, you can try it.
English , Simplified Chinese
Introduction
i18next's typescript type definition generator can generate type definitions from multiple language translation json files, and supports nested objects and parameters.
use
The type definition generation of the international configuration of this cli itself is also done by cli (bootstrapping)
i18next-dts-gen --dirs src/i18n # 扫描这个目录下的 json 文件并生成 index.d.ts 类型定义
Details
$ i18next-dts-gen -h
Usage: bin [options]
根据 json 生成 .d.ts 类型定义
Options:
-i, --dirs <dirs...> 包含一或多个翻译文件的目录
-w, --watch 是否使用监视模式
-h, --help display help for command
Code
The following is used in nodejs
// src/util/I18nextUtil.ts
import i18next from 'i18next';
export enum LanguageEnum {
En = 'en',
ZhCN = 'zhCN',
}
export class I18nextUtil<
T extends Record<
string,
{
params: [key: string] | [key: string, params: object];
value: string;
}
>
> {
constructor() {}
async changeLang(lang: LanguageEnum) {
await i18next.changeLanguage(lang);
}
/**
* 加载国际化
*/
async init(resources: Record<LanguageEnum, object>, language: LanguageEnum) {
await i18next.init({
lng: language,
resources: Object.entries(resources).reduce((res, [k, v]) => {
Reflect.set(res, k, {
translation: v,
});
return res;
}, {}),
keySeparator: false,
});
}
/**
* 根据 key 获取翻译的文本
* @param args
*/
t<K extends keyof T>(...args: T[K]['params']): T[K]['value'] {
// @ts-ignore
return i18next.t(args[0], args[1]);
}
}
// src/constants/i18n.ts
import { TranslateType } from '../i18n';
import osLocale from 'os-locale';
import { I18nextUtil, LanguageEnum } from '../util/I18nextUtil';
export async function getLanguage(): Promise<LanguageEnum> {
const language = await osLocale();
/**
* os-locale => i18next 的语言类型字符串映射
*/
const map: Record<string, LanguageEnum> = {
'zh-CN': LanguageEnum.ZhCN,
'en-US': LanguageEnum.En,
};
return map[language] || LanguageEnum.En;
}
export const i18n = new I18nextUtil<TranslateType>();
// src/bin.ts
async function main() {
await i18n.init({ en, zhCN }, await getLanguage());
console.log(i18n.t('hello', { name: 'liuli' }));
}
Or in the browser
// App.tsx
function getLanguage() {
return navigator.language.startsWith('zh') ? LanguageEnum.ZhCN : LanguageEnum.En;
}
export const App: React.FC<AppProps> = () => {
useMount(async () => {
await i18n.init({ en, zhCN }, getLanguage());
// 然后再做其它的事情,例如加载路由
});
return <div />;
};
Of course, if you need to use it in other environments, you should only need to implement the corresponding getLanguage
function.
Skill
hint
navigation
Search and replace
motivation
Why are there many third-party type definition generators, and even the latest version of i18next has officially launched a typescript solution. Why do we have to write this?
In short, they are not perfect.
Let's start with the official solution of i18next. It replaces json files with ts files, but it cannot support parameters and nested objects.
Note: The latest version seems to use the recursive type and template string type of TypeScript 4.2 to ensure type safety, but this is actually not very easy to use. In addition, only react-i18next is available.
i18next-typescript 160ffcf966b174, which can almost meet our needs, except for one thing: support for object parameters. There is also i18n-codegen like Jack Chicory. The code design is very elegant, but again, it does not support ecosystems other than react.
In addition, as far as our generation is concerned, it is easier and more reasonable to use generators to generate simple types than to support this function from the type system.
design
FAQ
Does it support all the features of i18next?
No, what is supported here is only a subset of i18next.
- [x] Generate type definitions for multiple localized json configuration files
[x] Support include parameters
- [] Does not support nested parameters
- [] Doesn’t support nested keys - we think it’s enough to use. Split, and it’s easier to find and replace globally
- [] Does not support configuration of namespaces, nested split strings, we believe that the convention is greater than the configuration
- [] Configuration files other than json are not supported. We think json files are more friendly to non-developers and easier for developers to handle when needed. It is also cross-language, including golang/rust and i18n frameworks are supported
[] Does not support i18next namespace, about to split the translation file - most of the time there is not so much translated content, and I don’t want to split up to 1000.
Can pass different i18n objects
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。