news 2026/9/12 6:26:24

Dashy 多语言国际化(i18n)完整指南:语言切换、新语种接入与组件文案翻译

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dashy 多语言国际化(i18n)完整指南:语言切换、新语种接入与组件文案翻译

Dashy 多语言国际化(i18n)完整指南:语言切换、新语种接入与组件文案翻译

【免费下载链接】dashy🚀 A self-hostable personal dashboard built for you. Includes status-checking, widgets, themes, icon packs, a UI editor and tons more!项目地址: https://gitcode.com/GitHub_Trending/da/dashy

本文基于 docs/multi-language-support.md 编写,并结合 src/utils/languages.js、src/utils/i18n.js、src/components/Settings/LanguageSwitcher.vue 与 tests/locales/check-locales.js 等源码与测试文件进行深度印证与扩充。

Dashy 是一款自托管的个人仪表盘,天然面向全球用户,因此国际化(Internationalization,简称 i18n)是它的核心基础设施之一。本文围绕 Dashy 的 vue-i18n 多语言方案,完整讲解三条主线:普通用户如何切换界面语言贡献者如何新增一种语言开发者如何在新组件中接入可翻译文案,并深入源码层面解析语言加载、优先级回退与翻译覆盖率校验的底层实现。读完本文,你将能在自己的 Dashy 实例上切换语言,也能独立为 Dashy 提交一份完整、可被 CI 校验通过的新语种翻译,并掌握在 Vue 组件中正确使用$t的规范。

一、语言检测与回退机制

Dashy 默认会尝试使用浏览器或操作系统的语言设置。如果该语言还没有对应的翻译文件,则会自动回退到英语(English)。

这一行为在 src/utils/i18n.js 中定义:

