헤드리스 CMS + PolyLingo

헤드리스 CMS용 다국어 지원.

Polylang은 WordPress 사이트에 완전한 다국어 워크플로우를 제공했습니다. PolyLingo는 그 워크플로우를 Sanity, Contentful, Webflow, Framer 및 모든 다른 헤드리스 CMS에 REST API를 통해 오후 만에 통합합니다.

Sanity
완전 통합 예시
Contentful
완전 통합 예시
Webflow
완전 통합 예시
36
대상 언어

헤드리스 CMS 다국어 지원은 아직 해결되지 않은 문제입니다.

Sanity에는 국제화 플러그인이 있습니다. Contentful에는 로케일이 있습니다. 하지만 둘 다 콘텐츠를 번역하지 않고 여러 언어로 저장만 합니다. 그 언어 슬롯을 채우는 작업은 여전히 수동입니다. 영어 콘텐츠를 내보내고, 리치 텍스트나 JSON 구조를 깨뜨리는 번역 도구에 돌리고, 결과물을 수정한 후 다시 가져오고, 모든 언어에 대해 반복합니다. 정기적으로 게시하는 활발한 콘텐츠 팀에게 이 워크플로우는 확장되지 않습니다. 또한 대부분의 소규모 환경에서는 존재하지 않아 콘텐츠가 전혀 번역되지 않는 경우가 많습니다.

헤드리스 CMS 아키텍처는 콘텐츠 관리를 콘텐츠 전달과 분리합니다. 이는 유연성에 좋지만, CMS는 언어 변형을 저장하지만 번역된 콘텐츠로 채우는 계층이 없습니다. 직접 그 계층을 구축해야 합니다.

대부분 팀은 두 가지 상황 중 하나에 처합니다: DeepL에 복사해 붙여넣으며 수동 번역(느리고 오류 발생, 확장 불가)하거나, 번역 API와 맞춤 통합을 작성해 무기한 유지 관리합니다. 둘 다 좋은 해결책이 아닙니다. PolyLingo는 깔끔한 세 번째 옵션입니다.

PolyLingo는 CMS에 필요한 번역 계층입니다.

PolyLingo는 CMS 게시 워크플로우와 직접 통합됩니다. 콘텐츠가 게시될 때 작동하는 웹훅을 설정하고, 콘텐츠를 PolyLingo에 전달하며, 모든 언어에 대한 번역본을 받아 CMS에 다시 씁니다. Sanity의 경우 서버 액션 몇 줄이면 되고, Contentful은 웹훅 핸들러, 맞춤 환경은 HTTP 호출입니다. 번역 모델은 Markdown, HTML, JSON, 리치 텍스트 등 콘텐츠 형식을 이해하고 구조를 유지합니다.

모든 CMS에서 패턴은 일관됩니다: 원본 언어 콘텐츠를 가져오고, 모든 대상 언어와 함께 PolyLingo API를 호출하며, 관리 API를 통해 번역된 콘텐츠를 CMS에 다시 씁니다. 이는 빌드 타임 스크립트, CI 작업, 웹훅 핸들러 등 워크플로우에 맞게 실행됩니다.

PolyLingo는 Markdown, HTML, 일반 텍스트를 처리하므로 CMS가 리치 콘텐츠에 사용하는 형식과 호환됩니다. 구조화된 필드(제목, 본문, 요약)는 개별 번역 가능해 어떤 필드를 번역할지 세밀하게 제어할 수 있습니다.

🟠

Sanity + PolyLingo

Sanity의 문서 국제화 플러그인은 로케일별 연결된 문서 변형을 생성합니다. 아래 스크립트는 영어 기본 문서를 가져와 각 대상 언어에 대해 자동으로 번역 변형을 만듭니다.

문서별 i18n 패턴(로케일별 문서 1개)과 필드별 패턴(모든 로케일이 한 문서 내)에 모두 작동합니다. 필드별 패턴은 문서 대신 필드를 반복 처리하세요.

scripts/translate-sanity.mjs
// scripts/translate-sanity.mjs
// Fetches published posts and translates each to all target languages

import { createClient } from '@sanity/client'

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID,
  dataset: 'production',
  token: process.env.SANITY_TOKEN,
  apiVersion: '2024-01-01',
  useCdn: false,
})

const posts = await sanity.fetch(`*[_type == "post" && __i18n_lang == "en"]`)

