「2023.002|CN」Issues系列:关于 Hexo 及 NexT 主题的多语言支持
新的需求
我的老blog使用的是单语言 en
,但发布过英两种语言的 post,且二者内容没有交集。即文章要么是中文的,要么是英文的。
我计划在这个新blog里,尽可能每一篇post都用中日英三种语言分别发布,因此折腾了一下 Hexo 和 NexT Theme 的多语言支持,这里记录一下其中遇到的问题。
Hexo 及 NexT 主题多语言支持的特点
版本
- "hexo": "6.3.0"
- "hexo-theme-next": "8.17.1"
相关文档
- Hexo Docs/ Customization / Internationalization (i18n)
- NexT Docs / Theme Settings / i18n
相关配置
分别参考 Hexo 和 NexT Theme 的官方文档找到相关配置
Hexo
1 | language: |
NexT Theme
1 | # Show multilingual switcher in footer. |
Issues
Issue#1[部分解决]: 多语言支持未生效
我在 NexT Theme 的官方文档里评论指出了我在配置多语言支持时遇到的问题:
the doc missed a necessary plugin hexo-generator-index-i18n, what's more, in my test you should also create a post with each language, or the related i18n_dir will not be generated. The latter may be a bug.
即存在两个问题:
- 文档中没有说明 必须安装一个多语言相关的插件 hexo-generator-index-i18n,多语言支持才能使用
- 必须发布多语言的文章,相关的多语言文件夹
i18n_dir
才会被创建。
下面分别讨论这两个问题
缺少hexo-generator-index-i18n 插件
解决起来很简单,使用如下命令安装即可:
1 | $ npm install hexo-generator-index-i18n --save |
i18n_dir 与 post 文章关联问题
首先再详细描述一下这个问题,使用如下命令查看我的 blog 源码(Hexo)目录,如下所示:
1 | (base) ➜ Blog tree pwnfan -L 1 |
这些 index.html
用于显示主题对主页面基本组成元素,如菜单等,不同语言下会显示对应语言的菜单。
切换语言可以通过页面最下端的 language_switcher
中实现。
正常情况下,即使不发布任何文章,应该都可以使用 language_switcher
切换语言,以显示不同的主页菜单等基本组成元素。
但实际的情况是,NexT 主题存在bug,若不分别发布各种语言的文章,它就不会生成对应语言的文件夹(即i18n_dir,如 ja)及 index.html
,从而就无法使用 language_switcher
进行多语言切换,更不用说使用其他语言的如菜单之类对页面基本组成元素了。
而且 NexT Theme 并不支持在单个 post 里的 Front-matter 里设置多个语言,如果设置了多个且其中包含 en,则会被默认设置为 en,其他语言将被忽略。
这个问题目前没有好的办法解决,只能通过发布各种语言的 post 来解决。
可以给官方提一个 github issue,不过考虑到官方可能不认为这是一个bug而是一种可选的需求,他们只是选择了另一种需求来实现,所以我只是在相关的文档出发表了评论提出了这个问题。
Issue#2[已解决]: 多语言 post assets 包含问题
期望效果
post assets 是 Hexo 的 post 需要引用的资源,其中最常见的资源便是图片了。
如果按照 Hexo 的设计理念,在撰写多语言的 post 时,针对同一篇 blog 文章,我们需要创建多个不同语言版本的 post,如:
1 | # default language is English |
此时,_posts 目录结构如下:
1 | source/_posts |
在这种情况下,如果文章里需要引用图片,可能遇到如下两种情况:
- 不同语言的 post 都使用同样的图片:最常见的情况
- 不同语言的 post 使用的某些图片可能不同:比较少见,但是还是偶尔会有这类需求,比如我们制作的图片中包含文字说明,且会根据对应语言制作包含对应语言的文字说明的图片版本。
- 概括一下这种需求:既存在相同的 assets(如图片),又要支持语言相关的独立 assets(如图片)
- 引发了一个问题:图片(Assets)文件夹应该如何组织,才能满足上述需求?
问题分析 && 相关信息
我们知道 Hexo 的 Assets 文件夹有两种组织形式:
- 所有 post 共用同一个 Assets 文件夹 比如 images 文件夹
- 虽然这是默认的方式,但是我个人不推荐这种方式
- 当文章多起来时,资源文件如图片都包含在同一个文件夹内,虽然可以通过文件名区分,但是还是会非常的乱
- 它的优点是能够方便的在不同的地方如 post 中复用之前加入的 assets,但是实际上在这类 blog 应用中,assets 的复用率极低
- 每个 post 有独立的 assets 文件夹
- 我比较喜欢这种方式
- 参考文档
- Hexo Docs - Asset Folders
- Hexo Docs - Tag Plugins - Include Assets
- 具体设置方法:在 Hexo 配置文件
_config.yml
中设置post_asset_folder: true
- 这种方式在处理上述的需求 "即存在相同的 assets(如图片),也要支持语言相关的独立 assets(如图片)"时,也会遇到问题
- 可以粗暴的将相同的图片分别存储在对应语言的 assets 文件夹中,但存在重复文件会浪费磁盘和远程 git 仓库的空间
当我们将设置了 post_asset_folder: true
之后,加上多语言的配置,文件夹结构如下:
1 | (base) ➜ pwnfan git:(dev) ✗ tree source/_posts |
解决方法
为满足 既存在相同的 assets,又要支持语言相关的独立 assets
的需求,可以采用如下方式:
按照上节推荐,在 Hexo 配置文件
_config.yml
中设置post_asset_folder: true
相同的 assets:假如原始 post 的语言为英文,然后分别翻译为中文 post 和日文 post。则可以在中文和日本的 post 中可以直接使用英文 post 的 asset,以避免前文提到的存储重复文件的问题
- 这里需要专门说明一下,在中文和日文的 post 中引用外部的 assets,不能使用相对路径,而需要使用这种“绝对路径”
{% img /en/post/Issues-Series-About-Hexo-and-NexT-Theme-Multi-Language-Support/1.png "image title" %}
- “绝对路径” 之所以包含引号,是因为它也并不是真正意义上的绝对路径,而是以目录
{blog_source_folder}/public/
为根目录的绝对路径
- “绝对路径” 之所以包含引号,是因为它也并不是真正意义上的绝对路径,而是以目录
- 这里需要专门说明一下,在中文和日文的 post 中引用外部的 assets,不能使用相对路径,而需要使用这种“绝对路径”
语言相关的独立 assets:直接放在对应语言的 assets 文件夹中即可
Issue#3[已解决]:NexT Theme 在不同页面切换语言的 Bug ⚠️
问题详述
- 当前 NexT Theme 下在不同页面(主页、post、404、tags等)来回切换语言,可能会出现切换到后的页面返回 404 的情况
期望效果
- 主页
- 可以切换多语言
- 当前 NexT Theme 已支持这种切换
- 非 post 页面(about、404、tags等)
- 当前 NexT Theme 切换其他语言后会变成 404,因为它不支持对这类页面的多语言定制,但在生成的切换 URL 中却指定了语言。这是一个bug,需要修复。简单起见期望修改为在这类页面中不可切换多语言,或者使切换无效。
- 若用户在这些页面内切换了语言,即使我们按照上述的期望方式使切换无效,即让切换后的URL保持不变,但由于NexT的实现机制,仍然会进行页面刷新操作。要想让其不刷新,修改起来比较复杂,简单起见保持不变。
- post 页面:
- 可以进行多语言切换,若不存在对应语言的 post,则返回404
- 当前 NexT Theme 不能很好的支持用户在 config 中定制 permalink 的多言转换。这也另一种bug,也需要修复。
问题分析 && 相关信息
原因是由于 NexT Theme 生成多语言的 URL 的时候存在bug,没有充分考虑当前 Path、permalink 格式、以及多语言的关系导致。
问题代码位于
1 | {%- if theme.language_switcher and languages.length > 1 %} |
i18n_path(language)
代码:
100 | /** |
解决方法
我在 fork 版本中尝试修复了这个问题:
https://github.com/pwnfan/hexo-theme-next/commit/4a4ce6301ba5e0f7e05eea353c5c771e102dc8bb
这里实现并使用了新的函数 i18n_path_pwnfan
,因为上节中提到的 期望效果
包含我的个人主观观点和需求,因此可能不适合给官方 PR,所以只在自己的 fork 仓库里做了修复:
1 | {%- if theme.language_switcher and languages.length > 1 %} |
109 | /** |
Issue#4[部分解决]: 如何翻译?
请参考另一篇 post 「2023.003/CN」用 ChatGPT 高效翻译 Hexo 文章
Hexo NexT Theme 多语言支持的另一种方式?
可以看到,Hexo 及 NexT Theme 的多语言支持及包含:
- 对主页面基本组成元素的多语言支持,如菜单等
- 多语言 post
而我能想到的另一种可以变相地实现多言语 post 的方式是:
将 Hexo 及 NexT Theme 设置为只有一种语言,比如
en
借助 NexT Theme 专有的 Tag Plugins 中的 Tabs,可以变相地实现多语言。 参考代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% tabs Multiple Language Post Example %}
<!-- tab English -->
Where are you from?
<!-- endtab -->
<!-- tab 日本語 -->
ご出身地はどちらですか。
<!-- endtab -->
<!-- tab 中文 -->
你来自哪里?
<!-- endtab -->
{% endtabs %}效果如下:
Where are you from?
ご出身地はどちらですか。
你来自哪里?
这种方式存在的问题:
- 没有对主页面基本组成元素的多语言支持,如菜单等
- 有两种组织 post 中 Tags 的方式(以支持 en/ja/zh-CN 三种语言为例)
- post 中只使用 3个 Tags,分别包含不同语言的全部文章内容
- 优点:代码相对简单清晰
- 缺点
- Tabs 有固定的样式框,影响整个 blog 的页面风格
- ToC 生成存在问题
- 针对 post 文章中的每一个段落,分别使用独立的 3种语言的 Tags
- 优点:
- ToC 可以跟正常工作如初
- 不影响 blog 的整体页面风格
- 缺点:
- 文章段落较多时,需要使用大量的 Tags 标签 {% tabs %},编写繁琐、代码不整洁
- ToC 只能使用一种语言
- 优点:
- post 中只使用 3个 Tags,分别包含不同语言的全部文章内容
技术文档多语言支持的最佳实践
前文记录了为让 Hexo 和 NexT Theme 满足自己对写多语言的 post 的需求,而进行的尝试和思考,显然对我的需求来。
那么我目前为止看过的技术文档的多语言文档中,哪种是最优雅的呢?我另写了一篇文章「2023.004|CN」最佳实践系列:技术文档多语言支持 来讨论这个问题。