使用 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 - 初稿