for (const post of posts) {
  const response = await fetch('https://api.usepolylingo.com/v1/translate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      content: post.body_markdown,
      format: 'markdown',
      targets: ['es', 'fr', 'de', 'ja', 'zh'],
    }),
  })

  const { translations } = await response.json()

  for (const [lang, content] of Object.entries(translations)) {
    await sanity.create({
      _type: 'post',
      __i18n_lang: lang,
      __i18n_base: { _type: 'reference', _ref: post._id },
      title: translations[lang + '_title'] || post.title,
      slug: { current: `${post.slug.current}-${lang}` },
      body_markdown: content,
    })
  }
}
🔵

Contentful + PolyLingo

Contentful은 동일 항목 내 필드로 로케일 변형을 저장합니다. 아래 스크립트는 Contentful 관리 API를 사용해 영어 항목을 가져와 번역하고, 번역된 콘텐츠를 로케일별 필드에 직접 씁니다 — 수동 복사-붙여넣기 불필요.

Contentful은 BCP 47 로케일 코드를 사용합니다(예: es-ES, es 아님). PolyLingo의 ISO 639-1 코드를 Contentful 로케일 설정에 맞게 매핑하세요.

scripts/translate-contentful.mjs
// scripts/translate-contentful.mjs
// Translates Contentful entries to all target locales

import contentful from 'contentful-management'

const client = contentful.createClient({
  accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN,
})

const space = await client.getSpace(process.env.CONTENTFUL_SPACE_ID)
const env = await space.getEnvironment('master')
const entries = await env.getEntries({ content_type: 'blogPost', locale: 'en-US' })

for (const entry of entries.items) {
  const enBody = entry.fields.body['en-US']

  const response = await fetch('https://api.usepolylingo.com/v1/translate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      content: enBody,
      format: 'markdown',
      targets: ['es-ES', 'fr-FR', 'de-DE'],
    }),
  })

  const { translations } = await response.json()

  for (const [locale, content] of Object.entries(translations)) {
    entry.fields.body[locale] = content
  }

  await entry.update()
  await entry.publish()
}
🌀

Webflow + PolyLingo

Webflow의 로컬라이제이션 API(CMS 및 비즈니스 플랜에서 사용 가능)는 로케일별 필드 콘텐츠를 지원합니다. 아래 스크립트는 CMS 컬렉션 항목을 가져와 HTML 본문 필드를 번역하고, Webflow v2 API를 통해 각 로케일 변형에 번역을 씁니다.

Webflow는 리치 텍스트 필드를 HTML로 저장합니다. PolyLingo의 HTML 번역은 Webflow가 생성한 모든 마크업 — 사용자 지정 클래스, 속성, 포함 요소 — 을 그대로 보존합니다.

scripts/translate-webflow.mjs
// scripts/translate-webflow.mjs
// Webflow Localization API + PolyLingo

const headers = {
  'Authorization': `Bearer ${process.env.WEBFLOW_API_TOKEN}`,
  'accept-version': '2.0.0',
  'Content-Type': 'application/json',
}

// Fetch English CMS items
const itemsRes = await fetch(
  `https://api.webflow.com/v2/collections/${process.env.WEBFLOW_COLLECTION_ID}/items`,
  { headers }
)
const { items } = await itemsRes.json()

for (const item of items) {
  const response = await fetch('https://api.usepolylingo.com/v1/translate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      content: item.fieldData['body-html'],
      format: 'html',
      targets: ['es', 'fr', 'de'],
    }),
  })

  const { translations } = await response.json()

  // Write translated content back to Webflow locale fields
  for (const [lang, content] of Object.entries(translations)) {
    await fetch(
      `https://api.webflow.com/v2/collections/${process.env.WEBFLOW_COLLECTION_ID}/items/${item.id}/locales/${lang}`,
      { method: 'PATCH', headers, body: JSON.stringify({ fieldData: { 'body-html': content } }) }
    )
  }
}

PolyLingo가 헤드리스 CMS 사용자에게 제공하는 것

  • Sanity — 웹훅을 통한 게시 시 번역, 문서 로케일에 다시 쓰기
  • Contentful — 영어 로케일 업데이트 시 항목 자동 번역
  • Webflow — API를 통한 CMS 컬렉션 항목 번역
  • API가 있는 모든 헤드리스 CMS — 통합 패턴 동일
  • 리치 텍스트, Markdown, HTML 모두 올바르게 보존
  • 36개 언어를 한 번의 요청으로 — 언어별 호출 불필요
  • 관리 API가 있는 모든 CMS와 작동
  • 게시할 때마다 콘텐츠 재번역 가능 — 수동 동기화 불필요

표준 다국어 CMS 워크플로우

1

원본 언어로 콘텐츠 작성

영어(또는 원본 언어)로 콘텐츠를 생성하고 게시하세요. CMS는 이를 권위 있는 버전으로 저장합니다. 편집 워크플로우를 변경할 필요 없습니다.

2

번역 스크립트 실행

