国际化

把语言包放在 src/locales,Gemkit 会生成 i18n 实例、带语言前缀的 URL 和服务端语言识别。

src/locales/
  en.json    默认语言,/about
  zh.json    /zh/about

一个 JSON 文件就是一种语言,文件名是语言代码。默认语言默认没有路径前缀,其他语言使用 /<lang>。

配置

pluginGemkit({
  defaultLanguage: 'en',
  prefixDefaultLanguage: false,
});
选项 说明
defaultLanguage 默认语言。存在 en 时默认是 en,否则是文件名排序后的第一个语言。
prefixDefaultLanguage 默认语言也使用前缀(/en/about),没有前缀的路径会跳转到它。

默认语言必须有对应的语言文件,否则构建失败。

每个页面都存在于所有语言中。src/pages 中不需要重复页面文件:/zh/about 使用同一个 src/pages/about.ts 模块和 zh 语言包渲染。

生成的模块

$gemkit/i18n 导出:

导出 说明
i18n I18n 实例,类型来自默认语言包。
languages 语言代码列表,按文件名排序。
defaultLanguage 默认语言代码。
prefixDefaultLanguage 默认语言是否有前缀。
localizePath (lang, path?) => string,加上语言前缀的路径。
import { languages, localizePath } from '$gemkit/i18n';

html`<p>${i18n.get('title')}</p>`;

// 使用普通链接切换语言,页面会重新加载。
html`${languages.map((lang) => html`<a href=${localizePath(lang)}>${lang}</a>`)}`;

i18n 是 Gem 的 I18n 实例。变量替换、后备语言和缓存见 Gem 的 i18n 文档。

页面内容

页面、布局和元素通过 i18n.get 读取翻译。语言包在渲染前加载,所以服务端首屏和水合使用同一种语言:

@page({ title: () => i18n.get('about') })
export default class AboutPage extends GemElement {
  @template()
  #content = () => html`<h1>${i18n.get('about')}</h1>`;
}

语言由 URL 决定,而不是 navigator.language。没有前缀的请求使用默认语言,未知前缀是一个普通的 404 (或者被当作默认语言路径的一部分)。

切换语言

使用普通的 <a href> 和 localizePath。切换语言会重新加载页面:base path、语言包和所有内容都会改变, 重新加载最简单,也和服务端渲染的结果一致。

html`<a href=${localizePath('zh')}>中文</a>`;

动态路径

paths 会针对每种语言调用一次,所以同一个路由在不同语言中可以有不同数量的页面:

export function paths({ lang }: { lang: string }) {
  return posts[lang].map(({ slug }) => ({ slug }));
}

开启 prefixDefaultLanguage 时,Gemkit 还会为没有前缀的路径生成跳转页面, 静态托管不需要服务器也能跳转到默认语言。

模板

服务端会把 <html lang> 设置为当前语言。只有在需要给未渲染的文档提供默认值时, 才需要把它写进 src/index.html,渲染的页面会替换它。

API 路由

API 路由不分语言。无论当前语言是什么,它们始终使用不带语言前缀的路径,例如 /api/likes/1。