【JavaScript応用講座】JSONで始める!i18n(多言語対応)実装入門

1. i18nとは?

i18n は “internationalization”(インターナショナライゼーション)の略称で、 “i” から “n” までの18文字を省略して名付けられています。

簡単にいえば、アプリケーションやゲームを多言語に対応させるための設計・実装のことです。

✅ 関連用語

  • i18n:国際化(internationalization)
  • l10n:ローカライズ(localization)
  • t9n:翻訳(translation)

✅ なぜ必要?

  • 世界中のユーザーに使ってもらうため
  • 表示する言語を切り替えられるようにするため
  • 管理やメンテナンスを効率化するため(=翻訳ファイルの分離)

💬 例:日本語と英語で表示切り替え

Key 日本語 英語
title ゲーム開始 Start Game
settings 設定 Settings
exit 終了 Exit

2. 言語ファイルを準備する

i18nをシンプルに始めるなら、言語ごとにJSONファイルを分ける方法が一番手軽でおすすめです。

📁 例:フォルダ構成

/assets/data/lang_ja.json
/assets/data/lang_en.json

📝 lang_ja.json

{
  "title": "ゲーム開始",
  "settings": "設定",
  "exit": "終了"
}

📝 lang_en.json

{
  "title": "Start Game",
  "settings": "Settings",
  "exit": "Exit"
}

💡 ポイント

  • キー(例:title, settings)はすべての言語で共通にする
  • 翻訳ファイルを増やすだけで言語追加が可能になる

3. JSONを読み込んで画面に反映

ここでは、選択した言語のJSONファイルを fetch() で読み込み、HTMLの要素に反映する基本的な実装を紹介します。

🔄 HTML側(例)

<h1 id="title"></h1>
<button id="settings-btn"></button>
<button id="exit-btn"></button>

📥 JavaScriptで読み込み

async function loadLanguage(lang = "ja") {
  const res = await fetch(`/assets/data/lang_${lang}.json`);
  const translations = await res.json();

  document.getElementById("title").textContent = translations.title;
  document.getElementById("settings-btn").textContent = translations.settings;
  document.getElementById("exit-btn").textContent = translations.exit;
}

// 初期読み込み(例:日本語)
loadLanguage("ja");

💡 応用のヒント

  • data-i18n=“key名” を使ってループで一括更新することも可能
  • ボタンで言語を切り替える実装はこのあと紹介します

4. 切り替えボタンとlocalStorage保存

言語選択をユーザーが行えるようにし、その選択を localStorage に保存して、次回アクセス時にも同じ言語が適用されるようにします。

🌐 HTMLに言語切り替えボタンを追加

<button onclick="switchLanguage('ja')">日本語</button>
<button onclick="switchLanguage('en')">English</button>

🔁 JavaScriptで切り替え処理を実装

async function switchLanguage(lang) {
  localStorage.setItem("lang", lang);
  await loadLanguage(lang);
}

🚀 ページ初期表示時に保存された言語を適用

const savedLang = localStorage.getItem("lang") || "ja";
loadLanguage(savedLang);

💡 ポイント

  • localStorage はページを再読み込みしても値が保持される(ブラウザに永続保存)
  • ユーザーの利便性を高める重要な工夫

5. 実用例:ボタン・UI・テキストに適用

i18nの導入は、ボタンやメニューといったUIだけでなく、説明文やポップアップ、モーダルなどゲームやアプリ全体に展開できます。

ここでは、共通のキーを data-i18n 属性に持たせ、JavaScriptで一括置き換えする方法を紹介します。

🧩 HTML例(共通構造)

<h1 data-i18n="title"></h1>
<button data-i18n="settings"></button>
<p data-i18n="description"></p>

⚙ JavaScriptで一括反映

async function loadLanguage(lang = "ja") {
  const res = await fetch(`/assets/data/lang_${lang}.json`);
  const translations = await res.json();

  document.querySelectorAll("[data-i18n]").forEach(el => {
    const key = el.getAttribute("data-i18n");
    if (translations[key]) {
      el.textContent = translations[key];
    }
  });
}

✅ メリット

  • UIを構造的に書ける(HTMLと翻訳が分離)
  • 言語ファイルを更新すれば即反映される
  • コードの保守性・拡張性が高い

よくある落とし穴