수동, 예약 실행 또는 CMS의 게시 이벤트로 트리거되는 웹훅을 통해 스크립트를 실행하세요. 스크립트는 문서별로 한 번 PolyLingo를 호출해 모든 대상 언어를 처리하고, 모든 번역을 한 번에 CMS에 씁니다.

3

배포 — 번역된 콘텐츠가 실시간으로 제공됨

프런트엔드는 평소처럼 CMS에서 로케일별 콘텐츠를 읽습니다. 프런트엔드 코드를 변경할 필요 없습니다. 번역된 콘텐츠가 각 로케일 경로에 맞는 언어로 표시됩니다.

이 기능이 필요한 대상

✍️

Sanity 또는 Contentful 콘텐츠 팀

편집자는 영어로 게시합니다. 번역된 콘텐츠가 모든 로케일에 자동으로 나타나며, 편집 팀이 번역 도구를 다룰 필요가 없습니다.

🏢

다국어 사이트를 구축하는 에이전시

모든 고객 사이트에 다국어 지원이 필요합니다. PolyLingo는 스택 내 모든 헤드리스 CMS에서 작동하는 재사용 가능하고 청구 가능한 통합을 제공합니다.

🌍

현지화된 제품 콘텐츠가 필요한 전자상거래

제품 설명, 카테고리 페이지, 블로그 콘텐츠가 게시 시 자동으로 번역됩니다. 로케일별 가격과 결합해 완전한 현지화 쇼핑 경험을 제공합니다.

헤드리스 CMS 다국어에 관한 자주 묻는 질문

PolyLingo가 여기에 나열되지 않은 CMS와도 작동하나요?

네. 관리 API가 있는 모든 CMS는 동일한 패턴으로 통합할 수 있습니다 — 콘텐츠를 가져오고, PolyLingo를 호출하며, 다시 씁니다. Prismic, Storyblok, DatoCMS, Strapi, Ghost, Directus 모두 관리 API가 있어 이 방식으로 작동합니다. 위 Sanity, Contentful, Webflow 통합 예시는 이 패턴을 보여줍니다.

내장 이미지와 링크가 포함된 리치 텍스트도 번역할 수 있나요?

네. HTML 번역은 이미지(src 및 alt 속성 올바르게 처리), 링크(href 보존, 링크 텍스트 번역), iframe 등 모든 내장 요소를 보존합니다. 예외는 명시적으로 번역 불가로 표시된 콘텐츠 — 예를 들어 코드 블록은 절대 번역되지 않습니다.

번역하지 말아야 할 콘텐츠는 어떻게 처리하나요?

비번역 필드(슬러그, 날짜, 기술 식별자)가 있는 구조화 콘텐츠는 번역할 필드만 보내세요. 번역 가능/불가능 섹션이 혼합된 리치 텍스트는 HTML 형식을 사용하세요 — PolyLingo가 코드 블록 등 구조화 요소를 보존하며 텍스트만 번역합니다.

CMS에 중첩된 콘텐츠 유형이 있으면 어떻게 하나요?

깊게 중첩된 콘텐츠(다른 문서를 참조하는 문서)는 문서 유형별로 독립 번역하세요. 순환 참조를 피하고 어떤 콘텐츠를 번역할지 명확히 제어할 수 있습니다. 문서 간 참조는 CMS가 유지하며, PolyLingo는 필드 콘텐츠만 다룹니다.

원본 콘텐츠가 변경될 때 번역을 어떻게 동기화하나요?

권장 패턴은 CMS 웹훅을 통해 게시 이벤트마다 번역 스크립트를 트리거하는 것입니다. 이렇게 하면 원본 변경 시 번역 콘텐츠가 업데이트됩니다. 업데이트가 덜 빈번하면 야간 스케줄이나 프로덕션 배포 전 실행도 좋습니다.

자동 게시 대신 "검토 필요"로 번역을 표시할 수 있나요?

CMS에 따라 다릅니다. Contentful과 Sanity는 모두 초안 상태를 지원해 번역 콘텐츠를 게시 대신 초안으로 작성할 수 있어 각 로케일이 라이브 되기 전에 사람이 검토할 수 있습니다. 위 스크립트 예시는 즉시 게시/생성하지만, 마지막 단계를 수정해 검토 워크플로우용 초안 생성으로 바꿀 수 있습니다.

지금 바로 헤드리스 CMS에 다국어 지원을 추가하세요.

무료 플랜. 월 50,000 토큰. 신용카드 불필요.

무료로 번역 시작하기

무료 플랜 — 월 50,000 토큰. 모든 CMS와 호환.

헤드리스 CMS용 다국어 지원 — Sanity, Contentful 등 — PolyLingo | PolyLingo