共有文字列の翻訳
共有文字列を国際化する方法
概要
共有文字列とは、アプリ内の複数の箇所で使用される文字列や定数のことです。
たとえば、複数のコンポーネントで使われる文字列や、複数のファイルで使われる定数がある場合があります。
従来の i18n ライブラリでは、共有文字列ごとに辞書を用意する必要がありました。
このガイドでは、msg()
関数と useMessages()
フックを使って、Next.js アプリの共有文字列を簡単に国際化する方法を解説します。
本ガイドでは次の内容を扱います:
msg()
関数を使うべきタイミング
msg()
関数と useMessages()
フックの使い方
変数の使い方
例
よくある落とし穴
msg()
関数を使うべきとき
msg()
関数は、翻訳対象の文字列にマークを付けるためのシンプルな関数です。
文字列を含む共有オブジェクトの例を見てみましょう。
export const navData = {
home: {
label: 'Home',
description: 'The home page',
icon: 'home',
href: '/',
},
about: {
label: 'About',
description: 'Information about the company',
icon: 'info',
href: '/about',
},
}
通常、このオブジェクトを国際化するには、各文字列ごとに辞書エントリを作成するか、オブジェクトを翻訳済みデータを返す関数にリファクタリングする必要があります。
たとえば、次のようにします。
export const getNavData = (t: (content: string) => string) => ({
home: {
label: t('Home'),
description: t('The home page'),
icon: 'home',
href: '/',
},
about: {
label: t('About'),
description: t('Information about the company'),
icon: 'info',
href: '/about',
},
})
アプリ内で navData
をインポートしていた箇所では、代わりに getNavData(t)
を呼び出す必要があります。
import { getNavData } from '@/navData';
export default function Home() {
const t = useGT();
const navData = getNavData(t);
return <div>{navData.home.label}</div>;
}
これは手間が増える原因になります。解決策は msg()
関数を使うことです。
msg()
関数の使い方
msg()
関数を使うには、文字列をそのまま関数に渡します。
この関数は翻訳に利用できる特別なエンコード済み文字列を返します。
import { msg } from 'gt-next';
export const navData = [
{
label: msg('Home'), // 出力の型は `string`
description: msg('The home page'),
icon: 'home',
href: '/',
},
{
label: msg('About'),
description: msg('Information about the company'),
icon: 'info',
href: '/about',
},
]
続いて、そのエンコード済み文字列を useMessages()
フックに渡します。
import { useMessages } from 'gt-next';
import { navData } from '@/navData';
export default function Home() {
const m = useMessages();
return (
<div>
{navData.map((item) => (
<div key={item.label}>{m(item.label)}</div>
))}
</div>
);
}
非同期コンポーネント内では、useMessages()
フックの代わりに getMessages()
関数を使用してください。
import { getMessages } from 'gt-next/server';
export default async function Home() {
const m = await getMessages();
return (
<div>
{navData.map((item) => (
<div key={item.label}>{m(item.label)}</div>
))}
</div>
);
}
注意: msg()
関数が返す文字列はエンコードされており、元の入力文字列とは異なります。
元の文字列を取得したい場合は、decodeMsg()
でデコードしてください。
変数の使用
文字列がテンプレート文字列で、変数を含む場合は、msg()
関数にその変数を渡せます。
テンプレート文字列内の ${}
記法を {variable}
に置き換え、msg()
の第2引数に変数名をキーとするオブジェクトを渡します。
const items = 100;
export const pricing = [
{
name: 'Basic',
price: 100,
description: `The basic plan includes ${items} items`
},
]
次のようになります:
import { msg } from 'gt-next';
const items = 100;
export const pricing = [
{
name: 'Basic',
price: 100,
description: msg('The basic plan includes {items} items', { items })
},
]
{items}
プレースホルダーは、items
変数の値に置き換えられます。
これにより、翻訳内で動的な値を表示できます。
API の詳細は、API reference を参照してください。
gt-next
は ICU メッセージ形式をサポートしており、変数のフォーマットも可能です。
const price = 100;
msg('There are {count, plural, =0 {no items} =1 {one item} other {{count} items}} in the cart', { count: 10 });
ICU メッセージ形式は、変数を柔軟かつ強力にフォーマットする方法です。 詳細は、ICU message format documentation を参照してください。
例
- 共有グローバルオブジェクトを翻訳する
import { msg } from 'gt-next';
export const llmData = [
{
name: 'GPT-4.1',
id: 'gpt-4.1',
description: msg('GPT-4.1 is a large language model developed by OpenAI'),
},
{
name: 'Claude 3.7 Sonnet',
id: 'claude-3-7-sonnet',
description: msg('Claude 3.7 Sonnet is a large language model developed by Anthropic'),
},
];
import { llmData } from '@/llms';
import { useMessages } from 'gt-next';
export default function MyComponent() {
const m = useMessages();
return (
<div>
{llmData.map((llm) => (
<div key={llm.id}>
<h1>{llm.name}</h1>
<p>{m(llm.description)}</p>
</div>
))}
</div>
)
}
export const llms = [
{
name: 'GPT-4.1',
id: 'gpt-4.1',
description: 'GPT-4.1 is a large language model developed by OpenAI',
},
{
name: 'Claude 3.7 Sonnet',
id: 'claude-3-7-sonnet',
description: 'Claude 3.7 Sonnet is a large language model developed by Anthropic',
},
]
import { llms } from '@/llms';
export default function MyComponent() {
return (
<div>
{llms.map((llm) => (
<div key={llm.id}>
<h1>{llm.name}</h1>
<p>{llm.description}</p>
</div>
))}
</div>
)
}
- 関数の戻り値を翻訳する
import { msg } from 'gt-next';
function mockData() {
return [
{
name: 'GPT-4.1',
id: 'gpt-4.1',
company: 'OpenAI',
},
{
name: 'Claude 3.7 Sonnet',
id: 'claude-3-7-sonnet',
company: 'Anthropic',
},
];
}
export function getData() {
const data = mockData();
const modifiedData = data.map((item) => ({
...item,
description: msg('{name} is a large language model developed by {company}', {
name: item.name,
company: item.company,
}),
}));
return modifiedData;
}
import { getData } from '@/llms';
import { useMessages } from 'gt-next';
export default function MyComponent() {
const m = useMessages();
const data = getData();
return (
<div>
{data.map((item) => (
<div key={item.id}>
<h1>{item.name}</h1>
<p>{m(item.description)}</p>
</div>
))}
</div>
)
}
import { msg } from 'gt-next';
function mockData() {
return [
{
name: 'GPT-4.1',
id: 'gpt-4.1',
company: 'OpenAI',
},
{
name: 'Claude 3.7 Sonnet',
id: 'claude-3-7-sonnet',
company: 'Anthropic',
},
];
}
export function getData() {
const data = mockData();
const modifiedData = data.map((item) => ({
...item,
description: `${item.name} is a large language model developed by ${item.company}`,
}));
return modifiedData;
}
import { getData } from '@/llms';
export default function MyComponent() {
const data = getData();
return (
<div>
{data.map((item) => (
<div key={item.id}>
<h1>{item.name}</h1>
<p>{item.description}</p>
</div>
))}
</div>
)
}
よくある落とし穴
useMessages()
の呼び出し忘れ
msg()
は入力文字列をエンコードするため、JSX などでそのまま使用することはできません。
エンコード済みの文字列を直接使おうとすると、次のような見慣れない文字列になります:
const encodedString = msg('Hello, world!');
console.log(encodedString); // "Hello, world!:eyIkX2hhc2giOiJkMjA3MDliZGExNjNlZmM2In0="
これを解決するには、useMessages()
フックを使って翻訳済みの文字列を取得します。
import { useMessages } from 'gt-next';
const encodedString = msg('Hello, world!');
export default function MyComponent() {
const m = useMessages();
return <div>{m(encodedString)}</div>; // 現在の言語での "Hello, world!"
}
また、元の文字列を復元したい場合は、decodeMsg()
関数を使用できます。
import { decodeMsg } from 'gt-next';
const encodedString = msg('Hello, world!');
const decodedString = decodeMsg(encodedString);
console.log(decodedString); // "Hello, world!"
動的コンテンツを msg()
で囲む
すべての文字列はビルド時に確定している必要があります。つまり、動的コンテンツを msg()
で囲むことはできません。
たとえば、次は無効です:
const dynamicContent = msg(`Hello, ${name}`);
これを解決するには、動的部分は変数として用意し、それを msg()
に渡します。
const dynamicContent = msg('Hello, {name}', { name });
動的コンテンツを msg()
で囲もうとすると、CLI ツールが警告します。
次のステップ
msg()
の API リファレンス を参照してください。useMessages()
の API リファレンス を参照してください。<T>
コンポーネント について詳しく学びましょう。
このガイドはいかがですか?