如何实现 CSS i18n?
提示
You can click this button to toggle locales.
在一个页面中实现 i18n
为了使 Valaxy 成为一个国际化的项目,i18n 是必不可少的。
常见的 i18n 方案为采用不同的路径(如 /zh-CN/)或解析不同的域名(cn.xxx.xxx)来分别维护。
此外还可使用 crowdin 平台辅助用户进行多语言翻译。
但对于博客来说,这显然都很麻烦。 当你需要 i18n 时,你不得不同时维护多个目录下的文章。 当文章间存在相同的示例时,你还需要维护相同的内容。非常不优雅。
Valaxy 中, 站点的独立字段部分(如文章目录:Table of Contents)基于 vue-i18n 实现, 而文章内容部分的大段文本则采用另一种 CSS i18n 的方案。
Vue-i18n
配置 Vite Vue-i18n 插件 @intlify/unplugin-vue-i18n:
在 locales 目录下配置 zh-CN.yml 与 en.yml:
并在主入口文件(如 main.ts)中初始化:
此时即可在 Vue 中使用 t('') 来翻译对应字段文本。
Messages when SSG
vue-i18n 支持使用虚拟模块 @intlify/unplugin-vue-i18n/messages 的方式来导入多语言。
可惜的是,它并没有完美地支持 SSR。#78 | intlify/bundle-tools
而 Vite 的 import.meta.globEager 导入必须使用静态字符串。
当拥有确定目录时,它是奏效的,但 Valaxy 还需要将 Valaxy 自身的 locales 与主题的 locales 以及用户自定义的 locales 进行合并。 这意味着我们不能使用变量来拼接字符串进行导入,对于不同包管理器的目录结构不同,我们很难确定这些 locales 处于何处的相对位置。
因此我采用插件虚拟模块(@valaxyjs/locales)的形式实现(依次导入各目录下的 locales 数据并合并):
Vite 虚拟模块的原理其实就是拼接字符串。
最后在 i18n 的初始化文件加载:
CSS i18n - Another solution
CSS i18n - 另一种互补解决方案
文章部分拥有大段的文本,而 vue-i18n 的场景则在于一些独立的字段翻译。
而传统的分文件独立管理的方式,对于博客来说其实并不方便。 大多数情况,你并不会想专门建立一个文件夹来管理它。
因此我尝试使用纯 CSS 解决该问题。
思路
即借助 CSS 规则,根据对应语言,显示对应区块内容。 大体方案:通过 markdown-it-container 设置 fence 预编译 Markdown, 当页面初始化或切换语言时,为 html 添加对应语言类,编写对应 CSS 以在该类下显示对应语言的区块。
When the page initializes or switches languages, add the corresponding language class to html and write the corresponding CSS to display the corresponding language block under that class. :::
优势:
- 可在同一个 Markdown 文件中进行维护,书写便捷
- 预加载与实时切换
- URL 不变,便于管理与分享,且切换无需刷新页面
- 渐进式翻译(只翻译部分内容并可共用示例内容等)
- 当你在同一个文件编写文档时,GitHub Copilot (VSCode 插件) 甚至很容易帮助你补全翻译!
劣势:
- 多语言内容被渲染在同一页面中,增加冗余(但我觉得这微小的体积完全是可以接受的)
Result
效果如下(点击按钮切换):
另一种 i18n 方案。
更多内容:…
中文
书写方式如下:
Steps
实现步骤
为了能够借助 CSS 处理 i18n,我们借助 markdown-it-container 的 fence 包裹 Markdown 中需要参与 i18n 的内容。
这可以使:
lang 是 HTML 的一个标准字段。
为避免 class 命名冲突,我们可以采用 CSS attribute 的查询方式。
首先将 i18n 全部隐藏: :::
lang is a standard field in HTML.
To avoid class naming conflicts, we can use the CSS attribute query.
First, hide all i18n:
编写 CSS/SCSS 规则,设定 html lang 为对应语言时,显示对应语言的元素即可。
Write CSS/SCSS rules and set html lang to display elements in the corresponding language when it is the corresponding language.
为了帮助用户记住自己的语言,还请不要忘记初始化。
To help users remember their language, please also don’t forget to initialize.
切换语言时则可做如下处理:
When switching languages, the following can be done.
值得一提的是,在查看 lang 文档时,我意外地发现 :lang 也是一种支持的选择器。 因此上述的 CSS 中 [lang="xxx"] 也可以替换为 :lang(xxx)。
但是 :lang() 也会命中默认语言的 div(没有 lang 字段,但处于含有 lang 的标签中),因此为了安全,我们还是应该使用 class 的属性查询。
It’s worth mentioning that when looking at the lang documentation, I accidentally found that :lang is also a supported selector. So [lang="xxx"] in the CSS above could also be replaced with :lang(xxx).
However, :lang() will also hit the default language div (which has no lang field but is in a tag containing lang), so to be safe we should still use the class attribute query.
我认为 vue-i18n 与 CSS i18n 的互补,可以非常好地解决单页内的 i18n 切换。 不妨一试?
I think vue-i18n complements CSS i18n and could be a very good solution for i18n switching within a single page. Why not give it a try?
Q.E.D.