엔지니어링//9 최소 읽기 시간

GitHub으로 번역을 자동 동기화하는 방법: 완전한 CI/CD 현지화 가이드

Eray Gündoğmuş
공유

대부분의 팀은 번역을 번거로운 수동 루프로 관리합니다: 문자열 내보내기, 번역자에게 전송, 대기, 파일 받기, 리포지토리에 복사, 머지 충돌 해결, 반복. 자주 배포하거나 여러 언어를 지원하는 경우 이 프로세스는 빠르게 무너집니다.

더 나은 방법이 있습니다. GitHub 리포지토리를 번역 관리 시스템에 직접 연결함으로써, 코드의 새 문자열 감지부터 완성된 번역의 풀 리퀘스트 배달까지 전체 사이클을 자동화할 수 있습니다.

이 가이드에서는 better-i18n을 사용하여 GitHub와의 자동 번역 동기화를 설정하는 방법을 설명합니다. Webhook 메커니즘, PR 기반 배달 워크플로, 멀티 리포지토리 설정, Doctor CI 상태 확인 워크플로를 다룹니다.

번역 동기화를 자동화해야 하는 이유

설정에 들어가기 전에, 수동 번역 워크플로의 비용을 이해하는 것이 중요합니다:

  • 컨텍스트 전환 — 개발자가 코드와 번역 파일 사이를 왔다 갔다 합니다
  • 오래된 번역 — 수동 내보내기는 번역자가 오래된 소스 문자열로 작업한다는 의미입니다
  • 머지 충돌 — JSON 번역 파일은 여러 명이 건드리면 충돌의 온상이 됩니다
  • 누락된 커버리지 — 새 기능이 번역 없이 배포되었는지 자동으로 알 수 있는 방법이 없습니다
  • 느린 배달 — 번역이 지속적으로 흐르는 대신 큐에서 대기합니다

GitHub와 번역 플랫폼 간의 동기화를 자동화하면 이러한 병목 현상이 해소됩니다. 리포지토리에 대한 모든 푸시가 키 감지를 트리거합니다. 모든 완성된 번역이 풀 리퀘스트로 돌아옵니다. CI 검사가 프로덕션에 도달하기 전에 누락된 번역을 잡아냅니다.

아키텍처 개요

동기화 시스템에는 두 가지 방향이 있습니다:

인바운드 (코드에서 클라우드로): 개발자가 코드를 푸시하면, GitHub가 better-i18n에 Webhook을 전송합니다. 플랫폼은 번역 파일을 읽고, 새로운 또는 변경된 키를 감지하며, 클라우드 대시보드를 업데이트합니다. 번역자는 즉시 새 문자열을 확인합니다.

아웃바운드 (클라우드에서 코드로): 번역이 완성되면, better-i18n이 업데이트된 JSON 파일과 함께 리포지토리에 풀 리퀘스트를 생성합니다. 팀은 표준 코드 리뷰 워크플로를 통해 검토하고 머지합니다.

두 방향 모두 HMAC-SHA256 서명 검증으로 보호됩니다. 인증되지 않은 페이로드는 처리되지 않습니다.

1단계: GitHub App 설치

GitHub 계정을 better-i18n에 연결하는 것부터 시작합니다:

  1. dash.better-i18n.com으로 이동하여 프로젝트 설정을 엽니다
  2. Connect GitHub을 클릭하여 better-i18n GitHub App을 설치합니다
  3. 앱이 접근할 수 있는 리포지토리를 선택합니다 — 특정 리포지토리 또는 전체 조직에 대한 접근을 허용할 수 있습니다
  4. 요청된 권한을 승인합니다

better-i18n은 최소한의 권한을 요청합니다:

권한목적
Repository Contents번역 파일 읽기/쓰기 (설정된 패턴과 일치하는 파일만)
Pull Requests번역 업데이트를 위한 PR 생성
Webhooks동기화를 트리거하는 푸시 이벤트 수신

앱은 소스 코드, 환경 변수, 시크릿, 또는 번역 파일 패턴 외의 파일에 절대 접근하지 않습니다.

2단계: 리포지토리 추가

GitHub App이 설치되면, 동기화하려는 리포지토리를 추가합니다. 대시보드에서 또는 프로그래밍 방식으로 할 수 있습니다:

github.addRepository({
  project: "your-org/your-project",
  repositoryId: 123456789,
  branch: "main",
  filePattern: "locales/{locale}/{namespace}.json"
})

filePattern 매개변수는 better-i18n에 번역 파일을 찾을 위치를 알려줍니다. 일반적인 패턴은:

  • locales/{locale}/{namespace}.json — 네임스페이스 파일이 있는 로케일 폴더
  • src/i18n/{locale}.json — 로케일당 단일 파일
  • packages/*/locales/{locale}.json — 패키지별 번역이 있는 Monorepo

멀티 리포지토리 지원

단일 리포지토리에 제한되지 않습니다. better-i18n은 여러 멀티 리포지토리 구성을 지원합니다:

  • Monorepo: 번역 프로젝트를 공유하는 여러 앱을 가진 단일 리포지토리
  • 마이크로 프론트엔드: 공유 번역 세트에 기여하는 여러 리포지토리
  • 플랫폼 + 모바일: 동일한 번역 소스에서 동기화하는 웹과 모바일 리포지토리

각 리포지토리는 자체 파일 패턴과 브랜치 구성을 가질 수 있습니다. 번역은 모든 연결된 리포지토리와 중앙 클라우드 프로젝트 간에 흐릅니다.

3단계: Webhook 메커니즘 이해

리포지토리가 연결되면, GitHub가 better-i18n에 Webhook 이벤트를 전송합니다. 각 이벤트가 하는 일은 다음과 같습니다:

푸시 이벤트

push 이벤트는 주요 동기화 트리거입니다. 연결된 브랜치에 코드를 푸시하면:

  1. GitHub가 푸시 페이로드를 better-i18n에 전송합니다
  2. 페이로드 서명이 HMAC-SHA256을 사용하여 검증됩니다
  3. better-i18n이 변경된 파일이 설정된 파일 패턴과 일치하는지 확인합니다
  4. 번역 파일이 변경된 경우, 동기화 작업이 시작됩니다
  5. 새 키가 클라우드 프로젝트에 추가되고, 변경된 번역이 업데이트됩니다
  6. 동기화 상태가 추적되고 대시보드에 표시됩니다

설정된 브랜치에 대한 푸시만 동기화를 트리거합니다. 명시적으로 설정하지 않은 한 피처 브랜치에 푸시해도 동기화가 트리거되지 않습니다.

설치 라이프사이클 이벤트

이벤트발생하는 일
installation.deleted통합이 자동으로 제거되고 모든 Webhook 구독이 정리됩니다
installation.suspend동기화 일시 중지 — 설치가 재개될 때까지 Webhook이 처리되지 않습니다
installation.unsuspend동기화 재개 — Webhook이 다시 정상적으로 처리됩니다

이러한 이벤트는 통합이 GitHub App 설치 상태와 동기화된 상태를 유지하도록 합니다. 조직 관리자가 앱을 일시 중단하면, better-i18n은 실패한 Webhook 배달을 쌓는 대신 즉시 처리를 중단합니다.

4단계: PR 기반 번역 워크플로 설정

아웃바운드 흐름 — 번역을 리포지토리에 다시 배달하는 것 — 은 직접 푸시 대신 풀 리퀘스트를 사용합니다. 이것은 의도적인 설계 선택입니다:

왜 풀 리퀘스트인가?

  • 코드 리뷰는 번역에도 적용됩니다. 리뷰어는 번역이 프로덕션에 도달하기 전에 컨텍스트 문제, 플레이스홀더 오류, 형식 문제를 발견할 수 있습니다.
  • CI가 번역 변경에 대해 실행됩니다. 테스트 스위트, 린터, 빌드 프로세스가 새 번역이 아무것도 깨지 않는지 검증합니다.
  • git에 완전한 감사 추적. 모든 번역 변경은 정확히 무엇이 변경되었는지 보여주는 명확한 diff를 가진 커밋입니다.
  • 쉬운 롤백. 다른 변경 사항을 되돌리는 것과 동일한 방식으로 번역 PR을 되돌립니다.

작동 방식

  1. 번역자 또는 AI 에이전트가 better-i18n 대시보드에서 번역을 업데이트합니다
  2. 번역 배치가 준비되면 게시를 트리거합니다
  3. better-i18n이 리포지토리에 브랜치를 생성합니다 (예: better-i18n/translations-2026-03-13)
  4. 업데이트된 번역 JSON 파일이 해당 브랜치에 커밋됩니다
  5. 설정된 베이스 브랜치에 대한 풀 리퀘스트가 열립니다
  6. 팀이 diff를 검토하고 만족하면 머지합니다

PR에는 번역 파일만 포함됩니다 — 다른 파일은 건드리지 않습니다. diff는 어떤 키가 어떤 언어에서 변경되었는지 명확하게 보여줍니다.

번역 게시

여러 채널을 통해 게시를 트리거할 수 있습니다:

  • 대시보드: 프로젝트 설정에서 "Publish to GitHub" 버튼을 클릭합니다
  • REST API: 게시 엔드포인트를 프로그래밍 방식으로 호출합니다
  • MCP 도구: AI 에이전트 워크플로에서 publishTranslations를 사용합니다

5단계: Doctor CI 워크플로 활성화

Doctor 워크플로는 모든 커밋에서 번역 상태를 모니터링하는 GitHub Actions 워크플로입니다. 번역을 위한 ESLint라고 생각하면 됩니다.

워크플로 생성

tRPC API를 사용하여 워크플로 파일을 생성합니다:

github.createDoctorWorkflow({
  project: "your-org/your-project",
  repositoryId: 123456789
})

이것은 리포지토리에 .github/workflows/i18n-doctor.yml 파일을 생성합니다.

Doctor가 확인하는 것

누락된 번역 — 소스 언어에는 있지만 하나 이상의 타겟 언어에 없는 키. Doctor는 언어별 정확한 수를 보고합니다.

사용되지 않는 키 — 번역 파일에 정의되어 있지만 코드베이스에서 한 번도 참조되지 않는 키. 이것들은 번역 번들을 비대하게 만들고 번역자를 혼란스럽게 합니다.

형식 일관성 — 모든 언어에 걸친 ICU 메시지 구문 검증. 영어에서는 {count}이지만 스페인어에서는 {nombre}와 같은 불일치 플레이스홀더나 깨진 복수 규칙 같은 오류를 잡아냅니다.

커버리지 보고서 — 진행 상황을 한눈에 추적할 수 있도록 언어별 완료 비율.

Doctor 출력 예시

i18n Doctor Report
==================

Coverage:
  en: 100% (source)
  tr: 94.2% (missing 23 keys)
  de: 87.1% (missing 51 keys)
  ja: 78.3% (missing 86 keys)

Unused keys: 12
Format errors: 0

Result: WARNING — 3 languages below 95% threshold

Doctor는 GitHub의 체크 시스템과 통합됩니다. 누락된 번역이나 형식 오류를 도입하는 풀 리퀘스트는 경고 상태를 표시하여 리뷰어에게 번역 영향에 대한 즉각적인 가시성을 제공합니다.

Doctor와 CLI 결합

가장 철저한 커버리지를 위해 CI 파이프라인에서 Doctor와 better-i18n CLI를 결합합니다:

name: i18n Health Check
on: [push, pull_request]

jobs:
  i18n:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"

      # 번역되어야 할 하드코딩된 문자열 스캔
      - name: Scan for untranslated strings
        run: npx @better-i18n/cli scan --ci

      # 로컬 키를 클라우드 프로젝트와 비교
      - name: Sync coverage report
        run: npx @better-i18n/cli sync --format json

      # Doctor는 생성된 워크플로를 통해 자동으로 실행됩니다

scan --ci 명령은 번역되지 않은 문자열이 발견되면 0이 아닌 코드로 종료되어 PR을 차단합니다. sync --format json 명령은 기계가 읽을 수 있는 커버리지 보고서를 출력합니다.

6단계: pre-commit 훅 설정

코드가 커밋되기 전에 — 더 일찍 번역 문제를 잡습니다 — pre-commit 훅을 사용하여:

# Husky 사용
npx husky init
echo "npx @better-i18n/cli scan --staged --ci" > .husky/pre-commit

--staged 플래그는 CLI에 현재 git 스테이징 영역의 파일만 스캔하도록 지시하여, 큰 코드베이스에서도 훅을 빠르게 유지합니다.

더 세밀한 제어를 위해 lint-staged를 사용합니다:

{
  "lint-staged": {
    "*.{tsx,jsx}": ["better-i18n scan --ci"]
  }
}

동기화 추적 및 Observability

모든 동기화 작업 — 인바운드 또는 아웃바운드 — 은 전체 상태와 로그가 있는 작업으로 추적됩니다. MCP 도구 또는 REST API를 통해 동기화 기록을 조회할 수 있습니다:

볼 수 있는 동기화 유형:

  • initial_import — 리포지토리가 연결될 때의 첫 번째 동기화. 기존의 모든 번역 파일을 가져옵니다.
  • source_sync — GitHub 푸시 이벤트에 의해 트리거됩니다. 변경된 번역 파일을 클라우드에 동기화합니다.
  • cdn_upload — 런타임 소비를 위해 CDN에 번역을 Deploy합니다.
  • batch_publish — 번역을 풀 리퀘스트로 GitHub에 다시 게시합니다.

각 작업에는 타임스탬프, 상태 (대기 중, 실행 중, 완료, 실패), 디버깅을 위한 자세한 로그가 포함됩니다.

모든 것을 종합하기

설정 후 완전한 자동화된 번역 워크플로는 다음과 같습니다:

  1. 개발자가 사용자 대면 문자열을 포함한 새 기능을 추가합니다
  2. 컴포넌트에서 useTranslations('namespace') 또는 getTranslations('namespace')를 사용합니다
  3. pre-commit 훅이 scan --staged를 실행하고 번역되지 않은 문자열에 대해 경고합니다
  4. 개발자가 GitHub에 푸시합니다
  5. 푸시 Webhook이 인바운드 동기화를 트리거합니다 — 새 키가 better-i18n 대시보드에 나타납니다
  6. Doctor CI가 PR에서 실행되고 번역 커버리지를 보고합니다
  7. 번역자 (또는 MCP를 통한 AI 에이전트)가 새 키를 번역합니다
  8. 번역이 게시됩니다 — 업데이트된 JSON 파일과 함께 리포지토리에 PR이 생성됩니다
  9. 팀이 번역 PR을 검토하고 머지합니다
  10. CDN Deploy가 런타임에 번역을 즉시 사용 가능하게 만듭니다

전체 사이클이 수동 파일 복사, 내보내기/가져오기 단계, 또는 머지 충돌 해결 없이 실행됩니다. 번역은 코드베이스와 클라우드 사이를 지속적으로 흐릅니다.

일반적인 설정

여러 앱이 있는 Monorepo

your-monorepo/
├── apps/
│   ├── web/
│   │   └── locales/{locale}/{namespace}.json
│   └── admin/
│       └── locales/{locale}/{namespace}.json

두 앱을 모두 커버하는 파일 패턴으로 리포지토리를 한 번 연결합니다: apps/*/locales/{locale}/{namespace}.json.