const i18n = createI18n({ legacy: false, globalInjection: true, locale: defaultLanguage, // 默认语言 fallbackLocale: defaultLanguage, // 回退语言 messages: registered, });

其中defaultLanguage来自 src/utils/config/defaults.js,其默认值为'en',即英语既是默认语言也是回退语言。fallbackLocale保证了任何缺失的翻译键都能落到英语,而不会出现空白文案。

legacy: false表示使用 vue-i18n 的 Composition API 模式;globalInjection: true则允许在模板中直接使用全局注入的$t函数,而无需在每个组件里手动引入。

二、如何切换语言

2.1 在 UI 中手动切换

在 Dashy 界面的配置菜单(Config Menu)中,点击Language(语言)按钮,会打开语言选择弹窗,从下拉列表中选择目标语言即可。你的选择会被保存到浏览器的localStorage中,下次打开 Dashy 时依然生效。

从源码 src/components/Settings/LanguageSwitcher.vue 可以看到完整的交互链路:

  • 下拉列表由languages数组映射生成,每项显示为“国旗 emoji + 语言名”(friendlyName);
  • 选择语言并点击保存按钮后,saveLanguage()会先通过checkLocale()确认语言在availableLocales中;
  • 随后调用loadLocale(code)动态加载对应的 JSON 翻译文件,并通过i18n.global.setLocaleMessage(code, msg)注册到运行时;
  • 最后把语言码写入localStorage.setItem(localStorageKeys.LANGUAGE, code)并关闭弹窗。

2.2 通过配置文件设置

你也可以在conf.yml配置文件中直接指定语言。在appConfig.language字段填入受支持语言的 ISO 代码即可,例如德语:

appConfig: language: de

2.3 语言的解析优先级

综合 src/utils/config/ConfigHelpers.js 中的getUsersLanguage()实现,语言的实际解析优先级为:

  1. localStorage中保存的用户手动选择(键名为language,见 defaults.js);
  2. 配置文件config.appConfig.language
  3. 内置默认值'en'

同时,该函数还维护了一个legacyAliases兼容映射:{ cn: 'zh-CN' },即旧配置中写cn的老用户会被自动映射到简体中文,避免升级后语言设置失效。最终代码会在 src/utils/languages.js 的languages数组中查找匹配项,若找不到则返回undefined,由上层回退处理。

2.4 当前支持的语言列表

以仓库当前 src/utils/languages.js 为准,Dashy 共注册了以下 32 种语言/方言(代码、名称、国旗):

语言代码语言名称语言代码语言名称
enEnglishglGalego
en-GBEnglish (British)ruРусский
arالعربيةroRomana
bgБългарскиskSlovenčina
bnবাংলাslSlovenščina
csČeštinasvSvenska
daDansktrTürkçe
deDeutschukUkrainian
elΕλληνικάzh-CN简体中文
esEspañolzh-TW繁體中文
frFrançaiskyКыргызча
hiहिन्दीnbNorsk
huMagyarnlNederlands
itItalianoplpolski
ja日本語ptPortuguês
ko한국어zz-piratePirate

语言代码遵循 2 位 ISO-639 下的同名 JSON 文件,例如de.jsonzh-CN.json

三、如何添加一种新语言

Dashy 使用 vue-i18n 管理多语言支持。添加新语言只需三步:创建翻译文件、翻译内容、注册到应用。

3.1 第一步:创建语言文件

在 src/assets/locales/ 目录下为你的语言新建一个 JSON 文件。

  • 标准语言:使用 2 位 ISO-639 代码命名,例如德语de.json、法语fr.json、西班牙语es.json
  • 方言/地区语言:使用带后缀的 CLDR 格式命名,例如en-GB.json(英式英语)、zh-CN.json(简体中文)、zh-TW.json(繁体中文)。

3.2 第二步:翻译内容

以 src/assets/locales/en.json 为模板,将 JSON 的**值(value)**翻译成目标语言,键(key)保持不变。某些条目可以留空不译——缺失的键会自动回退到英语。

特别注意:翻译值中如果出现花括号包裹的内容(如{theme}{name}),花括号内的内容必须原样保留,因为这是 vue-i18n 的变量插值占位符,运行时会被动态替换。以德语theme-maker段落为例:

{ "theme-maker": { "export-button": "Benutzerdefinierte Variablen exportieren", "reset-button": "Stile zurücksetzen für", "show-all-button": "Alle Variablen anzeigen", "save-button": "Speichern", "cancel-button": "Abbrechen", "saved-toast": "{theme} Erfolgreich aktualisiert", "reset-toast": "Benutzerdefinierte Farben für {theme} entfernt" }, }

3.3 第三步:注册到应用

在 src/utils/languages.js 的languages数组中追加你的语言元数据,包含语言名称、ISO 代码和国旗 emoji:

export const languages = [ { name: 'English', code: 'en', flag: '🇬🇧' }, { name: 'German', code: 'de', flag: '🇩🇪' }, // 语言名称、ISO 代码与国旗 emoji ];

注册后,翻译文件会通过 src/utils/languages.js 中的import.meta.glob批量匹配加载:

const loaders = import.meta.glob([ '../assets/locales/*.json', '!../assets/locales/en.json', // 排除英语,它作为默认与回退语言 ]); export const loadLocale = async (code) => { if (code === 'en') return en; const loader = loaders[`../assets/locales/${code}.json`]; if (!loader) throw new Error(`Unsupported locale: ${code}`); const mod = await loader(); return mod.default; };

也就是说,只要 JSON 文件命名正确并放在locales/目录、且被注册进languages数组,就会被 Vite 自动识别为可动态加载的语言包,无需再改动其他构建配置。en.json被显式排除出 glob,因为它必须作为默认与回退语言提前同步注册(见 i18n.js 中“先注册全部代码、空对象回退英语”的预注册逻辑)。

完成以上三步后,还可以把你的新语言补充到仓库根目录 README.md 的 Language Switching 小节,并(可选)署名,以便为你的贡献留档。如果你不习惯提交 Pull Request,也可以直接把翻译好的文件交给维护者,由维护者合并进应用。

四、翻译覆盖率检查:yarn validate-locales

Dashy 内置了一个翻译 lint/测试脚本,用于验证翻译文件的完整性与合法性,并输出每种语言的覆盖率报告:

yarn validate-locales

该命令定义在 package.json:

"validate-locales": "node tests/locales/check-locales.js"

脚本本体位于 tests/locales/check-locales.js,它会被 CI 在 Pull Request 时自动执行,也是新增语言后必须通过的门禁。它会依次执行以下检查:

  • 失败(failure):所有语言文件均已注册、存在且可被正确解析为 JSON 对象(根节点必须是对象,非数组、非 null);
  • 失败(failure)languages.js中注册了代码但缺少对应 JSON 文件;
  • 失败(failure):存在 JSON 文件但未在languages.js中注册;
  • 失败(failure):代码中使用了en.json中不存在的翻译键;
  • 警告(warn)en.json中存在从未在代码中被引用的冗余键;
  • 警告(warn):其他语言包中存在en.json或代码中都没有用到的多余键;
  • 覆盖率报告:其他语言相对en.json的翻译完成度百分比,按字母序逐行展示(≥80% 为青色、≥50% 为黄色、更低为红色,100% 为绿色)。

脚本通过正则扫描 src 下所有.vue.js文件中的$t$tci18n.ti18n.global.t调用,将字面量键与动态前缀分别提取后与各语言包做交叉比对;对于运行时拼接键名的动态调用(如反引号模板字符串),脚本会提取其静态前缀进行前缀匹配,无法静态验证的调用点也会在输出中单独列出提示。少数间接使用的键(如 JsonEditor、AuthButtons、InitServiceWorker 中的动态键)被维护在IGNORED_KEYS集合中以免误报。

一句话总结:该脚本保证“英语是唯一真相源”——代码里用到的键必须在en.json中存在,而其他语言只要缺失键就会回退英语,因此不强制 100% 翻译,但英语缺失就是硬错误。

五、在新组件中添加可翻译文案

如果你正在开发一个新组件(或发现某个旧组件遗漏了翻译),任何展示给用户的文本都应从组件中抽离,存放到语言文件中。得益于全局注入,接入过程非常简单。

5.1 第一步:在 en.json 中添加翻译文本

打开 src/assets/locales/en.json,找到合适的段落,或新建一个段落。假设新组件叫my-widget,可以这样组织:

"my-widget": { "awesome-text": "I am some text, that will be seen by the user" }

必须为所有文本提供英语翻译。其他语言的缺失不是问题(会自动回退英语),但英语缺失就意味着没有任何内容可以展示。

5.2 第二步:在组件模板中使用 $t

语言文件就绪后,可在组件模板中通过全局$t函数传入翻译键来获取对应文案:

<p>{{ $t('my-widget.awesome-text') }}</p>

这里的{{ }}是 Vue 的插值语法,表示内部是 JavaScript/动态表达式。渲染结果为:

<p>I am some text, that will be seen by the user</p>

5.3 在 JavaScript 中程序化使用

如果需要从组件脚本中程序化展示文案(例如 toast 弹窗),使用this.$t

alert(this.$t('my-widget.awesome-text'))

5.4 变量插值(Interpolations)

当翻译文案需要插入动态变量时,vue-i18n 支持类似 mustache 的插值语法。先在语言文件中定义带{变量名}占位符的文案:

{ "welcome-message": "Hello {name}!" }

然后在调用时把变量作为$t的第二个参数(JSON 对象)传入:

$t('welcome-message', { name: 'Alicia' })

渲染结果:

Hello Alicia!

这就是文档 3.2 节中“花括号内内容必须保留”的原因——{theme}{name}这类占位符正是插值变量。vue-i18n 还支持复数(Pluralization)、日期时间与数字格式化(Datetime & Number Formatting)、消息格式(Message Formatting)等高级特性,详见 vue-i18n 官方指南。

5.5 完整实例:搜索栏组件

以 src/components/Settings/SearchBar.vue 为范例,模板中使用$t渲染标签与占位符:

<template> <form> <label for="search-input">{{ $t('search.search-label') }}</label> <input v-model="searchValue" :placeholder="$t('search.search-placeholder')" /> </form> </template>

对应的翻译键定义在 src/assets/locales/en.json:

{ "search": { "search-label": "Search", "search-placeholder": "Start typing to filter", "clear-search-tooltip": "Clear Search", "enter-to-search-web": "Press enter to search the web", "enter-to-open-url": "Press enter to open URL", "enter-to-launch-first": "Press enter to launch first match" }, ... }

注意该组件中searchNote会根据不同场景动态选择search.enter-to-search-web/search.enter-to-open-url/search.enter-to-launch-first三个键(见 SearchBar.vue),这正是 5.4 节所述“动态前缀”类用法,check-locales.js的静态扫描会覆盖此类调用。

六、底层原理小结

从源码层面回看 Dashy 的多语言架构,可以总结出三条核心设计:

  1. 单真相源(Single Source of Truth)en.json是所有语言的基准,其他语言允许缺失并回退英语,但英语键的缺失属于硬失败,由validate-locales在 CI 中强制把关;
  2. 动态按需加载:借助 Vite 的import.meta.glob,languages.js 只需维护一份languages元数据数组,翻译 JSON 即可被自动发现并按需懒加载(见loadLocale),英语则常驻内存;
  3. 三级优先级与兼容性:用户语言依次取 localStorage →appConfig.language→ 默认en,并通过legacyAliasescnzh-CN)保证历史配置平滑迁移;全局注入的$t/this.$t让组件内接入翻译几乎零成本。

无论是终端用户、翻译贡献者还是组件开发者,都可以依据本文在 Dashy 中完成语言切换、新语种接入与文案国际化;新增语言后务必运行yarn validate-locales,通过覆盖率与一致性检查,再提交 Pull Request 参与上游协作。

【免费下载链接】dashy🚀 A self-hostable personal dashboard built for you. Includes status-checking, widgets, themes, icon packs, a UI editor and tons more!项目地址: https://gitcode.com/GitHub_Trending/da/dashy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 6:25:09

伺服内嵌EtherNet/IP:SPI从站适配改造与联调避坑全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 6:24:57

深入理解volatile关键字在多线程与嵌入式开发中的应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/12 6:22:20

工业级机器人摄像头:运动控制+实时视觉+闭环决策实战

1. 从“Cmara Robtica”这个词开始&#xff0c;我们到底在谈什么&#xff1f;“Cmara Robtica”——西班牙语&#xff0c;直译是“机器人摄像头”。但这个词在真实工程场景里&#xff0c;从来不是字面意思的简单叠加。它不等于“一个装了轮子的监控头”&#xff0c;也不代表“带…

作者头像 李华
网站建设 2026/9/12 6:20:41

RetroArch 音频延迟优化完全指南:3 个参数把 50ms 压到 20ms

RetroArch 音频延迟优化完全指南&#xff1a;3 个参数把 50ms 压到 20ms 【免费下载链接】RetroArch Cross-platform, sophisticated frontend for the libretro API. Licensed GPLv3. 项目地址: https://gitcode.com/GitHub_Trending/re/RetroArch 玩模拟器时你有没有这…

作者头像 李华
网站建设 2026/9/12 6:20:01

AI工程师的硬核技能图谱:从系统直觉到可执行调试

1. 项目概述&#xff1a;这不是一个“安装包”&#xff0c;而是一份可执行的AI时代硬核技能图谱你搜“andrej-karpathy-skills”&#xff0c;大概率不是想找某位教授的简历PDF&#xff0c;也不是想下载一个叫“Karpathy Skills.exe”的程序——这根本不存在。真正驱动搜索的&am…

作者头像 李华