Skip to content

多语言(i18n)

TokUI 的可见文案分两层,多语言职责必须切开:

内容归谁管怎么切
L1 组件 chromearia-label、placeholder、空态、默认按钮字、分页总数、日期星期等"骨架文案"TokUIsetLocale()
L2 业务文案DSL 里的 tt:/l:/tx:/opt: 等属性值(卡片标题、表单标签、选项文本)你的应用后端按 locale 发不同 DSL

关键:TokUI 不翻译 DSL 文本[btn tx:提交] 的「提交」原样渲染。框架只负责 L1 骨架文案的多语言——内置 zh-CN + en-US,其余语种可注入。

切换语言

1. 构造时指定(推荐)

js
import { TokUI } from '@jboltai/tokui';
import '@jboltai/tokui/css';

const ui = new TokUI({
  container: '#app',
  locale: 'en-US',          // 'zh-CN' | 'en-US' | 'en' | 'zh' | ...
});

2. 运行时切换

js
import { setLocale, getLocale } from '@jboltai/tokui';

setLocale('en-US');         // 接受别名:'en' / 'en-GB' → 'en-US','zh' / 'zh-TW' → 'zh-CN'
getLocale();                // → 'en-US'

3. 自动探测(默认行为)

不传 locale 时,按 document.documentElement.langnavigator.languagezh-CN 顺序探测。给 <html lang="en"> 即可让全站 chrome 走英文。

已渲染的 DOM 不会自动刷新——setLocale 只影响后续渲染。要热切就调 ui.rerender()(见下节),库会就地重画已渲染内容。

在应用中接入(就地切换)

切换语言后让已渲染的 DOM 即时跟随,用实例方法 rerender()。TokUI 实例自动缓存最近一次 render() / feed() 的 DSL,rerender() 清容器后按当前 locale/theme 重新一次性渲染——应用无需自己缓存 DSL。

js
import { TokUI, setLocale } from '@jboltai/tokui';
import '@jboltai/tokui/css';

const ui = new TokUI({ container: '#app', locale: 'zh-CN' });

ui.render('[pagination total:5 count:42 show-total clk:noop]');

// 切语言 + 就地刷新(无网络、不清周围 DOM)
setLocale('en-US');
ui.rerender();   // → 分页/共42条 变 Pagination/42 items

流式渲染同样适用——实例累积 feed 的 chunk,rerender() 重放完整内容:

js
ui.startStream();
controller.on('chunk', c => ui.feed(c));   // _lastDsl 自动累积
controller.on('end',  () => ui.endStream());

// 流结束后切语言
setLocale('en-US');
ui.rerender();

配合 localStorage 持久化(首次访问恢复上次语言):

js
const LANG_KEY = 'app-lang';
const ui = new TokUI({
  container: '#app',
  locale: localStorage.getItem(LANG_KEY) || 'zh-CN',   // 启动即用上次语言
});

function switchLang(locale) {
  setLocale(locale);
  localStorage.setItem(LANG_KEY, locale);
  ui.rerender();
}

多容器场景(如聊天,每条消息一个实例):rerender()实例级——每个容器对应一个 TokUI 实例,切换时遍历各实例调用:

js
const instances = [];   // 每条消息 push 一个 ui
function switchLang(locale) {
  setLocale(locale);
  instances.forEach(ui => ui.rerender());
}

rerender() 无缓存内容(从未渲染过)或无容器时返回 false。流式进行中调用会按已到达的 DSL 重画,一般待 endStream() 后调用。

业务文案(L2,DSL 里的 tt:/l: 等)rerender() 不会翻译——它们是 DSL 字面量。要切业务文案,后端按 locale 发不同 DSL,或应用层在 feed 前翻译(见下节)。

注册新语种

内置仅 zh-CN + en-US。其余语种按需注入,可只传缺失的 key(未覆盖的回退 zh-CN):

js
import { registerLocale, setLocale } from '@jboltai/tokui';

registerLocale('ja-JP', {
  'common.close': '閉じる',
  'common.ok': 'OK',
  'pagination.aria': 'ページネーション',
  'pagination.totalCount': '全{count}件',
  'chart.empty': 'データなし',
  // ... 未列出的 key 自动回退 zh-CN
});

setLocale('ja-JP');

registerLocale 增量合并:多次调用对同一 locale 累加,不覆盖未传的 key。

实时演示

下面这份 DSL 的卡片标题与选项是业务文案(固定中文),但分页总数、aria-label、空态、select placeholder 是组件 chrome——用右上角的语言开关(setLocale)切换,chrome 文案即时跟随:

data-tokui-theme="default"
1[card tt:"分页 / 选择 / 空态(chrome 随语言切换)"]
2 [pagination total:5 count:42 show-total clk:noop]
3 [select l:城市 id:i18nCity]
4 [opt v:bj tx:北京]
5 [opt v:sh tx:上海]
6 [/select]
7 [chart t:bar]
8[/card]
加载 TokUI…
TokUI DSL · 左代码右渲染

内置文案目录(L1)

约 80 个 key,按 组件.位置 语义命名。完整清单见源码 src/core/i18n.jsSTRINGS。常用分组:

分组示例 key中文英文
common.*common.close / common.ok / common.loading关闭 / 确定 / 加载中Close / OK / Loading
pagination.*pagination.totalCount共{count}条{count} items
lightbox.*lightbox.zoomIn / lightbox.rotateLeft放大 / 左旋90°Zoom in / Rotate 90° left
chart.*chart.empty / chart.seriesDefault暂无数据 / 系列No data / Series
datepicker.*datepicker.title / datepicker.weekday.1{y}年{m}月 / 一{m}/{y} / Mon
status.*status.running / status.done运行中 / 完成Running / Done
command.*command.placeholder / command.noResult输入关键词搜索... / 没有找到匹配结果Type to search... / No results found
bubble.*bubble.you / bubble.ai / bubble.system / bubble.assistant你 / AI / 系统 / 助手You / AI / System / Assistant

插值用 {name} 占位符,如 t('pagination.totalCount', { count: 42 })共42条 / 42 items

业务文案(L2)怎么做多语言

业务文案在 DSL 里,由后端生成。三种常见范式:

1. 后端按 locale 分发(推荐)

js
// 后端根据请求的 Accept-Language 或用户设置,发对应语言的 DSL
const dsl = locale === 'en'
  ? '[card tt:"Order Detail"][p Total: ¥128][/card]'
  : '[card tt:"订单详情"][p 合计: ¥128][/card]';
ui.render(dsl);

2. 前端词表预翻译

维护一份业务词表,feed 前替换 DSL 中的 key:

js
const BIZ = {
  'en': { '订单详情': 'Order Detail', '合计': 'Total' },
  'ja': { '订单详情': '注文詳細', '合计': '合計' },
};
function translate(dsl, locale) {
  const dict = BIZ[locale] || {};
  return Object.keys(dict).reduce((s, k) => s.split(k).join(dict[k]), dsl);
}
ui.feed(translate(chunk, getLocale()));

3. 后端直接用 locale 中立的 key + 前端翻译(复杂场景,需约定 key 规范)

TokUI 不强加任何一种——DSL 是数据,业务翻译策略由你的架构决定。

性能

t() 是单次对象属性查找 + 可选 {name} 替换;setLocale 仅替换内部字典引用(O(1)),单态查表可被引擎内联。相对硬编码字面量,渲染期开销不可测量。

下一步