In Laravel, when would you keep translations in lang/es.json rather than lang/es/*.php group files, and how does __() decide which one to read?
answer
- source text as the key
- one flat file per locale
- JSON checked before group files
- no fallback-locale JSON lookup
- 13 reworded the reset mail subject
basics
~20 sUse lang/es.json, keyed by the original sentence, for many UI strings; use PHP group files for stable short keys and nested arrays. __() checks the locale's JSON file first, then parses the key as group.item.
solid answer
~40 sJSON files hold one flat object per locale whose keys are the source-language text, so `__('Donate now')` needs no invented key and simply prints the English when Spanish is missing; the docs recommend them for apps with many strings, and the framework's own mail lines use this style. PHP group files use short keys like `donations.cta`, allow nesting and arrays, and keep keys stable when the English copy changes. `__()` first looks the exact string up in the current locale's JSON file; only if that misses does it parse the key into group and item and search `lang/{locale}/{group}.php` for the current and then the fallback locale. JSON has no fallback-locale step, and a key that matches a group file name, like `__('Action')` with `lang/nl/action.php`, returns that whole file.
code
json · 5 lines{
"Donate now": "Dona ahora",
"Reset your password": "Restablece tu contraseña",
"Thank you, :name!": "¡Gracias, :name!"
}go deeper
Know both layouts, lang/es.json with sentences as keys and lang/es/group.php with short keys, and how each is read with __().
Walk through get(): JSON for the current locale first, then group files for current and fallback locales, then the key, and name the group-name collision trap.
Explain upgrade risk: reworded source strings orphan JSON translations silently, so add tests that diff keys and review framework string changes on every major upgrade.
Set a team convention on which text goes to JSON and which to groups, balancing translator tooling, key stability and the cost of silent drift.
## Two storage styles Laravel supports two ways to store **translation strings**, and one application can use both at once. - **PHP group files** (short keys): `lang/es/donations.php` returns an array such as `['cta' => 'Dona ahora']`, read with `__('donations.cta')`. - **JSON files** (source text as key): `lang/es.json` holds `{"Donate now": "Dona ahora"}`, read with `__('Donate now')`. | Aspect | `lang/{locale}/*.php` | `lang/{locale}.json` | |---|---|---| | Key | short, invented (`donations.cta`) | the source sentence | | Structure | nested arrays allowed | one flat object per locale | | Missing translation shows | the raw key | the source-language text | | Fallback locale consulted | yes | no | | Key stability when copy changes | stable | the key changes with the text | | Framework usage | validation, auth, pagination | notification mail lines | ## How __() picks a source The translator's `get()` method follows a fixed order: 1. Load the **current locale's JSON** file and look up the exact key string. If found, apply replacements and return it. 2. Otherwise **parse the key** on dots: the first segment is a group (a file name), the rest is the item path. 3. Look in `lang/{locale}/{group}.php` for the current locale, then for the **fallback locale**. 4. If all of that misses, return the key itself. Two consequences follow directly from that code path: - A missing entry in `es.json` is **not** looked up in `en.json`. The source sentence is returned, which is usually acceptable because the key already is English. If a team puts short keys like `home.title` into JSON files, though, a missing Spanish entry prints `home.title`, not the English line. - A key without a dot that matches a group file name returns that entire group. The docs' example: translating `__('Action')` for the `nl` locale while `lang/nl/action.php` exists but `lang/nl.json` does not returns the whole contents of `action.php`. ## Choosing between them **Prefer JSON** when: - the interface has hundreds of short, one-off strings and inventing keys slows developers down; - you want untranslated strings to degrade to readable English; - you are translating the framework's own notification mail, whose lines (`Reset your password`, `Verify Email Address`) are looked up by source text. **Prefer PHP groups** when: - the text is long or marketing copy likely to be reworded, so a stable key protects every translation; - you need arrays or nesting (per-field lists, enumerated labels); - the lines belong to a framework group such as `validation.php`. ## The brittleness of source-text keys Because a JSON key *is* the sentence, any change to the source text orphans every translation of it. Laravel 13 is a real example: the default password reset mail subject changed from `Reset Password Notification` to `Reset your password`. An application whose `lang/es.json` translated the old sentence silently started sending the English subject after the upgrade; the upgrade guide tells you to update translation overrides that depend on the previous string. Nothing errors, so only a test or a diff of the framework's strings catches it. ## Practical pattern Many teams combine the two: JSON for interface text, PHP groups for long copy and anything structured. The only rule to keep is that JSON keys must not collide with group file names. ## Migrating between the styles Moving a feature from group files to JSON, or back, is a code change plus a data move across every locale file: 1. List every key the feature uses, per locale. 2. Write the new entries in each locale, keeping placeholder names identical. 3. Replace the calls in views and PHP code. 4. Delete the old entries only after a completeness test passes for every locale. ## Working with translators - JSON files are **flat and self-describing**: a translator sees the English sentence and its translation side by side, which suits external translation tools and spreadsheets. - Group files carry **context through their names**: `donations.form.submit` tells a translator where the text appears, which a bare sentence like `Submit` does not. - Short, ambiguous source sentences are the weak spot of JSON keys: `Order` as a noun and `Order` as a verb share one key and therefore one translation. Use a group key when the same English word needs two translations.
- If lang/es.json lacks a sentence that lang/en.json translates, what does a Spanish request show?The key string itself. The translator loads JSON only for the requested locale and then falls through to group-file lookup, which applies the fallback locale; `en.json` is never consulted. With source-text keys that is the English sentence anyway; with short keys stored in JSON it is the raw key.
- How do you translate Laravel's built-in password reset email into Spanish?Add its source sentences to `lang/es.json`: the notification calls `Lang::get('Reset your password')` and similar lines, so JSON keys matching those exact strings take effect. After a framework upgrade, diff those strings, because a reworded source sentence silently stops matching.
saying these in an interview costs you the question
- JSON translations fall back to en.json when a Spanish entry is missing
- __() reads PHP group files before it checks the JSON file
- You must choose one style per application; mixing them is unsupported
- JSON keys stay valid when the English source sentence is reworded
- A JSON file can nest arrays exactly like a PHP group file