Convert JSON to YAML

Convert JSON message catalogs into YAML for Symfony, Rails or configuration-driven pipelines.

Trading strictness for readability

JSON and YAML describe the same data. YAML is in fact a superset of JSON, so every valid JSON document is already valid YAML. The reason to convert is not capability but ergonomics: YAML drops the braces, the quotes and the trailing-comma rules, and adds the one thing JSON conspicuously lacks — comments.

Input — messages.json

{
    "welcome": "Welcome back, :name!",
    "cart": {
        "empty": "Your cart is empty",
        "items": ":count item|:count items"
    }
}

Output — messages.yaml

welcome: 'Welcome back, :name!'
cart:
    empty: 'Your cart is empty'
    items: ':count item|:count items'

Nesting becomes indentation, and the tree structure is preserved exactly. Values are quoted where quoting is required to keep them unambiguous and left bare where it is not.

Placeholders, pipe-separated plural forms and embedded HTML all pass through untouched, because the converter treats values as opaque text. That is the safe default: a tool that rewrites message bodies on your behalf is a tool that will eventually corrupt one, and translation bugs are unusually expensive to find because they are invisible to anyone who does not read the language.

What you gain, concretely

  • Comments. You can annotate a string with the screen it appears on, a character budget, or a warning that a test asserts against it. JSON has no comment syntax at all, which is its single biggest weakness as an authoring format.
  • Cleaner diffs. Adding a key to a JSON object touches the previous line to add a comma. In YAML it is a one-line addition, which makes review noticeably easier across large translation files.
  • Fewer syntax errors from humans. A misplaced comma or an unclosed brace invalidates a whole JSON file. YAML has no punctuation to forget, so non-developers break it far less often.
  • Native fit for config pipelines. CI systems, Kubernetes manifests, Ansible and most static site generators speak YAML, so translation data can live alongside deployment configuration rather than beside it.
  • Framework compatibility. Symfony reads YAML message catalogues directly, and Rails expects them, so this is the natural export when copy is shared beyond a JavaScript stack.

Comments are the one that changes daily practice most. A translation file accumulates hard-won knowledge — this string is legally reviewed, this one has a thirty-character budget, this one appears twice and must stay consistent — and JSON gives you nowhere to record any of it. Teams end up keeping that context in a wiki that goes stale, or in the head of whoever wrote the string.

The trade is real, though. YAML is dramatically more complex to parse than JSON, with anchors, aliases, merge keys, tagged types and multiple documents per file all in the specification. For pure machine-to-machine interchange JSON remains the safer choice. Convert to YAML when a human is going to open the file.

The type coercion trap

JSON is explicit about types: a quoted value is a string, always. YAML infers type from the shape of an unquoted value, and translation files are full of short strings that look like something else. This is the single most common way a converted YAML file misbehaves.

Unquoted Parsed as Intended
no boolean false Norwegian locale code, or the word
yes / on / off boolean a label
null / ~ null the literal word
1.10 the number 1.1 a version string
2026-08-16 a date object a date as text
12:30 sexagesimal number in YAML 1.1 a time label

The no case is well known enough to have a name — the Norway problem — because a list of language codes containing no silently becomes false. It is the best argument for keeping quotes on translation values even when the parser does not strictly require them.

Punctuation causes a second family of problems. In an unquoted value, a colon followed by a space begins a new mapping, so Error: try again parses as a key rather than a sentence. A leading - reads as a list item, # starts a comment, and & or * introduce anchors and aliases. Quoting neutralises all of them at once.

The converter quotes values that would otherwise be coerced. If you tidy the file by hand afterwards, leave those quotes alone — removing them is how these bugs get introduced.

Multi-line copy and indentation rules

JSON encodes line breaks as \n inside a single quoted string. YAML offers block scalars instead, and the two available styles are not interchangeable.

Literal keeps newlines, folded joins them

email:
    body: |
        Line one stays on its own line.
        Line two does too.
    intro: >
        These two lines are joined
        into one with a space between them.

Picking the wrong style silently reformats your copy — a folded block applied to an address destroys the line breaks, and a literal block applied to a wrapped paragraph introduces hard breaks that show up in rendered HTML. When you are unsure, keep the single-line quoted form with explicit escapes, which behaves identically in every parser.

Two mechanical rules matter. Indentation must be spaces, never tabs; YAML forbids tabs outright and the resulting parse error is often reported several lines away from the offending character. And trailing whitespace is significant inside block scalars, while most editors strip it on save, so any string that depends on a trailing space should be quoted rather than written as a block.

Finally, note that duplicate keys are permitted by the YAML specification and most parsers silently keep the last one. JSON parsers behave similarly, so a duplicate in the source survives the conversion as a single surviving value — worth checking for if a string disappears between formats.

Who consumes the YAML afterwards

The destination shapes how much extra work the conversion needs, because the message tree is only part of what a framework expects.

Destination Expected file Extra work
Symfony translations/messages.en.yaml Rename :name to %name%
Rails config/locales/en.yml Wrap everything under an en: root key
Jekyll / Hugo _data/*.yml None; read as data directly
Localisation platform varies Usually none

Rails is the case people forget. It requires a top-level key naming the locale, wrapping the entire tree, and a converted file without it simply will not load. Symfony encodes the same information in the filename instead, so no wrapper is needed there.

Plural forms are the part no converter can handle for you. Rails expects named one and other sub-keys under each message; Symfony has moved to ICU MessageFormat; Laravel packs the forms into a single pipe-separated string. Each encoding expresses the same idea in a structurally different way, and how many forms a language needs is a property of the language rather than the file. Confirm each target with the pluralization tester before rewriting, because a source catalogue that only ever supported two forms will convert its incompleteness faithfully into the new format.

Frequently asked questions

Does the converter keep :placeholders and plural pipes intact?

Yes. Values are treated as opaque strings, so Laravel placeholders like :name or :count and pipe-separated plural forms come through unchanged.

How is nesting handled?

Nested arrays in JSON map one-to-one to nested structures in YAML; nothing is flattened.

Is my data uploaded or stored?

The conversion runs on our server but nothing is persisted: files are converted in memory and the response is returned immediately. For PHP input, only string, number and array literals are parsed — code is never executed.

Is there a size limit?

Yes, 200 KB per conversion, which covers even very large lang files. If you need bulk conversion across many locales, import your files into LangSyncer and export any format.

Converting files by hand gets old fast

Import your lang files into LangSyncer once, edit and translate them in a dashboard, and export any format — or skip files entirely with live translations.

We use cookies to improve your experience and analyze site traffic. Cookie Policy

Cookie Preferences

Essential

Required for the site to work

Analytics

Help us improve the site

Marketing

Personalized ads and content