튜토리얼//9 최소 읽기 시간

GitHub Translation Sync: 로컬라이제이션 워크플로 자동화하기

Eray Gündoğmuş
공유

브랜치마다 흩어진 번역 파일, JSON 키의 수동 복사-붙여넣기, "프랑스어 번역 누가 푸시했어요?"라는 Slack 메시지——이런 상황이 낯설지 않다면, 로컬라이제이션 워크플로에 자동화가 필요합니다.

GitHub Translation Sync는 번역 파일을 코드베이스와 동기화하는 수작업을 없애줍니다. 리포지토리에서 새로운 번역 키를 감지하고, 번역을 위해 전송하며(AI 또는 사람), 완성된 번역을 Pull Request로 반환합니다——모두 Git 워크플로를 벗어나지 않고 처리됩니다.

이 가이드에서는 GitHub Translation Sync를 처음부터 설정하고, PR 기반 워크플로를 구성하고, CI/CD와 통합하며, 팀 협업에 최적화하는 방법을 단계별로 안내합니다.


GitHub Translation Sync란?

GitHub Translation Sync는 Git 리포지토리와 번역 관리 플랫폼 사이의 양방향 동기화 메커니즘입니다. 번역 파일을 수동으로 푸시하고 풀하는 대신, Sync가 다음을 수행합니다:

  1. 새롭거나 변경된 키를 감지합니다 (코드를 푸시할 때)
  2. 번역 플랫폼으로 전송합니다 (번역을 위해)
  3. 완성된 번역을 Pull Request로 반환합니다
  4. 브랜치 간에 번역 파일을 동기화합니다

왜 PR 기반 번역 전달인가?

기존 로컬라이제이션 워크플로는 CLI 푸시/풀 명령을 사용합니다. PR 기반 방식이 더 나은 이유:

CLI Push/PullPR 기반 Sync
수동 프로세스로 잊어버리기 쉬움자동으로 항상 동기화
번역 검토 단계 없음PR 리뷰로 문제 발견
메인 브랜치에 직접 커밋브랜치 보호 정책 준수
감사 추적 없음번역의 완전한 Git 이력
한 사람의 책임PR 알림으로 팀 전체에 공개
머지 충돌이 빈번Sync가 Rebase를 자동 처리

사전 조건

GitHub Translation Sync를 설정하기 전에 필요한 것들:

  • 번역 파일이 포함된 GitHub 리포지토리 (JSON, YAML, PO 또는 ARB 형식)
  • Better i18n 프로젝트 (better-i18n.com에서 가입)
  • 리포지토리 관리자 액세스 (GitHub App 설치용)
  • 일관된 디렉터리 구조의 번역 파일

지원되는 디렉터리 구조

# 패턴 1: 로케일 디렉터리
locales/
  en/
    common.json
    dashboard.json
  es/
    common.json
    dashboard.json

# 패턴 2: 로케일 파일 접미사
locales/
  common.en.json
  common.es.json
  dashboard.en.json
  dashboard.es.json

# 패턴 3: 로케일 접두사가 있는 단일 디렉터리
i18n/
  en.json
  es.json
  fr.json

# 패턴 4: 프레임워크 특화 (예: Flutter ARB)
lib/l10n/
  app_en.arb
  app_es.arb

1단계: GitHub App 설치하기

GitHub Sync는 리포지토리의 번역 파일에 대한 읽기/쓰기 액세스 권한을 가진 GitHub App을 통해 작동합니다.

  1. Better i18n 프로젝트 대시보드로 이동합니다
  2. SettingsIntegrations로 이동합니다
  3. Connect GitHub를 클릭합니다
  4. 동기화할 리포지토리를 선택합니다
  5. 요청된 권한을 승인합니다

권한 설명

권한필요한 이유
Read: Code새롭거나 변경된 번역 키 감지
Read/Write: Pull Requests완성된 번역으로 PR 생성
Read: Metadata리포지토리 정보 액세스
WebhooksSync 트리거를 위한 푸시 이벤트 수신

GitHub App은 번역 관련 파일에만 액세스합니다. 소스 코드, 환경 변수, 시크릿은 읽지 않습니다.


2단계: Sync 설정하기

리포지토리 루트에 설정 파일을 생성합니다:

