使用 i18next/vue-i18n + Typescript,讓多語言前端專案的開發更順暢
前言
筆者之前接手了一個前端專案,有中、日、英三國語言版本,而且文本中的翻譯還有多層嵌套,因此在開發的時候很容易打錯一個字而造成i18n找不到key、畫面顯示錯誤的字。
因此特別搜尋了一下有沒有辦法利用Typescript的特性、讓t(“”)
有自動提示的功能呢?
還真的找到有網友分享他在React專案中的解法 ;同時,我也查到Vue專案、官方推薦的使用方式,因此來特別紀錄一下,在React和Vue專案中分別可以怎麼應用。
React的多語言提示
Dependancy: i18next+react-i18next+typescript
首先,假設已經在React專案中設定好 i18next
和react-i18next
,這個專案的架構為:
- src
- App.tsx
- index.tsx
- i18n
- index.ts
- langs
- en.ts
- zh.ts
- jp.ts
- package.json
- tsconfig.json
./src/i18n/langs/en.ts內容如下:
export default {
hello: 'Hello',
fruit: {
apple: 'Apple',
banana: 'Banana'
}
};
./src/i18n/index.ts:
import en from '@/i18n/langs/en'
import jp from '@/i18n/langs/jp'
import zh from '@/i18n/langs/zh'
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
export enum LangType {
ZH_HK = 'zh_hk',
EN_US = 'en_us',
JA_JP = 'ja_jp',
}
export const resources = {
[LangType.EN_US]: {
translation: en,
},
[LangType.ZH_HK]: {
translation: zh,
},
[LangType.JA_JP]: {
translation: jp,
},
}
i18n.use(initReactI18next).init({
resources,
fallbackLng: LangType.EN_US,
debug: true,
interpolation: {
escapeValue: false,
},
})
export default i18n
./src/index.ts:
import i18n from '@/i18n'
import { I18nextProvider } from 'react-i18next'
///...其他設定
root.render(
<React.StrictMode>
<I18nextProvider i18n={i18n}>
<App />
</I18nextProvider>
</React.StrictMode>,
)
以上是 React 專案使用多語言的基本設定,那麼要如何讓i18n支援Typescript的type提示(目前版本:i18next: ^21.9.2
、react-i18next: ^11.18.6
)?在根目錄中新增*.d.ts
來覆蓋兩者Dependency中的關鍵Type(目前先命名為react-i18n.d.ts
)。
主要概念為:
- 引入一個翻譯json,使其成為一個標準的Type,並轉換為一個key對應的type
./src/react-i18n.d.ts:
// import the original type declarations
import 'react-i18next'
import { LangType, resources } from '@/i18n'
// import all namespaces (for the default language, only)
import en from '@/i18n/langs/en'
type I18nStoreType = typeof en
export type I18nT = {
(key: keyof I18nStoreType): string
}
給i18n暴露出來的返回類型(
TFunction
)來extends。declare module 'i18next' { // eslint-disable-next-line @typescript-eslint/no-empty-interface interface TFuntion extends I18nT {} }
覆蓋
react-i18next
下的CustomTypeOptions
// react-i18next versions higher than 11.11.0 declare module 'react-i18next' { interface CustomTypeOptions { resources: typeof resources[LangType.EN_US] } }
基本上就完成了。在組件中使用t("")
便會出現Typescript提示:
Vue的多語言提示
首先,也假設已經在Vue專案中設定好了vue-i18n
。而主要邏輯也跟React專案一樣,暴露一個翻譯模板,然後把其轉換為Type,並複寫i18n的type。不過在vue-i18n
的官方issue可以看到,vue-i18n
還可以對輸入的所有resource進行類型檢查,如果en.ts
有一個翻譯但zh.ts
沒有,那麼就會在i18n初始化時就會報錯:
- i18n
- index.ts
- langs
- en.ts
- zh.ts
在./i18n/index.ts
中,在基本設定之外,同時也暴露en.ts
轉化為Type:
import { createI18n } from 'vue-i18n';
import en from './langs/en.ts';
import zh from './langs/zh.ts';
export type MessageSchema = typeof en;
const i18n = createI18n({
legacy: false,
locale: 'en-us',
globalInjection: true,
fallbackLocale: 'en-us',
messages:{
"en-us":en,
"zh-tw":zh
}
});
export default i18n;
在./src
下新增ts類型文件,目前命名為vue-i18n.d.ts
:
import { DefineLocaleMessage } from 'vue-i18n';
import { MessageSchema } from './i18n';
declare module 'vue-i18n' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DefineLocaleMessage extends MessageSchema {}
}
透過declare module,來覆蓋npm包的默認類型定義。
這樣一來基本上也算完成了。
ChangeLog
- 20220930 - 初稿