|
문서
데모 예약플랫폼
플랫폼MCPCLIAPI워크플로
가이드
변경 로그

로컬라이제이션

  • 개요
  • 번역 API
  • 웹 앱 로컬라이제이션
  • 모바일 앱 로컬라이제이션
  • String Catalogs로 iOS 로컬라이제이션
  • strings.xml로 Android 로컬라이제이션
  • 이메일 로컬라이제이션
  • 정적 콘텐츠(예: .md, .json)
  • Markdoc으로 Next.js 사용하기
  • Rails + i18n

워크플로

  • MCP로 엔진 설정하기
  • Jira 트리아지
  • CI/CD

Markdoc으로 Next.js App Router 로컬라이제이션하기

Lingo.dev CLI는 설정된 로컬라이제이션 엔진을 통해 Markdoc 파일과 JSON UI 문자열 카탈로그를 번역합니다. Markdoc은 타입이 지정된 React 기반 커스텀 태그를 지원하는 Markdown 기반 저작 포맷으로, 장문 콘텐츠와 인터랙티브 컴포넌트를 함께 사용하는 Next.js App Router 사이트에 특히 잘 어울립니다.

이 가이드는 Next.js App Router 사이트를 처음부터 끝까지 로컬라이즈하는 전 과정을 안내합니다. CLI 설정, 로캘별 콘텐츠 구성, 동적 라우트에서의 Markdoc 렌더링, 그리고 Lingo.dev GitHub App을 활용한 번역 자동화까지 모두 다룹니다.

데모 리포지토리

함께 따라 하려면 lingodotdev/markdoc-nextjs-localization-example를 클론하거나 포크하세요. 이 리포지토리에는 Markdoc 콘텐츠, Lingo.dev CLI 설정, CI 워크플로가 포함된 작동하는 Next.js App Router 앱이 준비되어 있습니다.

Next.js + Markdoc 로컬라이제이션 작동 방식#

대부분의 Next.js App Router 사이트는 로컬라이즈된 콘텐츠를 두 레이어로 나눠 관리합니다:

레이어포함되는 내용예시 파일
장문 콘텐츠마케팅 페이지, 문서, 블로그 게시물src/content/en/pages/home.md
UI 문자열내비게이션 바 레이블, CTA, 버튼 상태src/content/en/ui.json

라우트는 src/app/[lang]/ 아래에 위치하며, 요청 시점에 해당 로캘의 파일을 읽어옵니다. 미들웨어는 브라우저의 Accept-Language 헤더를 기준으로 기본 로캘을 선택하고, / 같은 기본 경로를 /en(또는 가장 적합한 로캘)로 리디렉션합니다.

CLI는 frontmatter와 커스텀 태그를 그대로 유지한 채 Markdoc 파일을 파싱하고, UI 문자열 카탈로그는 JSON으로 처리합니다. 두 파일 모두 변경된 부분만 로컬라이제이션 엔진을 통해 번역한 뒤, 소스 파일과 나란히 로캘별 파일로 저장합니다.

사전 준비#

1

로컬라이제이션 엔진 만들기

CLI를 실행할 때마다 콘텐츠는 어떤 LLM 모델과 용어집, 브랜드 보이스, 규칙을 적용할지 정하는 설정인 로컬라이제이션 엔진을 거쳐 전송됩니다. Lingo.dev dashboard에서 로컬라이제이션 엔진을 만들고 CI용 API key를 생성하세요.

2

Node.js 확인

CLI를 사용하려면 Node.js 22 이상이 필요합니다:

bash
node -v
3

Next.js 프로젝트 설정

프로젝트에는 App Router(src/app/)와 로캘별 콘텐츠 디렉터리가 필요합니다. 데모 리포지토리는 src/content/ 아래에 로캘마다 하나의 디렉터리(예: src/content/en/)를 두고, 그 안에 두 개의 하위 폴더(pages/, blog/)와 ui.json 파일을 배치합니다. 라우팅의 기본은 Next.js internationalization에서 확인하세요.

콘텐츠 구성#

