2026年版:i18n対応AIモダンWebアプリの構築方法

Lingui + AI翻訳を活用した多言語Webアプリ構築の完全ガイド。Next.js、Claude、T3 Turboを組み合わせ、17言語への対応を自動化する手法を解説します。

2026年版:i18n対応AIモダンWebアプリの構築方法
Feng LiuFeng Liu
2026年1月24日

title: 2026年のi18n:AIとLinguiで実現する、次世代の多言語対応アーキテクチャ content: さて、2026年のi18n(国際化)について真剣に話しましょう。

ほとんどのチュートリアルでは、手作業で文字列を翻訳したり、翻訳者を雇ったり、あるいは微妙なGoogle翻訳APIを使ったりするように教えています。しかし、よく考えてください。私たちは今、Claude Sonnet 4.5の時代に生きているんですよ。なぜ2019年のような翻訳のやり方をしているんですか?

今回は、私たちが実際に構築した、17言語を流暢に話すプロダクションレベルのWebアプリの裏側をお見せします。理にかなった「2ピース構成」のi18nアーキテクチャです。

  1. Lingui: 抽出、コンパイル、ランタイムの魔法を担当
  2. カスタムi18nパッケージ: LLMを活用し、文脈を理解した自動翻訳を担当

使用するスタックは? Create T3 Turbo(Next.js, tRPC, Drizzle, Postgres, Tailwind)そして AI SDKです。もし2026年にもなってこれを使っていないなら、別の議論が必要ですね。

さあ、作りましょう。


従来のi18nの問題点

従来のi18nワークフローはこんな感じでした:

# 文字列を抽出
$ lingui extract

# ??? どうにかして翻訳を入手 ???
# (翻訳者を雇う、怪しいサービスを使う、泣き寝入りする)

# コンパイル
$ lingui compile

この真ん中のステップ、悪夢ですよね。

  • 人間の翻訳者に高いお金を払う(遅い、高い)
  • 基本的な翻訳APIを使う(文脈無視、ロボットっぽい)
  • 手動で翻訳する(スケールしない)

私たちは、もっとうまくやれます。


2ピース・アーキテクチャ

これが私たちのセットアップです:

┌─────────────────────────────────────────────┐
│  Next.js App (Lingui Integration)           │
│  ├─ マクロで文字列を抽出                       │
│  ├─ コード内の Trans/t コンポーネント            │
│  └─ コンパイル済みカタログによるランタイムi18n     │
└─────────────────────────────────────────────┘
              ↓ .poファイルを生成
┌─────────────────────────────────────────────┐
│  @acme/i18n Package (LLM Translation)       │
│  ├─ .poファイルを読み込み                       │
│  ├─ Claude/GPT-5でバッチ翻訳                   │
│  ├─ 文脈認識、プロダクト固有の用語に対応            │
│  └─ 翻訳された.poファイルを書き込み               │
└─────────────────────────────────────────────┘
              ↓ TypeScriptにコンパイル
┌─────────────────────────────────────────────┐
│  Compiled Message Catalogs                  │
│  └─ 高速で型安全なランタイム翻訳                  │
└─────────────────────────────────────────────┘

ピース1 (Lingui) は開発者体験(DX)を担当します。 ピース2 (カスタムi18nパッケージ) は翻訳の魔法を担当します。

それぞれ詳しく見ていきましょう。


Technical flow diagram: web UI → AI translation cloud → database, three tiers connected by arrows

パート1: Next.jsでのLinguiセットアップ

インストール

T3 Turboモノレポ内で以下を実行します:

# apps/nextjs ディレクトリにて
pnpm add @lingui/core @lingui/react @lingui/macro
pnpm add -D @lingui/cli @lingui/swc-plugin

Lingui設定

apps/nextjs/lingui.config.tsを作成します:

import type { LinguiConfig } from "@lingui/conf";

const config: LinguiConfig = {
  locales: [
    "en", "zh_CN", "zh_TW", "ja", "ko",
    "de", "fr", "es", "pt", "ar", "it",
    "ru", "tr", "th", "id", "vi", "hi"
  ],
  sourceLocale: "en",
  fallbackLocales: {
    default: "en"
  },
  catalogs: [
    {
      path: "<rootDir>/src/locales/{locale}/messages",
      include: ["src"],
    },
  ],
};

export default config;

最初から17言語対応です。 やらない理由はありませんよね?

Next.jsとの統合

next.config.jsを更新して、LinguiのSWCプラグインを使用するようにします:

