Best Practices for Setting Up Hexo Multilingual Blogs
Created|Updated
Summary-AI
CloseX
Initializing...
Learn how to create the best multilingual blog with Hexo in this ultimate guide. Simplify your setup process and unlock the power of internationalization to reach a global audience. This tutorial offers clear steps and expert tips to help you master Hexo’s multilingual features and elevate your blogging experience effortlessly.
ABOUT
AIGC
Go Home
Introduction
Creating a multilingual Hexo blog isn’t simple, but it’s not as hard as you might imagine! After a few days of tinkering, I’ve summarized a set of clear, understandable steps that should help you easily set up multiple languages. This tutorial focuses particularly on the Butterfly theme, but it will likely also apply to other themes. Whether you’re a Hexo beginner or have been experimenting for a while, this guide should provide some help. Let’s get started!
The method used on this site involves duplicating the entire site and placing it in the output folder. The benefit is a clearer structure, while the downside is that changes can be more cumbersome to apply.
# URL ## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project' url:https://example.com/cn root:/cn/
# Directory source_dir:source-cn public_dir:cn
# Include / Exclude file(s) ## include:/exclude: options only apply to the 'source/' folder include: exclude: ignore: -source-jp/ -source-fr/ -source/
Keep other parts unchanged. The main modifications are the basic settings.
When you run hexo s --config _config-cn.yml, you will access localhost:4000/cn/, so you need to adjust the url and root accordingly.
Modifying theme configuration
I’m using Butterfly 4.13. Although versions 5.0.0 and later made some adjustments to the configuration files, these changes should not significantly affect this tutorial. If you’re using a newer version, refer to the official documentation for any necessary adjustments.
Add _config.butterfly-(language code).yml.
For example, for Chinese:
1 2 3 4 5 6 7 8 9 10 11
# Change the Menu to Chinese menu: # Article[/article] # List[/list] # Link[/link] # About[/overview] 文章: 中文:/tags/ 中文:/archives/ 中文:/analysis/ 中文:/sitemap/
Keep other parts unchanged. The main modifications are basic configurations.
Copying the source folder
Duplicate the source folder and rename it to source-(language code).
Use Cursor’s Composer Agent function to maintain the structure and translate all files under source. I had 64 files total, and the translation took about 20 minutes. Occasionally, you need to confirm manually, but overall, the experience is fine.
Adding the scripts folder
Create a scripts folder in the root directory and add config-debug.js and change_path.js.
config-debug.js matches the site configuration file with the theme configuration file (though this solution might not be optimal, it works until we find a better automatic matching method for Hexo configs). change_path.js handles file paths. Both scripts were written by Claude and are usable.
hexo.extend.filter.register('after_render:html', function(str, data) { // Get the config file name from command line arguments const configFile = process.argv.find(arg => arg.includes('config-') && arg.endsWith('.yml')); if (!configFile) return str;
// Extract language code const langMatch = configFile.match(/config-([a-z]{2,})\.yml$/); if (!langMatch) return str; const lang = langMatch[1]; if (lang === 'default') return str;
// Process HTML static resource paths let result = str;
// General resource path processing rule constprocessPath = (match, p1, p2, p3) => { // Check if in the exclude list if (excludePaths.some(exclude => p2.startsWith(exclude))) { return match; } // Check if the path already contains the language prefix if (p2.startsWith(`/${lang}/`)) { return match; } // Ensure the path starts with / and is not a relative path if (p2.startsWith('/') && !p2.startsWith('//')) { return`${p1}/${lang}${p2}${p3}`; } return match; };
// Apply all patterns patterns.forEach(({ pattern }) => { result = result.replace(pattern, processPath); });
return result; });
// Register helper function to manually control resource paths in templates hexo.extend.helper.register('langPath', function(path) { if (!path || typeof path !== 'string') return path; const configFile = process.argv.find(arg => arg.includes('config-') && arg.endsWith('.yml')); if (!configFile) return path; const langMatch = configFile.match(/config-([a-z]{2,})\.yml$/); if (!langMatch) return path; const lang = langMatch[1]; if (lang === 'default') return path; // Check if it's an excluded path const excludePaths = [ '/search.xml', '/sitemap.xml', '/robots.txt', '/feed.xml', 'http://', 'https://', 'data:image', '//', 'ws://', 'wss://' ]; if (excludePaths.some(exclude => path.startsWith(exclude))) { return path; }
// Check if path already includes the language prefix if (path.startsWith(`/${lang}/`)) { return path; } return path.startsWith('/') ? `/${lang}${path}` : path; });
Optional Content
The following components are mainly for optimizing user experience and can be used as needed:
footer.pug
This is the footer component, mainly used to display copyright, ICP filings, blogrolls, etc. The language switch button can be placed in footer.pug or navbar.pug. For a better user experience, you could implement a JS script: if the user’s browser language is different and a matching language version exists, preload it. If it’s not 404, show a popup asking the user to switch languages.
head.pug
This is mainly to add language link tags for search engines. Currently, it’s only done for the homepage. In theory, you could write this into the configuration file. To save effort, I did it this way. It works as is.
Chinese version post URL: example.com/cn/posts/666.html (no comments by default, since comments are based on the post URL)
By modifying the twikoo.pug configuration, you can show the original post’s comments on the Chinese version, thus sharing comment data across languages. Visitors will see the complete comment history no matter which language version of the article they view.
Here is the twikoo.pug configuration file. Just copy and paste:
At this point, we’ve completed all the configuration work for the multilingual blog.
To test generating static files for all language versions, run the following command:
1
hexo clean && hexo g && hexo clean --config _config-cn.yml && hexo g --config _config-cn.yml && hexo clean --config _config-fr.yml && hexo g --config _config-fr.yml && hexo clean --config _config-jp.yml && hexo g --config _config-jp.yml
You can put this in package.json and then deploy each language version to your server using hexo d as needed.
After running the above commands, simply deploy each language version of the content to the server according to its configuration file. Now you’ve successfully launched a multilingual Hexo blog!
If you use frameworks like Next.js, configuring multilingual support is even simpler…
CloseX exploring the unknown, fusing AI to design a tech-driven future. A flexible organization focused on software and hardware innovation and knowledge sharing, advancing civilization.