跳转到内容

国际化 ​

默认行为:组件内建的文本是英文,其余全部跟随 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,可按子树覆盖),自定义元素侧是 setXhConfig(模块级,整个文档一份)。

全局注入一次 ​

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

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

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

运行时切语言 ​

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

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

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

语言包是一个普通对象(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 以局部切换语言。

  • createToastService / createDialogService 自带宿主应用,无法接入组件树中的注入。它们的按钮文案使用各自创建时的选项(okText 默认 OK、cancelText 默认 Cancel、translations),需要整个服务与应用同语言时通过 config 传入:

    ts
    const dialog = createDialogService({
      config: { locale: "zh-CN", translations: { dialog: { close: "关闭" } } },
      okText: "确定",
      cancelText: "取消",
    });

    config 是一份普通的 XhConfig,服务会在自己的子树中 provideXhConfig 一次;未提供时使用内建默认。

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

  • 自定义元素无法接入 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 的全局值 → 组件内建默认(文案为英文,locale 再向宿主浏览器读取一次),translations 逐键合并。切换语言时再次调用,已挂载的元素随之重渲染:

ts
setXhConfig({ locale: "en-US", translations: enUS });

两处差别:

  • setXhConfig 是整份替换,不做深合并;修改一处需要传入整份配置。Vue 侧语义相同。
  • 它是模块级的,整个文档一份,没有 Vue 那种只在某棵子树切换语言的能力。需要分区时,把该部分渲染为 Vue 组件,或逐实例设置 .translations。

Released under The MIT License