플랫폼별 별도 리포지토리

각 리포지토리를 자체 파일 패턴으로 독립적으로 연결합니다. 번역은 중앙 클라우드 프로젝트를 통해 공유됩니다 — 한 번 업데이트하고 모든 리포지토리에 게시합니다.

피처 브랜치 워크플로

기본적으로, 동기화는 설정된 베이스 브랜치 (보통 main)에 대한 푸시에서만 트리거됩니다. 피처 브랜치에서 번역 동기화를 원하는 팀의 경우, 리포지토리 설정에서 여러 브랜치를 설정합니다.

결론

GitHub와의 번역 동기화를 자동화하면 국제 제품 개발을 늦추는 수동 병목 현상이 해소됩니다. Webhook 기반의 인바운드 동기화는 번역자가 새로운 문자열로 작업을 계속할 수 있게 합니다. PR 기반의 아웃바운드 배달은 번역 변경 사항이 코드와 동일한 리뷰 프로세스를 거치도록 보장합니다. Doctor CI는 프로덕션에 도달하기 전에 커버리지 격차를 잡아냅니다.

설정에는 약 15분이 걸립니다: GitHub App을 설치하고, 리포지토리를 추가하고, 파일 패턴을 설정하고, Doctor 워크플로를 활성화합니다. 그 시점부터 번역은 코드베이스와 클라우드 사이를 자동으로 흐릅니다.

여러 리포지토리에 걸쳐 번역을 관리하거나 몇 가지 이상의 언어로 사용자에게 배포하고 있다면, 이 자동화는 첫 번째 스프린트에서 비용을 상쇄합니다.

Comments

Loading comments...