文字列

useGT と getGT を使ってプレーンテキストの文字列を国際化する方法

文字列の翻訳は、JSX を使わずにテキストの翻訳へ直接アクセスでき、属性、オブジェクトのプロパティ、プレーンテキストの値に最適です。同期コンポーネントでは useGT、非同期コンポーネントでは getGT を使用します。

同期処理と非同期処理の使い分け

  • 同期コンポーネント: Reactコンポーネント向けのuseGTフック
  • 非同期コンポーネント: 非同期コンポーネント向けのgetGTの非同期関数

クイックスタート

同期的コンポーネント

import { useGT } from 'gt-next';

function MyComponent() {
  const t = useGT();
  return (
    <input 
      placeholder={t('メールアドレスを入力してください')}
      title={t('メールアドレス入力欄')}
    />
  );
}

非同期コンポーネント

import { getGT } from 'gt-next/server';

async function MyServerComponent() {
  const t = await getGT();
  return (
    <input 
      placeholder={t('メールアドレスを入力してください')}
      title={t('メールアドレス入力欄')}
    />
  );
}

文字列翻訳を使うタイミング

JSX ではなくプレーンテキストが必要な場合に、文字列翻訳が適しています。

HTML 属性

const t = useGT();

<input 
  placeholder={t('商品を検索...')}
  aria-label={t('商品検索')}
  title={t('カタログを検索')}
/>

オブジェクトのプロパティ

const t = useGT();

const user = {
  name: 'John',
  role: 'admin',
  bio: t('React開発経験5年のソフトウェア開発者'),
  status: t('現在プロジェクト対応可能')
};

設定と定数

const t = useGT();

const navigationItems = [
  { label: t('ホーム'), href: '/' },
  { label: t('製品'), href: '/products' },
  { label: t('お問い合わせ'), href: '/contact' }
];

<T> を使うべき場面

JSX コンテンツには、<T> コンポーネントを使用します。

// ✅ JSXコンテンツには<T>を使用
<T><p><strong>当ストア</strong>へようこそ!</p></T>

// ✅ プレーンテキストにはt()を使用
<input placeholder={t('商品を検索')} />

変数の使用

基本のvariables

プレースホルダーを動的なvalueに置き換えます:

const t = useGT();
const itemCount = 5;

// プレースホルダーを含む文字列
const message = t('カートに{count}個のアイテムがあります', { count: itemCount });
// 結果: "カートに5個のアイテムがあります"

複数のvariables

const t = useGT();
const order = { id: 'ORD-123', total: 99.99, date: '2024-01-15' };

const confirmation = t(
  '注文 {orderId} (${total}) を {date} に承りました',
  { 
    orderId: order.id, 
    total: order.total, 
    date: order.date 
  }
);

ICUメッセージ形式

高度なフォーマットには、ICU 構文を使用します:

const t = useGT();
translate('{count, plural, =0 {カートに商品はありません} =1 {カートに商品が1点あります} other {カートに{count}点の商品があります}}', { count: 10 });

ICU Message Format については、Unicode のドキュメントをご覧ください。

フォーム入力欄

import { useGT } from 'gt-next';

function ContactForm() {
  const t = useGT();
  
  return (
    <form>
      <input 
        type="email"
        placeholder={t('メールアドレスを入力')}
        aria-label={t('メールアドレス入力欄')}
      />
      <textarea 
        placeholder={t('プロジェクトについてお聞かせください...')}
        aria-label={t('プロジェクト概要')}
      />
      <button type="submit">
        {t('送信')}
      </button>
    </form>
  );
}

ナビゲーション メニュー

import { useGT } from 'gt-next';

function Navigation() {
  const t = useGT();
  
  const menuItems = [
    { label: t('ホーム'), href: '/', icon: 'home' },
    { label: t('企業情報'), href: '/about', icon: 'info' },
    { label: t('サービス'), href: '/services', icon: 'briefcase' },
    { label: t('お問い合わせ'), href: '/contact', icon: 'mail' }
  ];

  return (
    <nav>
      {menuItems.map((item) => (
        <a key={item.href} href={item.href} title={item.label}>
          <Icon name={item.icon} />
          {item.label}
        </a>
      ))}
    </nav>
  );
}

