Convert Laravel PHP array to YAML
YAML is the native translation format in Symfony and Rails; converting your Laravel lang arrays makes them portable across frameworks.
Laravel arrays into the format the rest of the world uses
YAML is the native translation format in Symfony and Rails, the default in most static site generators, and a common input for localisation platforms and CI pipelines. Laravel is the outlier in preferring PHP arrays. Converting your lang files to YAML makes them portable to any of those ecosystems without asking the receiving system to understand PHP.
Input — lang/en/messages.php
<?php
return [
'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 maps directly onto YAML indentation, so the structure you had in PHP is the structure you get out. Values are quoted where quoting is required and left bare where it is not, which keeps the output readable while staying unambiguous.
The appeal is mostly editorial. YAML strips away the ceremony of PHP array syntax — no opening tag, no return, no arrows, braces or trailing commas — leaving something a non-developer can edit without fear. That readability is exactly why translation platforms and content teams gravitate to it, and why the format shows up wherever people who do not write code still need to change text.
YAML will guess the type of your strings, and it will be wrong
This is the defining hazard of the format. YAML tries to infer types from unquoted values, and translation files are full of short strings that look like something other than text. A parser reading unquoted YAML can hand your application a boolean, a number, or a null where you expected a sentence.
| Unquoted value | YAML 1.1 reads it as | You wanted |
|---|---|---|
no |
boolean false | the Norwegian locale code, or the word "no" |
yes |
boolean true | the word "yes" |
on / off |
boolean | a toggle 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 (YAML 1.1) | a time label |
The no case is famous enough to have a name — the Norway problem — because a language list containing no for Norwegian silently becomes false. It has broken real production systems, and it is the single best argument for quoting every translation value rather than trusting the parser.
The converter quotes values that would otherwise be coerced. If you hand-edit the YAML afterwards, keep the quotes — removing them to "tidy up" is how these bugs get introduced.
A second class of problem is punctuation that YAML treats as syntax. A colon followed by a space inside an unquoted value starts a new mapping, so Error: try again parses as a key. A leading - reads as a list item, a leading # as a comment, and a leading * or & as an alias or anchor. Quoting neutralises all of these.
Multi-line strings and where they break
Translation values are not always one line. Email bodies, legal text and long descriptions all run to paragraphs, and YAML offers two block styles for them that behave very differently.
Literal block keeps newlines; folded block joins them
terms:
literal: |
First line stays on its own line.
Second line does too.
folded: >
These two lines will be joined
into a single line with a space.
Choosing the wrong one silently reformats your copy. A folded block applied to an address or a poem destroys the line breaks; a literal block applied to a wrapped paragraph introduces newlines that show up as hard breaks in HTML. When in doubt, use a quoted single-line string with explicit \n escapes, which behaves identically everywhere.
Trailing whitespace is also significant in block scalars, and most editors strip it on save. If a string depends on a trailing space — a label followed by a value, for instance — quote it rather than relying on block syntax to preserve it.
What you are converting for
-
Migrating to Symfony. Symfony reads
translations/messages.{locale}.yamlnatively. This conversion produces the message tree; you will still need to rename placeholders from:nameto%name%. -
Sharing copy with a Rails service. Rails expects a top-level locale key wrapping everything, so the converted tree needs nesting under
en:before Rails will load it. - Feeding a static site generator. Jekyll, Hugo and Eleventy all read YAML data files directly, making this the shortest path from a Laravel app to a marketing site that shares its copy.
- Localisation platform imports. Several platforms treat YAML as their canonical format and support it more completely than JSON, particularly for plural metadata.
- Human-reviewable diffs. YAML diffs are cleaner than PHP arrays: no braces, no trailing commas, and indentation changes are obvious in review.
- Config-driven pipelines. Ansible, Kubernetes manifests and most CI systems speak YAML, so translation data can travel alongside deployment configuration.
Bear in mind that YAML is a superset of JSON and considerably more complex to parse. Anchors, aliases, merge keys, multiple documents per file and tagged types all exist, and a hostile or careless YAML file can be far more surprising than the equivalent JSON. For pure data interchange, JSON is often the safer target; choose YAML when a human is going to read and edit the file.
Indentation must be spaces. YAML forbids tabs for indentation entirely, and the error a tab produces is frequently unhelpful — some parsers report a problem several lines below the actual tab. If a converted file suddenly refuses to load after a manual edit, check for a stray tab before anything else.
Placeholders and plurals do not travel unchanged
The converter treats values as opaque text, which is deliberate: silently rewriting message bodies is how translations get corrupted. That means the syntax inside your strings arrives in YAML exactly as it left PHP, and the receiving framework may not understand it.
| Concern | Laravel | Symfony | Rails |
|---|---|---|---|
| Variable | :name |
%name% |
%{name} |
| Plural syntax | one|many |
ICU or legacy intervals | one: / other: sub-keys |
| File naming | lang/en/messages.php |
messages.en.yaml |
config/locales/en.yml |
| Root key | none | none | locale code required |
Plurals are the part that cannot be fixed with a search and replace. Laravel encodes forms positionally in one string separated by pipes. Rails uses named sub-keys, so :count item|:count items has to become a nested mapping with one and other children. Symfony has moved to ICU MessageFormat, which expresses the same idea as {count, plural, one {...} other {...}}.
The same plural string in Rails-style YAML
cart:
items:
one: '%{count} item'
other: '%{count} items'
Plan that transformation as a deliberate step rather than expecting the converter to guess it. Which forms a locale needs is a property of the language, not of the file format, and the pluralization tester will tell you how many each target actually requires before you start rewriting.
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 Laravel PHP array 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.
Other free tools
Untranslated Key Detector
Scan any website for leaked i18n keys: dot-notation identifiers, missing-translation markers and unrendered placeholders your users can see.
Blade Hardcoded String Scanner
Paste a Blade template and find text that should be behind __() — with suggested keys and a generated lang file.
Pluralization Tester
Pick a language, type a number, see which CLDR plural category applies — live, via your browser's Intl.PluralRules.
AI Translation Cost Calculator
Keys × languages = quota units. Estimate the initial run and the monthly churn for your project.
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.