const linguiConfig = require("./lingui.config");

module.exports = {
  experimental: {
    swcPlugins: [
      [
        "@lingui/swc-plugin",
        {
          // これによりビルドが高速化されます
        },
      ],
    ],
  },
  // ... その他の設定
};

サーバーサイドのセットアップ

src/utils/i18n/appRouterI18n.tsを作成します:

import { setupI18n } from "@lingui/core";
import { allMessages } from "./initLingui";

const locales = ["en", "zh_CN", "zh_TW", /* ... */] as const;

const instances = new Map<string, ReturnType<typeof setupI18n>>();

// 全ロケールのi18nインスタンスを事前に作成
locales.forEach((locale) => {
  const i18n = setupI18n({
    locale,
    messages: { [locale]: allMessages[locale] },
  });
  instances.set(locale, i18n);
});

export function getI18nInstance(locale: string) {
  return instances.get(locale) ?? instances.get("en")!;
}

なぜこれが必要か? Server ComponentsにはReact Contextがないからです。これでサーバーサイド翻訳が可能になります。

クライアントサイドのProvider

src/providers/LinguiClientProvider.tsxを作成します:

"use client";

import { I18nProvider } from "@lingui/react";
import { setupI18n } from "@lingui/core";
import { useEffect, useState } from "react";

export function LinguiClientProvider({
  children,
  locale,
  messages
}: {
  children: React.ReactNode;
  locale: string;
  messages: any;
}) {
  const [i18n] = useState(() =>
    setupI18n({
      locale,
      messages: { [locale]: messages },
    })
  );

  useEffect(() => {
    i18n.load(locale, messages);
    i18n.activate(locale);
  }, [locale, messages, i18n]);

  return <I18nProvider i18n={i18n}>{children}</I18nProvider>;
}

layout.tsxでアプリをラップします:

import { LinguiClientProvider } from "@/providers/LinguiClientProvider";
import { getLocale } from "@/utils/i18n/localeDetection";
import { allMessages } from "@/utils/i18n/initLingui";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const locale = getLocale();

  return (
    <html lang={locale}>
      <body>
        <LinguiClientProvider locale={locale} messages={allMessages[locale]}>
          {children}
        </LinguiClientProvider>
      </body>
    </html>
  );
}

コード内での翻訳の使用

Server Componentsの場合:

import { msg } from "@lingui/core/macro";
import { getI18nInstance } from "@/utils/i18n/appRouterI18n";

export async function generateMetadata({ params }) {
  const locale = getLocale();
  const i18n = getI18nInstance(locale);

  return {
    title: i18n._(msg`Pricing Plans | acme`),
    description: i18n._(msg`Choose the perfect plan for you`),
  };
}

Client Componentsの場合:

"use client";

import { Trans, useLingui } from "@lingui/react/macro";

export function PricingCard() {
  const { t } = useLingui();

  return (
    <div>
      <h1><Trans>Pricing Plans</Trans></h1>
      <p>{t`Ultimate entertainment experience`}</p>

      {/* 変数付き */}
      <p>{t`${credits} credits remaining`}</p>
    </div>
  );
}

このマクロ構文が鍵です。 Linguiはビルド時にこれらを抽出します。


パート2: AI搭載の翻訳パッケージ

ここからが面白いところです。

パッケージ構造

packages/i18n/を作成します:

packages/i18n/
├── package.json
├── src/
│   ├── translateWithLLM.ts      # コアとなるLLM翻訳ロジック
│   ├── enhanceTranslations.ts   # バッチ処理プロセッサ
│   └── utils.ts                 # ヘルパー関数

package.json

{
  "name": "@acme/i18n",
  "version": "0.1.0",
  "dependencies": {
    "@acme/ai": "workspace:*",
    "openai": "^4.77.3",
    "pofile": "^1.1.4",
    "zod": "^3.23.8"
  }
}

LLM翻訳エンジン

これが秘密のソース、translateWithLLM.tsです:

import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { z } from "zod";

const translationSchema = z.object({
  translations: z.array(
    z.object({
      msgid: z.string(),
      msgstr: z.string(),
    })
  ),
});

