Initializing...
Hexoを使用して最高の多言語ブログを作成する方法を学び、この究極のガイドで設定プロセスを簡素化し、国際化の力を解き放ってグローバルなオーディエンスにリーチしましょう。このチュートリアルは、明確なステップと専門的なヒントを提供し、Hexoの多言語機能をマスターして、ブログ体験を簡単に向上させるのに役立ちます。
介绍
多言語対応の Hexo ブログを構築するのは決して簡単ではありませんが、想像ほど難しくもありません!数日間格闘した末に、明確でわかりやすい手順をまとめてみました。このチュートリアルは主に Butterfly テーマを対象としていますが、他のテーマにも大抵は適用できるはずです。Hexo 初心者から、既にしばらく触っている上級者まで、このチュートリアルはきっとお役に立てるでしょう。それでは始めましょう!
本サイトの方法は、基本的に全サイトをコピーして出力用フォルダに配置する方式です。この方法の利点は構成が比較的わかりやすい点、欠点は変更がやや面倒な点です。
以下はディレクトリ構成の例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| // 配置ファイル your_hexo_blog ├── _config.yml <-- デフォルト ├── _config-cn.yml ├── _config-fr.yml ├── _config-jp.yml
// テーマ設定ファイル ├── _config.butterfly.yml <-- デフォルト ├── _config.butterfly-cn.yml ├── _config.butterfly-fr.yml ├── _config.butterfly-jp.yml
// ページ用メインディレクトリ ├── source <-- デフォルト ├── source-cn ├── source-fr ├── source-jp
// 新しいスクリプト用フォルダ ├── scripts <-- スクリプトファイル ├── config-debug.js ├── change_path.js
|
以下は変更が必要なファイル(オプション)です。
1 2 3 4 5 6 7
| (基本的にはテーマフォルダ内) hexo-theme-butterfly/layout/includes ├── footer.pug ├── head.pug └── third-party └── comments └── twikoo.pug
|
使用ツール:
- Cursor Agent(Claude 3.5 Sonnet)
- Cursor Chat (GPT-4o)
configファイルの変更
_config-(言語コード).yml
を新規作成してください。
中国語(zh-CN)の例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| language: zh-CN
url: https://example.com/cn root: /cn/
source_dir: source-cn public_dir: cn
include: exclude: ignore: - source-jp/ - source-fr/ - source/
|
他はそのままでOKです。ここでは主に基本的な設定を変更しました。
hexo s --config _config-cn.yml
を実行した場合、localhost:4000/cn/
にアクセスすることになるので、url
と root
を合わせて変更してください。
テーマ設定ファイルの変更
ここでは butterfly 4.13 バージョンを使用しています。5.0.0 以降のバージョンでは設定ファイルに若干の変更がありますが、本チュートリアルの内容への影響はさほどありません。新しいバージョンをお使いの場合は公式ドキュメントを参考にしてください。
_config.butterfly-(言語コード).yml
を新規作成します。
中国語(zh-CN)の例:
1 2 3 4 5 6 7 8 9 10 11
| menu: 文章: 中文: /tags/ 中文: /archives/ 中文: /analysis/ 中文: /sitemap/
|
他はそのままでOKです。ここでは主に基本設定を変更しました。
source フォルダのコピー
source
フォルダをコピーし、source-(言語コード)
とリネームしてください。
Cursor の Composer Agent 機能を使って、構造を保ちつつ source
以下のファイルをすべて翻訳することができます。私は合計64記事を20分ほどで翻訳しました。途中で手動で確認する必要がある場合もありますが、全体的な体験は良好でした。
scripts フォルダの追加
ルートディレクトリに scripts
フォルダを追加し、config-debug.js
と change_path.js
を配置してください。
config-debug.js
はサイト設定ファイルとテーマ設定ファイルをマッチングさせるためのものです(この方法は最適解ではないかもしれませんが、より良い Hexo の自動マッチング方法が見つかるまではこれで十分です)。change_path.js
はファイルパスの処理と変更を担当します。これら二つのスクリプトは Claude が作成したもので、そのまま利用可能です。
config-debug.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| 'use strict';
hexo.extend.filter.register('before_generate', async () => { const configArg = process.argv.find(arg => arg.startsWith('_config-')); if (!configArg) return;
const langMatch = configArg.match(/config-([a-z]{2,})\.yml$/); if (!langMatch) return;
const lang = langMatch[1]; const themeConfigPath = `_config.butterfly-${lang}.yml`;
await new Promise((resolve, reject) => { try { const yaml = require('js-yaml'); const fs = require('fs'); const fileContent = fs.readFileSync(themeConfigPath, 'utf8'); const themeConfig = yaml.load(fileContent); if (!themeConfig || typeof themeConfig !== 'object') { throw new Error('Invalid theme config format'); }
hexo.theme.config = themeConfig; console.log(`Theme config loaded successfully: ${themeConfigPath}`); resolve(); } catch (e) { console.error(`Failed to load theme config: ${themeConfigPath}`, e); reject(e); } }); });
|
change_path.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| 'use strict';
hexo.extend.filter.register('after_render:html', function(str, data) { const configFile = process.argv.find(arg => arg.includes('config-') && arg.endsWith('.yml')); if (!configFile) return str;
const langMatch = configFile.match(/config-([a-z]{2,})\.yml$/); if (!langMatch) return str; const lang = langMatch[1]; if (lang === 'default') return str;
const excludePaths = [ '/search.xml', '/sitemap.xml', '/robots.txt', '/feed.xml', 'http://', 'https://', 'data:image', '//', 'ws://', 'wss://' ];
let result = str;
const processPath = (match, p1, p2, p3) => { if (excludePaths.some(exclude => p2.startsWith(exclude))) { return match; } if (p2.startsWith(`/${lang}/`)) { return match; } if (p2.startsWith('/') && !p2.startsWith('//')) { return `${p1}/${lang}${p2}${p3}`; } return match; };
const patterns = [ { pattern: /(src=["'])(\/[^"']+)(["'])/g }, { pattern: /(href=["'])(\/[^"']+)(["'])/g }, { pattern: /(data-url=["'])(\/[^"']+)(["'])/g }, { pattern: /(content=["'])(\/[^"']+)(["'])/g }, { pattern: /(url\(["']?)(\/[^"')]+)(["']?\))/g } ];
patterns.forEach(({ pattern }) => { result = result.replace(pattern, processPath); });
return result; });
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; 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; }
if (path.startsWith(`/${lang}/`)) { return path; } return path.startsWith('/') ? `/${lang}${path}` : path; });
|
オプション内容
以下のコンポーネントはユーザー体験改善用で、必要に応じて選択的に使用できます。
これはフッターコンポーネントで、著作権情報、ICP番号、フレンドリンクなどを表示するためのものです。言語切り替えボタンは footer.pug や navbar.pug に配置可能です。よりよい体験を求めるなら、JSでユーザーのブラウザ言語を判定して自動的に言語版に切り替えるか、ポップアップでユーザーに言語選択させるなど、より洗練された実装が可能です。
head.pug
ここでは多言語用の link タグを追加します。検索エンジンによる認識のためです。現在はトップページのみ対応しています。本当は設定ファイルに書いた方がいいですが、手間を省くためにこの方法を取っています。とりあえず動けばOKという感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| if is_home() - var fullUrl = config.url - var baseUrl = fullUrl.replace(/\/(cn|jp|fr)$/, '') // 言語サフィックスを除去して基本URLを取得 - var currentLang = fullUrl.match(/\/(cn|jp|fr)$/) ? fullUrl.match(/\/(cn|jp|fr)$/)[1] : 'en' // 現在の言語を取得 if currentLang === 'cn' link(rel="alternate" hreflang="zh-CN" href=`${baseUrl}/cn`) link(rel="alternate" hreflang="en" href=baseUrl) link(rel="alternate" hreflang="ja-JP" href=`${baseUrl}/jp`) link(rel="alternate" hreflang="fr" href=`${baseUrl}/fr`) link(rel="alternate" hreflang="x-default" href=baseUrl) else if currentLang === 'jp' link(rel="alternate" hreflang="ja-JP" href=`${baseUrl}/jp`) link(rel="alternate" hreflang="en" href=baseUrl) link(rel="alternate" hreflang="zh-CN" href=`${baseUrl}/cn`) link(rel="alternate" hreflang="fr" href=`${baseUrl}/fr`) link(rel="alternate" hreflang="x-default" href=baseUrl) else if currentLang === 'fr' link(rel="alternate" hreflang="fr" href=`${baseUrl}/fr`) link(rel="alternate" hreflang="en" href=baseUrl) link(rel="alternate" hreflang="zh-CN" href=`${baseUrl}/cn`) link(rel="alternate" hreflang="ja-JP" href=`${baseUrl}/jp`) link(rel="alternate" hreflang="x-default" href=baseUrl) else link(rel="alternate" hreflang="en" href=baseUrl) link(rel="alternate" hreflang="zh-CN" href=`${baseUrl}/cn`) link(rel="alternate" hreflang="ja-JP" href=`${baseUrl}/jp`) link(rel="alternate" hreflang="fr" href=`${baseUrl}/fr`) link(rel="alternate" hreflang="x-default" href=baseUrl)
|
twikoo.pug
コメント融合のための設定です。
例:
twikoo.pug
設定を変更することで、中国語版の記事でも元記事のコメントを表示させ、コメントデータを共有できます。これにより、ユーザーはどの言語版であっても完全なコメント履歴を閲覧できます。
twikoo.pug
の設定例(コピー&ペーストでOK):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| - const { envId, region, option } = theme.twikoo - const { use, lazyload, count } = theme.comments - const multi_lang = theme.twikoo && theme.twikoo.multi_lang_prefix_handling === true - const lang_prefixes = multi_lang ? theme.twikoo.lang_prefixes : null
script. (() => { const getCount = () => { const countELement = document.getElementById('twikoo-count') if(!countELement) return twikoo.getCommentsCount({ envId: '!{envId}', region: '!{region}', urls: [window.location.pathname], includeReply: false }).then(res => { countELement.textContent = res[0].count }).catch(err => { console.error(err) }) }
const init = () => { // 処理後のパスを取得 let path = window.location.pathname if (!{multi_lang} && !{JSON.stringify(lang_prefixes)}) { const prefixes = !{JSON.stringify(lang_prefixes)} if (prefixes && prefixes.length > 0) { const langRegex = new RegExp(`^/(${prefixes.join('|')})/`) path = path.replace(langRegex, '/') } }
twikoo.init(Object.assign({ el: '#twikoo-wrap', envId: '!{envId}', region: '!{region}', path: path, onCommentLoaded: () => { btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.tk-owo-emotion)')) } }, !{JSON.stringify(option)}))
!{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''} }
const loadTwikoo = () => { if (typeof twikoo === 'object') setTimeout(init,0) else getScript('!{url_for(theme.asset.twikoo)}').then(init) }
if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo) else loadTwikoo() } else { window.loadOtherComment = loadTwikoo } })()
|
_config.butterfly-(言語コード).yml
で twikoo
の設定を変更し、multi_lang_prefix_handling
を true
にしてください。
1 2 3 4 5 6 7 8 9 10 11 12
|
twikoo: envId: region: visitor: false multi_lang_prefix_handling: true lang_prefixes: - cn - fr - jp option:
|
まとめ
これで、多言語対応のブログを構築するためのすべての設定は完了です。
すべての言語バージョンの静的ファイルを生成するには、以下のコマンドを実行します。
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
|
その後、package.json
に記載して各自 hexo d
を使ってサーバーにデプロイすればOKです。
以上のコマンドが完了したら、各言語バージョンを hexo d
を用いてサーバーに配置すれば、多言語対応の Hexo ブログが完成します!
もし Next.js のようなフレームワークを使うなら、多言語対応はより簡単に実現できます。
参考:
Cover From Internet