Hexo, as a static blog framework, has limited native support for multiple languages and only supports generating single-language websites. To address the need for dynamic language switching, community developers have created plugins like hexo-generator-i18n. However, their functionality is still limited to multilingual support for pages and archives, and they cannot handle multilingual posts.
hexo-theme-reimu initially didn’t support multiple languages either, but in version v1.4.0, I referenced the implementation of hexo-generator-i18n
and added multilingual support for pages/posts/archives. This functionality was further improved in version v1.5.0. This article will explain how I added multilingual support to hexo-theme-reimu and how to use its multilingual features.
Design Approach
Prerequisites:
- Assuming the default language path is:
https://:baseurl/a/b.html
, paths for other languages will be:https://:baseurl/:lang/a/b.html
. - All page generation is based on the default language as the foundation/fallback. Content without a default language version will not be displayed in navigation.
Page Multilingual Support
Since pages don’t have previous (prev) and next article features, and are generated directly based on the directory structure under source
, multilingual support is relatively simple. For multilingual pages that already exist in the source
directory, we can directly use Hexo’s built-in generator. For pages that only have the default language, we need to write a custom generator to copy the default language pages to other language paths. [source#generator-page-i18n]
Post Multilingual Support
Posts are the most challenging part because Hexo generates posts based on permalinks by default, which don’t include language information. This means we must define custom fields in the front-matter to explicitly identify language types, for example:
1 | title: Hello |
Additionally, posts have previous (prev) and next article features that we need to support, ensuring correct article titles are displayed without duplicates.
To solve these issues, I implemented the following design:
- Hexo’s default post generator can’t meet our requirements for prev and next posts, so we need to rewrite it to correctly link post prev and next. [source#generator-post] [source#generator-post-with-lang]
- Generate posts with and without
lang
separately - Posts without
lang
link their prev and next to adjacent posts withoutlang
- Posts with
lang
generate corresponding paths and link their prev and next to adjacent posts with/withoutlang
- Generate posts with and without
- For posts without user-defined
lang
fields, we need to write a custom generator to copy default language posts to other language paths. [source#generator-post-i18n]
Archive Multilingual Support
Archives include index
, tag
, category
, and archive
pages. The main challenges are:
- How to generate archive pages in different languages (e.g., if a post’s title is “你好” in Chinese and “Hello” in English, it should display “你好” on Chinese archive pages and “Hello” on English archive pages)
- How to prevent user-written multilingual posts from displaying repeatedly in different languages
hexo-generator-i18n
doesn’t support multilingual posts, so it simply copies default language archive pages to other language paths, resulting in identical content across different languages and duplicate display of user-written multilingual posts.
To solve this, I optimized the implementation of hexo-generator-i18n
:
- To prevent duplicate posts, we need to rewrite Hexo’s default archive generators (hexo-generator-index, hexo-generator-category, hexo-generator-tag, hexo-generator-archive) to exclude all user-written multilingual posts. [source#generator-override]
- Since we excluded user-written multilingual posts, we need to replace post titles with corresponding language titles (if available) during template rendering. [source#helper-get_posts_by_lang]
- Write a generator to copy default language archives to other language paths. [source#generator-archive-i18n]
Link Navigation
Hexo’s built-in url_for
helper doesn’t support multiple languages, so we need to write our own helper to generate multilingual links. [source#helper-url_for_lang]
Search Multilingual Support
hexo-theme-reimu supports both Algolia search (hexo-algoliasearch) and local search (hexo-generator-search), but neither plugin natively supports multilingual posts (posts with lang
have permalinks without lang
information, resulting in incorrect navigation links for multilingual articles).
- For Algolia search, I forked the code @reimujs/hexo-algoliasearch
- For local search, I integrated it directly into the theme and fixed multilingual article navigation links [source#search]
PJAX Multilingual Support
PJAX itself doesn’t support switching between languages. For example, when navigating from a Chinese page to an English page, menu content won’t change to English (in fact, PJAX shouldn’t be called in this case).
Fortunately, PJAX provides a getElements
hook, allowing us to specify which a tags on the page can use PJAX. (PJAX should only be used when navigating within the same language site) [source#Pjax.prototype.getElements]
How to Use
Assuming the default language is zh-CN
and the additional language is en
.
Basic Configuration
Outer _config.yml
configuration:
1 | language: zh-CN # Cannot be an array |
Inner _config.yml
(_config.reimu.yml
) configuration:
1 | i18n: |
Page Multilingual Support
Create an en
directory under source
and place corresponding language pages in it:
1 | |—— source |
This structure will generate the following multilingual pages:
1 | |—— public |
Post Multilingual Support
WARNING
Since multilingual posts’ permalinks must match the default language post, and the default permalink format is :year/:month/:day/:title/
, where title
usually differs across languages, it’s recommended to define a custom field like urlname
in the front-matter to replace title
, and use :urlname/
in the permalink.
Assume the permalink format in _config.yml
is:
1 | permalink: :year/:month/:day/:urlname/ |
Default language front-matter:
1 |
|
This will generate https://:baseurl/2023/12/25/hello/
.
Other language (en) front-matter must be:
1 |
|
This will generate https://:baseurl/en/2023/12/25/hello/
.
Known Issues
- Since the theme rewrites the post generator to implement multilingual support, when this feature is enabled, the hot reload functionality of
hexo s
won’t automatically detect article updates. To preview multilingual content changes in real-time, manually executehexo clean && hexo g
to refresh the cache.