ブログに戻る
Terminal output showing a translation script writing de.json and fr.json, alongside a folder tree and a browser rendering the German locale route.

PolyLingoでNext.jsアプリを30分以内に翻訳する方法

By Robert

PolyLingoで30分以内にNext.jsアプリを翻訳する方法

このチュートリアルの終わりには、動作する多言語対応のNext.js App Routerプロジェクトが完成しています。文字列はmessages/en.jsonに抽出され、必要なすべての言語の翻訳済みロケールファイルが用意され、next-intlがルートごとに適切なファイルを提供し、コンテンツが変更されるたびに再実行できる単一のNodeスクリプトがあります。

翻訳プラットフォームへの登録は不要です。言語ごとの固定料金もありません。1回のAPIコールで全てのターゲット言語を一括処理します。

必要なもの:

  • App Routerを使用したNext.jsプロジェクト(Next.js 14または15)
  • Node.js 18以降
  • 無料のPolyLingoアカウントとAPIキー

ステップ1: PolyLingo APIキーを取得する(5分)

usepolylingo.comで無料アカウントを作成します。無料プランでは月に100,000トークンが含まれており、中規模のロケールファイルを10言語以上に何度も翻訳するのに十分です。

ログインしたら、ダッシュボードのAPI keysに移動してキーを作成します。キーの完全な値は一度しか表示されないため、すぐにコピーしてください。

プロジェクトの環境変数として追加します。バージョン管理にコミットしたり、クライアント側コードで公開しないでください:

# .env.local
POLYLINGO_API_KEY="pl_your_key_here"

APIが利用可能かどうかを確認します:

curl -sS "https://api.usepolylingo.com/v1/health"

"status": "ok"を含む小さなJSONペイロードが返ってくるはずです。


ステップ2: next-intlをインストールしてルーティングを設定する(10分)

ライブラリをインストールします:

npm install next-intl

プロジェクトルートにi18n.tsファイルを作成します。これはnext-intlにサポートするロケールと、各リクエストに対して正しいメッセージファイルを読み込む方法を伝えます:

// i18n.ts
import { getRequestConfig } from 'next-intl/server'

export const locales = ['en', 'de', 'fr'] as const
export type Locale = (typeof locales)[number]
export const defaultLocale: Locale = 'en'

export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale
  if (!locale || !locales.includes(locale as Locale)) {
    locale = defaultLocale
  }
  return {
    locale,
    messages: (await import(`./messages/${locale}.json`)).default,
  }
})

ミドルウェアを追加してルートにロケールのプレフィックスを付けます:

// middleware.ts
import createMiddleware from 'next-intl/middleware'
import { locales, defaultLocale } from './i18n'

export default createMiddleware({
  locales: [...locales],
  defaultLocale,
  localePrefix: 'as-needed',
})

export const config = {
  matcher: ['/((?!api|_next|.*\\..*).*)'],
}

ページファイルをapp/[locale]/の下に移動します。ルートレイアウトを更新してlocaleパラメータを受け取り、子要素をNextIntlClientProviderでラップします:

// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: Promise<{ locale: string }>
}) {
  const { locale } = await params
  const messages = await getMessages()
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  )
}

ステップ3: 文字列をJSONメッセージファイルに抽出する(10分)

プロジェクトルートにmessages/フォルダを作成します。en.jsonファイルにソース文字列を追加します。next-intlはネストされたキー構造を使用します:

{
  "Home": {
    "title": "Welcome",
    "cta": "Get started"
  }
}

ページを更新して、ハードコードされた文字列の代わりにuseTranslationsを使います:

// app/[locale]/page.tsx
import { useTranslations } from 'next-intl'

export default function HomePage() {
  const t = useTranslations('Home')
  return (
    <main>
      <h1>{t('title')}</h1>
      <button type="button">{t('cta')}</button>
    </main>
  )
}

次に翻訳スクリプトを書きます。これはmessages/en.jsonを読み込み、format: "json"を指定してPolyLingo APIに送信し、ターゲットロケールごとに1つの出力ファイルを書き出します。format: "json"フラグはAPIにキー構造を保持し、文字列値のみを翻訳するよう指示します。ネストされたキー、配列、非文字列型はそのまま返されます。

// scripts/translate-messages.mjs
// 実行コマンド: node scripts/translate-messages.mjs

import fs from 'node:fs'
import path from 'node:path'

const API_KEY = process.env.POLYLINGO_API_KEY
const API_URL = (process.env.POLYLINGO_API_URL || 'https://api.usepolylingo.com/v1').replace(/\/$/, '')
const TARGETS = ['de', 'fr'] // 必要に応じてロケールを追加してください

const enPath = path.join('messages', 'en.json')
const en = JSON.parse(fs.readFileSync(enPath, 'utf8'))

const res = await fetch(`${API_URL}/translate`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    content: JSON.stringify(en),
    format: 'json',
    targets: TARGETS,
    model: 'standard',
  }),
})

if (!res.ok) {
  const err = await res.text()
  throw new Error(`PolyLingo ${res.status}: ${err}`)
}

const { translations } = await res.json()

for (const locale of TARGETS) {
  const out = path.join('messages', `${locale}.json`)
  fs.writeFileSync(out, JSON.stringify(JSON.parse(translations[locale]), null, 2) + '\n')
  console.log('Wrote', out)
}

実行します:

node scripts/translate-messages.mjs

以下のような出力が表示されます:

Wrote messages/de.json
Wrote messages/fr.json

これらのファイルを開いて確認してください。キーはen.jsonと同一で、文字列の値だけが翻訳されています。


ステップ4: ルートの動作確認(5分)

開発サーバーを起動します:

npm run dev

http://localhost:3000http://localhost:3000/deにアクセスします。見出しとボタンがそれぞれ英語とドイツ語で表示されるはずです。スクリプトのTARGETS配列とi18n.tslocales配列にロケールを追加し、スクリプトを再実行してさらに多くの言語を追加できます。

PolyLingoダッシュボードのUsageでトークン使用量を確認してください。小さなロケールファイルを2言語に翻訳した場合、月間付与トークンの数百トークンを使用した程度です。


次に進むには

ロケールを追加しましょう。 スクリプトはTARGETSにいくつエントリがあっても1回のリクエストで送信します。日本語、スペイン語、アラビア語を追加してもAPIコールは1回だけです。

CIに組み込みましょう。 GitHub ActionsのリポジトリシークレットにPOLYLINGO_API_KEYを追加し、ビルドパイプラインの一部としてスクリプトを実行します。en.jsonが変更されるたびにロケールファイルが自動的に同期されます。

他のフォーマットも翻訳しましょう。 同じスクリプトパターンはMarkdownドキュメントページ(format: "markdown")やHTMLメールテンプレート(format: "html")にも使えます。APIはすべてのケースで構造を保持します。

大規模プロジェクトにはバッチエンドポイントを使いましょう。 複数のJSONファイル(機能ごとに1つなど)がある場合、POST /translate/batchは最大100件のアイテムを1回のリクエストで受け付け、それぞれにidformatを指定できます。


無料で試す

PolyLingoの無料プランは月に100,000トークンが含まれています。クレジットカードは不要です。

curl -sS -X POST "https://api.usepolylingo.com/v1/translate" \
  -H "Authorization: Bearer $POLYLINGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "{\"Home\":{\"title\":\"Welcome\",\"cta\":\"Get started\"}}",
    "format": "json",
    "targets": ["de", "fr", "es", "ja"]
  }'

APIキーを取得する