Menu

国際化

Nexty.devはnext-intlを活用して包括的な多言語サポートを提供し、ルートレベルの言語切り替え、自動言語検出、シームレスなロケール管理を含む堅牢な国際化ソリューションを提供します。

このガイドではnext-intlの基本的な使用方法をカバーし、新しい言語と翻訳コンテンツの追加方法を説明し、Nextyが提供する高度な国際化機能について探求します。

ファイル構造

i18n/
├── messages/                # 翻訳ファイルディレクトリ
│   ├── en/                  # 英語翻訳
│   │   └── common.json
│   ├── zh/                  # 中国語翻訳
│   │   └── common.json
│   └── ja/                  # 日本語翻訳
│       └── common.json
├── request.ts               # next-intl リクエスト設定
└── routing.ts               # ルーティングと言語設定
 
components/
├── LocaleSwitcher.tsx       # 言語切り替えコンポーネント
└── LanguageDetectionAlert.tsx  # 言語検出アラートコンポーネント
 
stores/
└── localeStore.ts           # 言語状態管理
 
middleware.ts                # ミドルウェア設定
next.config.mjs              # Next.js設定

基本的な使用方法

コンポーネントでの翻訳の使用

Reactコンポーネントでは、useLocaleフックを使用して現在のロケールを取得し、useTranslationsフックで翻訳コンテンツにアクセスします。

import { useLocale, useTranslations } from 'next-intl';
 
export default function MyComponent() {
  const locale = useLocale();
  const t = useTranslations('Home');
  
  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

注目すべき点

APP Routerモードでは、useLocaleuseTranslationsはクライアントコンポーネントとサーバーコンポーネントの両方で動作します。サーバーコンポーネントの場合は、コンポーネント定義でasyncキーワードを使用しないだけで、サーバーサイドレンダリングを維持しながらこれらのフックが適切に機能します。

サーバーコンポーネントでの使用

asyncサーバーコンポーネントでは、getLocaleを使用して現在のロケールを取得し、getTranslationsで翻訳コンテンツにアクセスします。

import { getLocale, getTranslations } from 'next-intl/server';
 
export default async function ServerComponent() {
  const locale = await getLocale();
  const t = await getTranslations('Home');
  
  return (
    <div>
      <h1>{t('title')}</h1>
    </div>
  );
}

パラメータ化された翻訳の使用

JSONファイル内:

{
  "welcome": "ようこそ、{name}さん!",
  "countdown": "あと{countdown}秒で終了します"
}

コンポーネント内:

const message = t('welcome', { name: 'John' });
const timer = t('countdown', { countdown: countdown.toString() });

ローデータの使用

配列やオブジェクトなどの複雑なデータ構造については、t.raw()メソッドを使用してローの翻訳データにアクセスします:

const headerLinks = t.raw('Header.links') as HeaderLink[];
const features = t.raw('Landing.Features.items');

国際化対応Linkコンポーネント

@/i18n/routingから公開されたLinkコンポーネントを使用します。hrefプロップには言語プレフィックスは不要で、next-intlが現在の言語に基づいてロケールルーティングを自動的に処理します。

import { Link as I18nLink } from '@/i18n/routing';
 
<I18nLink href="/about">
  {t('aboutUs')}
</I18nLink>

国際化対応useRouter

プログラムによるナビゲーションには@/i18n/routinguseRouterフックを使用します。Linkコンポーネントと同様に、next-intlがロケールルーティングを自動的に処理するため、ルートに言語プレフィックスは不要です。

import { useRouter } from '@/i18n/routing';
 
const router = useRouter();
 
const handleClick = () => {
  router.push('/dashboard');
};

新しい言語追加の手順

ステップ1. 言語設定の更新

i18n/routing.tsのロケール設定に新しい言語を追加します:

i18n/routing
export const LOCALES = ['en', 'zh', 'ja', 'ko'] // 韓国語を追加
export const DEFAULT_LOCALE = 'en'
export const LOCALE_NAMES: Record<string, string> = {
  'en': "English",
  'zh': "中文", 
  'ja': "日本語",
  'ko': "한국어", // 韓国語の表示名を追加
};

ステップ2. 翻訳ファイルの作成

言語用の新しいディレクトリを作成し、既存の翻訳ファイルをテンプレートとしてコピーします:

mkdir i18n/messages/ko
cp i18n/messages/en/common.json i18n/messages/ko/common.json

ステップ3. コンテンツの翻訳

新しい言語ファイル内のすべてのコンテンツを翻訳します。i18n/messages/ko/common.jsonを編集し、英語コンテンツを韓国語翻訳に置き換えます。

ステップ4. ミドルウェア設定の更新

新しいロケールを含めるためにミドルウェアマッチャーを更新します:

middleware.ts
export const config = {
  matcher: [
    '/',
 
    '/(en|zh|ja|ko)/:path*', // 韓国語を追加
 
    '/((?!api|_next|_vercel|auth|.*\\.|favicon.ico).*)'
  ]
};

ステップ5. ルートリダイレクトの更新(オプション)

必要に応じて、next.config.mjsで対応するリダイレクトルールを追加します:

next.config.mjs
{
  source: "/ko/dashboard", 
  destination: "/ko/dashboard/settings",
  permanent: true,
}

注目すべき点

言語を削除するには、これらの手順を逆にします:設定からロケールを削除し、翻訳ファイルを削除し、ミドルウェアマッチャーを更新します。

新しい翻訳ネームスペースの追加

新しいページや機能を追加する際は、翻訳を論理的なネームスペースに整理するために別のJSONファイルを作成することをお勧めします。

例えば、既存のcommon.jsonと併せて、ランディングページの翻訳用に新しいLanding.jsonを追加できます:

i18n/messages/en/
├── common.json
├── Landing.json         # 新しいLandingの追加

リクエスト設定の更新:

i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
import { routing } from './routing';
 
export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale;
 
  if (!locale || !routing.locales.includes(locale as any)) {
    locale = routing.defaultLocale;
  }
 
  const common = (await import(`./messages/${locale}/common.json`)).default;
  const Landing = (await import(`./messages/${locale}/Landing.json`)).default; // Landingをインポート
 
  return {
    locale,
    messages: {
      ...common,
      Landing, // Landingをインポート
    }
  };
});