ダイナミックコンテンツファクトリ

// utils/productData.js
export function getProductMessages(t) {
  return {
    categories: [
      { id: 'electronics', name: t('電化製品') },
      { id: 'clothing', name: t('衣料品') },
      { id: 'books', name: t('書籍') }
    ],
    statusMessages: {
      available: t('在庫あり・発送可能'),
      backordered: t('取り寄せ中・2〜3週間で発送'),  
      discontinued: t('この商品は販売終了しました')
    },
    errors: {
      notFound: t('商品が見つかりません'),
      outOfStock: t('申し訳ございません。この商品は現在在庫切れです')
    }
  };
}

// components/ProductCard.jsx
import { useGT } from 'gt-next';
import { getProductMessages } from '../utils/productData';

function ProductCard({ product }) {
  const t = useGT();
  const messages = getProductMessages(t);
  
  return (
    <div>
      <h3>{product.name}</h3>
      <p>{messages.statusMessages[product.status]}</p>
      <span>{messages.categories.find(c => c.id === product.categoryId)?.name}</span>
    </div>
  );
}

メタデータを扱う Server Component

import { getGT } from 'gt-next/server';

export async function generateMetadata({ params }) {
  const t = await getGT();
  
  return {
    title: t('製品カタログ — 必要なものが見つかります'),
    description: t('高品質な製品を豊富なラインアップからご覧いただけます'),
    openGraph: {
      title: t('製品をショッピング'),
      description: t('高評価アイテムのお得な情報を見つけよう'),
    }
  };
}

export default async function ProductPage() {
  const t = await getGT();
  
  return (
    <div>
      <h1>{t('注目の製品')}</h1>
      <p>{t('最新・人気のアイテムをご覧ください')}</p>
    </div>
  );
}

よくある問題点

実行時の動的コンテンツ

文字列はビルド時に確定している必要があり、動的コンテンツは翻訳できません。

// ❌ 動的な内容は動作しません
function MyComponent() {
  const [userMessage, setUserMessage] = useState('');
  const t = useGT();
  
  return <p>{t(userMessage)}</p>; // これは失敗します
}

// ✅ あらかじめ定義した文字列を使う
function MyComponent() {
  const [messageType, setMessageType] = useState('welcome');
  const t = useGT();
  
  const messages = {
    welcome: t('アプリへようこそ!'),
    goodbye: t('ご利用ありがとうございます!')
  };
  
  return <p>{messages[messageType]}</p>;
}

Hook のルール違反

useGT を使用する場合は、React のフックのルールに従ってください。

// ❌ フックを条件分岐の中で呼び出さないでください
function MyComponent({ showMessage }) {
  if (showMessage) {
    const t = useGT(); // フックのルール違反
    return <p>{t('こんにちは!')}</p>;
  }
  return null;
}

// ✅ フックは必ずトップレベルで呼び出してください
function MyComponent({ showMessage }) {
  const t = useGT();
  
  if (showMessage) {
    return <p>{t('こんにちは!')}</p>;
  }
  return null;
}

同期処理と非同期処理の取り違え

コンポーネントの種類に合った適切な関数を使ってください。

// ❌ 誤り: 非同期コンポーネントで useGT を使用している
export default async function AsyncComponent() {
  const t = useGT(); // これは動作しません
  return <p>{t('Hello')}</p>;
}

// ✅ 正しい: 非同期コンポーネントで getGT を使用する  
export default async function AsyncComponent() {
  const t = await getGT();
  return <p>{t('Hello')}</p>;
}

// ✅ 正しい: 同期コンポーネントで useGT を使用する
export default function SyncComponent() {
  const t = useGT();
  return <p>{t('Hello')}</p>;
}

実行時に翻訳が必要な真に動的なコンテンツについては、Dynamic Content Guideをご覧ください。

次のステップ

このガイドはいかがですか?