# better-i18n.yml
sync:
  # 소스 언어 — 이 로케일의 파일에서 키가 추출됩니다
  sourceLanguage: en

  # 대상 언어 — 이 언어들에 대한 번역이 생성됩니다
  targetLanguages:
    - es
    - fr
    - de
    - ja
    - ko
    - "zh-CN"

  # 번역 파일의 경로 패턴
  # {locale}과 {namespace} 플레이스홀더를 사용한 Glob 패턴 지원
  paths:
    - "locales/{locale}/{namespace}.json"

  # 대안: 로케일당 단일 파일
  # paths:
  #   - "locales/{locale}.json"

  # 변경 사항을 감시할 브랜치 (기본값: main)
  baseBranch: main

  # 번역 전달 방법
  delivery:
    # "pr"은 Pull Request 생성, "commit"은 직접 푸시 (비권장)
    method: pr

    # PR 브랜치 명명 패턴
    branchPattern: "i18n/sync-{timestamp}"

    # 모든 검사 통과 시 PR 자동 머지 (브랜치 보호 필요)
    autoMerge: false

    # 번역 PR에 할당할 리뷰어
    reviewers:
      - "@i18n-team"

    # 번역 PR에 추가할 레이블
    labels:
      - "i18n"
      - "automated"

  # AI 번역 설정
  ai:
    # 새 키를 AI로 자동 번역
    autoTranslate: true

    # PR 생성 전 사람의 승인 요구
    requireApproval: false

    # AI 번역가를 위한 커스텀 지시사항
    instructions: |
      - Use formal register for German (Sie, not du)
      - Keep brand name "Better i18n" untranslated in all languages
      - Use Oxford comma in English translations

설정 옵션 참조

옵션타입기본값설명
sourceLanguagestring"en"소스 로케일 코드
targetLanguagesstring[][]대상 로케일 코드
pathsstring[]자동 감지번역 파일의 Glob 패턴
baseBranchstring"main"감시할 브랜치
delivery.methodstring"pr"전달 방법: pr 또는 commit
delivery.autoMergebooleanfalse검사 통과 시 자동 머지
delivery.reviewersstring[][]PR 리뷰어 (사용자 또는 팀)
delivery.labelsstring[]["i18n"]PR 레이블
ai.autoTranslatebooleantrue새 키를 AI로 번역
ai.requireApprovalbooleanfalsePR 전 승인 요구
ai.instructionsstring""커스텀 AI 지시사항

3단계: Sync 워크플로 이해하기

설정 완료 후, 전형적인 개발 사이클에서 일어나는 일:

새 키 추가하기

개발자                   GitHub                Better i18n              GitHub
    |                        |                       |                      |
    |-- 커밋 푸시 ----------->|                       |                      |
    |   (en/common.json에    |-- Webhook ----------->|                      |
    |    새 키 추가)          |                       |                      |
    |                        |                       |-- AI가 번역 -------->|
    |                        |                       |   모든 대상 언어에   |
    |                        |                       |   대해 새 키 번역    |
    |                        |                       |                      |
    |                        |<---------- 번역이 담긴 PR 생성 -------------|
    |                        |                       |                      |
    |<-- PR 알림 ------------|                       |                      |
    |                        |                       |                      |
    |-- 검토 및 머지 -------->|                       |                      |

기존 키 업데이트하기

기존 키의 소스 텍스트를 수정할 때:

  1. Sync가 Webhook을 통해 변경 사항을 감지합니다
  2. 기존 번역이 "검토 필요"로 표시됩니다
  3. AI가 업데이트된 번역을 생성합니다
  4. 업데이트된 번역으로 PR이 생성됩니다
  5. 리뷰어 비교를 위해 이전 번역이 PR 설명에 보존됩니다

키 삭제하기

소스 로케일에서 키를 제거할 때:

  1. Sync가 삭제를 감지합니다
  2. 해당 키가 모든 대상 로케일 파일에서 제거됩니다
  3. 삭제 내용이 담긴 PR이 생성됩니다
  4. 감사 목적으로 제거된 모든 키가 PR 설명에 나열됩니다

4단계: CI/CD 통합

번역이 머지되기 전에 문제를 발견하기 위해 CI/CD 파이프라인에 번역 검증을 추가합니다.

GitHub Actions 워크플로

# .github/workflows/i18n-validate.yml
name: Validate Translations