新しいネームスペースの使用

const t = useTranslations('Landing');

SSG(静的サイト生成)のベストプラクティス

注目すべき点

  • v2.3.0以前では、レイアウト内の動的メソッドが真のSSG実装を妨げていました。ページはまだサーバーサイドレンダリング(SSR)されており、SEOメリットは維持されていましたが、静的生成によるパフォーマンス向上は提供されませんでした。
  • v2.3.0以降については、SSGを実現するために以下の実装アプローチに従ってください。

ブログ詳細ページ、プライバシーポリシー、利用規約ページなどの静的コンテンツページでは、サーバーCPU消費量を削減し、ページ読み込みパフォーマンスを向上させるためにSSGを使用することをお勧めします。

シナリオ1:ロケールが唯一の動的パラメータであるページ

適用可能なページ/privacy-policy/zh/privacy-policyのようなURL。唯一の動的パラメータがロケールプレフィックスである場合。

実装アプローチ

  1. paramsからlocaleパラメータを抽出する(SSG互換性のためにgetLocale()を避ける)
  2. generateStaticParamsを実装してサポートされているすべてのロケール用にページを事前生成
app/[locale]/privacy-policy/page.tsx
export default async function Page({ params }: { params: Params }) {
  // ✅ SSG互換性のためにparamsからロケールを抽出
  // ❌ 静的生成を妨げるためgetLocale()を使用しない
  const { locale } = await params;
 
  // ... その他のコード ...
 
  return (
    // ... ページコンテンツ ...
  );
}
 
export async function generateStaticParams() {
  return LOCALES.map((locale) => ({
    locale,
  }));
}

シナリオ2:マルチレベル動的ルートを持つページ

適用可能なページ/blogs/nexty-dev-stand-out/zh/blogs/nexty-dev-stand-outのようなURL。動的パラメータが言語プレフィックスとコンテンツ識別子(ブログスラッグなど)の両方を含む場合。

実装アプローチ:同じ原則に従いますが、generateStaticParamsはより複雑なパラメータの組み合わせを処理する必要があります。

app/[locale]/blogs/[slug]/page.tsx
export default async function BlogPage({ params }: { params: Params }) {
  const { locale, slug } = await params;
  
  // ... その他のコード ...
 
  return (
    // ... ページコンテンツ ...
  );
}
 
export async function generateStaticParams() {
  // ロケールとスラッグのすべての可能な組み合わせを生成
  const allParams: { locale: string; slug: string }[] = [];
 
  for (const locale of LOCALES) {
    // 現在のロケール用のすべてのブログスラッグをデータソースから取得
    const blogSlugs = await getBlogSlugs(locale); // あなたのデータ取得ロジック
    
    for (const slug of blogSlugs) {
      allParams.push({ locale, slug });
    }
  }
 
  return allParams;
}

SSG実装の確認

npm run buildを実行した後、.next/server/app/enディレクトリに静的HTMLファイルが生成されていることを確認してください:

ssg result

.htmlファイルの存在は、ページがビルド時に静的生成されていることを確認します。

詳細については:動的ルートに関するNext.jsドキュメントを参照してください。

Nextyの高度な機能

自動言語検出とユーザープロンプト

システムはユーザーの初回訪問時にブラウザ言語を自動検出します。検出された言語が現在のウェブサイト言語と異なる場合、以下の動作で言語切り替えプロンプトが表示されます:

  • 自動非表示:10秒後に自動的に閉じます
  • 手動制御:ユーザーが手動でプロンプトを非表示にできます
  • 永続的な設定:非表示状態がCookieを使用して30日間保存されます

SEO最適化

各言語バージョンは独立したURLとローカライズされたメタデータを生成し、最適な検索エンジンインデックス化を保証します:

import { constructMetadata } from "@/lib/metadata";
 
export async function generateMetadata({ params }: MetadataProps): Promise<Metadata> {
  const { locale } = await params;
  const t = await getTranslations({ locale, namespace: "Home" });
 
  return constructMetadata({
    page: "Home",
    title: t("title"),
    description: t("description"),
    locale: locale as Locale,
    path: `/`,
  });
}