블로그로 돌아가기
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로 30분 이내에 Next.js 앱 번역하는 방법

By Robert

PolyLingo로 30분 이내에 Next.js 앱 번역하기

이 튜토리얼이 끝나면 작동하는 다국어 Next.js App Router 프로젝트를 갖게 됩니다: 문자열은 messages/en.json에 추출되고, 필요한 모든 언어에 대한 번역된 로케일 파일이 생성되며, next-intl이 경로별로 올바른 파일을 제공하고, 콘텐츠가 변경될 때마다 다시 실행할 수 있는 단일 Node 스크립트가 준비됩니다.

가입할 번역 플랫폼이 없습니다. 언어별 고정 요금도 없습니다. 하나의 API 호출로 모든 대상 언어를 한 번에 처리합니다.

필요한 것:

  • App Router를 사용하는 Next.js 프로젝트 (Next.js 14 또는 15)
  • Node.js 18 이상
  • 무료 PolyLingo 계정과 API 키

1단계: PolyLingo API 키 받기 (5분)

usepolylingo.com에서 무료 계정을 만드세요. 무료 플랜은 매월 100,000 토큰을 포함하며, 이는 중간 크기의 로케일 파일을 10개 이상의 언어로 여러 번 번역할 수 있는 양입니다.

가입 후 대시보드의 API 키로 이동하여 키를 생성하세요. 전체 값은 한 번만 볼 수 있으니 즉시 복사하세요.

프로젝트에 환경 변수로 추가하세요. 절대 버전 관리에 커밋하거나 클라이언트 측 코드에 노출하지 마세요:

# .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에 보내며, 대상 로케일별로 출력 파일을 작성합니다. format: "json" 플래그는 API에 키 구조를 유지하고 문자열 값만 번역하도록 지시합니다 — 중첩 키, 배열, 비문자열 타입은 모두 변경되지 않고 그대로 반환됩니다.

// scripts/translate-messages.mjs
// Run with: 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에서 토큰 사용량을 확인하세요. 두 개 언어로 번역된 작은 로케일 파일의 경우 월별 할당량에서 몇 백 토큰만 사용됩니다.


다음 단계

더 많은 로케일 추가하기. 스크립트는 TARGETS에 몇 개 항목이 있든 한 번의 요청만 보냅니다. 일본어, 스페인어, 아랍어를 추가해도 API 호출은 한 번입니다.

CI에 연동하기. GitHub Actions에 POLYLINGO_API_KEY를 저장소 시크릿으로 추가하고 빌드 파이프라인의 일부로 스크립트를 실행하세요. en.json이 변경될 때마다 로케일 파일이 자동으로 동기화됩니다.

다른 포맷 번역하기. 같은 스크립트 패턴이 Markdown 문서 페이지(format: "markdown")와 HTML 이메일 템플릿(format: "html")에도 작동합니다. API는 모든 경우에 구조를 유지합니다.

대규모 프로젝트에는 배치 엔드포인트 사용하기. 여러 개의 별도 JSON 파일(예: 기능 영역별) 이 있다면, POST /translate/batch는 최대 100개 항목을 한 번에 요청할 수 있으며, 각 항목은 고유한 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 키 받기