export async function translateWithLLM(
  messages: Array<{ msgid: string; msgstr: string }>,
  targetLocale: string,
  options?: { model?: string }
) {
  const prompt = `You are a professional translator for acme, an AI-powered creative platform.

Translate the following strings from English to ${getLanguageName(targetLocale)}.

CONTEXT:
- acme is a platform for AI chat, image generation, and creative content
- Keep brand names unchanged (acme, Claude, etc.)
- Preserve HTML tags, variables like {count}, and placeholders
- Adapt culturally where appropriate
- Maintain tone: friendly, creative, engaging

STRINGS TO TRANSLATE:
${JSON.stringify(messages, null, 2)}

Return a JSON object with this structure:
{
  "translations": [
    { "msgid": "original", "msgstr": "translation" },
    ...
  ]
}`;

  const result = await generateText({
    model: openai(options?.model ?? "gpt-4o"),
    prompt,
    temperature: 0.3, // 低いほど一貫性が増す
  });

  const parsed = translationSchema.parse(JSON.parse(result.text));
  return parsed.translations;
}

function getLanguageName(locale: string): string {
  const names: Record<string, string> = {
    zh_CN: "Simplified Chinese",
    zh_TW: "Traditional Chinese",
    ja: "Japanese",
    ko: "Korean",
    de: "German",
    fr: "French",
    es: "Spanish",
    pt: "Portuguese",
    ar: "Arabic",
    // ... etc
  };
  return names[locale] ?? locale;
}

なぜこれが機能するのか:

  • 文脈認識: LLMは「acme」が何であるかを知っています。
  • 構造化された出力: Zodスキーマにより有効なJSONを保証します。
  • 低いTemperature: 翻訳の一貫性を保ちます。
  • フォーマット維持: HTMLや変数はそのまま保持されます。

バッチ翻訳プロセッサ

enhanceTranslations.tsを作成します:

import fs from "fs";
import path from "path";
import pofile from "pofile";
import { translateWithLLM } from "./translateWithLLM";

const BATCH_SIZE = 30; // 一度に30文字列ずつ翻訳
const DELAY_MS = 1000; // レート制限対策

export async function enhanceTranslations(
  locale: string,
  catalogPath: string
) {
  const poPath = path.join(catalogPath, locale, "messages.po");
  const po = pofile.parse(fs.readFileSync(poPath, "utf-8"));

  // 未翻訳の項目を探す
  const untranslated = po.items.filter(
    (item) => item.msgid && (!item.msgstr || item.msgstr[0] === "")
  );

  if (untranslated.length === 0) {
    console.log(`✓ ${locale}: All strings translated`);
    return;
  }

  console.log(`Translating ${untranslated.length} strings for ${locale}...`);

  // バッチ処理
  for (let i = 0; i < untranslated.length; i += BATCH_SIZE) {
    const batch = untranslated.slice(i, i + BATCH_SIZE);
    const messages = batch.map((item) => ({
      msgid: item.msgid,
      msgstr: item.msgstr?.[0] ?? "",
    }));

    try {
      const translations = await translateWithLLM(messages, locale);

      // POファイルを更新
      translations.forEach((translation, index) => {
        const item = batch[index];
        if (item) {
          item.msgstr = [translation.msgstr];
        }
      });

      console.log(`  ${i + batch.length}/${untranslated.length} translated`);

      // 進捗を保存
      fs.writeFileSync(poPath, po.toString());

      // レート制限
      if (i + BATCH_SIZE < untranslated.length) {
        await new Promise((resolve) => setTimeout(resolve, DELAY_MS));
      }
    } catch (error) {
      console.error(`  Error translating batch: ${error}`);
      // 次のバッチへ継続
    }
  }

  console.log(`✓ ${locale}: Translation complete!`);
}

バッチ処理により、トークン制限を防ぎ、コストを節約します。

翻訳スクリプト

apps/nextjs/script/i18n.tsを作成します:

import { enhanceTranslations } from "@acme/i18n";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);

const LOCALES = [
  "zh_CN", "zh_TW", "ja", "ko", "de",
  "fr", "es", "pt", "ar", "it", "ru"
];

async function main() {
  // ステップ 1: コードから文字列を抽出
  console.log("📝 Extracting strings...");
  await execAsync("pnpm run lingui:extract --clean");

  // ステップ 2: 欠けている文字列を自動翻訳
  console.log("\n🤖 Translating with AI...");
  const catalogPath = "./src/locales";

  for (const locale of LOCALES) {
    await enhanceTranslations(locale, catalogPath);
  }

  // ステップ 3: TypeScriptにコンパイル
  console.log("\n⚡ Compiling catalogs...");
  await execAsync("npx lingui compile --typescript");

  console.log("\n✅ Done! All translations updated.");
}

main().catch(console.error);

