跳到主要内容
版本:3.0.0

多语言

Midway 提供了多语言组件,让业务可以快速指定不同的语言,展示不同的文案,也可以在 HTTP 场景配合请求参数,请求头等方式来使用。

相关信息:

描述
可用于标准项目
可用于 Serverless
可用于一体化
包含独立主框架
包含独立日志

安装组件

$ npm i @midwayjs/i18n@3 --save

或者在 package.json 中增加如下依赖后,重新安装。

{
"dependencies": {
"@midwayjs/i18n": "^3.0.0",
// ...
},
}

使用组件

将 i18n 组件配置到代码中。

import { Configuration } from '@midwayjs/core';
import * as i18n from '@midwayjs/i18n';

@Configuration({
imports: [
// ...
i18n
]
})
export class MainConfiguration {
//...
}

使用

组件提供了 MidwayI18nService 服务,用于翻译多语言文本。

使用 translate 方法,传入不同的文本关键字和参数,返回不同语言的文本内容。

@Controller('/')
export class UserController {

@Inject()
i18nService: MidwayI18nService;

@Get('/')
async index(@Query('username') username: string) {
return this.i18nService.translate('HELLO_MESSAGE', {
args: {
username
},
});
}
}

配置多语言文案

你可以在配置文件中直接配置,但是大多数情况下,文案会很多,有时候甚至可能文案在远端服务上,这个时候直接配置就不太现实。

一般来说,我们会将文案单独放到某个文案配置目录中,比如 src/locales

src/locale 这个目录为例,我们举个例子,结构如下:

.
├── src
│ ├── locales
| │ ├── en_US.json
| │ └── zh_CN.json
│ └── controller
│ └── home.controller.ts
├── package.json
└── tsconfig.json

这里我们建了两个多语言的文件,en_US.jsonzh_CN.json,分别代表英文和中文。

文件内容分别如下:

// src/locales/en_US.json
{
"hello": "Hello {username}",
"email": "email id",
"login": "login account",
"createdAt": "register date"
}
// src/locales/zh_CN.json
{
"hello": "你好 {username}",
"email": "邮箱",
"login": "帐号",
"createdAt": "注册时间"
}

每行一个字符串对,是一个标准的 JSON 格式内容,也可以使用 js/ts 文件,花括号中是可替换的参数占位。

同时,需要在配置中加入这两个 JSON,其中 default 是语言的默认分组。

// src/config/config.default.ts
export default {
// ...
i18n: {
// 把你的翻译文本放到这里
localeTable: {
en_US: {
default: require('../locale/en_US'),
},
zh_CN: {
default: require('../locale/zh_CN'),
}
},
}
}

这样就可以使用了,使用输出如下。

this.i18nService.translate('hello', {
args: {
username: 'harry',
},
locale: 'en_US',
});

// output: Hello harry.

this.i18nService.translate('hello', {
args: {
username: 'harry',
},
locale: 'zh_CN',
});

// output: 你好 harry.

多语言文案分组

在如下配置中,用户配置的多语言文案在 default 分组中。

// src/config/config.default.ts
export default {
// ...
i18n: {
// 把你的翻译文本放到这里
localeTable: {
en_US: {
default: require('../locale/en_US'),
},
zh_CN: {
default: require('../locale/zh_CN'),
}
},
}
}

这样做的好处是,在其他组件或者业务代码中,我们也可以使用不同的分组名,来添加其他的多语言文案。

比如:

// src/config/config.default.ts
export default {
// ...
i18n: {
// 把你的翻译文本放到这里
localeTable: {
en_US: {
default: require('../locale/en_US'),
user: require('../locale/user_en_US'),
},
zh_CN: {
default: require('../locale/zh_CN'),
user: require('../locale/user_zh_CN'),
}
},
}
}

在代码中,如果调用非默认分组,需要指定分组参数。

this.i18nService.translate('user.hello', {
args: {
username: 'harry',
},
group: 'user', // 指定其他分组
locale: 'en_US',
});

多语言文案格式

多语言文本中可以添加参数,参数可以有 对象数组 两种形式。

