最近、言語を切り替えて広い視聴者に訴求するウェブサイトを作成するというエキサイティングなプロジェクトに取り組みました。これにより、通常は異なる言語や地域のユーザーに適応させるためのコンテンツの適応、アクセス可能性、関連性を高めるという「ローカリゼーション」の概念をよりよく理解することができました。

ローカリゼーションは単に単語を翻訳するだけでなく、ユーザーがどの言語を使用しているかに関係なく、自宅にいるような感覚を提供する体験を作り出すことです。たとえば、Amazonのようなグローバルプラットフォームでは、言語の切り替えが非常にスムーズであり、それはほとんど魔法のような感覚です。ユーザーエクスペリエンスを向上させるだけでなく、この機能は、より広い視聴者に到達し、世界中の顧客との強いつながりを育むことで、ビジネスの拡大に重要な役割を果たします。

目次

i18nとは何か、なぜ使うのか?

i18nは、アプリケーションが複数の言語をサポートすることを意味します。”i18n”は、「internationalization」の最初の「i」と最後の「n」の間に18文字があることに由来しています。これは、テキストの翻訳、日付や数字のフォーマット、通貨の管理、地域ごとの慣習に対応することで、あなたのアプリを世界中のユーザーに適応させることを目的としています。

国際化を有効にすることで、あなたのアプリは単なるツールではなく、ユーザーの好みや文化に直接訴える包括的なプラットフォームになります。

さあ、始めましょう。

このコンセプトを実現する方法を示すために、非常にシンプルなデモマルチリンガルWebアプリケーションを作成し、ダークモード切り替え機能を追加します。

前提条件

  1. Reactの基本知識 – コンポーネントの作成や状態の管理、useStateuseEffectなどのHooksの使用方法を理解している必要があります。Reactが初めての場合は、公式Reactドキュメントを参照してください。しっかりとした基礎を身につけることをお勧めします。

  2. 国際化の概念に精通する – 国際化(i18n)の基本を知り、その重要性を理解することで、プロジェクトの文脈がわかります。この記事の前半セクションでは基本事項が説明されています。

  3. Tailwind CSS – スタイリングにはTailwind CSSを使用します。これはHTMLから離れることなく、モダンでレスポンシブなデザインを構築するのに役立つユーティリティ優先のCSSフレームワークです。馴染みがない場合は、Tailwindのドキュメントをご覧ください。

  4. Node.js – 依存関係を処理するためにシステムにNode.jsがインストールされていることを確認してください。最新バージョンはNode.jsからダウンロードできます。

  5. パッケージマネージャー – プロジェクトの依存関係を管理するためには、npm(Node.jsに含まれています)またはyarnが必要です

使用するツール

  1. コードエディター

  2. ローカライゼーションライブラリ: react-i18next

  3. アイコンライブラリ: hero-icons

ステップ1: プロジェクトのセットアップ方法

プロジェクトの初期化

高速セットアップのために Vite を使用:

npm create vite@latest multilingual-demo

ターミナルに表示される指示に従い、以下の画像のように開発用にReactとTypeScriptを選択してください:

依存関係のインストール

このプロジェクトに必要な依存関係をインストールするために、ターミナルで以下のコマンドを実行してください:

npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend heroicons 
npm install tailwindcss postcss autoprefixer  
npx tailwindcss init

TailwindCSSの設定

tailwind.config.tsファイルを更新してください:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  darkMode: "class", //ダークモード機能のため
  theme: {
    container: {
      center: true,
      padding: "1.25rem",
      screens: {
        sm: "1200px",
      },
    },
    extend: {},
  },
  plugins: [],
};

src/index.cssにTailwindCSSを追加してください:

@tailwind base;  
@tailwind components;  
@tailwind utilities;

ステップ2:i18nextを使用した国際化のセットアップ方法

i18nextの初期化

srcフォルダにファイルを作成し、i18nextを設定してください:

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";

i18next.use(LanguageDetector).use(initReactI18next).use(Backend).init({
  returnObjects: true,
  fallbackLng: "en", // 選択された言語が設定されていない場合のフォールバック言語
  debug: true, //エラーを見るために有効にする
  //   lng: "en", //デフォルト言語は英語
});