package.jsonに追加します:

{
  "scripts": {
    "i18n": "tsx script/i18n.ts",
    "lingui:extract": "lingui extract",
    "lingui:compile": "lingui compile --typescript"
  }
}

i18nパイプラインの実行

# すべてを支配するたった一つのコマンド
$ pnpm run i18n

📝 Extracting strings...
Catalog statistics for src/locales/{locale}/messages:
┌──────────┬─────────────┬─────────┐
│ Language │ Total count │ Missing │
├──────────┼─────────────┼─────────┤
│ en       │         847 │       0 │
│ zh_CN    │         847 │     123 │
│ ja       │         847 │      89 │
└──────────┴─────────────┴─────────┘

🤖 Translating with AI...
Translating 123 strings for zh_CN...
  30/123 translated
  60/123 translated
  90/123 translated
  123/123 translated
✓ zh_CN: Translation complete!

⚡ Compiling catalogs...
✅ Done! All translations updated.

これだけです。 コードに新しい文字列を追加し、pnpm i18nを実行すれば、ドカンと17言語に翻訳されます。


Before/after split screen: left shows stressed developer with translation papers and $1000 bill

ロケールの切り替え

UXの部分も忘れてはいけません。ロケールスイッチャーの実装です:

"use client";

import { useLocaleSwitcher } from "@/hooks/useLocaleSwitcher";
import { useLocale } from "@/hooks/useLocale";

const LOCALES = {
  en: "English",
  zh_CN: "简体中文",
  zh_TW: "繁體中文",
  ja: "日本語",
  ko: "한국어",
  // ... etc
};

export function LocaleSelector() {
  const currentLocale = useLocale();
  const { switchLocale } = useLocaleSwitcher();

  return (
    <select
      value={currentLocale}
      onChange={(e) => switchLocale(e.target.value)}
    >
      {Object.entries(LOCALES).map(([code, name]) => (
        <option key={code} value={code}>
          {name}
        </option>
      ))}
    </select>
  );
}

フックの実装:

// hooks/useLocaleSwitcher.tsx
"use client";

import { setUserLocale } from "@/utils/i18n/localeDetection";

export function useLocaleSwitcher() {
  const switchLocale = (locale: string) => {
    setUserLocale(locale);
    window.location.reload(); // ロケール適用のために強制リロード
  };

  return { switchLocale };
}

設定をCookieに保存します:

// utils/i18n/localeDetection.ts
import { cookies } from "next/headers";

export function setUserLocale(locale: string) {
  cookies().set("NEXT_LOCALE", locale, {
    maxAge: 365 * 24 * 60 * 60, // 1年
  });
}

export function getLocale(): string {
  const cookieStore = cookies();
  return cookieStore.get("NEXT_LOCALE")?.value ?? "en";
}

上級編:型安全な翻訳

型安全性が欲しいですか? Linguiなら大丈夫です。

// こうではなく:
t`Hello ${name}`

// msg記述子を使用:
import { msg } from "@lingui/core/macro";

const greeting = msg`Hello ${name}`;
const translated = i18n._(greeting);

IDEが翻訳キーをオートコンプリートしてくれます。美しいですね。


パフォーマンスに関する考慮事項

1. ビルド時のコンパイル

Linguiは翻訳を最小化されたJSONにコンパイルします。ランタイムでのパース負荷はありません。

// コンパイル後の出力(最小化):
export const messages = JSON.parse('{"ICt8/V":["動画"],"..."}');

2. サーバーカタログのプリロード

起動時にすべてのカタログを一度だけロードします(前述の appRouterI18n.ts を参照)。リクエストごとのファイルI/Oは発生しません。

3. クライアントバンドルサイズ

アクティブなロケールのみをクライアントに送信します:

<LinguiClientProvider
  locale={locale}
  messages={allMessages[locale]} // 1つのロケールのみ
>

4. LLMコストの最適化

  • バッチ翻訳: API呼び出しごとに30文字列
  • 翻訳のキャッシュ: 変更のない文字列は再翻訳しない
  • 安価なモデルの使用: 重要度の低い言語にはGPT-4o-miniを使用

私たちのコストは? 800以上の文字列 × 16言語でわずか2〜3ドル程度です。人間の翻訳者に比べれば小銭のようなものです。


フルスタック統合

T3 Turboの他の部分とどう連携するか見てみましょう。

tRPCとi18n

// server/api/routers/user.ts
import { createTRPCRouter, publicProcedure } from "../trpc";
import { msg } from "@lingui/core/macro";

