목차
브랜치마다 흩어진 번역 파일, JSON 키의 수동 복사-붙여넣기, "프랑스어 번역 누가 푸시했어요?"라는 Slack 메시지——이런 상황이 낯설지 않다면, 로컬라이제이션 워크플로에 자동화가 필요합니다.
GitHub Translation Sync는 번역 파일을 코드베이스와 동기화하는 수작업을 없애줍니다. 리포지토리에서 새로운 번역 키를 감지하고, 번역을 위해 전송하며(AI 또는 사람), 완성된 번역을 Pull Request로 반환합니다——모두 Git 워크플로를 벗어나지 않고 처리됩니다.
이 가이드에서는 GitHub Translation Sync를 처음부터 설정하고, PR 기반 워크플로를 구성하고, CI/CD와 통합하며, 팀 협업에 최적화하는 방법을 단계별로 안내합니다.
GitHub Translation Sync란?
GitHub Translation Sync는 Git 리포지토리와 번역 관리 플랫폼 사이의 양방향 동기화 메커니즘입니다. 번역 파일을 수동으로 푸시하고 풀하는 대신, Sync가 다음을 수행합니다:
- 새롭거나 변경된 키를 감지합니다 (코드를 푸시할 때)
- 번역 플랫폼으로 전송합니다 (번역을 위해)
- 완성된 번역을 Pull Request로 반환합니다
- 브랜치 간에 번역 파일을 동기화합니다
왜 PR 기반 번역 전달인가?
기존 로컬라이제이션 워크플로는 CLI 푸시/풀 명령을 사용합니다. PR 기반 방식이 더 나은 이유:
| CLI Push/Pull | PR 기반 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을 통해 작동합니다.
- Better i18n 프로젝트 대시보드로 이동합니다
- Settings → Integrations로 이동합니다
- Connect GitHub를 클릭합니다
- 동기화할 리포지토리를 선택합니다
- 요청된 권한을 승인합니다
권한 설명
| 권한 | 필요한 이유 |
|---|---|
| Read: Code | 새롭거나 변경된 번역 키 감지 |
| Read/Write: Pull Requests | 완성된 번역으로 PR 생성 |
| Read: Metadata | 리포지토리 정보 액세스 |
| Webhooks | Sync 트리거를 위한 푸시 이벤트 수신 |
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
설정 옵션 참조
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
sourceLanguage | string | "en" | 소스 로케일 코드 |
targetLanguages | string[] | [] | 대상 로케일 코드 |
paths | string[] | 자동 감지 | 번역 파일의 Glob 패턴 |
baseBranch | string | "main" | 감시할 브랜치 |
delivery.method | string | "pr" | 전달 방법: pr 또는 commit |
delivery.autoMerge | boolean | false | 검사 통과 시 자동 머지 |
delivery.reviewers | string[] | [] | PR 리뷰어 (사용자 또는 팀) |
delivery.labels | string[] | ["i18n"] | PR 레이블 |
ai.autoTranslate | boolean | true | 새 키를 AI로 번역 |
ai.requireApproval | boolean | false | PR 전 승인 요구 |
ai.instructions | string | "" | 커스텀 AI 지시사항 |
3단계: Sync 워크플로 이해하기
설정 완료 후, 전형적인 개발 사이클에서 일어나는 일:
새 키 추가하기
개발자 GitHub Better i18n GitHub
| | | |
|-- 커밋 푸시 ----------->| | |
| (en/common.json에 |-- Webhook ----------->| |
| 새 키 추가) | | |
| | |-- AI가 번역 -------->|
| | | 모든 대상 언어에 |
| | | 대해 새 키 번역 |
| | | |
| |<---------- 번역이 담긴 PR 생성 -------------|
| | | |
|<-- PR 알림 ------------| | |
| | | |
|-- 검토 및 머지 -------->| | |
기존 키 업데이트하기
기존 키의 소스 텍스트를 수정할 때:
- Sync가 Webhook을 통해 변경 사항을 감지합니다
- 기존 번역이 "검토 필요"로 표시됩니다
- AI가 업데이트된 번역을 생성합니다
- 업데이트된 번역으로 PR이 생성됩니다
- 리뷰어 비교를 위해 이전 번역이 PR 설명에 보존됩니다
키 삭제하기
소스 로케일에서 키를 제거할 때:
- Sync가 삭제를 감지합니다
- 해당 키가 모든 대상 로케일 파일에서 제거됩니다
- 삭제 내용이 담긴 PR이 생성됩니다
- 감사 목적으로 제거된 모든 키가 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}`
});
필수 상태 검사
번역 검증을 필수화하기 위해 브랜치 보호를 설정합니다:
- Settings → Branches → Branch protection rules로 이동합니다
- Require status checks to pass before merging을 활성화합니다
- 다음 검사를 추가합니다:
Validate translation coverageValidate 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는 대부분의 머지 충돌을 자동으로 처리합니다:
- PR 생성 전 번역 브랜치 Rebase
- 번역 파일에 JSON 인식 머지 사용 (줄 기반이 아님)
- 충돌이 감지되면 Sync 재실행
자동 해결이 불가능한 경우, Sync가 PR에 충돌을 설명하는 댓글을 추가하고 수동 해결 단계를 제안합니다.
6단계: 모니터링 및 문제 해결
Sync 상태 대시보드
Better i18n 대시보드에서 Sync 상태를 모니터링합니다:
- Sync 이력: 상태별 각 Sync 이벤트 (성공, 부분적, 실패)
- 보류 중인 번역: 번역 대기 중인 키
- 커버리지 트렌드: 언어별 시간에 따른 번역 커버리지
- PR 활동: 열린, 머지된, 닫힌 번역 PR
일반적인 문제와 해결책
문제: 푸시 시 Sync가 트리거되지 않음
- GitHub App이 리포지토리에 설치되어 있는지 확인합니다
- Settings → Webhooks에서 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
이 설정으로:
main또는staging으로의 모든 푸시가 Sync를 트리거합니다- 새 키가 몇 분 내에 AI로 번역됩니다
- 번역이 자동 머지 가능한 PR로 도착합니다
- 피처 브랜치가 자체 번역 PR을 받습니다
- CI/CD가 머지 전 커버리지와 구문을 검증합니다
결론
GitHub Translation Sync는 로컬라이제이션을 수동적이고 오류가 발생하기 쉬운 프로세스에서 기존 개발 워크플로에 자연스럽게 통합되는 자동화 파이프라인으로 변환합니다. 핵심 원칙:
- 번역은 Git에 존재합니다 — 완전한 이력, 브랜치 보호, 코드 리뷰
- Sync는 자동입니다 — 잊어버리기 쉬운 수동 푸시/풀 명령 불필요
- PR이 검토를 가능하게 합니다 — 번역이 다른 코드 변경과 마찬가지로 검토됩니다
- CI/CD가 품질을 강제합니다 — 커버리지 임계값과 구문 검증이 잘못된 번역을 차단합니다
- AI가 가속화합니다 — 새 키가 며칠이 아닌 몇 분 내에 번역됩니다
설정에는 약 30분이 소요됩니다. 첫 번째 주에 절약되는 시간은 일반적으로 설정 투자를 초과합니다.
번역 워크플로를 자동화할 준비가 되셨나요? Better i18n을 시작하고 몇 분 내에 GitHub 리포지토리를 연결하세요.