このファイルの内容を簡単に見てみましょう。これは翻訳機能を有効にするためのコア設定を行い、言語切り替え機能がアプリ全体でスムーズに動作することを確認する責任があります。

  • i18next:翻訳に使用しているコア内部化ライブラリ。

  • LanguageDetector:ブラウザの設定に基づいてユーザーの希望言語を自動的に検出するのに役立ちます。

  • initReactI18next:Reactとi18nextプラグインを統合し、useTranslation HookなどのHooksやその他のユーティリティを提供する責任があります。

  • Backend:外部ソースから翻訳データを動的に取得します。この場合、JSONファイルを使用します。

このファイルをmain.tsxファイルにインポートしてください:

//main.tsx

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import "./i18n.tsx";  //ここにインポート

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <React.Suspense fallback="loading">
      <App />
    </React.Suspense>
  </StrictMode>
);

翻訳ファイルを作成

public/localesディレクトリに、各言語のサブフォルダ(たとえば、enfr)を作成し、translation.jsonファイルを含めます:

en/translation.json

{
    "greeting": "Welcome to the Language Playground",
    "detail": {
        "line1": "Did you know that over 7,000 languages are spoken worldwide?",
        "line2": "This Playground demonstrates how web applications can support users in multiple languages, making them accessible and inclusive to people from different backgrounds."
    }
}

fr/translation.json

{
    "greeting": "Bienvenue sur le terrain de jeu linguistique",
    "detail": {
        "line1": "Saviez-vous que plus de 7 000 langues sont parlées dans le monde ?",
        "line2": "Ce terrain de jeu démontre comment les applications web peuvent prendre en charge les utilisateurs dans plusieurs langues, les rendant accessibles et inclusives aux personnes de différents horizons."
    }
}

ここで、i18nextに供給される翻訳ファイルを持つ多くの言語を追加できます。 JSONファイル内のキーは、それらをウェブサイトで表示する際の参照として使用されることに注意してください。

ステップ3:コンポーネントの構築方法

srcディレクトリにcomponentsフォルダを作成し、次のコンポーネントを追加してください:

言語セレクター

LanguageSelectorコンポーネントを作成します- ユーザーが言語を動的に切り替えるのを支援するselect要素を含みます:

import { useEffect, useState } from "react";
import i18next from "i18next";
import { useTranslation } from "react-i18next";

type languageOption = { language: string; code: string };

const languageOptions: languageOption[] = [
  {
    language: "English",
    code: "en",
  },
  { language: "French", code: "fr" },
  { language: "German", code: "de" },
  { language: "Spanish", code: "es" },
  { language: "Arabic", code: "ar" },
  { language: "Yoruba", code: "yo" },
];

const LanguageSelector = () => {
  // i18nextの検出された言語またはデフォルトの言語から初期言語を設定
  const [language, setLanguage] = useState(i18next.language);

  const { i18n } = useTranslation();

  const handleLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedLanguage = e.target.value;
    setLanguage(selectedLanguage);
    i18next.changeLanguage(selectedLanguage); // i18nextで言語を更新
  };

  useEffect(() => {
    document.body.dir = i18n.dir(); //ボディをltrまたはrtlに設定
  }, [i18n, i18n.language]);

  return (
    <select
      id="language"
      value={language}
      onChange={handleLanguageChange}
      className="p-2 border border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
        dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 dark:focus:border-indigo-400 dark:focus:ring-indigo-700 dark:focus:ring-opacity-50"
    >
      {languageOptions.map(({ language, code }, key) => (
        <option value={code} key={key}>
          {language}
        </option>
      ))}
    </select>
  );
};

export default LanguageSelector;
  • i18nextによって検出された言語またはデフォルトの設定言語で言語を初期化します。

  • useTranslationフックは、国際化設定とやり取りするためにi18nextからi18nインスタンスを公開します。

  • handleLanguageChange関数は、ユーザーがドロップダウンメニューから新しい言語を選択したときにトリガーされ、ユーザーが選択した言語を更新するために使用されます。