export const userRouter = createTRPCRouter({
  subscribe: publicProcedure
    .mutation(async ({ ctx }) => {
      // エラーも翻訳可能です!
      if (!ctx.session?.user) {
        throw new TRPCError({
          code: "UNAUTHORIZED",
          message: ctx.i18n._(msg`You must be logged in`),
        });
      }

      // ... サブスクリプションのロジック
    }),
});

コンテキスト経由でi18nインスタンスを渡します:

// server/api/trpc.ts
import { getI18nInstance } from "@/utils/i18n/appRouterI18n";

export const createTRPCContext = async (opts: CreateNextContextOptions) => {
  const locale = getLocale();
  const i18n = getI18nInstance(locale);

  return {
    session: await getServerAuthSession(),
    i18n,
    locale,
  };
};

Drizzleによるデータベース

ユーザーのロケール設定を保存します:

// packages/db/schema/user.ts
import { pgTable, text, varchar } from "drizzle-orm/pg-core";

export const users = pgTable("user", {
  id: varchar("id", { length: 255 }).primaryKey(),
  locale: varchar("locale", { length: 10 }).default("en"),
  // ... その他のフィールド
});

AI SDKとの統合

AIの応答をオンザフライで翻訳します:

import { openai } from "@ai-sdk/openai";
import { generateText } from "ai";
import { useLingui } from "@lingui/react/macro";

export function useAIChat() {
  const { i18n } = useLingui();

  const chat = async (prompt: string) => {
    const systemPrompt = i18n._(msg`You are a helpful AI assistant for acme.`);

    return generateText({
      model: openai("gpt-4"),
      messages: [
        { role: "system", content: systemPrompt },
        { role: "user", content: prompt },
      ],
    });
  };

  return { chat };
}

学んだベストプラクティス

1. 常にマクロを使用する

// ❌ Bad: ランタイム翻訳(抽出されない)
const text = t("Hello world");

// ✅ Good: マクロ(ビルド時に抽出される)
const text = t`Hello world`;

2. 文脈がすべて

翻訳者のためにコメントを追加しましょう:

// i18n: This appears in the pricing table header
<Trans>Monthly</Trans>

// i18n: Button to submit payment form
<button>{t`Subscribe Now`}</button>

Linguiはこれらを翻訳者へのメモとして抽出します。

3. 複数形を適切に扱う

import { Plural } from "@lingui/react/macro";

<Plural
  value={count}
  one="# credit remaining"
  other="# credits remaining"
/>

言語によって複数形のルールは異なります。Linguiがそれを処理してくれます。

4. 日付/数値のフォーマット

Intl APIを使用しましょう:

const date = new Intl.DateTimeFormat(locale, {
  dateStyle: "long",
}).format(new Date());

const price = new Intl.NumberFormat(locale, {
  style: "currency",
  currency: "USD",
}).format(29.99);

5. RTL(右書き)サポート

アラビア語などのために、方向を処理します:

export default function RootLayout({ children }) {
  const locale = getLocale();
  const direction = locale === "ar" ? "rtl" : "ltr";

  return (
    <html lang={locale} dir={direction}>
      <body>{children}</body>
    </html>
  );
}

Tailwind設定に追加:

module.exports = {
  plugins: [
    require('tailwindcss-rtl'),
  ],
};

方向に対応したクラスを使用:

<div className="ms-4"> {/* margin-start, LTR/RTL両方で機能 */}

デプロイ前のチェックリスト

出荷する前に確認しましょう:

  • pnpm i18nを実行して、すべての翻訳が最新であることを確認
  • 本番モードで各ロケールをテスト
  • ロケールCookieの永続性を確認
  • アラビア語のRTLレイアウトを確認
  • ロケールスイッチャーのUXをテスト
  • SEO用のhreflangタグを追加
  • 必要に応じてロケールベースのルーティングを設定
  • LLM翻訳コストを監視

結果

このシステムを導入した結果:

  • 17言語をサポート(初期状態で)
  • 約850の文字列を自動翻訳
  • 完全翻訳にかかった総コストは2〜3ドル
  • 新しい文字列を追加した際の更新サイクルは2分
  • 手動翻訳作業はゼロ
  • 文脈を理解した高品質な翻訳

これと比較してみてください:

  • 人間の翻訳者:1単語あたり0.10〜0.30ドル = 1,000ドル以上
  • 従来のサービス:依然として高価で遅い
  • 手作業:スケールしない

