# General Translation Knowledge Base: 翻訳ファイルなしで i18n はできますか？
URL: https://generaltranslation.com/ja/kb/i18n-without-translation-files.mdx
Docs index: https://generaltranslation.com/llms.txt
Description: はい。gt-react または gt-next を使えば、コンテンツは `<T>` コンポーネントで囲んだままコンポーネント内にインラインで記述でき、翻訳はビルド時に出力として生成されます。そのため、キーやロケールごとの JSON ファイルを作成したり、同期を保ったりする必要はありません。
Category: 国際化
Last updated: 2026-05-07
Authors: Jackie Chen, Ernest McCarter
Published: 2026-05-07

JavaScript アプリを国際化したことがある人なら、このワークフローはおなじみでしょう。i18nライブラリをインストールして `en.json` ファイルを作成し、ユーザーに表示される文字列をすべてコンポーネントから抜き出します。それぞれにキーを割り当て、元の文字列があった場所でそのキーを参照します。あとは、サポートする言語の数だけその JSON ファイルを複製します。`es.json`、`fr.json`、`ja.json` といった具合です。

最初のうちは問題ありません。文字列が 30 個、言語が 3 つなら、エントリは 90 個です。

ところが、アプリは成長していきます。半年後には文字列が 400 個、言語が 12 に増え、十数個のファイルにまたがるエントリは 4,800 個に達します。ある開発者が新機能を追加して新しい文字列を 5 つ書いたものの、翻訳ファイルのうち 3 つを更新し忘れます。東京のユーザーが日本語の画面で英語のフォールバックを目にするまで、誰もそれに気づきません。やがて誰かが翻訳管理システムの導入を言い出します。

<Video src="https://assets.gtx.dev/i18n-no-translation.mp4" />

## Video

The video is narrated in English, with English captions.