对象形式如下,使用花括号作为占位符。

Hello {username}

使用时,通过配置传递,按对象 key 覆盖变量。

async index(@Query('username') username: string) {
return this.i18nService.translate('hello', {
args: {
username
},
});
}

数组形式如下,使用数字作为占位符。

Hello {0}

使用时,通过配置传递,格式是数组形式,按数组顺序覆盖数字变量。

async index(@Query('username') username: string) {
return this.i18nService.translate('hello', {
args: [username]
});
}

动态添加多语言文案

有时候,多语言文案可能放在远端,比如数据库等,我们可以通过 addLocale 方法进行动态添加。

比如,在配置加载后,代码使用前。

// configuration.ts

// ...
@Configuration({
imports: [
koa,
i18n
]
})
export class MainConfiguration {

@Inject()
i18nService: MidwayI18nService;

async onReady() {
this.i18nService.addLocale('zh_TW', {
hello: '你好,{username} 美麗的世界'
});
}


// ...
}

在代码中就可以使用。

async index(@Query('username') username: string) {
return this.i18nService.translate('hello', {
args: [username],
locale: 'zh_TW'
});
}

通过参数指定当前语言

一般情况下,默认语言为 en_US,用户的浏览器访问一般会自带 Accept-Language 头,所以会正确识别语言。比如用中文浏览器访问,就能正常显示中文。

除此之外,在 HTTP 场景下可以通过 URL Query,Cookie,Header 来指定语言。

优先级从上到下:

  • query: /?locale=en-US
  • cookie: locale=zh-TW
  • header: Accept-Language: zh-CN,zh;q=0.5

当传递了这些参数之后,多语言数据会自动保存到当前用户的 Cookie 中,下次请求会直接用该设定好的语言。

手动设置语言

可以通过调用 saveRequestLocale 设置当前语言。

async index() {
// ...
this.i18nService.saveRequestLocale('zh_CN');
}

如果开启了 writeCookie 配置,设置后会保存到当前用户的 Cookie 中,下次请求会使用该设置。

语言选择优先级

