你正在查看 Lit 的旧版本文档。点击 这里查看最新版本。

本地化

本地化是在你的应用和组件中支持多语言和多地区的过程。Lit 通过 @lit/localize 库提供一流的本地化支持,它具有许多优势,使其成为第三方本地化库的良好替代选择:

  • 原生支持在本地化模板中使用表达式和 HTML 标记。无需新的语法和插值运行时进行变量替换——只需使用你已有的模板。

  • 切换语言区域时自动重新渲染 Lit 组件。

  • 仅 1.27 KiB(压缩 + 最小化后)的额外 JavaScript。

  • 可选地为每个语言区域编译,将额外 JavaScript 减少到 0 KiB。

安装 @lit/localize 客户端库和 @lit/localize-tools 命令行界面。

npm i @lit/localize
npm i -D @lit/localize-tools
  1. 将字符串或模板包装在 msg 函数中(详情)。
  2. 创建 lit-localize.json 配置文件(详情)。
  3. 运行 lit-localize extract 生成 XLIFF 文件(详情)。
  4. 编辑生成的 XLIFF 文件,添加 <target> 翻译标签(详情)。
  5. 运行 lit-localize build 输出本地化版本的字符串和模板(详情)。

要使字符串或 Lit 模板可本地化,请将其包装在 msg 函数中。msg 函数返回当前活动语言区域版本的给定字符串或模板。

在你有任何可用翻译之前,msg 只是返回原始字符串或模板,因此即使你还没有准备好真正进行本地化,使用它也是安全的。