콘텐츠는 역할에 따라 나누는 것이 좋습니다. 장문 페이지와 게시물은 Markdoc으로 작성하고, 짧은 UI 문자열은 컴포넌트에서 바로 불러올 수 있도록 JSON에 저장합니다.

text
src/content/
  en/                  # Source locale
    pages/home.md      # Long-form Markdoc
    blog/hello.md
    ui.json            # UI strings (navbar, CTAs, button states)
  es/                  # Target locales – generated by Lingo.dev
  fr/
  de/

Markdoc 파일은 페이지별 메타데이터(title, description, date, author)를 위한 frontmatter와 React 컴포넌트로 렌더링되는 커스텀 태그를 지원합니다. 최소한의 페이지 예시는 다음과 같습니다:

markdown
---
title: Author once in Markdoc, ship in every language.
description: An example Next.js App Router app that localizes Markdoc with Lingo.
---

{% inline-callout type="info" %}
This page is authored in Markdoc and translated by Lingo.dev.
{% /inline-callout %}

## Built from three pieces

Markdoc custom tags render as React components – even interactive ones.

CLI 설정#

CLI를 설치하고 로그인하세요:

bash
npm install -g @lingo.dev/cli
lingo login

그다음 설정 파일을 생성하고 엔진에 연결하세요:

bash
lingo init
lingo link

lingo init은 소스 및 대상 로캘, 그리고 번역할 파일 패턴이 포함된 .lingo/config.json를 생성하고, lingo link은 orgId와 engineId를 추가합니다. 팀원 모두와 CI가 같은 설정을 사용하도록 .lingo/config.json를 커밋하세요.

이 프로젝트의 설정에는 두 가지 파일 패턴이 정의되어 있습니다. 하나는 Markdoc 콘텐츠용이고, 다른 하나는 UI 문자열 카탈로그용입니다:

json
{
  "orgId": "org_...",
  "engineId": "eng_...",
  "sourceLocale": "en",
  "targetLocales": ["es", "fr", "de"],
  "files": [
    { "pattern": "src/content/en/pages/*.md" },
    { "pattern": "src/content/en/blog/*.md" },
    { "pattern": "src/content/en/ui.json" }
  ]
}

각 경로의 로캘 세그먼트는 대상 로캘에 맞게 치환됩니다. src/content/en/pages/home.md은 src/content/es/pages/home.md로, src/content/en/ui.json은 src/content/de/ui.json로 바뀝니다. 소스 경로에는 반드시 로캘 코드가 포함되어야 합니다. 형식은 파일 확장자를 기준으로 자동 감지되므로 Markdoc(.md)와 JSON(.json) 파일에는 별도의 타입 지정이 필요 없습니다. 자세한 내용은 Configuration과 Formats를 참고하세요.

단일 파일 카탈로그

새 CLI는 로캘별 파일 1개씩, 그리고 경로에 로캘 코드가 포함된 구조를 전제로 합니다(위 예시처럼). UI 문자열이 여러 로캘을 담은 단일 JSON 파일에 있다면, 그 구조(이전 json-per-locale 버킷)는 아직 새 CLI에서 지원되지 않습니다—계속 legacy CLI를 사용하고 지원 여부는 변경 로그를 확인하세요. 권장되는 방식은 로캘별로 파일을 분리하는 것입니다.

App Router에서 Markdoc 렌더링하기#

일반적인 동적 라우트는 문서를 불러온 뒤 변환된 트리를 렌더링합니다. 데모 리포지토리에서는 이를 위해 간단한 헬퍼를 제공합니다:

ts
// src/lib/markdoc.ts
export async function loadDoc(
  locale: Locale,
  collection: "pages" | "blog",
  slug: string,
) {
  const raw = await fs.readFile(
    path.join(process.cwd(), "src/content", locale, collection, `${slug}.md`),
    "utf8",
  );
  const ast = Markdoc.parse(raw);
  const frontmatter = ast.attributes.frontmatter
    ? parseFrontmatter(ast.attributes.frontmatter)
    : {};
  const content = Markdoc.transform(ast, { ...schema, variables: { frontmatter } });
  return { frontmatter, content };
}

