はじめに
グローバル化が進む現代において、Webアプリケーションの多言語対応は重要な要素となっています。今回、小説読書プラットフォーム「NovelCat」において、日本語と英語の完全な多言語対応を実装しました。単純な翻訳だけでなく、それぞれの言語圏のユーザーに最適化された読書体験を提供する仕組みについて、開発者として実装した経験をもとに詳しく解説します。
実際のサイト:
この記事では、Hugoの多言語機能を活用した実装方法から、言語ごとに異なる読書体験の最適化まで、多言語対応の実践的なノウハウをお伝えします。
開発状況: 現在GitHubでのオープンソース化に向けた準備を進めており、リポジトリ公開後に本記事を更新予定です。
免責事項: この記事は開発者本人による実装記録です。環境や要件によって最適な実装方法は異なる場合があります。
なぜ多言語対応が必要だったのか
小説というコンテンツは、言語や文化に深く根ざしたものです。日本語の小説を読む際は縦書きが好まれる一方で、英語圏では横書きが一般的です。また、読書設定の項目名や操作方法も、それぞれの言語圏のユーザーに馴染みのある形で提供する必要がありました。
単純に翻訳するだけでなく、以下の要素を考慮した多言語対応を目指しました:
文化的な読書習慣の違い: 日本語は縦書き、英語は横書きを基本とする読書文化の違いへの対応
UI表現の最適化: 各言語における自然な表現とユーザビリティの向上
コンテンツの言語特性: 日本語と英語それぞれの文字特性に応じた表示最適化
検索エンジン対応: 各言語でのSEO最適化とアクセシビリティの確保
Hugoによる多言語サイト構成
基本設定
Hugoの多言語機能を活用して、以下の構成でサイトを設計しました:
# hugo.yaml
languages:
jp:
languageCode: ja-jp
contentDir: content/jp
languageName: 日本語
weight: 1
en:
languageCode: en-us
contentDir: content/en
languageName: English
weight: 2
この設定により、content/jp/
とcontent/en/
に分けてコンテンツを管理し、それぞれ独立したサイト構造を持たせています。
ディレクトリ構造
content/
├── jp/
│ ├── posts/
│ │ ├── kyokansha-ch1.md
│ │ └── trump-reincarnation-ch1.md
│ └── sidebar/
│ └── bio.md
└── en/
├── posts/
│ ├── kyokansha-ch1.md
│ └── trump-reincarnation-ch1.md
└── sidebar/
└── bio.md
同一作品でも、言語ごとに独立したファイルとして管理することで、翻訳の品質管理と更新の柔軟性を確保しています。
テンプレートレベルでの多言語対応
条件分岐による表示制御
最も重要な実装は、テンプレート内での言語判定と条件分岐です:
{{ if eq .Language.Lang "jp" }}
<h3>読書設定</h3>
<button class="settings-collapse-btn">設定を閉じる ▲</button>
{{ else }}
<h3>Reading Settings</h3>
<button class="settings-collapse-btn">Close Settings ▲</button>
{{ end }}
この手法により、同一テンプレートで複数言語に対応しながら、それぞれに最適化された表示を実現しています。
読書設定UIの完全多言語化
読書設定パネルでは、すべての機能ラベルを言語に応じて切り替えています:
<div class="setting-item">
{{ if eq .Language.Lang "jp" }}
<label>表示モード</label>
<div class="display-mode-controls">
<button class="reading-mode-toggle" onclick="toggleVerticalMode()">縦書き</button>
<button class="fullscreen-toggle" onclick="toggleFullscreen()">全画面</button>
</div>
{{ else }}
<label>Display Mode</label>
<div class="display-mode-controls">
<button class="reading-mode-toggle" onclick="toggleVerticalMode()">Vertical</button>
<button class="fullscreen-toggle" onclick="toggleFullscreen()">Fullscreen</button>
</div>
{{ end }}
</div>
JavaScriptでの動的多言語対応
言語判定とテキスト動的変更
静的なテンプレートだけでなく、JavaScriptでの動的なテキスト変更も多言語対応が必要でした:
function toggleVerticalMode() {
const body = document.body;
const button = document.querySelector('.reading-mode-toggle');
const isJapanese = document.documentElement.lang === 'jp';
if (body.classList.contains('vertical-mode')) {
body.classList.remove('vertical-mode');
button.textContent = isJapanese ? '縦書き' : 'Vertical';
localStorage.setItem('readingMode', 'horizontal');
} else {
body.classList.add('vertical-mode');
button.textContent = isJapanese ? '横書き' : 'Horizontal';
localStorage.setItem('readingMode', 'vertical');
}
}
document.documentElement.lang
により現在の言語を判定し、ボタンテキストや確認メッセージを適切な言語で表示しています。
確認ダイアログの多言語化
ユーザー操作に対する確認メッセージも多言語対応しています:
window.resetAllSettings = function() {
const isJapanese = document.documentElement.lang === 'jp';
const confirmMessage = isJapanese ?
'すべての設定をデフォルトに戻しますか?' :
'Reset all settings to default?';
if (!confirm(confirmMessage)) {
return;
}
// リセット処理...
const resetMessage = isJapanese ?
'設定をリセットしました' :
'Settings have been reset';
alert(resetMessage);
};
章ナビゲーションの多言語対応
自動章検出システム
章間ナビゲーション機能では、言語ごとに独立したコンテンツ管理が重要でした:
{{ range where .Site.Pages "Type" "posts" }}
{{ if and .File (eq .Language.Lang $.Language.Lang) (hasPrefix .File.BaseFileName (printf "%s-ch" $seriesName)) }}
{{ $seriesPages = $seriesPages | append . }}
{{ end }}
{{ end }}
(eq .Language.Lang $.Language.Lang)
により、現在の言語と同じ言語のページのみを章リストに含めることで、言語間でのコンテンツ混在を防いでいます。
章ナビゲーションUIの多言語化
章の表示と操作も言語に応じて最適化しています:
function toggleChapterList() {
const chapterList = document.getElementById('chapter-list');
const button = document.querySelector('.chapter-list-toggle');
const isJapanese = document.documentElement.lang === 'jp';
if (chapterList) {
const isHidden = chapterList.classList.toggle('hidden');
const chapterCount = document.querySelectorAll('.chapter-list-item').length;
if (button) {
const navText = button.querySelector('.nav-text');
if (navText) {
if (isHidden) {
navText.innerHTML = isJapanese ?
`章一覧を表示<br><small>${chapterCount}章</small>` :
`Show Chapters<br><small>${chapterCount} chapters</small>`;
} else {
navText.innerHTML = isJapanese ?
`章一覧を隠す<br><small>${chapterCount}章</small>` :
`Hide Chapters<br><small>${chapterCount} chapters</small>`;
}
}
}
}
}
実際の章ナビゲーション機能は日本語版と英語版で比較体験できます。
言語切り替え機能の実装
相互リンクシステム
各記事には、対応する他言語版への切り替えリンクを設置しています:
<div class="language-switch">
{{ if eq .Language.Lang "jp" }}
{{ range .Translations }}
{{ if eq .Language.Lang "en" }}
<a href="{{ .RelPermalink }}" class="lang-switch-link">Read in English</a>
{{ end }}
{{ end }}
{{ else if eq .Language.Lang "en" }}
{{ range .Translations }}
{{ if eq .Language.Lang "jp" }}
<a href="{{ .RelPermalink }}" class="lang-switch-link">日本語で読む</a>
{{ end }}
{{ end }}
{{ end }}
</div>
Hugoの.Translations
機能により、同一コンテンツの他言語版を自動的に検出し、適切なリンクを生成しています。
SEO対応と技術的配慮
言語メタタグとhreflang
検索エンジン最適化のため、適切なメタタグを設定しています:
<html lang="{{ .Language.Lang }}">
<head>
<meta name="language" content="{{ .Language.LanguageCode }}">
<!-- 他言語版へのhreflangタグ -->
{{ range .Translations }}
<link rel="alternate" hreflang="{{ .Language.LanguageCode }}" href="{{ .Permalink }}">
{{ end }}
</head>
URL構造の最適化
各言語版は独立したURL構造を持ち、検索エンジンでの適切なインデックス化を実現しています:
- 日本語版:
https://novelcat.pages.dev/posts/article-name/
- 英語版:
https://novelcat.pages.dev/en/posts/article-name/
実装時の課題と解決策
課題1: HTML言語属性の設定ミス
初期実装では、HTML要素のlang
属性が正しく設定されておらず、JavaScriptでの言語判定が失敗していました。
問題のコード:
<html lang="{{ .Site.Language }}">
解決策:
<html lang="{{ .Language.Lang }}">
.Site.Language
ではなく.Language.Lang
を使用することで、正しい言語コードを取得できました。
課題2: 設定の言語間継承
読書設定をlocalStorageで保存する際、言語を切り替えても設定が引き継がれるよう実装しました。これにより、ユーザーは言語を変更しても同じ読書環境で作品を楽しめます。
課題3: 文字エンコーディングと表示
日本語と英語では文字エンコーディングや表示特性が異なるため、CSSでの調整が必要でした:
/* 日本語の場合の最適化 */
[lang="jp"] {
font-family: "Hiragino Kaku Gothic ProN", "Hiragino Sans", Meiryo, sans-serif;
}
/* 英語の場合の最適化 */
[lang="en"] {
font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
パフォーマンスと保守性の向上
コンテンツ管理の効率化
言語ごとに独立したファイル管理により、以下のメリットを実現しています:
翻訳品質の管理: 各言語版を独立して編集・校正可能
更新の柔軟性: 一方の言語版のみの更新や、言語特有のコンテンツ追加が容易
検索・分析: 言語ごとのアクセス分析やコンテンツ最適化が可能
キャッシュとビルド最適化
Hugo の多言語機能により、各言語版は独立してビルドされ、効率的なキャッシュ戦略を実現しています。
今後の拡張予定
現在、さらなる多言語対応の改善を計画しています:
追加言語の対応: 中国語(繁体字・簡体字)、韓国語等への拡張検討
地域特性の対応: 同一言語でも地域による表現の違いへの対応
RTL言語への対応: アラビア語等の右から左に読む言語への対応研究
音声読み上げ: 各言語での自然な音声読み上げ機能の実装
まとめ
Hugoの多言語機能とJavaScriptを組み合わせることで、単純な翻訳を超えた、各言語圏に最適化された読書体験を提供する多言語サイトを構築できました。特に、静的サイトジェネレーターでありながら、動的な言語切り替えとローカルストレージによる設定継承を実現したことで、ユーザビリティの高い多言語対応を達成しています。
多言語対応は技術的な実装だけでなく、各言語圏の文化や読書習慣への理解が重要であることを実感しました。今後も継続的に改善を重ね、より多くの読者にとって使いやすいプラットフォームを目指していきます。
ぜひ両言語版をお試しください:
言語切り替え機能や、それぞれの言語に最適化された読書設定をご体験ください。
技術仕様
- フレームワーク: Hugo v0.147.9
- 対応言語: 日本語(ja-jp)、英語(en-us)
- 多言語機能: Hugo Multilingual Mode
- SEO対応: hreflang、language meta tags
- URL構造: Path-based language routing
関連リンク
- NovelCat 日本語版 - 日本語最適化された読書体験
- NovelCat English - 英語版サイト
- 多言語比較体験ページ - 日英切り替えをお試しください
- GitHubリポジトリ - オープンソース化準備中(公開後に本記事を更新予定)
- Hugo Multilingual Documentation
- neopost - ベースとして使用させていただいたHugoテーマ
今すぐ体験:
- 日本語: https://novelcat.pages.dev/posts/trump-reincarnation-ch1/
- English: https://novelcat.pages.dev/en/posts/trump-reincarnation-ch1/
開発者注記: この記事は実際の開発経験に基づいて作成されており、実装の詳細やトラブルシューティングについては開発者本人の知見を基にしています。