[Can I do i18n without translation files?](https://caxecubb9c64cezv.public.blob.vercel-storage.com/films/kb-i18n-without-translation-files.mp4) (46 秒)

General Translation lets you stop writing translation files with `gt-react` or `gt-next`. Run `npx gt init`, write the English inside a `<T>` component, then add `npx gt translate` to your build script so every build writes the translations.

## ファイル管理という「税金」

翻訳ファイルのメンテナンス負担は、サポートする言語の数に比例して膨らんでいきます。文字列を1つ変更するだけで、十数個のファイルを更新しなければなりません。しかも、ファイル同士は次第に同期がずれていきます。整合性を保つには、ツールや自動化、あるいは徹底した運用ルールが欠かせません。

数々の回避策が生まれていること自体が、その実情を物語っています。翻訳キーを自動生成する VS Code 拡張機能、JSON を TypeScript インターフェースと照合して検証する型ジェネレーター、未使用のキーを検出するリンター、すべてのロケールが同期されているかを確認する CI チェックなどです。うまく機能しているワークフローなら、わざわざこうしたツールを作る必要はありません。

UI にはある内容が書かれ、翻訳ファイルには別の内容が書かれ、その2つをキーベースのマッピングがかろうじてつないでいるのです：

```tsx
function CheckoutSummary({ itemCount, discount }) {
  const { t } = useTranslation('checkout');

  return (
    <div>
      <h2>{t('summary.title')}</h2>
      <p>{t('summary.item_count', { count: itemCount })}</p>
      {discount && <p>{t('summary.discount_applied', { percent: discount })}</p>}
      <p>{t('summary.total_label')}</p>
      <small>{t('summary.tax_notice')}</small>
    </div>
  );
}
```

このコンポーネントが何をレンダリングするのかを把握するには、`checkout.json` を開いて `summary` 名前空間を探し、5つのキーを突き合わせなければなりません。コンポーネントが数百にもなれば、コードレビューはファイルを行き来するだけの作業になってしまいます。

ICU メッセージ構文が、この問題をさらに深刻にします。`i18next`、`react-intl`、`next-intl` などのライブラリが複数形、性別、補間に使うこの形式は、JSON 文字列の中に埋め込まれた独自のミニ言語です。`{count, plural, one {# item} other {# items}}` で波括弧の位置を一つ間違えても、ランタイムまで問題は表面化しません。これを検出するリンターもありますが、多くのチームは導入していません。一方、インラインのコードなら、入力した瞬間から TypeScript コンパイラと IDE がチェックしてくれます。

## コードを直接翻訳する

そもそも文字列を抽出しないとしたら、どうでしょうか？

```tsx
import { T } from 'gt-react';

function CheckoutSummary({ itemCount, discount }) {
  return (
    <T>
      <div>
        <h2>Order Summary</h2>
        <p>You have {itemCount} items in your cart.</p>
        {discount && <p>{discount}% discount applied!</p>}
        <p>Total (before tax):</p>
        <small>Tax calculated at checkout.</small>
      </div>
    </T>
  );
}
```

[`gt-react`](/docs/react/react-quickstart) の [`<T>`](/docs/react/reference/components/t) コンポーネントは、JSX のブロックを翻訳対象としてマークします。英語のテキストはコンポーネント内にそのまま記述します。ユーザーがスペイン語でアクセスすると、[`<T>`](/docs/react/reference/components/t) 内のコンテンツがスペイン語訳に置き換えられます。構造やフォーマットはそのまま維持されます。

`t('checkout.summary.title')` も、`en.json` も、常に同期させておく必要があるロケールごとの JSON ファイルも必要ありません。

## ビルド成果物としての翻訳

翻訳はデプロイ時に生成されます。[GT CLI](/docs/cli/reference/commands/translate) がコードベースをスキャンして [`<T>`](/docs/react/reference/components/t) コンポーネント内のコンテンツをすべて検出し、各対象言語の翻訳を生成します。生成されたファイルは、コンパイル済みの CSS やバンドル済みの JavaScript と同じく、gitignore 対象のディレクトリに出力されます。

```json title="gt.config.json"
{
  "defaultLocale": "en",
  "locales": ["es", "fr", "ja", "de", "ko", "zh"],
  "files": {
    "gt": {
      "output": "public/_gt/[locale].json"
    }
  }
}
```

```txt title=".gitignore"
public/_gt/
```

開発環境では、翻訳はオンデマンドで行われます。文字列を変更してリロードすれば、すぐに日本語で表示されます。本番環境では、すべてが事前に生成され、CDN から配信されます。翻訳ファイル自体はディスク上に残りますが、あくまでビルドの出力成果物であり、自分で作成・管理するものではありません。

## 実際に何が変わるのか

その違いは、アーキテクチャ図よりも日々の作業の中にこそ表れます。

ページに新しいセクションを追加する PR は、見たとおり「ページに新しいセクションを追加する PR」として読めます。レビュアーが目にするのは `checkout.summary.discount_applied_notice` ではなく、実際の文言です。コードレビューのたびに JSON ファイルと見比べる必要はもうありません。

リファクタリングの負担も軽くなります。コンポーネントの名前を変えても、移動しても、分割しても問題ありません。再マッピングすべきキーが存在しないため、翻訳はコンテンツに自然に追従します。言語の追加も、設定を 1 行変更するだけです。コンポーネントを削除すれば、その翻訳は静かに生成されなくなるだけです。誰も片付けたがらない孤立したキーが 15 個のファイルに散らばったまま残ることもありません。

## i18nライブラリの今後の方向性

エコシステム全体の潮流は、インライン文字列へと向かっています。`i18next` や `react-intl` といった初期のi18nライブラリは、機械翻訳がまだ実用的ではなく、すべての文字列を人間の翻訳者に引き渡す必要があった時代に作られました。当時は、受け渡し用のフォーマットとして辞書を使うのが理にかなっていました。しかし、その制約はもはや存在しません。文字列ファイルを別途並行して管理し続けることは、開発者体験の面でコストが大きく、その正当性はますます薄れつつあります。

`next-intl` は、辞書モードに加えて[自動抽出機能付きの実験的なインラインメッセージ](https://next-intl.dev/docs/usage/extraction)を導入しました。[Lingui の Compiler](https://lingui.dev/ref/swc-plugin) は、インラインのタグ付きテンプレートからビルド時にメッセージを抽出します。[Paraglide](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) は異なるアプローチを採用しており、メッセージファイルをロケールごとのツリーシェイキング可能な関数へとコンパイルします。手法はそれぞれ異なるものの、いずれもコンテンツをコンポーネントの近くに置く方向へと進んでいます。GT はこの流れを最後まで推し進めました。JSX こそが信頼できる唯一の情報源であり、翻訳はコンパイル工程の一部になります。

## トレードオフ

コンテンツをインラインで書くとき、あなたは自分の母語で書いています。コンポーネントの構造も、文のパターンも、UI の流れも、すべて英語 (あるいはあなたのソース言語) での考え方を反映しています。一方、`next-intl` のような辞書ベースのアプローチは、設計上、言語への依存度が低くなっています。コンポーネントにはどの言語の文も実際には含まれず、別の場所を参照するキーがあるだけだからです。

とはいえ、ほとんどの開発者は UI を作る段階で、すでにひとつの言語で考えています。レイアウトも、文言も、ボタンのラベルも、まず英語で構想されます。文字列がインラインにあろうと JSON ファイルにあろうと、その偏りは設計に織り込まれているのです。私たちは、実際の作業スタイルに合わせるべきなのは i18n フレームワークの側だと考えています。言語的な中立性のためにコンテンツをキーへと抽象化するのではなく、アプリは自然に作り、翻訳はフレームワークに任せましょう。

## はじめに

```bash
npx gt@latest init
```

セットアップウィザードを実行すると、プロジェクトの設定、依存関係のインストール、開発用の翻訳ホットリロードのセットアップが行われます。詳しい手順については [CLI クイックスタート](/docs/cli/quickstart) を参照してください。

完了したら、コンテンツを [`<T>`](/docs/react/reference/components/t) でラップして開発サーバーを起動し、[`<LocaleSelector>`](/docs/react/reference/components/locale-selector) コンポーネントを使って言語を切り替えましょう。

```tsx
import { LocaleSelector } from 'gt-react';

function Header() {
  return (
    <header>
      <nav>{/* ... */}</nav>
      <LocaleSelector />
    </header>
  );
}
```

開発環境では翻訳がオンデマンドで行われるため、アプリをすぐに任意の言語で確認できます。本番環境では、事前生成済みの翻訳が使用されます。ビルドに翻訳ステップを追加し、ホスティングサービスで `GT_PROJECT_ID` と `GT_API_KEY` を設定してください：

```json title="package.json"
{
  "scripts": {
    "build": "npx gt translate && next build"
  }
}
```

[`translate`](/docs/cli/reference/commands/translate) コマンドは、コードベース内で [`<T>`](/docs/react/reference/components/t) が使われている箇所をすべてスキャンして翻訳を生成します。そのため、アプリのビルド時にはすべてのロケールの準備が整っています。

`gt-react` はオープンソースです。Next.js App Router の場合は [`gt-next`](/docs/react/nextjs-quickstart) を、React Native の場合は [`gt-react-native`](/docs/react/react-native-quickstart) を使用してください。

## Related questions

- [JSX 内の条件付きテキストはどう翻訳すればよいですか？](https://generaltranslation.com/ja/kb/translate-conditional-jsx.mdx): 三項演算子の各分岐を個別に翻訳するのではなく、文全体を `<T>` コンポーネントでラップし、条件を `<Branch>` で表現しましょう。こうすることで翻訳者が文脈全体を把握でき、言語ごとに語順を組み替えられます。
- [React で複数形を扱うには？](https://generaltranslation.com/ja/kb/how-to-handle-plurals-in-react.mdx): n === 1 のような条件分岐で複数形の選択ロジックをハードコードするのは避けましょう。英語のみのアプリなら小さなヘルパーで十分ですが、多言語アプリではロケール対応の複数形規則が必要になります。gt-react や gt-next の `<Plural>` のような i18n コンポーネントを使えば、あらゆる言語の複数形に対応できます。
- [開発者にとって最適なローカライゼーションソフトウェアとは？](https://generaltranslation.com/ja/kb/best-localization-software.mdx): ライブラリ、翻訳管理システム、翻訳ベンダーを個別に組み合わせるのではなく、コード内のi18nライブラリから翻訳、配信まで、パイプライン全体を1つでカバーできるシステムを選びましょう。

## Previous and next questions

- Previous question: [開発者にとって最適なローカライゼーションソフトウェアとは？](https://generaltranslation.com/ja/kb/best-localization-software.mdx)
- Next question: [Crowdin の代替ツール：General Translation と Crowdin の比較](https://generaltranslation.com/ja/kb/general-translation-vs-crowdin.mdx)

## Sitemap

See the [Knowledge Base sitemap](https://generaltranslation.com/ja/kb/sitemap.md) for every article.
