转换模式

在 Lit Localize 转换模式下,系统会为每个区域设置生成一个单独的文件夹。每个文件夹包含该区域设置的应用程序的完整独立构建版本,所有运行时的 @lit/localize 代码都被移除:

  • msg 调用被替换为每个区域设置中字符串或模板的静态本地化版本。
  • str 标签被移除。
  • @lit/localize 导入被移除。
  • 模板会被优化,通过尽可能将表达式合并到父模板中来移除不必要的表达式。

例如,给定以下源代码:

// src/launch-button.js
import {msg} from '@lit/localize';

render() {
return html`<button>${msg('Launch rocket')}</button>`
}

将会生成以下文件:

// locales/en/launch-button.js
render() {
return html`<button>Launch rocket</button>`
}

// locales/es-419/launch-button.js
render() {
return html`<button>Lanza cohete</button>`
}

在你的 lit-localize.json 配置中,将 mode 属性设置为 transform,并将 output.outputDir 属性设置为你希望生成本地化应用程序文件夹的位置。有关更多详情,请参阅转换模式设置

在你的 JavaScript 或 TypeScript 项目中,可以选择调用 configureTransformLocalization,传入一个包含以下属性的对象:

  • sourceLocale: string:源模板编写所用的区域设置。指定为区域设置代码(例如:"en")。

configureTransformLocalization 返回一个包含以下属性的对象:

  • getLocale:返回活动区域设置代码的函数。

例如:

import {configureTransformLocalization} from '@lit/localize';

export const {getLocale} = configureTransformLocalization({
sourceLocale: 'en',
});

在转换模式下,活动区域设置由你加载的 JavaScript 包决定。页面加载时如何确定要加载哪个包由你决定。

例如,如果你的应用程序的区域设置反映在 URL 路径中,你可以在 HTML 文件中包含一个内联脚本,检查 URL 并插入适当的 <script> 标签:

在动态选择脚本名称时,始终验证你的区域设置代码。下面的例子是安全的,因为只有匹配我们已知区域设置代码的脚本才能被加载,但如果我们的匹配逻辑不够精确,可能会导致插入不安全 JavaScript 的错误或攻击。

import {allLocales} from './generated/locales.js';

const url = new URL(window.location.href);
const unsafeLocale = url.searchParams.get('locale');
const locale = allLocales.includes(unsafeLocale) ? unsafeLocale : 'en';

const script = document.createElement('script');
script.type = 'module';
script.src = `/${locale}.js`;
document.head.appendChild(script);

为了获得更好的性能,你可以在服务器上将适当的脚本标签静态渲染到你的 HTML 文件中。这样浏览器可以尽早开始下载你的脚本。

在转换模式下,setLocale 函数不可用。相反,重新加载页面,这样下一次加载将选择不同的区域设置包。

例如,这个 locale-picker 自定义元素在从下拉列表中选择新的区域设置时加载一个新的 URL:

import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
import {getLocale} from './localization.js';
import {allLocales} from './generated/locales.js';

@customElement('locale-picker');
export class LocalePicker extends LitElement {
render() {
return html`
<select @change=${this.localeChanged}>
${allLocales.map(
(locale) =>
html`<option value=${locale} selected=${locale === getLocale()}>
${locale}
</option>`
)}
</select>
`;
}

localeChanged(event: Event) {
const newLocale = (event.target as HTMLSelectElement).value;
const url = new URL(window.location.href);
if (url.searchParams.get('locale') !== newLocale) {
url.searchParams.set('locale', newLocale);
window.location.assign(url.href);
}
}
}
import {LitElement, html} from 'lit';
import {getLocale} from './localization.js';
import {allLocales} from './generated/locales.js';

export class LocalePicker extends LitElement {
render() {
return html`
<select @change=${this.localeChanged}>
${allLocales.map(
(locale) =>
html`<option value=${locale} selected=${locale === getLocale()}>
${locale}
</option>`
)}
</select>
`;
}

localeChanged(event) {
const newLocale = event.target.value;
const url = new URL(window.location.href);
if (url.searchParams.get('locale') !== newLocale) {
url.searchParams.set('locale', newLocale);
window.location.assign(url.href);
}
}
}
customElements.define('locale-picker', LocalePicker);

如果你使用 Rollup,并且希望使用集成解决方案而不是单独运行 lit-localize build 命令,可以在 Rollup 配置中从 @lit/localize-tools/lib/rollup.js 导入 localeTransformers 函数。

这个函数生成一个 {locale, transformer} 对象数组,你可以结合 transformers 选项和 @rollup/plugin-typescript 为每个区域设置生成单独的包。

如果你编写 JavaScript,不用担心这里看到使用 TypeScript 编译器。Lit Localize 依赖 TypeScript 编译器来解析、分析和转换你的源代码,但它也可以处理普通的 JavaScript 文件!

以下 rollup.config.mjs 为每个区域设置生成一个压缩包到 ./bundled/<locale>/ 目录:

import typescript from '@rollup/plugin-typescript';
import {localeTransformers} from '@lit/localize-tools/lib/rollup.js';
import resolve from '@rollup/plugin-node-resolve';
import {terser} from 'rollup-plugin-terser';

// 默认从 ./lit-localize.json 读取配置。
// 传递路径可以从其他位置读取配置。
const locales = localeTransformers();

export default locales.map(({locale, localeTransformer}) => ({
input: `src/index.ts`,
plugins: [
typescript({
transformers: {
before: [localeTransformer],
},
}),
resolve(),
terser(),
],
output: {
file: `bundled/${locale}/index.js`,
format: 'es',
},
}));
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import {terser} from 'rollup-plugin-terser';
import summary from 'rollup-plugin-summary';
import {localeTransformers} from '@lit/localize-tools/lib/rollup.js';

// 默认从 ./lit-localize.json 读取配置。
// 传递路径可以从其他位置读取配置。
const locales = localeTransformers();

export default locales.map(({locale, localeTransformer}) => ({
input: `src/index.js`,
plugins: [
typescript({
transformers: {
before: [localeTransformer],
},
// 指定要生成的 ES 版本和模块格式。参见
// https://www.typescriptlang.org/docs/handbook/tsconfig-json.html
tsconfig: 'jsconfig.json',
// Rollup 打包之前,转换后的模块将被发送到的临时目录。
outDir: 'bundled/temp',
// @rollup/plugin-typescript 始终只匹配 ".ts" 文件,无论
// 我们的 jsconfig.json 中有什么设置。
include: ['src/**/*.js'],
}),
resolve(),
terser(),
summary({
showMinifiedSize: false,
}),
],
output: {
file: `bundled/${locale}/index.js`,
format: 'es',
sourcemap: true,
},
}));