跳转到内容

多语言

本框架采用运行时 i18n:构建期不做 xliff 抽取,也不做 AOT 内联。模板中的 i18n 和 ICU 由全局 $localize 在运行时查询译文表。

因此构建选项中没有 localize,也无需按语言分别构建产物——一份产物 + 一份译文表。

// angular.json -> options
{ "polyfills": ["@angular/localize/init"] }

/init 必须写全。 主入口不会自动挂载全局 $localize,只写 @angular/localize 时构建 可以通过,但运行时会抛 Cannot set properties of undefined (setting 'translate')。

tsconfig.app.json
{ "compilerOptions": { "types": ["@angular/localize/init"] } }

否则在 TS 中使用 $localize 缺少类型声明。

消息应统一用 i18n="@@xxx" 显式命名,译文表的 key 就是 xxx。若不命名,编译器会 按文案计算哈希,文案改动一个字符 id 就会变化,无法维护。

import { clearTranslations, loadTranslations } from '@angular/localize';
const EN: Record<string, string> = {
'i18n.title': 'Internationalization',
'i18n.cartCount': '{$INTERPOLATION} item(s) in the cart',
'i18n.select': '{VAR_SELECT, select, male {he} female {she} other {they}}',
'i18n.plural':
'{VAR_PLURAL, plural, =0 {nothing yet} one {just one} other {{INTERPOLATION} in total}}',
};
export function applyLocale(id: 'zh' | 'en') {
clearTranslations(); // loadTranslations 只加不减,切回去必须先清
loadTranslations(id === 'en' ? EN : {});
}

loadTranslations(translations) 只接受一个参数,不存在第二个 locale 参数, 多传会被静默忽略。

译文中的占位符名必须与编译产物逐字一致,书写错误不会报错,只会静默退回源文案。

源写法 占位符
{{ a }} 单个插值 {$INTERPOLATION}
多个插值 {$INTERPOLATION_1} {$INTERPOLATION_2} …
ICU select 的变量 {VAR_SELECT, select, …}(裸写,不带 $)
ICU plural 的变量 {VAR_PLURAL, plural, …}
ICU 分支中的插值 {INTERPOLATION}

ICU 相关的几条由 ɵɵi18nPostprocess 在译文求值之后再替换,因此形态与插值不同。

占位符名称不应凭猜测,建议在产物中 grep 确认:

终端窗口
grep -o '`:[^`]*`' dist/<app>/pages/i18n/i18n-entry.js

模板文案的译文在组件 consts 首次求值时即已确定,而 consts 每个组件类型只求值一次。 因此译文必须在该组件首次渲染之前就位。

// main.ts —— bootstrap 之前
applyLocale(readStoredLocale());
bootstrapApplication();

运行过程中切换语言只对尚未渲染的组件生效,已渲染的组件需要重启小程序才会更新。 在取值时求值的内容不受影响,比如在 TS 中直接调用 $localize:

get liveLabel() {
return $localize`:@@i18n.live:这一行每次取值都重新求值`;
}

$localize 必须作为模板标签使用($localize`…`)。作为普通函数调用时无法获取 .raw,内部会抛出错误。

写法 支持
i18n="@@id" 静态文案 ✅
i18n="@@id" + {{插值}} ✅
ICU { x, select, … } / { x, plural, … } ✅
i18n-alt / i18n-title / i18n-aria-label 等属性 ✅(带插值的走 setProperty,静态的走 setAttribute)
TS 中 $localize`:@@id:文案` ✅

ICU 有两条排版限制:ICU 前后的空白会被并入消息本体,因此包含 ICU 的那一行不能折行; 分支中也不能放标签。

<!-- 这样 -->
<p i18n="@@i18n.plural">
{count(), plural, =0 {还没有商品} one {只有一件} other {{{count()}} 件}}
</p>

小程序没有 localStorage,用 wx.setStorageSync(或 MpApiService.setStorageSync):

export function readStoredLocale(): LocaleId {
return wx.getStorageSync('demo.locale') === 'en' ? 'en' : 'zh';
}

也可以在 onLaunch 中读取小程序的 language 作为默认值。