なぜ2026年にこれが重要なのか

いいですか、Webはグローバルです。2026年にもなって英語だけでリリースしているなら、世界の90%を置き去りにしていることになります。

しかし、従来のi18nは苦痛でした。このアプローチなら些細なことになります:

  1. Trans/tマクロでコードを書く(2秒)
  2. pnpm i18nを実行する(自動)
  3. 世界に出荷する(利益)

Linguiの開発者体験 + LLMによる翻訳の組み合わせは、ゲームチェンジャーです。得られるものは:

  • 型安全な翻訳
  • ゼロオーバーヘッドのランタイム
  • 自動抽出
  • 文脈を理解したAI翻訳
  • 1言語あたり数円のコスト
  • 無限にスケール可能

さらに先へ

レベルアップしたいですか? 以下を試してみてください:

動的コンテンツの翻訳

翻訳をデータベースに保存します:

// packages/db/schema/content.ts
export const blogPosts = pgTable("blog_post", {
  id: varchar("id", { length: 255 }).primaryKey(),
  titleEn: text("title_en"),
  titleZhCn: text("title_zh_cn"),
  titleJa: text("title_ja"),
  // ... etc
});

保存時に自動翻訳:

import { translateWithLLM } from "@acme/i18n";

export const blogRouter = createTRPCRouter({
  create: protectedProcedure
    .input(z.object({ title: z.string() }))
    .mutation(async ({ input }) => {
      // 全言語に翻訳
      const translations = await Promise.all(
        LOCALES.map(async (locale) => {
          const result = await translateWithLLM(
            [{ msgid: input.title, msgstr: "" }],
            locale
          );
          return [locale, result[0].msgstr];
        })
      );

      await db.insert(blogPosts).values({
        id: generateId(),
        titleEn: input.title,
        ...Object.fromEntries(translations),
      });
    }),
});

ユーザー提供の翻訳

ユーザーにより良い翻訳を提案してもらいましょう:

export const i18nRouter = createTRPCRouter({
  suggestTranslation: publicProcedure
    .input(z.object({
      msgid: z.string(),
      locale: z.string(),
      suggestion: z.string(),
    }))
    .mutation(async ({ input }) => {
      await db.insert(translationSuggestions).values(input);

      // メンテナーに通知
      await sendEmail({
        to: "i18n@acme.com",
        subject: `New translation suggestion for ${input.locale}`,
        body: `"${input.msgid}" → "${input.suggestion}"`,
      });
    }),
});

翻訳のA/Bテスト

どの翻訳がコンバージョンにつながるかテストします:

const variant = await abTest.getVariant("pricing-cta", locale);

const ctaText = variant === "A"
  ? t`Start Your Free Trial`
  : t`Try acme Free`;

コード

これらはすべて、実際のアプリで稼働しているプロダクションコードです。完全な実装は私たちのモノレポにあります:

t3-acme-app/
├── apps/nextjs/
│   ├── lingui.config.ts
│   ├── src/
│   │   ├── locales/           # コンパイル済みカタログ
│   │   ├── utils/i18n/        # i18nユーティリティ
│   │   └── providers/         # LinguiClientProvider
│   └── script/i18n.ts         # 翻訳スクリプト
└── packages/i18n/
    └── src/
        ├── translateWithLLM.ts
        ├── enhanceTranslations.ts
        └── utils.ts

最後に

2026年において、多言語対応のAIアプリを作ることはもはや難しくありません。ツールは揃っています:

  • Lingui: 抽出とランタイムのために
  • Claude/GPT: 文脈を理解した翻訳のために
  • T3 Turbo: 最高のDXのために

翻訳に何千ドルも払うのはやめましょう。アプリを英語だけに限定するのもやめましょう。

世界に向けて作り、速く出荷し、AIを使う。

それが2026年のやり方です。


質問や問題点はありますか? Twitterで私を見つけるか、LinguiドキュメントおよびAI SDKドキュメントをチェックしてください。

さあ、その多言語アプリを出荷しに行きましょう。世界が待っています。 excerpt: 2026年のi18nは、手動翻訳や高価な外注に頼る必要はありません。LinguiとLLM(Claude/GPT)を組み合わせた「2ピース・アーキテクチャ」により、17言語対応のWebアプリを低コストかつ自動で構築する方法を解説します。T3 Turboスタックを用いた実践的なコード例とともに、次世代の国際化フローを紹介します。

シェア

Feng Liu

Feng Liu

shenjian8628@gmail.com