简体中文
国际化
组件内部自带的提示文本(如搜索占位符、复制成功提示、清空按钮等)走统一的语言环境管理,没有把中文或英文硬编码在每个组件里。随包默认内置的语言是英文(en)和简体中文(zh-CN)。在 TypeScript 类型定义中,AstryxLocale 指的就是这两种内置语言代码。
通常只需在应用的顶层根组件提供一次语言配置,下层所有子组件就可以自动通过 useAstryxLocale 和 useAstryxMessages 读取当前的语言文本。未配置时,默认使用英文。
ts
import { provideAstryxLocale } from '@astryx-vue/core/locale'
// 全局配置为简体中文
provideAstryxLocale('zh-CN')与国际化相关的常用函数(如 provideAstryxLocale、useAstryxLocale、useAstryxMessages、useLocale、useTranslator、normalizeLocale、loadAstryxCatalog 和 useCollator)均可以从 @astryx-vue/core/locale 引入,在包根 @astryx-vue/core 也有重新导出。仅日历相关的底层格式化工具 formatCalendarMonth 和 weekdayNames 专属于 @astryx-vue/core/locale。
normalizeLocale 函数会自动标准化常见的语言标签:
- 把
zh、zh-CN、zh-Hans等归一化为zh-CN,对上内置的简体中文文案; - 其他标签只规范大小写,地区信息会保留,例如
en-gb变成en-GB、ja-jp变成ja-JP。日期、数字、排序因此按这个地区的习惯来; - 内置文案只分「简体中文」和「其他」两种:
en、en-GB、fr-FR这些没有交messages的语言,组件文字都用英文; - 空值或无法解析的标签会自动回退到英文
en。
在组件之外读语言和翻译(useLocale、useTranslator)
组件里的格式化(日期、数字、相对时间、排序)都读最近一层 InternationalizationProvider 的语言,不读 navigator.language。你自己写的格式化函数也应该这样:
ts
import { useLocale, useTranslator } from '@astryx-vue/core/locale'
const { locale } = useLocale()
const { t } = useTranslator()
// 语言变了,这两个值会跟着变
const label = computed(() => new Intl.DateTimeFormat(locale.value).format(date))
const announce = () => t('@astryx.pagination.pageAnnounce', { current: 3 })t 收的是原版的 @astryx.* 键,返回当前语言的成品文案。键认不出来时原样返回这个键,方便一眼看出写错了。在事件回调里也可以直接调用。
支持中文和英文以外的更多语言
原版 Astryx 在 packages/core/locales 目录下存放了约 30 份基于 React Intl 格式的语言 JSON 文件(包含 ja-JP、zh-TW、ar-SA 等)。我们将这些官方多语言资源完整同步在包内的 locales/ 目录下。
你可以借助 loadAstryxCatalog 函数,将这些原版 JSON 转换映射为组件库可用的 AstryxMessages 结构,然后作为第二个参数传递给 provideAstryxLocale:
ts
import { loadAstryxCatalog, provideAstryxLocale } from '@astryx-vue/core/locale'
import catalog from '@astryx-vue/core/locales/ja-JP.json'
// 加载日语语言包
const loaded = loadAstryxCatalog('ja-JP', catalog)
provideAstryxLocale(loaded.locale, loaded.messages)加载器具备容错处理:
- 如果原版 JSON 中某些键名在 Vue 版中未被使用,会保存在返回对象的
unmapped属性中,避免数据丢失; - 如果某项文本在 JSON 中缺失,会自动回退到内置英文,涉及的字段路径记录在
fallbackFields中; - 如果组件需要某项文本但外部 JSON 根本没有提供,会记录在
missingKeys中。
提示:内置英文和原版
en.json逐字一致,pnpm check:i18n-catalog会守着这一点。内置的zh-CN是我们自己写的中文,比原版zh-CN.json更口语一些,请不要用原版的那份去覆盖它。其他语言才建议通过该加载器引入。如果只调用
provideAstryxLocale('ja-JP')而没有传入转换后的messages,那么组件内部的日期和字母排序会按日语规则运行,但组件自带的提示语仍会保持英文;同理,传入zh-TW也不会自动偷懒降级为简体中文。
如果你是维护者,需要同步原版最新语言包,运行 node scripts/vendor-astryx-locales.mjs 即可,它默认拉原版 main 的最新提交;需要复现某一次抓取时,把提交号当参数传进去。实际用到的提交号写在 locales/SOURCE.json,来源与开源许可证说明记录在 packages/core/THIRD_PARTY_NOTICES.md 中。
拉完后跑一次 pnpm check:i18n-catalog(pnpm check 里也会跑)。它会检查:
- 内置
en和zh-CN的每个字段都有,而且形状一样; - 内置文案用上了原版的每个占位符,内置英文和原版英文逐字一致;
- 29 种语言的每句译文都能解析,占位符的名字和写法(
{name}、number、plural、select)和英文原文一致,没有过期的键; - 原版新增的每个键,要么已经接进
packages/core/src/locale/upstream/,要么在scripts/i18n-catalog/coverage.mjs里写明了为什么暂不接入。
按当前语言排序字符串(useCollator)
在不同国家和语言下,文字的排序习惯差异很大(例如拼音排序与注音、假名排序)。useCollator 函数返回一个基于当前语言环境的 Intl.Collator 实例,并包装在 Vue 的 computed 中。未设置语言时默认按英文处理:
ts
import { useCollator } from '@astryx-vue/core/locale'
const collator = useCollator({ numeric: true })
// 按照当前语言环境自然排序
items.sort((a, b) => collator.value.compare(a.name, b.name))区分组件内置文案与业务文案
AstryxMessages 仅管理组件库内置的通用文案(如「复制」、「清除」等)。你在业务中自己写进组件的文字(例如 <Button label="提交表单" /> 传入的 label),是由你的业务代码完全掌控的,切换组件库语言不会去修改你的业务文字。
文档站顶部切换的中英文,是通过 VitePress 路由实现的两个完全独立的页面树(分别位于根路径 / 和 /en/),属于文档网站层面的多语言;而文档中的实时组件示例,则会自动根据当前页面的文档语言调用 provideAstryxLocale。
从右往左排版与伪语言测试
阿拉伯语、希伯来语、波斯语这些语言从右往左写。这件事组件库已经做了大半,但有两步得你自己做。
第一步:告诉组件方向
InternationalizationProvider 有一个 dir 属性,取 'ltr' 或 'rtl'。不传的话,方向由 locale 推出来:ar-SA、he 得到 rtl,其余得到 ltr。
推导靠 getLocaleDirection()。它先问浏览器要 Intl.Locale 的书写方向;浏览器还不支持时,退回一张内置的从右往左语言清单(ar、he、fa、ur、ps、sd 等)。认不出来的标签按 ltr 处理,不会报错。
vue
<script setup lang="ts">
import { InternationalizationProvider } from '@astryx-vue/core/InternationalizationProvider'
</script>
<template>
<!-- 方向由 locale 推出来 -->
<InternationalizationProvider locale="ar-SA">
<App />
</InternationalizationProvider>
<!-- 语言是英文,但强制按从右往左排版:用来测布局 -->
<InternationalizationProvider locale="en" dir="rtl">
<App />
</InternationalizationProvider>
</template>自己在组合式函数里要读方向,用 useLocaleDirection(),它返回一个 ref,语言变了会跟着变:
ts
import { useLocaleDirection } from '@astryx-vue/core/InternationalizationProvider'
const direction = useLocaleDirection()
// 'ltr' 或 'rtl'第二步:自己设置 DOM 的 dir
dir 属性只决定 Astryx 组件读到的方向,它不会去改 DOM。这一步必须你来做,通常写在 <html> 上:
ts
import { getLocaleDirection } from '@astryx-vue/core/InternationalizationProvider'
// 浏览器里切换语言时同步一次
document.documentElement.dir = getLocaleDirection(locale.value)
document.documentElement.lang = locale.value漏掉这一步的后果:文字仍然从左往右排,标点和混排文本的方向不对,滚动条位置也反。组件内部的镜像是对的,页面上看着还是反的。
组件做了什么
- 内外边距和定位。 组件样式大量使用逻辑属性:
padding-inline-start、margin-inline-end、inset-inline-start。方向一反,这些值自动换边,不需要第二套样式。 - 文字对齐。 用的是
text-align: start和end,不是left和right。 - 少数位置单独处理。 提示框、悬停卡片的
start、end弹层在[dir='rtl']下会翻边;面包屑的分隔斜杠会跟着改方向;滑杆的握把用:dir(rtl)单独修正。 - 键盘方向键。 日历读
document.documentElement.dir:从右往左时,左右方向键的作用是反的。滚动相关的计算也会按书写方向换轴。
这也解释了为什么第二步不能省:日历和滚动这类逻辑读的就是 DOM 上的 dir,不是 provider 的值。
伪语言测试
最省事的做法不是装语言包,而是两步:
- 把
dir设成rtl,<html dir="rtl">也设上,看整个页面还站不站得住。 - 把文案换成一段更长的假文字,看布局会不会被撑破。
第二步用 overrides 属性。它按语言覆盖指定键,只改你写的那几条:
vue
<script setup lang="ts">
import { InternationalizationProvider } from '@astryx-vue/core/InternationalizationProvider'
// 键和原版语言包一致。这里故意写长,用来挤布局
const pseudo = {
'@astryx.table.noData': '没有找到任何数据,这句话被故意写得很长很长,用来检查单元格会不会被撑破',
}
</script>
<template>
<InternationalizationProvider locale="zh-CN" :overrides="pseudo">
<App />
</InternationalizationProvider>
</template>检查清单
方向改成 rtl、文案拉长以后,逐条看这几件:
- 数字、价格、日期还是从左往右读,没有跟着整段反过来。
- 图标和文字的顺序对了:返回箭头指向右边,下拉箭头在文字的左边。
- 输入框里的光标从右边开始,占位文字靠右对齐。
- 侧边栏和侧面板换到了右边,拖拽手柄的方向也跟着反了。哪一处没换,就是那一处写死了
left或right。 - 长文案下按钮不换行,表格列不塌,标题不溢出色块。
- 表格里的数字列仍然右对齐(数字本身从右往左写会很怪)。
这套检查在深色模式、窄屏下各跑一遍,差不多就够用了。