生态产品多个设计器现已发布正式版

查看生态总览
跳转到内容

国际化 ​

默认行为:组件自己不写文案,界面文字一律取语言包,没配语言包时取英文语言包 enUS;其余全部跟随 locale,locale 未提供时跟随宿主浏览器。

具体为四条:

  • 文案(关闭按钮的读屏名称、分页的翻页说明、上传区的提示、色阶两端的 Less / More、表单校验报错、对话框服务的按钮、快捷键的键名与键帽字等)一律取自语言包。库附带十种常用语言的内建语言包,整份交给全局配置即可;个别说法经 translations 覆盖。
  • 日期时间类组件的月份名、星期名、段位顺序、周首日与默认格式串全部由 locale 决定,不写死。
  • locale 的解析链是实例 props → 全局配置 → 宿主的 navigator.language → en-US,calendar / date-picker / date-field / heatmap / time 都使用它。因此中文浏览器中不做任何配置也显示中文日期;服务端渲染期或显式 Scope 没有活动 Window 时读不到宿主语言,使用 en-US,不借用其他页面的 Window。
  • 例外一处:time-picker / time-field 的小时制不从宿主推断。hourCycle 与 locale 都未提供时始终为 24 小时制,需要 12 小时制时提供其中之一。

服务端渲染的应用应在服务端与客户端显式注入同一个 locale。如果服务端依赖无 DOM 时的 en-US、客户端又依赖 navigator.language,两端可能生成不同的月份、星期与段位顺序,形成水合差异。

周首日跟随 locale

一周从周日还是周一开始由 locale 决定:en-US 周日起,zh-CN 与多数欧洲语言周一起。 未配置 locale 时它跟随读者的浏览器变化,同一份代码在两台机器上排出的月历首列不同。 要固定为一种排法,显式传入 locale(实例上传入,或全局配置一次),不依赖默认值。

各适配器都有一处全局出口,语义一致:Vue 侧是 provideXhConfig(走 provide/inject,可按子树覆盖),React 侧是 XhConfigProvider(同样可按子树覆盖),自定义元素侧是 setXhConfig(模块级,整个文档一份)。

内建语言包 ​

每个适配器都有一个 locale 子入口,导出十份语言包:

导出名语言locale
zhCN简体中文zh-CN
zhTW繁體中文(台灣)zh-TW
enUSEnglishen-US
jaJP日本語ja-JP
koKR한국어ko-KR
frFRFrançaisfr-FR
deDEDeutschde-DE
esESEspañoles-ES
ptBRPortuguês (Brasil)pt-BR
ruRUРусскийru-RU

一份语言包就是一份全局配置 { locale, translations },整份交给全局出口:

ts
// Vue:应用根组件的 setup 中
import { provideXhConfig } from "@xihan-ui/vue";
import { zhCN } from "@xihan-ui/vue/locale";

provideXhConfig(zhCN);
tsx
// React:包在应用根上
import { XhConfigProvider } from "@xihan-ui/react";
import { zhCN } from "@xihan-ui/react/locale";

<XhConfigProvider config={zhCN}>
  <App />
</XhConfigProvider>;
ts
// 自定义元素:注册元素之后调用一次
import { setXhConfig } from "@xihan-ui/web-components";
import { zhCN } from "@xihan-ui/web-components/locale";

setXhConfig(zhCN);
  • 语言包同时带着 locale,日期时间类组件的月份名、星期名与周首日随之切换。只想换文案、日期仍跟随宿主浏览器时,只交 translations:provideXhConfig({ translations: zhCN.translations })。
  • 还要配其他项时先展开再加:provideXhConfig({ ...zhCN, size: "sm" })。个别文案要换成自己的说法,在实例上写对应的 translations 键即可,它压在语言包之上;要在全局改,用各适配器导出的 mergeXhConfig(zhCN, { translations: { dialog: { close: "关掉" } } }) 合并出一份新配置再交出去。
  • enUS 与其余九份同形,是完整的英文语言包。组件没配语言包时用的就是它;运行时从其他语言切回英文时也交它。
  • 语言包放在子入口而不是主入口:每份都是一整张文案表,只引入用到的那几份,其余由打包器摇掉。

语言包覆盖组件的每一条文案。以下几类刻意不收,因为它们没有缺省,或缺省取自实例自己的内容,语言包给了就不只是换语言,而是改了行为:

  • 作者自写的可见文字与标签:approval 的 approve / deny / notePlaceholder、clipboard.copy、download-trigger.trigger、mention.input、menu.content、prompt-input.input、question-flow 的 notePlaceholder / skip / continue / send、steps.list、tool-call.ranFor、tour 的 next / finish。这些需要时在实例上提供。
  • 条目自己的名字:拖拽播报里的 item(sortable / table / tabs / tree)缺省取条目的文字。
  • 由 Intl 按 locale 给出的说法:timestamp.justNow。

表单校验报错的模板在 translations.form 里({name} 换成字段名,{min} / {max} 换成规则界限):规则自己的 message 最优先,表单的 validateMessages 逐条压在语言包之上。

