How to translate YAML files for localization (2026 guide)

Kinga Pomykała
Kinga Pomykała
Last updated: March 23, 202617 min read
How to translate YAML files for localization (2026 guide)

YAML is the translation file format of choice for Ruby on Rails, Symfony, Laravel, and a growing number of JAMstack and backend setups. Its readable syntax and support for comments make it genuinely pleasant for localization work. Until it isn't. Indentation errors break parsers silently, keys drift out of sync between locales, and placeholders get mangled by translation engines that don't know what they are.

This guide covers how to translate YAML localization files correctly: the format itself, how it's used across popular frameworks, the translation approaches that work at different scales, and the mistakes that cause YAML localization to fail in production.

For a broader picture of how YAML fits into a full i18n architecture, alongside key naming conventions, locale detection, CI/CD pipelines, and translation management, see our complete technical guide to internationalization and software localization.

If you already know your YAML structure and want to go straight to automation, see our guide on how to auto-translate YAML files.

YAML for localization: The format

YAML (YAML Ain't Markup Language) is an indentation-based data format designed for human readability. In localization, it serves as the container for your translation strings, the same role JSON plays in JavaScript-heavy stacks.

A typical YAML localization file looks like this:

en:
  site:
    name: "Pillow Hotel"
    slogan: "Your comfort, our priority"
  navigation:
    home: "Home"
    rooms: "Rooms"
    dining: "Dining"
    spa: "Spa & Wellness"
    booking: "Book Now"
    contact: "Contact Us"
  homepage:
    welcome_title: "Welcome to Pillow Hotel"
    welcome_message: "Welcome back, %{name}! Your reservation is confirmed."
    rooms_count:
      one: "You have 1 room booked"
      other: "You have %{count} rooms booked"
  errors:
    not_found: "Page not found"
    unauthorized: "You don't have access to this page"

Key characteristics to understand before you start translating:

  • Indentation is structure.

    YAML uses spaces (never tabs) to define nesting. One extra or missing space breaks the file. Unlike JSON, a syntax error in YAML often produces no immediate parse error, it just silently misreads your structure.

  • The root key is often the locale code.

    In Rails and Symfony conventions, the top-level key is the language code (en:, de:, fr:). This is part of the format, not the content — don't translate it.

  • Scalar styles matter.

    Plain (Home), single-quoted ('Home'), and double-quoted ("Home") values are all valid YAML but behave differently with special characters and escape sequences. Translators editing raw files can introduce inconsistencies here.

  • Pluralization uses nested keys.

    Rails-style YAML handles plurals with one: / other: / few: / many: sibling keys under a parent. These must be preserved correctly in every target language; and different languagesrequire different plural keys.

For a deeper comparison of YAML versus JSON as a translation file format, including when to choose each, see YAML vs JSON for translation files.

YAML localization across frameworks

Different frameworks have different conventions for YAML structure, file naming, and plural handling. Knowing which convention your stack uses saves a lot of debugging time.

Ruby on Rails

Rails is the framework most associated with YAML localization. Translation files live in config/locales/ and are loaded automatically by the I18n module.

# config/locales/en.yml
en:
  site:
    name: "Pillow Hotel"
    slogan: "Your comfort, our priority"
  mailer:
    booking_confirmation:
      subject: "Your booking at %{hotel_name} is confirmed"
      greeting: "Dear %{name},"
      message: "Thank you for choosing Pillow Hotel."

Rails pluralization uses one and other for English, but other languages require additional forms. Polish, for example, needs one, few, many, and other:

# config/locales/pl.yml
pl:
  homepage:
    rooms_count:
      one: "Zarezerwowano 1 pokój"
      few: "Zarezerwowano %{count} pokoje"
      many: "Zarezerwowano %{count} pokoi"
      other: "Zarezerwowano %{count} pokoju"

Rails reads plural keys based on the i18n-js pluralization rules for each locale. If your target language needs plural keys that don't exist in the translation file, Rails falls back silently, meaning you can ship broken pluralization without any error.

Symfony (PHP)

Symfony supports YAML translation files in translations/ alongside XLIFF and PHP arrays. The format omits the root locale key that Rails uses:

# translations/messages.en.yaml
navigation.home: Home
navigation.rooms: Rooms
navigation.dining: Dining
navigation.spa: Spa & Wellness
navigation.booking: Book Now
errors.not_found: Page not found

Symfony also supports nested YAML:

# translations/messages.en.yaml
navigation:
  home: Home
  rooms: Rooms
  dining: Dining
site:
  name: Pillow Hotel

Pluralization in Symfony uses the pipe syntax within the value string rather than nested keys:

rooms_booked: "You have no rooms booked|You have one room booked|You have %count% rooms booked"

When translating Symfony YAML files, the plural separator (|) must be preserved exactly, translation engines that don't know about it will translate or remove it.

Laravel (with YAML packages)

Laravel's default format is PHP arrays, but many teams use YAML via packages like spatie/laravel-translation-loader or custom loaders. The structure typically mirrors the Symfony flat or nested style.

Next.js / React with i18next

Most React and Next.js setups use JSON rather than YAML for translation files, but some teams prefer YAML for its readability and use build-time conversion to JSON. If your project does this, the YAML files are the source of truth and the JSON files are generated, make sure your translation workflow targets the YAML source, not the generated JSON.

For React-specific localization, see our guides on best i18n libraries for React and how to localize a React app with i18next.

Common YAML translation errors (and how to avoid them)

These are the mistakes that cause YAML localization to break in production. Most are invisible until they cause a runtime error or a user files a support ticket.

Indentation errors

The most common YAML mistake. One misaligned space breaks the entire file or silently misreads your structure.

# Broken (spa is at the wrong indentation level)
en:
  navigation:
    home: "Home"
    rooms: "Rooms"
   spa: "Spa & Wellness"   # one space off (will parse incorrectly)

# Correct
en:
  navigation:
    home: "Home"
    rooms: "Rooms"
    spa: "Spa & Wellness"

Prevention: never have translators edit raw YAML files. Use a translation editor that stores strings in a database and exports valid YAML. If you must use raw files, add a YAML linter (like yamllint) to your CI pipeline.

Placeholder corruption

Translation engines that don't understand your placeholder format will translate or rearrange placeholder text, producing broken runtime strings.

# Source
en:
  homepage:
    welcome_message: "Welcome back, %{name}! Your reservation is confirmed."

# What a naive translation engine produces
de:
  homepage:
    welcome_message: "Willkommen zurück, %{Name}! Ihre Reservierung ist bestätigt."  # %{name} → %{Name}, now broken

# Correct
de:
  homepage:
    welcome_message: "Willkommen zurück, %{name}! Ihre Reservierung ist bestätigt."

Rails uses %{variable} syntax. Symfony uses %variable%. i18next uses {{variable}}. Make sure your translation tool knows which format you're using and protects those placeholders during translation.

Missing plural keys for target languages

Adding one and other works for English. For many other languages you need more forms, and missing forms cause silent fallback behavior.

# Incomplete Polish pluralization
pl:
  homepage:
    rooms_count:
      one: "Zarezerwowano 1 pokój"
      other: "Zarezerwowano %{count} pokoi"

# Complete
pl:
  homepage:
    rooms_count:
      one: "Zarezerwowano 1 pokój"
      few: "Zarezerwowano %{count} pokoje"
      many: "Zarezerwowano %{count} pokoi"
      other: "Zarezerwowano %{count} pokoi"

See our pluralization guide for a full breakdown of plural forms by language and how to implement them correctly.

Key drift between locales

A developer adds a new key to en.yml. The other locale files never get updated. The app falls back to English for those strings in all other languages, and nobody notices.

# en.yml: 4 navigation keys
en:
  navigation:
    home: "Home"
    rooms: "Rooms"
    dining: "Dining"
    spa: "Spa & Wellness"   # New key added

# de.yml: still 3 keys, spa key missing
de:
  navigation:
    home: "Startseite"
    rooms: "Zimmer"
    dining: "Restaurant"
    # spa: missing: silent fallback to English

Prevention: use a translation management platform that detects missing keys across locales and shows them in the UI. Add a YAML linter to CI that checks for missing keys between files. Consider a workflow where the source language is frozen and approved before translation, so translators aren't working on strings that might change.

Special characters breaking YAML parsing

Certain characters require quoting in YAML: colons followed by a space, hash signs, ampersands, and strings starting with certain characters.

# These will break YAML parsing
en:
  tagline: Your comfort, our priority: guaranteed    # colon + space in value
  hashtag: #PillowHotel                              # looks like a comment
  brand: Pillow & Comfort Hotels                     # ampersand

# Quote them
en:
  tagline: "Your comfort, our priority: guaranteed"
  hashtag: "#PillowHotel"
  brand: "Pillow & Comfort Hotels"

When translators edit YAML directly, they often introduce these issues. A translation management system that exports valid YAML eliminates this class of error entirely.

Translation approaches by team size

There are several approaches to translating YAML files.

Manual translation (solo / very small projects)

For a project with one developer and two or three languages, editing YAML files directly is viable. The workflow:

  1. Duplicate your source file (en.yml) for each new locale (es.yml, fr.yml).
  2. Keep key paths identical across all files; only translate the values.
  3. Run a YAML linter or validator after every edit.
Translating YAML files manually
Translating YAML files manually

For multi-language files (e.g., locales.yml):

  1. Add new language sections under the root key (e.g., es:).
  2. Translate values while preserving the structure.
  3. Validate the entire file to ensure no syntax issues.

Here is an example of a simple multi-language YAML file:

en:
  site:
    name: "Pillow Hotel"
    slogan: "Your comfort, our priority"
  navigation:
    home: "Home"
    rooms: "Rooms"
    dining: "Dining"

es:
  site:
    name: "Hotel Almohada"
    slogan: "Tu comodidad, nuestra prioridad"
  navigation:
    home: "Inicio"
    rooms: "Habitaciones"
    dining: "Restaurante"

This breaks down quickly as soon as you have more than three or four languages or more than one person touching the files.

Translation editor with a TMS (growing teams)

For teams with dedicated translators or multiple contributors, a translation management system like SimpleLocalize eliminates the raw-file editing problems entirely. Translators work in a purpose-built editor; developers work in code; the TMS keeps them in sync.

With SimpleLocalize, the workflow is:

  1. Import your source YAML files (via upload, CLI, or GitHub App).
  2. Add target languages in the Languages tab.
  3. Auto-translate to generate drafts for all missing strings.
  4. Review in the translation editor: add context, screenshots, comments and tasks for translators.
  5. Export clean, validated YAML back to your repo.
Translating YAML files with SimpleLocalize

Automated pipeline (CI/CD)

For products that ship regularly, translation should happen automatically as part of your build. New keys added to the source YAML are detected, translated, and synced back without manual steps.

A minimal GitHub Actions workflow:

name: 'Sync translations'

on:
  push:
    branches: [main]

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

      - name: Install SimpleLocalize CLI
        run: npm install -g simplelocalize-cli

      - name: Upload source YAML
        run: |
          simplelocalize upload \
            --apiKey ${{ secrets.SIMPLELOCALIZE_API_KEY }} \
            --uploadPath "config/locales/en.yml" \
            --uploadFormat yaml \
            --uploadLanguageKey en

      - name: Auto-translate missing keys
        run: |
          simplelocalize auto-translate \
            --apiKey ${{ secrets.SIMPLELOCALIZE_API_KEY }}

      - name: Download translated files
        run: |
          simplelocalize download \
            --apiKey ${{ secrets.SIMPLELOCALIZE_API_KEY }} \
            --downloadFormat yaml \
            --downloadPath "config/locales/{lang}.yml"

      - name: Commit translations
        run: |
          git config user.email "ci@yourapp.com"
          git config user.name "Translation Bot"
          git add config/locales/
          git diff --staged --quiet || git commit -m "chore: sync translations"
          git push

This triggers only when en.yml changes. New or modified keys are uploaded, auto-translated for all target locales, and committed back. By the time a PR lands on main, translated files are already updated.

For a deeper dive into CI/CD automation including GitHub App configuration, multi-file setups, and automation rules, see our guide on how to auto-translate YAML files.

Best practices for YAML translation files

Here are some tips to keep your YAML translations clean and maintainable:

  • Mirror the same key tree across all locales.

    Key paths must be identical in every locale file. Any deviation causes runtime errors or silent fallback. Use tooling to detect mismatches rather than catching them by eye.

  • Never have translators edit raw YAML.

    The format is too unforgiving. A translation editor that exports valid YAML eliminates an entire class of production errors.

  • Protect placeholders explicitly.

    Know which placeholder format your framework uses (%{name}, {{name}}, %name%) and make sure your translation tool protects that syntax. Test with a string that contains a placeholder before relying on any translation engine for production content.

  • Handle plurals per language, not per English convention.

    English needs two plural forms. Polish needs four. Arabic needs six. Define the correct plural keys for each target language upfront rather than discovering missing forms in production.

  • Keep keys stable; change values only.

    Translation keys are your API contract with your i18n library. Renaming a key means updating every locale file and every reference in your code simultaneously. Avoid it unless necessary.

  • Use descriptive, namespaced keys.

    checkout.payment.card_declined is better than error_7. Namespacing by feature (nav, auth, dashboard, errors) keeps large files organized and makes ownership clear when multipleteams contribute.

  • Freeze the source language before translation begins.

    Translating a string that is still being edited wastes effort and introduces inconsistency. Mark source strings as approved before sending them for translation.

  • Add a YAML linter to CI.

    Catch syntax errors before they reach staging. yamllint is a reliable, configurable option that integrates easily into any pipeline.

# Install and run yamllint
pip install yamllint
yamllint config/locales/

Choosing a translation engine for YAML files

The translation engine matters for quality, but so does what the engine receives. A translation engine that gets a bare string with no context will produce a worse translation than one that knows the string is a navigation button label in a fintech app for a younger audience.

Translation key screenshot and description context in SimpleLocalize
Translation key screenshot and description context in SimpleLocalize

Practical guidance by content type:

  • UI labels, navigation, error messages: DeepL or Google Translate produce reliable results quickly and cheaply. Good for bulk translation of short strings.
  • Onboarding, marketing, help text: LLM-based translation (OpenAI, Claude, Gemini) with project-level and key-level context produces noticeably better output for content where tone matters. Slower and more expensive, but worth it for user-facing copy that drives conversion or retention.
  • Legal, compliance, medical content: human translation with professional review. Machine translation can serve as a first draft, but these categories require domain expertise that current engines cannot reliably provide.

For a direct comparison with examples, see Auto-translation comparison with examples.

Summary

Translating YAML files correctly comes down to a few principles: preserve the structure, protect placeholders, handle plurals properly for each target language, and stop having translators edit raw files. The specific tool or workflow you use matters less than getting these fundamentals right.

For solo projects, manual editing with a linter is fine. For teams, a TMS that exports valid YAML removes most of the failure modes. For products shipping regularly, a CI/CD pipeline that handles the sync automatically means translation becomes a background concern rather than a release bottleneck.

The broader context, how YAML translation fits into key management, locale detection, fallback strategies, and deployment, is covered in our complete technical guide to internationalization.

FAQ

What is the correct structure for a YAML translation file?
It depends on your framework. Rails uses a root locale key (en:) followed by nested keys. Symfony omits the root locale key and uses either flat dotted keys or nested objects. The key requirement in all cases is that the same key paths exist in every locale file, with only the values translated.
How do I handle placeholders in YAML translations?
It depends on the tool. General-purpose translation APIs may translate placeholder text unless you mask it first. A localization platform like SimpleLocalize protects common placeholder formats automatically.
Why do my YAML translation files break after editing?
The most common cause is indentation errors: YAML is whitespace-sensitive and one misaligned space breaks the structure. Other causes include unquoted special characters (colons followed by spaces, hash signs at the start of a value) and inconsistent scalar styles. A YAML linter catches all of these before they reach production.
How do I keep YAML locale files in sync as the codebase grows?
Use a TMS with CI/CD integration that uploads your source file on every push, detects new or changed keys, auto-translates them, and syncs the updated files back to your repository. This turns key synchronization from a manual task into a background process.
How do I handle pluralization in YAML for non-English languages?
Define the plural keys required by each target language rather than copying the English pattern. English needs one and other. Polish needs one, few, many, and other. Arabic needs zero, one, two, few, many, and other. Your i18n framework documents which plural keys it expects per locale: check that documentation before sending files for translation, and verify the output contains all required keys before deploying.
Can I auto-translate YAML files in CI/CD?
Yes. The SimpleLocalize CLI integrates into any CI pipeline like GitHub Actions, GitLab CI, Bitbucket Pipelines, or custom runners. Upload your source YAML, trigger auto-translation, and download updated locale files as part of your standard build.
Kinga Pomykała
Kinga Pomykała
Content creator of SimpleLocalize

Get started with SimpleLocalize

  • All-in-one localization platform
  • Web-based translation editor for your team
  • Auto-translation, QA-checks, AI and more
  • See how easily you can start localizing your product.
  • Powerful API, hosting, integrations and developer tools
  • Unmatched customer support
Start for free
No credit card required5-minute setup
"The product
and support
are fantastic."
Laars Buur|CTO
"The support is
blazing fast,
thank you Jakub!"
Stefan|Developer
"Interface that
makes any dev
feel at home!"
Dario De Cianni|CTO
"Excellent app,
saves my time
and money"
Dmitry Melnik|Developer