生态产品多个设计器现已发布候选版本

查看生态总览
跳转到内容

国际化 ​

前端文案用 vue-i18n,枚举标签的事实源在后端,时间显示由请求头驱动。三块各有各的规则。

语言包 ​

vue-i18n(legacy: false),中英双包按模块拆文件,分住两处:packages/locales/ 只放 shell 自身的命名空间,业务命名空间在 src/locales/,启动时由 registerLocaleMessages() 合并进同一个 i18n 实例(setupI18n() 之后、app.mount() 之前调用)。

text
packages/locales/langs/zh-CN/    # shell 命名空间
├── common.ts        # 通用(按钮、字段、提示)
├── component.ts     # 公共组件文案
├── menu.ts          # 菜单标题
├── header.ts        # 顶栏
├── page.ts          # 页面壳
└── …

src/locales/langs/zh-CN/         # 业务命名空间
├── identity.ts      # 身份权限模块
├── log.ts           # 日志模块
├── message.ts       # 消息模块
└── …

键名约定 模块.实体.字段或动作:

ts
// src/locales/langs/zh-CN/identity.ts
export default {
  user: {
    page_name: '用户管理',
    col_status: '状态',
    action_reset_password: '重置密码',
  },
}
// 使用:t('identity.user.col_status')

切换语言:useLocale().setLocale('en-US')。

菜单文案的键怎么来 ​

后端 PageDescriptor.I18nKey 规则是 menu.{页面码中的 . 与 - 换成 _}:

页面码后端 I18nKeymenu.ts 里的键
identity.positionmenu.identity_positionidentity_position
log.permission-changemenu.log_permission_changelog_permission_change

缺文案时侧边栏会直接显示键名(如 menu.identity_position)——看到这个就是漏了这一步。

组件库内置文案 ​

日期选择器的星期、分页与下拉的读屏名这类 XiHan.UI 内置文案随应用 locale 切换:App.vue 里 provideXhConfig(useXhUiConfig()) 把语言标记与内建文案交给组件库。文案直接取组件库的内建语言包(@xihan-ui/vue/locale),应用语言与语言包的对应表在 packages/locales/xihan-ui.ts 的 xhLocales;组件库没有语言包的应用语言(目前是 hi-IN)取英文语言包,语言标记仍是应用语言,日期时间照常按该语言排。

要带业务语义的个别文案(如穿梭框箭头钮要念出目标栏标题)在组件实例上经 translations 覆盖,不改全局表。

加新语言时别忘了在 xhLocales 登记对应的语言包,否则业务文案切了、组件文案还停在英文;xh-config.test.ts 会把漏登记的语言报出来。

裸 @ 会白屏 ​

DANGER

语言包文案里出现裸 @(如 联系 @admin)会触发 vue-i18n 的 linked message 语法,抛 Invalid linked format 导致整页白屏。

必须转义成 {'@'}:

ts
// ❌ 白屏
contact: '联系 @admin'
// ✅
contact: "联系 {'@'}admin"

邮箱、社交账号、装饰性符号都是高发区。新增文案前扫一遍。

枚举标签:后端单一事实源 ​

枚举标签不在前端语言包里维护——事实源是后端枚举元数据(Enums.{culture}.json 全量),按请求头 X-Language 返回当前语言的标签。

前端有三条取值路径:

场景用法
Schema 页字段字段声明 dictionaryCode(枚举名或字典码),useSchemaDictionaries 批量拉取注入 field.options;单元格按值映射 label,搜索区自动渲染下拉
非 Schema 的下拉/标签useEnumOptions(enumName, fallback)(~/hooks),返回随语言/数据响应式更新的 computed
静态兜底元数据为空(未加载/未部署)时回退传入的 fallback 静态选项,保证绝不出现空下拉

切语言要响应式 ​

拉取由全局 useEnumService 并发去重,切 locale 时整库重取一次。useSchemaDictionaries / useEnumOptions 只读响应式状态——免刷新即随语言更新,各下拉不各自监听 locale,避免重复请求。

两个易错点

  1. business.ts 里的 *_OPTIONS 常量是写死中文的,仅作兜底,不要当成事实源使用。
  2. 选项的 value 要用枚举成员名,不是整数。后端实例数据经 JsonStringEnumConverter 序列化为成员名字符串(如 "Enabled"),只有用成员名才能与表格行数据匹配;整数值放在 valueText 备用。

时区 ​

  • 前端在请求拦截器里发 X-Timezone 头(用户已选时区优先,否则跟随浏览器 Intl)。
  • 后端存储恒 UTC,输出时按该头换算。
  • 用户可在偏好里选时区。

「时间显示差几小时」的排查顺序:请求有没有带这个头 → 用户选的时区对不对 → 后端存的是不是 UTC。

加一门新语言 ​

步骤做什么
1packages/locales/langs/ 与 src/locales/langs/ 下各加一套语言目录,按模块补齐文案;插件模块在 src/modules/<模块>/locales/<locale>.ts 各补一份
2在 packages/constants/app.ts 的 SUPPORTED_LOCALES、packages/locales/index.ts 的 messages 与 src/locales/index.ts 里登记新语言,并在 LocaleSwitcher.vue 的 LOCALES、LocaleFlag.vue 的国旗、packages/locales/xihan-ui.ts 的 xhLocales(组件库有该语言包时)各加一项
3后端补一份 Enums.{culture}.json 与业务资源文件(`Localization/ApiResponse
4后端每个环境的 XiHan:Localization:SupportedCultures 加上该文化——文化中间件只接受列表内的 X-Language,不在列内一律回退默认文化;appsettings.Production.json 不入库,要在服务器上单独改

第 3、4 步最容易漏——前端切了语言,接口返回的 message 和枚举标签却没变,要么是后端资源没补,要么是生产环境的 SupportedCultures 没登记(de-DE 上线时就漏过一次)。

相关页面 ​

Released under The MIT License