テキスト方向の実装

HTMLのdir属性は、特にテキスト方向が異なる言語を扱う際に、ウェブアプリケーションのアクセシビリティと包括性を確保するための重要な機能です。例えば:

  • 左から右(LTR): 英語、フランス語、スペイン語など、多くの言語がこの方向に従います。

    右から左(RTL): アラビア語やヘブライ語などの言語は、可読性と文化的文脈を維持するためにテキストの配置とレイアウトを反転させる必要があります。

この機能をアプリで実現するために、document.body.dirからのdirに設定し、useEffectフックを使用して言語選択の変更を監視します。

ダークモード切り替え

DarkModeToggleコンポーネントを作成して、ユーザーが選好に応じてライトモードとダークモードを切り替えられるようにします。

import { useEffect, useState } from "react";
import { SunIcon, MoonIcon } from "@heroicons/react/solid";

const DarkModeToggle = () => {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    // 最初の読み込み時にローカルストレージまたはシステム設定を確認
    const isDark =
      localStorage.getItem("theme") === "dark" ||
      (!localStorage.getItem("theme") &&
        window.matchMedia("(prefers-color-scheme: dark)").matches);
    setDarkMode(isDark);
    document.documentElement.classList.toggle("dark", isDark);
  }, []);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    document.documentElement.classList.toggle("dark", !darkMode);
    localStorage.setItem("theme", !darkMode ? "dark" : "light");
  };

  return (
    <button
      aria-label="Toggle dark mode"
      onClick={toggleDarkMode}
      className="p-1 rounded"
    >
      {darkMode ? (
        <SunIcon
          className="w-6 h-6 text-yellow-500 "
          onClick={toggleDarkMode}
        />
      ) : (
        <MoonIcon className="w-6 h-6 text-gray-900 " onClick={toggleDarkMode} />
      )}
    </button>
  );
};

export default DarkModeToggle;

ヘッダーコンポーネント

Headerコンポーネントは、DarkModeTogglelanguageSelectorコンポーネントの親コンポーネントとして機能します。

import DarkModeToggle from "./DarkModeToggle";
import LanguageSelector from "./LanguageSelector";

const Header = () => {
  return (
    <header className="container flex justify-between">
      <DarkModeToggle />
      <LanguageSelector />
    </header>
  );
};

export default Header;

ステップ4: メインアプリコンポーネント

src/appファイルに、以下を含める:

import { useTranslation } from "react-i18next";
import Header from "./components/Header";

const App = () => {
  const { t } = useTranslation();

  const line1 = t("detail.line1");
  const line2 = t("detail.line2");

  return (
    <div className="h-[100vh] bg-white text-black dark:bg-gray-900 dark:text-white py-8">
      <Header />
      <div className="container text-center max-w-2xl mt-28">
        <h1 className="text-4xl font-bold">{t("greeting")}</h1>
        <p className="mt-8">{line1}</p>
        <p className="mt-2">{line2}</p>
      </div>
    </div>
  );
};

export default App;
  • 「react-i18next」からのuseTranslationフックは、翻訳されたテキストを取得するために使用されるt関数を公開します。

  • この関数は、翻訳ファイル(例:en.jsonfr.jsonなど)からキーに基づいて翻訳された文字列を取得します。

これらの手順に従うことで、アプリは翻訳がシームレスに統合された完全な機能を備えるようになります。これがアプリの最終的な見た目です:

ライブデモをチェックして、GitHubでソースコードをご覧ください。

結論

ユーザーが好みの言語を選択できるウェブサイトを作成することは、技術的な成果だけでなく、ウェブを包括的で歓迎するものにする一歩です。

国際化(i18n)をReact-i18nextのようなツールとTailwind CSSで組み合わせることで、柔軟で使いやすく、世界中のユーザーにアクセスしやすいアプリケーションを構築することができます。

このプロジェクトでは、i18nの設定、言語切り替え機能の追加、およびより良い使い勝手のための「ダークモード」の組み込みについて説明しました。

参考文献

https://react.i18next.com/

https://www.youtube.com/watch?v=dltHi9GWMIo