Why Support Multiple Languages: To reach a broader user base. Although the internet theoretically connects people worldwide, most individuals instinctively cluster around their native languages. English and Chinese are the two most widely used languages globally, so support for these two languages is prioritized.
This document is part of the Hexo blog setup guide series, version 1.4, which mainly introduces the configuration and optimization plan for the Hexo blog using the Butterfly theme that supports language switching. The entire blog setup guide series can be found at: Hexo Blog Setup Guide: Systematic Solutions and Detailed Construction Process.
Overall Plan:
Hexo blog using the Butterfly theme.
The Chinese and English blogs are set up as two independent projects:
Chinese blog: my-hexo-blog-cn; English blog: my-hexo-blog-en.
Benefits: mutual backup; simplified management, reducing interference between Chinese and English versions.
The Chinese and English blogs are constructed as subdirectories rather than subdomains, which helps consolidate their search weight.
The overall process includes three stages: preparation, multi-language configuration support, and additional optimizations, comprising four modules: local Hexo - Chinese blog, local Hexo - English blog, GitHub, Cloudflare.
Use the “Switch language” feature to toggle between the Chinese and English blogs by adding or removing the /en/ prefix in the address bar.
Since the blog can only be deployed to Cloudflare Pages at the root directory, a Cloudflare worker is required to implement subdirectory deployment. The free quota is 100,000 requests per day, which is generally sufficient during language switching.
doctype html html(lang=config.language data-theme=theme.display_modeclass=htmlClassHideAside) head include ./head.pug body !=partial('includes/loading/index', {}, {cache: true})
if theme.background #web_bg(style=getBgPath(theme.background))
!=partial('includes/sidebar', {}, {cache: true})
#body-wrap(class=pageType) include ./header/index.pug
main#content-inner.layout(class=hideAside) if body div!= body else block content if theme.aside.enable && page.aside !== false include widget/index.pug
include ./rightside.pug include ./additional-js.pug
// Below is Switch language logic script. functionswitchLanguage(targetLang) { const currentPath = window.location.pathname; let newPath;
if (targetLang === 'cn') { // Switch to Chinese if (currentPath.startsWith('/en/')) { // Currently on an English page, need to remove /en/ prefix newPath = currentPath.substring(3) || '/';
// Check if new path exists fetch(newPath) .then(response => { if (response.ok) { // If page exists, redirect directly window.location.href = newPath; } elseif (response.status === 404) { // If page does not exist, redirect to Chinese homepage window.location.href = '/'; } }) .catch(() => { // Handle network errors or other issues with default behavior console.error('Network error occurred.'); }); } else { console.log('Already on the Chinese page.'); } } elseif (targetLang === 'en') { // Switch to English if (!currentPath.startsWith('/en/')) { // Currently on a Chinese page, need to add /en/ prefix newPath = '/en' + (currentPath === '/' ? '/' : currentPath);
// Check if new path exists fetch(newPath) .then(response => { if (response.ok) { // If page exists, redirect directly window.location.href = newPath; } elseif (response.status === 404) { // If page does not exist, redirect to no-match page window.location.href = '/en/no-match/'; } }) .catch(() => { // Handle network errors or other issues with default behavior console.error('Network error occurred.'); }); } else { // Already on an English page; no need to change. console.log('Already on the English page.'); } } }
Local Configuration for English Blog
Configuration of hexo’s config file
1 2 3
# Set basic URL and root directory for the website url:https://blog.dmindie.com/en/ root:/en/
Configuration of _config.butterfly.yml file
1 2 3 4 5 6 7 8 9 10 11 12
menu:# Navigation menu names Home:/||fasfa-home Categories:/categories/||fasfa-folder-open Tags:/tags/||fasfa-tags Archives:/archives/||fasfa-archive About:/about/||fasfa-heart#fas fa-heart; Link:/link/||fasfa-link # Add language switch menu items Language||fasfa-language: 简体中文:javascript:switchLanguage('cn') English:javascript:switchLanguage('en')
Adding Switch Language Logic
Add Switch language logic in layout.pug (my-hexo-blog-en/themes/butterfly/layout/includes/layout.pug), with code similar to that in the Chinese blog.
New no-match.md File
Purpose: When switching from Chinese to English, if no corresponding content exists, redirect to no-match first, then use Cloudflare worker to redirect to the English blog homepage.
Content of no-match (my-blog/my-hexo-blog-en/source/no-match.md):
1 2 3 4 5 6 7
--- title: No Match Found date: 2025-01-07 17:25:30 description: "The English version of this page is not available. You will be redirected to the homepage." ---
The English version of this page is not available. You will be redirected to the homepage.
Cloudflare Worker Configuration for English Blog
Worker Code Configuration
Log in to Cloudflare Dashboard and create a new Worker in the “Workers & Pages” section.
Use the following code to proxy requests starting with /en/ to the English blog project:
// Check if it's an English blog request if (url.pathname.startsWith('/en')) { // Replace with English blog address url.hostname = 'my-hexo-blog-en.pages.dev'; url.pathname = url.pathname.replace('/en', '');
event.respondWith( fetch(url.toString()).then(response => { // Check if it's a no-match page if (url.pathname === '/no-match/') { // If it's a /no-match/ page, redirect to English homepage returnResponse.redirect('<https://blog.dmindie.com/en/>', 302); } return response; }) ); } else { // Default handling for other requests (assumed as Chinese blog) event.respondWith(fetch(event.request)); } });
Binding Worker to Main Domain
Log in to Cloudflare Dashboard.
Go to Workers & Pages.
In the left menu, select Workers & Pages.
Find your newly created Worker (e.g., for multi-language proxy).
Configure worker routing rules:
Domain: dmindie.com
Route: blog.dmindie.com/en*
Fallback mode: Open automatically on failure.
Deployment Preview
After modifying both versions of the blog, deploy them to GitHub Pages and Cloudflare.
Open a browser and visit both versions of the blog:
https://blog.dmindie.com/
https://blog.dmindie.com/en
With these configurations in place, you can freely switch between languages on key pages such as home, categories, tags, archives, about, and links.
Multi-Language Optimization Support
Supporting Multi-Language Switching for Posts
Keeping Permalink Consistent
To facilitate quick switching between Chinese and English posts on blogs, ensure that their URLs remain consistent except for the difference in prefixes (/en/) in permalinks:
Simplified Multi-Language Deployment Only on GitHub Pages
Deployment Plan Adjustments
The first version of the blog will be deployed at GitHub Pages root directory.
Remaining versions will be deployed in subdirectories under GitHub Pages since multiple versions are automatically deployed into subdirectories there.
No longer deploy on Cloudflare Pages.
Adjusting Switch Language Logic
The config files for hexo and _config.butterfly.yml will remain consistent with previous plans.
For both Chinese and English projects within layout.pug (my-hexo-blog-cn/themes/butterfly/layout/includes/layout.pug) and layout.pug (my-hexo-blog-en/themes/butterfly/layout/includes/layout.pug), add Switch language logic as follows:
doctype html html(lang=config.language data-theme=theme.display_modeclass=htmlClassHideAside) head include ./head.pug body !=partial('includes/loading/index', {}, {cache: true})
if theme.background #web_bg(style=getBgPath(theme.background))
!=partial('includes/sidebar', {}, {cache: true})
#body-wrap(class=pageType) include ./header/index.pug
main#content-inner.layout(class=hideAside) if body div!= body else block content if theme.aside.enable && page.aside !== false include widget/index.pug
include ./rightside.pug include ./additional-js.pug // The following is the Switch language logic script. functionswitchLanguage(targetLang) { const currentPath = window.location.pathname; let newPath; if (targetLang === 'cn') { // If the target is Chinese if (currentPath.startsWith('/en/')) { // Currently on an English page, need to remove the /en/ prefix newPath = currentPath.substring(3) || '/'; } else { // Already on a Chinese page, no need to change newPath = currentPath; } } else { // If the target is English if (!currentPath.startsWith('/en/')) { // Currently on a Chinese page, need to add the /en/ prefix newPath = '/en' + (currentPath === '/' ? '/' : currentPath); } else { // Already on an English page, no need to change newPath = currentPath; } } // Check if the new path exists fetch(newPath) .then(response => { if (response.ok) { // If the page exists, redirect directly window.location.href = newPath; } else { // If the page does not exist, redirect to the corresponding language homepage window.location.href = targetLang === 'cn' ? '/' : '/en/'; } }) .catch(() => { // Redirect to the corresponding language homepage in case of an error window.location.href = targetLang === 'cn' ? '/' : '/en/'; }); }
Conclusion
With these configurations completed, anyone can set up a Hexo blog that supports multi-language switching effectively. Moving forward based on this multi-language solution will lead to further optimizations along with continuous updates of this article’s content.
If you have better solutions or ideas, please feel free to share them with us; we will continue updating this article to keep its content current and optimal.