国际化

使用 Gem 的国际化模块,你可以很迅速的为你的应用添加多语言支持。

开始

import { I18n } from '@mantou/gem/helper/i18n'; const en = { title: 'This is I18n', }; const i18n = new I18n({ fallbackLanguage: 'en', resources: { en, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }, }); console.log(i18n.get('title'));

实例化 I18n 对象时会自动根据浏览器设置选择适当的语言。语言包支持远端读取,以实现按需加载。

TIP

建议使用 CrowdinPontoon 类似的服务来维护你的语言包

在元素中可以直接使用 i18n.get('title') 来读取语言包中的值。

@customElement('app-root') class App extends GemElement { render = () => { return html` <button @click=${() => i18n.setLanguage('zh')}>zh</button> <p>${i18n.get('title')}</p> `; } }

缓存

用户选择语言或者远端语言包都可以进行缓存,在用户下次进入 App 时直接使用,使用 cache 指定,缓存数据将保存到 localStorage

const i18n = new I18n({ cache: true, fallbackLanguage: 'en', resources: { en, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }, });

变量替换

在多语言项目中,常常有变量嵌入的情况,例如:

html`Hello, ${username}`;

使用 Gem 时,你可以使用 $ 标记变量,例如:

const i18n = new I18n({ fallbackLanguage: 'en', resources: { en: { hello: 'Hello, $1! $2=> $1, Hello!', }, }, }); html`${i18n.get('hello', 'World', 'reverse')}`; // Hello, World! reverse=> World, Hello!

变量也可以是一个自定义渲染函数,它能将语言模版中提供的内容进行自定义渲染,下面的例子就是将语言条目中指定的单词 detail 渲染成链接:

const i18n = new I18n({ fallbackLanguage: 'en', resources: { en: { detail: 'See $1<detail>, $1<detail2>', }, }, }); html`${i18n.get('detail', (s) => html`<a href="#">${s}</a>`)}`;

URL 中指定语言

一般会为不同的语言生成唯一的 URL,因为这样能让搜索引擎进行准确的索引。I18n 在实例化时根据 urlParamsType 检查 URL,它支持:

  • path:根路径,例如 /zh/home
  • querystring:查询字符串,名称使用 urlParamsName 指定
  • ccTLD:子域名,例如 jp.website.com
  • gTLD:顶级域名,例如 website.jp
const i18n = new I18n({ fallbackLanguage: 'en', urlParamsType: 'ccTLD', resources: { en: { title: 'This is I18n', }, }, });

TIP

urlParamsTypepath 或者 querystring, 页面中的链接、history 路由切换都需要修改。 例如,urlParamsTypepath 时可以通过设置 history.basePath 来全局定义此操作:

new I18n({ urlParamsType: 'path', fallbackLanguage: 'zh-CN', resources: { 'zh-CN': zhCN, }, onChange(lang) { if (!history.basePath) { history.basePath = '/' + lang; const { path, query, hash } = history.getParams(); history.replace({ path, query, hash }); } else { const { path, query, hash } = history.getParams(); history.basePath = '/' + lang; history.replace({ path, query, hash }); } }, });

子模块

I18n.createSubModule 创建子模块,你可以按路由分割语言包,加快首屏渲染速度。

本地化

可以使用 <gem-route> 根据不同语言渲染不同的内容,只需要将 trigger 指定为 i18n 即可,例如:

const localizeRoutes = [ { pattern: 'zh-*', async getContent() { import('...'); return html`other element`; }, }, ]; html`<gem-route .trigger=${i18n} .routes=${localizeRoutes}></gem-route>`;

例子

import { customElement, GemElement, html, render } from '@mantou/gem'; import { I18n } from '@mantou/gem/helper/i18n'; import type { RouteItem } from 'duoyun-ui/elements/route'; import 'duoyun-ui/elements/route'; import '../elements/layout'; const en = { title: 'This is I18n', hello: 'Hello, $1! $2=> $1, Hello!', detail: 'See $1<detail>, $1<detail2>', }; const i18n = new I18n<typeof en>({ cache: true, fallbackLanguage: 'en', resources: { en, de: { title: 'Das ist I18n', }, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }, onChange: console.log, }); const i18nModule = i18n.createSubModule<typeof en>('test', { en, de: { title: 'Das ist I18n', }, zh: 'data:text/plain;base64,eyJ0aXRsZSI6Iui/meaYr0kxOG4ifQ==', }); const localizeRoutes: RouteItem[] = [ { pattern: 'en', getContent() { return html`en element`; }, }, { pattern: 'de', getContent() { return html`de element`; }, }, { pattern: '*', getContent() { return html`other element`; }, }, ]; @customElement('app-root') export class App extends GemElement { render() { return html` <button @click=${() => i18n.setLanguage('de')}>de</button> <button @click=${() => i18n.setLanguage('zh')}>zh</button> <button @click=${() => i18n.setLanguage('en')}>en</button> <button @click=${() => i18n.resetLanguage()}>reset</button> <p>Current language: ${i18n.currentLanguage}</p> <p>${i18n.get('title')}</p> <p>${i18n.get('hello', 'World', 'reverse')}</p> <p>${i18n.get('detail', (s) => html`<a href="#">${s}</a>`)}</p> <h2>sub module</h2> <p>${i18nModule.get('title')}</p> <p>${i18nModule.get('hello', 'World', 'reverse')}</p> <p>${i18nModule.get('detail', (s) => html`<a href="#">${s}</a>`)}</p> <h2>localize content</h2> <dy-route .trigger=${i18n} .routes=${localizeRoutes}></dy-route> `; } } render( html` <gem-examples-layout> <app-root></app-root> </gem-examples-layout> `, document.body, );