同理,实例自己的内容优先于任何 translations:直角坐标图数据表的列名先取轴标题,轴没有标题时才用 keyLabel / valueLabel;热力图发散色阶的对照条两端恒写数值,legendLow / legendHigh 只换顺序色阶两端的词;评分星里放了作者自己的内容(文字或带名字的节点),或作者直接在星上写了名字时以作者的为名,rating.item(语言包里的或实例给的)只在都没有时用。

要补一种语言,以任一份内建语言包为样板写一份同形的 XhLocale 对象,交给全局出口的方式不变。

全局注入一次 ​

ts
import { provideXhConfig } from "@xihan-ui/vue";

// 应用根组件的 setup 中
provideXhConfig({
  locale: "zh-CN",
  translations: {
    breadcrumb: { root: "面包屑" },
    pagination: { root: "分页", prevTrigger: "上一页", nextTrigger: "下一页" },
    dialog: { close: "关闭" },
    notification: { close: "关闭" },
  },
});

取值优先级从高到低:实例 props → 全局注入 → 英文语言包 enUS(locale 则向宿主浏览器读取一次)。translations 按键合并:全局提供整包,个别实例只覆盖需要修改的键。

运行时切语言 ​

传入 ref 或 getter,切换时使用这些文案的组件随之重渲染:

ts
import { provideXhConfig } from "@xihan-ui/vue";
import { enUS, zhCN } from "@xihan-ui/vue/locale";
import { computed, ref } from "vue";

const lang = ref<"zh" | "en">("zh");
provideXhConfig(computed(() => (lang.value === "zh" ? zhCN : enUS)));

语言包是一个普通对象(XhLocale,可直接当 XhConfig 用),按组件名收纳各自的 Translations;每个组件页的 Props 表列有它的文案键。

接受 locale 的组件 ​

locale 一律接受 BCP 47 语言标记(如 zh-CN、en-US),供以下组件使用:

组件locale 决定的内容未提供时
calendar / date-picker月份名、星期名、周首日、标题中年月的顺序宿主语言,再退 en-US
date-field年月日三段的顺序、上午 / 下午的写法宿主语言,再退 en-US
heatmap月份名、星期名宿主语言,再退 en-US
time用词(zh 开头使用中文,其余英文)与默认格式串宿主语言,再退 en-US
time-picker / time-field上午 / 下午的写法、推断小时制不读取宿主:小时制固定 24,上午 / 下午写 AM/PM

实例上显式提供 locale 的不受全局影响。

作用域与边界 ​

  • 注入走 Vue 的 provide/inject:在应用根 provide 即全应用默认,也可以只在某个子树 provide 以局部切换语言。

  • createNotificationService / createDialogService 自带宿主应用,无法接入组件树中的注入。它们的按钮与报错文字先取创建时的选项(okText / cancelText / actionErrorText),没给的取服务 config 里 translations.dialog 的 ok / cancel / actionError,再退英文语言包。需要整个服务与应用同语言时通过 config 传入:

    ts
    import { zhCN } from "@xihan-ui/vue/locale";
    
    // 确定 / 取消与失败提示都随 zhCN 换成中文;个别服务要别的说法时再传 okText 等选项
    const dialog = createDialogService({ config: zhCN });

    config 是一份普通的 XhConfig,内建语言包可直接传入,服务会在自己的子树中 provideXhConfig 一次;未提供时用英文语言包。自定义元素侧的服务没有 config,按宿主所在处的 setXhConfig 取。

  • 不注入时组件按原路径运行,没有额外开销。

  • 自定义元素无法接入 Vue 的注入,使用它自己的全局出口,见下文。

文档站的示例为什么逐实例传入

每条示例都是孤立片段,没有应用根可以注入,因此一律写成 :translations="{ … }"。 真实应用按上文全局配置一次即可,实例上只保留需要单独覆盖的键。

自定义元素侧 ​

自定义元素无法获取 Vue 的 provide/inject,文案是对象,只能通过 property 而不能通过 attribute 设置,因此逐实例设置是原有的唯一路径。setXhConfig 提供一处全局出口:

ts
import { setXhConfig } from "@xihan-ui/web-components";
import { defineXhElements } from "@xihan-ui/web-components/define";

defineXhElements();
setXhConfig({
  locale: "zh-CN",
  translations: {
    dialog: { close: "关闭" },
    pagination: { root: "分页", prevTrigger: "上一页", nextTrigger: "下一页" },
  },
});

取值优先级与 Vue 侧一致:元素上的 property → setXhConfig 的全局值 → 英文语言包 enUS(locale 则向宿主浏览器读取一次),translations 逐键合并。切换语言时再次调用,已挂载的元素随之重渲染:

ts
import { enUS } from "@xihan-ui/web-components/locale";

setXhConfig(enUS);

两处差别:

  • setXhConfig 是整份替换,不做深合并;修改一处需要传入整份配置。Vue 侧语义相同。
  • 它是模块级的,整个文档一份。只在某棵子树切换语言时,用 <xh-config> 包住那一部分:locale 写在标签上,translations 用 property 设置,与全局配置逐键合并,用法见全局配置的「自定义元素侧」。

Released under The MIT License