这些多种设置语言的方式,有着不同的优先级,如下优先级从高到低:

  • 1、i18nService.translate 方法显式指定的语言
  • 2、通过其他装饰器设置的语言,比如 @Validate 装饰器的参数(本质是调用了i18nService.translate 方法)
  • 3、通过 saveRequestLocale API 直接设置的当前语言
  • 4、通过浏览器 Query,Cookie,Header 设置的语言(本质是调用了 saveRequestLocale
  • 5、i18n 组件配置中的默认语言

关于语言大小写

在代码内部,我们会将所有的多语言,fallback 规则,写入的文本串,返回的 locale 结果,使用下面的规则替代

  • 1、使用中划线代替下划线
  • 2、使用小写代替大写

即所有的 en_US 都会变成 en-uszh_CN 会变成 zh-cn

这样做会安全的适配 URL 和 Cookie。

View 中使用

在 Web 类型的框架中,我们默认添加了 locals 变量支持,可以在模板引擎中使用。

假设我们使用的模板引擎是 Nunjucks,可以直接引用到 i18n 方法。

多语言文案如下:

{
"hello": "Hello {username}",
}

模板如下:

<span>{{ i18n('hello', user) }}</span>

示例如下:

// ...

@Controller('/')
export class UserController {

@Inject()
ctx: Context;

@Get('/')
async index() {
await this.ctx.render('index', {
// 注意这里是整个对象传递给模板
user: {
username: 'harry',
}
});
}
}

i18n 方法定义如下:

function i18n(templateName: string, args: Record<string, any>) {
// ...
}

方法名可以通过配置修改。

// src/config/config.default.ts
export default {
// ...
i18n: {
localsField: 'i18n',
}
}

配置

默认配置

大部分情况下,你只需要在配置 localeTable 添加你自己的多语言翻译即可。

下面是完整的配置,你可以在配置定义中找到。

// src/config/config.default.ts
export default {
// ...
i18n: {
// 默认语言 "en_US"
defaultLocale: 'en_US',

// 把你的翻译文本放到这里
localeTable: {
en_US: {
// group name
default: {
// hello: 'hello'
}
},
zh_CN: {
// group name
default: {
// hello: '你好'
}
},
},

// 语言映射,可以用 * 号通配
fallbacks: {
// 'en_*': 'en_US',
// pt: 'pt-BR',
},
// 是否将请求参数写入 cookie
writeCookie: true,
resolver: {
// url query 参数,默认是 "locale"
queryField: 'locale',
cookieField: {
// Cookie 里的 key,默认是 "locale"
fieldName: 'locale',
// Cookie 域名,默认为空,代表当前域名有效
cookieDomain: '',
// Cookie 默认的过期时间,默认一年
cookieMaxAge: FORMAT.MS.ONE_YEAR,
},
},
localsField: 'i18n',
}
}

默认情况下,多语言组件会将当前用户的语言回写到 Cookie 中,避免下次请求再进行查找以提高性能,我们可以通过配置关闭这个行为。

// src/config/config.default.ts
export default {
// ...
i18n: {
writeCookie: false,
}
}

请求解析配置

HTTP 场景下,我们提供了通过参数指定当前语言的能力。

默认情况下,组件通过下面的字段来查找。

  • query 的 locale 字段
  • cookie 的 locale 字段
  • header 的 Accept-Language 部分

我们可以通过配置修改查询的字段。

比如,修改 Query 的字段。

// src/config/config.default.ts
export default {
// ...
i18n: {
resolver: {
queryField: 'abc'
},
}
}

我们就可以通过 /?abc=en-US 来请求修改语言。

如果不希望通过请求来设置语言,可以将整个 resolver 解析关闭,对 Cookie 的回写也将同时停止。

// src/config/config.default.ts
export default {
// ...
i18n: {
resolver: false,
}
}

常用语言

语言语言包名
阿拉伯ar_EG
亞美尼亞hy_AM
保加利亚语bg_BG
加泰罗尼亚语ca_ES
捷克语cs_CZ
丹麦语da_DK
德语de_DE
希腊语el_GR
英语en_GB
英语(美式)en_US
西班牙语es_ES
爱沙尼亚语et_EE
波斯语fa_IR
芬兰语fi_FI
法语(比利时)fr_BE
法语fr_FR
希伯来语he_IL
印地语hi_IN
克罗地亚语hr_HR
匈牙利hu_HU
冰岛语is_IS
印度尼西亚语id_ID
意大利语it_IT
日语ja_JP
格鲁吉亚语ka_GE
卡纳达语kn_IN
韩语/朝鲜语ko_KR
库尔德语ku_IQ
拉脱维亚语lv_LV
马来语ms_MY
蒙古语mn_MN
挪威nb_NO
尼泊尔语ne_NP
荷兰语(比利时)nl_BE
荷兰语nl_NL
波兰语pl_PL
葡萄牙语(巴西)pt_BR
葡萄牙语pt_PT
斯洛伐克语sk_SK
塞尔维亚sr_RS
斯洛文尼亚sl_SI
瑞典语sv_SE
泰米尔语ta_IN
泰语th_TH
土耳其语tr_TR
罗马尼亚语ro_RO
俄罗斯语ru_RU
乌克兰语uk_UA
越南语vi_VN
简体中文zh_CN
繁体中文zh_TW

常见问题

1、测试配置全局语言不生效

一般场景下,你 无需 配置全局语言,因为浏览器访问会自动带上语言信息,比如中文浏览器自动返回中文,英文浏览器自动返回英文。

假如你明确希望测试全局设置的效果,请务必按下面操作:

  • 1、如果使用的是浏览器,请清空页面 cookie 再访问,因为 cookie 中会记录上一次用户的语言信息
  • 2、如果使用的是 Postman 等工具,请不要带上 cookie 以及语言相关的 Header,Query 等字段

2、测试返回的语言非预期

请使用浏览器进行测试,不要使用 Postman。

由于 Postman 请求不会带有浏览器语言相关的 Header,所以服务端无法自动判断语言。

如果你一定要使用 Postman,请参考浏览器请求,加上 Accept-Language Header。