App Router 페이지는 문서를 로캘별 UI 문자열과 연결해 주는 얇은 래퍼 역할을 합니다:

tsx
// src/app/[lang]/page.tsx
export default async function Home({ params }: PageProps<"/[lang]">) {
  const { lang } = await params;
  const doc = await loadDoc(lang, "pages", "home");
  const { home } = await getMessages(lang);

  return (
    <main>
      <h1>{doc.frontmatter.title}</h1>
      {renderMarkdoc(doc.content)}
    </main>
  );
}

커스텀 Markdoc 태그(callout, bento, blog-hero 등)는 markdoc.schema.ts에 선언하고, src/components/markdoc/ 아래의 React 컴포넌트에 연결합니다. 전체 API는 Markdoc schema docs를 참고하세요.

미들웨어에서 로캘 감지하기#

Next.js 미들웨어는 라우트가 렌더링되기 전에 요청을 검사합니다. 이를 활용하면 Accept-Language 헤더를 기준으로 기본 경로를 가장 잘 맞는 로캘로 리디렉션할 수 있습니다:

ts
// src/middleware.ts
export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const hasLocale = locales.some(
    (locale) => pathname === `/${locale}` || pathname.startsWith(`/${locale}/`),
  );
  if (hasLocale) return;

  const locale = pickLocale(request); // parses Accept-Language
  const url = request.nextUrl.clone();
  url.pathname = `/${locale}${pathname === "/" ? "" : pathname}`;
  return NextResponse.redirect(url);
}

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

방문자는 접두사를 직접 입력하지 않아도 /en, /es, /fr, /de로 이동하게 됩니다.

로컬에서 번역하기#

lingo login 후에는 push를 실행하세요. 첫 실행이거나 새 대상 로캘을 추가한 직후라면 전체를 채워 넣습니다:

bash
lingo push --backfill-missing

이후 실행에서는 변경분만 push하면 됩니다:

bash
lingo push

lingo push은 설정한 패턴과 일치하는 모든 파일을 읽고, 커밋된 .lingo/lock.jsonlock file을 기준으로 아직 번역되지 않은 항목을 찾아냅니다. 그런 다음 변경분만 로컬라이제이션 엔진으로 번역하고, 완료될 때까지 기다린 뒤 각 대상 로캘 디렉터리에 결과를 기록합니다. Frontmatter 키, Markdoc 커스텀 태그, JSON 구조는 그대로 유지되고 번역 가능한 텍스트만 바뀝니다. 다른 곳(예: CI)에서 생성된 번역을 가져오려면 lingo pull를 실행하세요.

실행 범위를 특정 파일로 제한하려면 glob을 전달하세요:

bash
lingo push "src/content/en/blog/*.md"

CI에서 자동화하기#

Lingo.dev GitHub App을 설치하고 리포지토리에 연결하세요. 서버 측에서 .lingo/config.json와 연결된 engineId를 읽고, 소스 콘텐츠가 변경될 때마다 번역 pull request를 자동으로 생성합니다—워크플로 파일도, 러너도, API key secret도, lock file 관리도 필요 없습니다.

배포 전 검증#

번역되지 않은 콘텐츠가 프로덕션에 배포되지 않도록 lingo check를 배포 게이트로 활용하세요. 아직 번역이 필요한 항목이 하나라도 있으면 0이 아닌 상태 코드로 종료됩니다:

bash
lingo check

이 단계는 Next.js 빌드 전에 별도의 CI 단계로 추가하세요:

yaml
- name: Verify translations
  run: lingo check
  env:
    LINGO_API_KEY: ${{ secrets.LINGO_API_KEY }}
- name: Build
  run: pnpm build

다음 단계#

Static Content Localization
Markdown, MDX, JSON, YAML 등 다양한 파일 형식 지원
Web App Localization
주요 웹 프레임워크 전반에서 활용되는 UI 문자열 패턴
CI/CD Workflows
GitHub App 및 자체 호스팅 러너 패턴
Glossaries
브랜드명과 기술 용어가 번역되지 않도록 고정

이 페이지가 도움이 되었나요?

Max PrilutskiyMax Prilutskiy·업데이트됨 8일 전·5 min read