Compare translation files and find what is missing
Paste your source locale and a target locale. The diff lists missing and extra keys, then checks
every value for broken :placeholders,
plural forms Laravel cannot select, HTML that went missing, empty values and text nobody translated.
Files are compared in memory and never stored.
How the comparison works
Both files are parsed without executing any code: PHP files are read token by token, so only
strings, numbers and nested arrays are accepted, and a PHP file that calls a function is rejected.
Nested arrays are flattened into the same dot notation you pass to
__(), so 'cart' => ['checkout' => '…']
becomes cart.checkout. Every
translation key
in the source that is absent from the target is reported as missing, and every key that exists only
in the target is reported as extra, usually a leftover from a string you deleted or renamed.
The completeness figure is the share of source keys that have a non-empty value in the target. It is the same number a translation coverage report tracks per locale, measured for the one file you pasted.
Why missing translations are hard to spot in Laravel
Laravel never throws on a missing translation. For a short key such as
cart.checkout it tries the fallback locale, and if that also
lacks the key it prints the key itself. With JSON files the key is usually the English sentence, so
a missing Spanish line simply shows English and looks fine to anyone who does not read Spanish.
Empty values behave differently by format: an empty string in a PHP file renders as nothing at all,
while an empty string in a JSON file falls back to the key. Both are reported here as empty.
The value checks
-
Placeholders. Laravel replaces
:name,:Nameand:NAMEfrom the samenamereplacement, changing only the capitalisation, so the three count as one placeholder here. A translated placeholder such as:nombreis never replaced and reaches the user as literal text. More on this in placeholders done right. -
Plural forms.
trans_choice()splits a line on every pipe. Segments that start with an explicit condition such as{0}or[2,*]are matched first; otherwise Laravel picks a segment by index using its own per-locale rule, and when that segment does not exist it returns the first one. Set the target locale and the tool checks the segment count against that rule. Without a locale it only compares segment counts, because languages legitimately differ: Polish needs three forms where English needs two. The trans_choice() deep dive covers the rules language by language. -
HTML tags. Opening and closing tags are counted on both
sides. A link or a
<strong>that disappeared from a translation is a silent bug when the line is printed with{!! !!}. -
Untranslated values. A target value identical to the
source is flagged only if real text remains once placeholders, tags and plural conditions are
removed. Values with fewer than four letters and all-capital values such as
OKorPDFare ignored. Single words are marked as info rather than warnings, since brand names and loanwords are often correct as they are.
A worked example
The example loaded above compares an English file with a Spanish one that contains the usual
problems: one key never added (cart.coupon), one key left behind
after a rename (legacy_banner), an empty
saved message, a translated :name
placeholder, a terms line that lost its link and its :url, a
plural line with a different set of conditions, and a settings label that was copied over in
English. The generated stub contains only the missing key, nested the same way as the source, so
you can paste it straight into the Spanish file.
Keep it from happening again
A one-off diff fixes today's files. To stop missing keys reaching production, add a key parity test that fails the build whenever a locale falls behind; the guide to testing Laravel localization with Pest has one you can copy. If you are choosing between formats, the JSON vs PHP lang files comparison explains how each behaves when a key is missing. And to check what users actually see, run the untranslated key detector against your live site.
Frequently asked questions
How do I find missing translation keys in Laravel?
Compare each target locale against your source locale key by key. Paste lang/en/messages.php and lang/es/messages.php (or en.json and es.json) into this tool and it lists every key the target lacks, plus a ready-to-paste stub. To catch new gaps automatically, add a key parity test to your test suite so the build fails when a locale falls behind.
What does Laravel show when a translation is missing?
It never throws. For a short key such as auth.failed it tries the fallback locale, and if that also lacks the key it returns the key itself. For JSON files the key is usually the source sentence, so a missing line quietly shows the source language. An empty string behaves differently by format: it renders blank in a PHP file and falls back to the key in a JSON file.
Are :name, :Name and :NAME different placeholders?
No. Laravel fills all three from the same name replacement, as written, with the first letter upper-cased, or fully upper-cased. The diff therefore treats them as one placeholder and only reports a mismatch when a name is missing or new, such as a :name that a translator turned into :nombre.
Why does it flag plural forms that look fine?
trans_choice() picks a pipe-separated segment by index using a per-locale rule, and returns the first segment when the index does not exist. If you set the target locale, the tool checks the segment count against that rule, so a Polish line with two forms is flagged because Laravel selects up to three. Lines with explicit {0} or [2,*] conditions are compared segment by segment with the source instead.
Is the content of my lang files stored or executed?
Neither. PHP files are parsed token by token, so only string, number and array literals are accepted and nothing is evaluated. Both files are compared in memory and discarded when the response is sent.
Useful? Link to it
The tool is free and stays free. Paste this into a README, a wiki page or a forum answer.
<a href="https://langsyncer.com/tools/lang-file-diff">Lang File Diff & Validator</a> by LangSyncer
Other free tools
Lang File Converters
Convert between Laravel PHP arrays, JSON, YAML and CSV — nesting, placeholders and plural pipes preserved.
Pseudo-Localization Generator
Turn a lang file into accented, expanded pseudo-text to catch hardcoded strings and layouts that break with longer languages.
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.
See missing translations for every locale at once
This diff checks one pair of files. LangSyncer imports all your lang files, shows coverage per language, translates the missing keys with AI and publishes the result without a deploy.