Skip to content
· 7 min read · langctl Team

How to Manage i18n in Git Workflows

Best practices for versioning translation files with Git. Covers branching strategies, conflict resolution, and maintaining clean translation history.

#i18n#git#workflow#version-control

Translation files are code. They should be versioned, reviewed, and maintained with the same care as any other source file.

But translation files have unique characteristics that can make Git workflows tricky. They’re often edited outside of IDEs, may be modified by non-developers, and changes can span many files simultaneously.

This guide covers practical strategies for managing i18n files in Git-based workflows.

File Organization

How you organize translation files affects how well they work with Git.

Flat structure

/locales
  en.json
  es.json
  fr.json
  de.json

Simple and works for smaller projects. Merges involve entire files, which can be problematic if they’re large.

Nested by feature/module

/locales
  /common
    en.json
    es.json
  /auth
    en.json
    es.json
  /dashboard
    en.json
    es.json

Smaller files mean smaller conflicts. Features can be developed and merged independently.

Language-first vs feature-first

Language-first (shown above): Each language is one file or one directory. Good for translators working on a single language.

Feature-first:

/features
  /auth
    /i18n
      en.json
      es.json
  /dashboard
    /i18n
      en.json
      es.json

Keeps translations close to the code they support. Good for developer-led workflows.

The right choice depends on who edits translations and how your codebase is organized.

Branching Strategies

Feature branches include translations

The simplest approach: feature branches include translation changes alongside code.

# Working on a feature branch
git checkout -b feature/user-profile

# Add code with new translation keys
# Add the translations themselves
# Commit together

git add src/components/Profile.tsx locales/en.json locales/es.json
git commit -m "Add user profile page with translations"

Pros: Changes are atomic; translations ship with features Cons: Translations may conflict if multiple features touch the same files

Separate translation branches

For larger projects, separate branches for translation work:

# Feature branch has placeholder keys
git checkout -b feature/user-profile
# Add code with keys
git commit -m "Add user profile page"

# Translation branch adds the actual strings
git checkout -b translations/user-profile
# Add translations
git commit -m "Add translations for user profile"

# Merge both
git checkout main
git merge feature/user-profile
git merge translations/user-profile

Pros: Translators can work independently; clear separation of concerns Cons: More complex workflow; potential for orphaned keys

Release-based translation updates

Translations updated at release boundaries:

# During sprint, features add placeholder keys
# Before release, translation branch catches up
git checkout -b release/2.1-translations
langctl pull  # or manually update files
git commit -m "Update translations for release 2.1"
git checkout main
git merge release/2.1-translations

Pros: Clean release process; batch translation work Cons: Translations lag behind code; harder to test during development

Handling Merge Conflicts

Translation files in JSON or YAML format are prone to merge conflicts, especially in the common pattern where keys are alphabetized.

Prevention

  • Smaller files: Split translations by feature or module
  • Consistent formatting: Use tools to maintain consistent key order
  • Frequent merges: Don’t let branches diverge too far

Resolution

When conflicts occur:

<<<<<<< HEAD
  "welcome.title": "Welcome back",
  "welcome.subtitle": "We missed you"
=======
  "welcome.title": "Hello again",
  "welcome.new_feature": "Try our new feature"
>>>>>>> feature/new-feature

Usually the resolution is to keep both changes:

  "welcome.title": "Welcome back",
  "welcome.subtitle": "We missed you",
  "welcome.new_feature": "Try our new feature"

For conflicting edits to the same key, you’ll need to decide which version is correct.

Tooling help

If the translated files are generated by a CLI tool, you don’t have to hand-merge them at all. With langctl, the non-source language files come from langctl pull, whose output is sorted and has no timestamps:

# Resolve your source-language file (e.g. locales/en.json) by hand, then:
langctl push --dry-run   # Preview which keys would be uploaded
langctl push             # Upload the new keys

# Regenerate the other languages instead of merging them
langctl pull
langctl pull --check     # Exits 7 if any file is still out of date

langctl doesn’t auto-merge conflicts for you; it just makes the generated files reproducible so you can regenerate them.

Code Review for Translations

Translation changes should go through code review like any other change.

What to review

  • Key naming: Are keys consistent with existing conventions?
  • Missing languages: Do all supported languages have the new keys?
  • Placeholders: Are variables and interpolations correct?
  • Formatting: Does the file maintain consistent structure?

Review checklist

## Translation Review
- [ ] Key names follow project conventions
- [ ] All supported languages updated
- [ ] No hardcoded text in source files
- [ ] Variable placeholders match between languages
- [ ] File formatting is consistent

Automated checks

In CI/CD, validate translation files:

- name: Check translations
  env:
    LANGCTL_API_KEY: ${{ secrets.LANGCTL_API_KEY }}
  run: |
    npx langctl@0 pull --check
    npx langctl@0 pull --require-complete

This catches out-of-date translation files (exit code 7) and missing translations (exit code 1) before merge.

Maintaining Translation History

Git history for translations serves several purposes:

  • Audit trail: Who changed what, when
  • Rollback capability: Revert problematic changes
  • Understanding context: Why was this text changed?

Commit messages

Good commit messages for translations:

Add translations for checkout flow

- New keys: checkout.title, checkout.submit, checkout.cancel
- Updated: cart.empty_message (improved clarity)
- Languages: en, es, fr

Bad commit messages:

Update translations

Keeping history clean

Avoid commits that only fix formatting or key ordering. If tools reorganize your translation files, commit those changes separately:

git commit -m "Reformat translation files (no content changes)"

This keeps content changes separate from formatting noise.

Working with External Translators

When non-developers contribute translations, Git workflows need adaptation.

Option 1: Managed access

Give translators Git access (or access through a platform like GitHub):

  • Create branches for translation work
  • Submit pull requests for review
  • Use review process for quality control

Works if translators are comfortable with Git basics.

Option 2: Sync through tooling

Use a translation platform that syncs with Git:

# Developer workflow
langctl push  # Send new keys from the source-language file

# Translator works in web interface; keys are published when reviewed

# Developer pulls back (only published keys are included)
langctl pull
git add locales/
git commit -m "Update translations from platform"

The translation platform handles the translator interface; Git handles versioning.

Option 3: Export/import cycle

For occasional translator involvement:

# Export one language for translators
langctl export -l es -o translations-es.json

# Translators edit the JSON file
# Preview, then import when done
langctl import translations-es.json -l es --overwrite --dry-run
langctl import translations-es.json -l es --overwrite
langctl pull
git add locales/
git commit -m "Import reviewed translations"

Less elegant but works with any translator who can edit a JSON file (Android XML, iOS .strings and ARB work too).

Summary: Best Practices

  1. Organize files to minimize merge conflicts (smaller files, logical grouping)

  2. Include translations in feature branches for atomic changes

  3. Review translation changes like code changes

  4. Automate validation in CI/CD to catch errors early

  5. Write meaningful commit messages that explain what changed

  6. Use tooling to help with sync, validation, and conflict resolution

  7. Plan for non-developer contributors if translators will be involved

Good Git hygiene for translations makes localization sustainable as projects grow.


Next steps: Automating i18n in CI/CD Pipelines covers how to integrate translation validation into your build process.

All posts