on:
  pull_request:
    paths:
      - "locales/**"
    types: [opened, synchronize]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v2

      - name: Install dependencies
        run: bun install

      - name: Validate translation coverage
        run: |
          bunx @better-i18n/cli coverage \
            --min 95 \
            --format github-actions

      - name: Validate ICU syntax
        run: |
          bunx @better-i18n/cli validate \
            --format github-actions

      - name: Check for unused keys
        run: |
          bunx @better-i18n/cli lint \
            --unused \
            --format github-actions

      - name: Comment coverage report on PR
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const { execSync } = require('child_process');
            const report = execSync('bunx @better-i18n/cli coverage --format markdown').toString();
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Translation Coverage Report\n\n${report}`
            });

필수 상태 검사

번역 검증을 필수화하기 위해 브랜치 보호를 설정합니다:

  1. SettingsBranchesBranch protection rules로 이동합니다
  2. Require status checks to pass before merging을 활성화합니다
  3. 다음 검사를 추가합니다:
    • Validate translation coverage
    • Validate ICU syntax

이를 통해 번역이 깨지거나 커버리지가 부족한 PR은 머지할 수 없게 됩니다.


5단계: 번역을 위한 브랜치 전략

기본값: 단일 번역 브랜치

기본적으로 각 Sync는 완성된 번역으로 새 브랜치를 생성합니다:

main ------------------------------------------------
  \                                 |
   \-- i18n/sync-20260315 ---------/
       (새 키에 대한 번역)

대부분의 팀에 잘 맞습니다. 각 Sync가 독립적이며 머지 전에 번역을 검토할 수 있습니다.

고급: 피처 브랜치 Sync

피처 브랜치를 사용하는 팀의 경우, 피처 브랜치를 대상으로 하도록 Sync를 설정합니다:

# better-i18n.yml
sync:
  baseBranch: main
  featureBranchSync:
    enabled: true
    # 이 브랜치를 대상으로 하는 PR의 번역 동기화
    targetBranches:
      - "main"
      - "develop"
    # 피처 PR과 동일한 브랜치를 대상으로 하는 번역 PR 생성
    followBranch: true

피처 브랜치 Sync를 사용하면:

main ------------------------------------------------
  \
   \-- feature/new-checkout -------------------------
        \                           |
         \-- i18n/new-checkout -----/
             (이 피처에 대한 번역)

머지 충돌 처리

Sync는 대부분의 머지 충돌을 자동으로 처리합니다:

  1. PR 생성 전 번역 브랜치 Rebase
  2. 번역 파일에 JSON 인식 머지 사용 (줄 기반이 아님)
  3. 충돌이 감지되면 Sync 재실행

자동 해결이 불가능한 경우, Sync가 PR에 충돌을 설명하는 댓글을 추가하고 수동 해결 단계를 제안합니다.


6단계: 모니터링 및 문제 해결

Sync 상태 대시보드

Better i18n 대시보드에서 Sync 상태를 모니터링합니다:

  • Sync 이력: 상태별 각 Sync 이벤트 (성공, 부분적, 실패)
  • 보류 중인 번역: 번역 대기 중인 키
  • 커버리지 트렌드: 언어별 시간에 따른 번역 커버리지
  • PR 활동: 열린, 머지된, 닫힌 번역 PR

일반적인 문제와 해결책

문제: 푸시 시 Sync가 트리거되지 않음

  • GitHub App이 리포지토리에 설치되어 있는지 확인합니다
  • SettingsWebhooks에서 Webhook이 활성화되어 있는지 확인합니다
  • 푸시가 설정된 baseBranch로 이루어지고 있는지 확인합니다
  • 변경된 파일이 설정된 paths 패턴과 일치하는지 확인합니다

문제: PR에 머지 충돌이 있음

  • Sync는 자동으로 Rebase하지만, 브랜치가 크게 분기된 경우 수동 해결이 필요할 수 있습니다
  • 번역 PR의 충돌을 해결한 후 푸시하세요——Sync는 해결 내용을 덮어쓰지 않습니다

문제: AI 번역 품질이 낮음

  • 설정에 상세한 ai.instructions를 추가합니다
  • 용어 목록과 브랜드 가이드라인을 포함합니다
  • 중요한 언어에 대해 requireApproval 활성화를 고려합니다

문제: 작은 PR이 너무 많음

  • 변경 사항을 묶기 위해 delivery.batchWindow를 설정합니다:
sync:
  delivery:
    # 여러 푸시를 하나의 PR로 묶기 위해 30분 대기
    batchWindow: 30

고급 설정

네임스페이스 필터링

특정 네임스페이스만 동기화합니다:

sync:
  paths:
    - "locales/{locale}/common.json"
    - "locales/{locale}/marketing.json"
  # 내부 네임스페이스를 명시적으로 제외
  exclude:
    - "locales/{locale}/internal.json"
    - "locales/{locale}/test-fixtures.json"

커스텀 커밋 메시지

sync:
  delivery:
    commitMessage: "chore(i18n): sync translations for {languages}"
    prTitle: "chore(i18n): translations update ({date})"
    prBody: |
      ## Automated Translation Update

      This PR contains translations synced from Better i18n.

      **Languages updated:** {languages}
      **Keys added:** {keysAdded}
      **Keys updated:** {keysUpdated}
      **Keys removed:** {keysRemoved}

      ---
      _Generated by Better i18n GitHub Sync_

Webhook 이벤트

커스텀 통합을 위해 Webhook으로 Sync 이벤트를 구독할 수 있습니다:

sync:
  webhooks:
    - url: "https://your-server.com/api/i18n-webhook"
      events:
        - sync.completed
        - sync.failed
        - pr.created
        - pr.merged
      secret: "${WEBHOOK_SECRET}"  # Better i18n 대시보드에서 설정

실제 예시: 이커머스 앱 설정

Next.js를 사용한 전형적인 이커머스 애플리케이션의 완전한 설정 예시입니다:

# better-i18n.yml
sync:
  sourceLanguage: en
  targetLanguages:
    - es
    - fr
    - de
    - ja
    - "pt-BR"
    - "zh-CN"

  paths:
    - "messages/{locale}/{namespace}.json"

  baseBranch: main

  delivery:
    method: pr
    branchPattern: "i18n/translations-{date}"
    autoMerge: true  # CI 통과 시 자동 머지
    reviewers:
      - "@frontend-team"
    labels:
      - "i18n"
      - "auto-merge"
    batchWindow: 15  # 15분 이내 변경 사항 묶기

  ai:
    autoTranslate: true
    requireApproval: false
    instructions: |
      E-commerce context:
      - "Cart" should use shopping cart terminology (not vehicle)
      - Currency amounts should not be translated (they are formatted by code)
      - Product names in {curly braces} are variables — do not translate
      - Use formal register for German and Japanese
      - Use informal register for Spanish and Portuguese
      - Keep "Better i18n" as-is in all languages

  featureBranchSync:
    enabled: true
    targetBranches: ["main", "staging"]
    followBranch: true

이 설정으로:

  1. main 또는 staging으로의 모든 푸시가 Sync를 트리거합니다
  2. 새 키가 몇 분 내에 AI로 번역됩니다
  3. 번역이 자동 머지 가능한 PR로 도착합니다
  4. 피처 브랜치가 자체 번역 PR을 받습니다
  5. CI/CD가 머지 전 커버리지와 구문을 검증합니다

결론

GitHub Translation Sync는 로컬라이제이션을 수동적이고 오류가 발생하기 쉬운 프로세스에서 기존 개발 워크플로에 자연스럽게 통합되는 자동화 파이프라인으로 변환합니다. 핵심 원칙:

  1. 번역은 Git에 존재합니다 — 완전한 이력, 브랜치 보호, 코드 리뷰
  2. Sync는 자동입니다 — 잊어버리기 쉬운 수동 푸시/풀 명령 불필요
  3. PR이 검토를 가능하게 합니다 — 번역이 다른 코드 변경과 마찬가지로 검토됩니다
  4. CI/CD가 품질을 강제합니다 — 커버리지 임계값과 구문 검증이 잘못된 번역을 차단합니다
  5. AI가 가속화합니다 — 새 키가 며칠이 아닌 몇 분 내에 번역됩니다

설정에는 약 30분이 소요됩니다. 첫 번째 주에 절약되는 시간은 일반적으로 설정 투자를 초과합니다.


번역 워크플로를 자동화할 준비가 되셨나요? Better i18n을 시작하고 몇 분 내에 GitHub 리포지토리를 연결하세요.

Comments

Loading comments...