import {html, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {msg} from '@lit/localize';

@customElement('my-greeter')
class MyGreeter extends LitElement {
@property()
who = 'World';

render() {
return msg(html`Hello <b>${this.who}</b>`);
}
}
import {html, LitElement} from 'lit';
import {msg} from '@lit/localize';

class MyGreeter extends LitElement {
static properties = {
who: {},
};

constructor() {
super();
this.who = 'World';
}

render() {
return msg(html`Hello <b>${this.who}</b>`);
}
}
customElements.define('my-greeter', MyGreeter);

任何你通常会使用 Lit 渲染的字符串或模板都可以被本地化,包括带有动态表达式和 HTML 标记的字符串或模板。

纯字符串:

msg('Hello World');

带表达式的纯字符串(有关 str 的详情,请参阅带表达式的字符串):

msg(str`Hello ${name}`);

HTML 模板:

msg(html`Hello <b>World</b>`);

带表达式的 HTML 模板:

msg(html`Hello <b>${name}</b>`);

本地化消息也可以嵌套在 HTML 模板中:

html`<button>${msg('Hello World')}</button>`;

包含表达式的字符串必须使用 htmlstr 标签才能被本地化。当你的字符串不包含任何 HTML 标记时,应优先使用 str 而不是 html,因为它的性能开销略低。如果在带有表达式的字符串上忘记了 htmlstr 标签,运行 lit-localize 命令时会报错。

错误写法:

import {msg} from '@lit/localize';
msg(`Hello ${name}`);

正确写法:

import {msg, str} from '@lit/localize';
msg(str`Hello ${name}`);

在这些情况下需要 str 标签,因为未标签化的模板字符串字面量在被 msg 函数接收之前会被求值为普通字符串,这意味着动态表达式的值无法被捕获并替换到本地化版本的字符串中。

语言区域代码是标识人类语言的字符串,有时还包括地区、文字或其他变体。

Lit Localize 不强制要求使用任何特定的语言区域代码系统,但强烈建议使用 BCP 47 语言标签标准。BCP 47 语言标签的一些示例:

  • en:英语
  • es-419:拉丁美洲使用的西班牙语
  • zh-Hans:简体中文

Lit Localize 定义了一些指代语言区域代码的术语。这些术语在本文档、Lit Localize 配置文件和 Lit Localize API 中使用:

源语言区域(Source locale)

用于在源代码中编写字符串和模板的语言区域。

目标语言区域(Target locales)

你的字符串和模板可以翻译成的语言区域。

活动语言区域(Active locale)

当前正在显示的全局语言区域。

Lit Localize 支持两种输出模式:

  • 运行时模式使用 Lit Localize 的 API 在运行时加载本地化消息。

  • 转换模式通过为每个语言区域构建单独的 JavaScript 包来消除 Lit Localize 运行时代码。

不确定使用哪种模式? 从运行时模式开始。因为核心 msg API 是相同的,所以之后切换模式很容易。

在运行时模式下,为每个语言区域生成一个 JavaScript 或 TypeScript 模块。每个模块包含该语言区域的本地化模板。当活动语言区域切换时,会导入该语言区域的模块,并重新渲染所有本地化组件。

运行时模式使切换语言区域非常快速,因为不需要重新加载页面。但与转换模式相比,渲染性能有轻微的性能损耗。

// locales/es-419.ts
export const templates = {
hf71d669027554f48: html`Hola <b>Mundo</b>`,
};

有关运行时模式的完整详情,请参阅运行时模式页面。

在转换模式下,为每个语言区域生成一个单独的文件夹。每个文件夹包含该语言区域下应用的完整独立构建,msg 包装器和所有其他 Lit Localize 运行时代码被完全移除。

转换模式不需要额外的 JavaScript(0 KiB),渲染速度极快。但切换语言区域需要重新加载页面以加载新的 JavaScript 包。

// locales/en/my-element.js
render() {
return html`Hello <b>World</b>`;
}
// locales/es-419/my-element.js
render() {
return html`Hola <b>Mundo</b>`;
}

有关转换模式的完整详情,请参阅转换模式页面。


运行时模式转换模式
输出每个目标语言区域一个动态加载的模块。每个语言区域一个独立的应用构建。
切换语言区域调用 setLocale()重新加载页面
JS 字节数1.27 KiB(压缩 + 最小化后)0 KiB
使模板可本地化msg()msg()
配置configureLocalization()configureTransformLocalization()
优势
  • 更快的语言区域切换。
  • 切换语言区域时更少的边际字节。
  • 更快的渲染。
  • 单一语言区域更少的字节。

lit-localize 命令行工具在当前目录中查找名为 lit-localize.json 的配置文件。复制粘贴下面的示例以快速开始,所有选项的完整参考请参阅 CLI 和配置页面。

如果你使用 JavaScript,请将 inputFiles 属性设置为 .js 源文件的位置。如果你使用 TypeScript,请将 tsConfig 属性设置为 tsconfig.json 文件的位置,并将 inputFiles 留空。

{
"$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json",
"sourceLocale": "en",
"targetLocales": ["es-419", "zh-Hans"],
"tsConfig": "./tsconfig.json",
"output": {
"mode": "runtime",
"outputDir": "./src/generated/locales",
"localeCodesModule": "./src/generated/locale-codes.ts"
},
"interchange": {
"format": "xliff",
"xliffDir": "./xliff/"
}
}
{
"$schema": "https://raw.githubusercontent.com/lit/lit/main/packages/localize-tools/config.schema.json",
"sourceLocale": "en",
"targetLocales": ["es-419", "zh-Hans"],
"inputFiles": [
"src/**/*.js"
],
"output": {
"mode": "runtime",
"outputDir": "./src/generated/locales",
"localeCodesModule": "./src/generated/locale-codes.js"
},
"interchange": {
"format": "xliff",
"xliffDir": "./xliff/"
}
}

运行 lit-localize extract 为每个目标语言区域生成一个 XLIFF 文件。XLIFF 是一种 XML 格式,受大多数本地化工具和服务支持。XLIFF 文件将写入 interchange.xliffDir 配置选项指定的目录。

lit-localize extract

例如,给定以下源代码:

msg('Hello World');
msg(str`Hello ${name}`);
msg(html`Hello <b>World</b>`);

将为每个目标语言区域生成 <xliffDir>/<locale>.xlf 文件:

<!-- xliff/es-419.xlf -->

<trans-unit id="s3d58dee72d4e0c27">
<source>Hello World</source>
</trans-unit>

<trans-unit id="saed7d3734ce7f09d">
<source>Hello <x equiv-text="${name}"/></source>
</trans-unit>

<trans-unit id="hf71d669027554f48">
<source>Hello <x equiv-text="&lt;b&gt;"/>World<x equiv-text="&lt;/b&gt;"/></source>
</trans-unit>

XLIFF 文件可以手动编辑,但更通常的做法是将其发送给第三方翻译服务,由语言专家使用专业工具进行编辑。

将 XLIFF 文件上传到你选择的翻译服务后,你最终会收到回复的新 XLIFF 文件。新 XLIFF 文件看起来与你上传的相同,但在每个 <trans-unit> 中插入了 <target> 标签。

当你收到新的翻译 XLIFF 文件时,将其保存到你配置的 interchange.xliffDir 目录,覆盖原始版本。

<!-- xliff/es-419.xlf -->

<trans-unit id="s3d58dee72d4e0c27">
<source>Hello World</source>
<target>Hola Mundo</target>
</trans-unit>

<trans-unit id="saed7d3734ce7f09d">
<source>Hello <x equiv-text="${name}"/></source>
<target>Hola <x equiv-text="${name}"/></target>
</trans-unit>

<trans-unit id="hf71d669027554f48">
<source>Hello <x equiv-text="&lt;b&gt;"/>World<x equiv-text="&lt;/b&gt;"/></source>
<target>Hola <x equiv-text="&lt;b&gt;"/>Mundo<x equiv-text="&lt;/b&gt;"/></target>
</trans-unit>

使用 lit-localize build 命令将翻译合并回你的应用。此命令的行为取决于你配置的输出模式

lit-localize build

有关每种模式下构建工作方式的详情,请参阅运行时模式转换模式页面。

使用 msg 函数的 desc 选项为你的字符串和模板提供人类可读的描述。大多数翻译工具会向翻译人员显示这些描述,强烈建议提供以帮助解释和说明消息的含义。

render() {
return html`<button>
${msg("Launch", {
desc: "Button that begins rocket launch sequence.",
})}
</button>`;
}

描述在 XLIFF 文件中使用 <note> 元素表示。

<trans-unit id="s512957aa09384646">
<source>Launch</source>
<note from="lit-localize">Button that begins rocket launch sequence.</note>
</trans-unit>

Lit Localize 使用字符串的哈希值自动为每个 msg 调用生成 ID。

如果两个 msg 调用共享相同的 ID,则它们被视为同一条消息,这意味着它们将作为一个单元翻译,并在两个地方替换为相同的翻译。

例如,以下两个 msg 调用在不同的文件中,但由于内容相同,它们将被视为一条消息:

// file1.js
msg('Hello World');

// file2.js
msg('Hello World');

以下内容会影响 ID 生成:

  • 字符串内容
  • HTML 标记
  • 表达式的位置
  • 字符串是否使用 html 标签

以下内容不会影响 ID 生成:

  • 表达式内的代码
  • 表达式的计算值
  • 文件位置

例如,以下所有消息共享相同的 ID:

msg(html`Hello <b>${name}</b>`);
msg(html`Hello <b>${this.name}</b>`);

但以下消息有不同的 ID:

msg(html`Hello <i>${name}</i>`);

注意,虽然提供描述不会影响 ID 生成,但具有相同 ID 但不同描述的多个消息在分析期间会产生错误,以避免提取的翻译单元中出现歧义。以下情况被视为无效

msg(html`Hello <b>${name}</b>`);
msg(html`Hello <b>${name}</b>`, {desc: 'A friendly greeting'});

请确保所有具有相同 ID 的消息也具有相同的描述。

可以通过为 msg 函数指定 id 选项来覆盖消息 ID。在某些情况下这可能是必要的,例如当相同的字符串有多种含义时,因为每种含义在另一种语言中可能有不同的写法:

msg('Buffalo', {id: 'buffalo-animal-singular'});
msg('Buffalo', {id: 'buffalo-animal-plural'});
msg('Buffalo', {id: 'buffalo-city'});
msg('Buffalo', {id: 'buffalo-verb'});