i18nの導入はシンプルに見えて、いくつかつまずきやすいポイントがあります。ここでは実際によく起きる問題とその解決策を紹介します。

❗ 非同期処理のタイミングずれ

loadLanguage("ja");
document.getElementById("title").textContent = "..."; // ← 上書きされてしまう!

✅ 対処法

  • 翻訳ファイルの読み込み完了後にUIの更新を行う
  • await を正しく使う or .then() を使って同期タイミングを管理

❗ 要素がまだ描画されていない

document.querySelectorAll("[data-i18n]").forEach(...); // 要素がまだDOMに存在しない場合、反映されない!

✅ 対処法

  • DOMContentLoaded イベントの後に呼び出す
document.addEventListener("DOMContentLoaded", () => {
  loadLanguage("ja");
});

❗ 翻訳ファイルにキーが存在しない

{
  "start": "ゲーム開始"
}

<h1 data-i18n="title"></h1> <!-- title が存在しない! -->

✅ 対処法

  • 翻訳キーの見直し&共通化(lintやチェックツールも活用)
  • 未定義のキーにはデフォルト値を入れるなどの工夫も可

❗ 言語切り替えが反映されない(キャッシュ問題)

  • ブラウザがJSONをキャッシュしていて、内容が変わっても反映されない場合がある

✅ 対処法

fetch(`/assets/data/lang_${lang}.json?ts=${Date.now()}`); // タイムスタンプを付与

これらの落とし穴を意識しておくだけで、実装の安定性が大きく変わります。

7. まとめ:i18nを味方につける

  1. まとめ:i18nを味方につける

i18n(国際化)は一見すると専門的な技術のように思われがちですが、JSONとJavaScriptを活用することで、意外なほどシンプルに実装できます。

今回の記事では、

  • 言語ファイルの設計(JSON)
  • 画面への反映方法(textContent / data-i18n)
  • 言語切り替えとlocalStorageの保存
  • よくあるミスと対処法

…と、i18nの実用的な導入フローを学びました。

🎯 これからやるべきこと

  • 自分のUIに data-i18n を取り入れてみる
  • 日本語+英語の2言語対応から始めてみる
  • 翻訳ファイルや設定を localStorage で保存してみる

💬 最後にひとこと

「i18n対応」は、“作品を自分の世界だけに留めない”という表現の広がり。たったひとつのJSONファイルが、あなたのゲームを世界に届ける鍵になるかもしれません。

今後もゲーム開発やWebアプリ制作の中で、i18nを“味方”にして、もっと自由に、もっと柔軟に表現していきましょう!

余談


user icon

ありがとうございます。

localStorage はページを再読み込みしても値が保持される(ブラウザに永続保存)

これは、DecapCMSの時にやった、

F12 → DevTools を起動 上部タブで「Application(アプリケーション)」を選択 左サイドバーから: 「Storage」→「Clear site data」にチェックを入れて「Clear site data」 「IndexedDB」→ すべてのデータベースを右クリック → 「Delete」

これかな…。


ai icon

そうです、それまさに localStorage や IndexedDB などの「Webストレージ系」のデータを完全に消す操作 です!


🔍 あらためて整理すると:

ストレージ種別 解説
localStorage キー・バリュー形式でデータを保存。ページを再読み込みしても値は保持される(ブラウザごとに永続)。容量は約5MB前後。
sessionStorage localStorageと似てるが、ブラウザタブを閉じると消える。
IndexedDB 構造化された大きなデータ保存用。CMSなど高度なWebアプリで利用されることが多い。
Cookies サーバーとのやりとりで使うことも多い。セッション管理などで使われる。

🔧 DevTools の Application タブでの操作

  • 「Clear site data」ボタン

    • localStorageIndexedDBCookiesCache Storage などをまるごと削除します。
  • localStorage だけを消す

    • 左ペインの「Storage → Local Storage → 自分のサイトURL」を選択 → 右側のキーを右クリック → 「Clear」

✅ 実用のヒント

i18nで使った localStorage.getItem("lang") の保存値もこの操作で削除されます。 つまり、リセットして再テストしたいときに便利です。


もし今後、

  • 設定を保持したいがリセットもしたい
  • 複数端末での挙動を検証したい

などが出てきたら、localStorageIndexedDB の扱い方をもう一段深く学んでもいいかもしれませんね。

希望があれば、その辺の講座も用意できます!