skip to content

In Laravel, how do you change a package's translation lines without editing vendor/, and what should the override file contain?

level: middleimportance: nice to knowfreq 20%

answer

  1. never edit vendor
  2. lang/vendor/{namespace}/{locale}
  3. namespace from package::group.key
  4. only the lines you change
  5. app JSON read after package JSON

basics

~10 s

Create lang/vendor/{namespace}/{locale}/{group}.php containing only the lines you want to change. Laravel merges it over the package's own file, so every line you leave out still comes from the package.

solid answer

~40 s

Packages register their translations under a namespace, and their keys look like `billing::invoices.paid`. To change a line, create `lang/vendor/billing/es/invoices.php` - namespace, locale, group - and return only the keys you want to override. The file loader reads the package's file and then merges your override over it with `array_replace_recursive`, so untouched lines, including nested ones, still come from the package and future package updates keep flowing in. The directory name is the namespace the package registered, not necessarily its Composer vendor name. Package strings that use sentence keys are overridden by adding the same sentence to your own `lang/es.json`, which is read after the package's JSON paths. The framework's own lines are not a vendor package: you override them in `lang/{locale}/validation.php` and friends.

code

php · 12 lines
php
<?php

// lang/vendor/billing/es/invoices.php
return [
    // overrides one line; every other billing::invoices line
    // still comes from the package's own es file
    'paid' => 'Donación recibida',
];

// elsewhere
__('billing::invoices.paid');    // Donación recibida
__('billing::invoices.overdue'); // the package's own Spanish line

go deeper

for a junior

Recall that vendor/ is never edited and that overrides go under lang/vendor/{namespace}/{locale}.

for a middle

Explain the merge: package file first, override applied with array_replace_recursive, so the file holds only changed lines, and JSON overrides go in your own locale JSON.

for a senior

Diagnose an override that is ignored, wrong namespace or missing locale level, and keep overrides minimal so package fixes still arrive on upgrade.

for a principal

Decide when to override a package's copy versus contributing the translation upstream, weighing maintenance of local forks of text.

## Why not edit the package Anything under `vendor/` is overwritten by the next `composer install` or `composer update`, so edits there vanish on deploy. Laravel gives you a **vendor override directory** instead. ## How packages expose translations A package registers its translation directory under a **namespace** from its service provider, and its lines are addressed as `namespace::group.key`: ```php __('billing::invoices.paid'); ``` The namespace is whatever the package chose when registering, which is often, but not always, the package's short name. Read the package's docs or its keys to find it. ## Writing the override The path is: ``` lang/vendor/{namespace}/{locale}/{group}.php ``` For a package namespaced `billing` with a group `invoices`, a Spanish override is `lang/vendor/billing/es/invoices.php`: ```php <?php return [ 'paid' => 'Donación recibida', ]; ``` Rules for the file: - **Only include the lines you change.** The loader reads the package's own file first and then applies your file with `array_replace_recursive`, so missing keys keep the package's value. - **Nested arrays merge key by key**, not wholesale, for the same reason. - **One file per group and locale.** Overriding Polish needs `lang/vendor/billing/pl/invoices.php` too. ## Overrides by string type | What you want to change | Where the override goes | |---|---| | A package's short-key group line | `lang/vendor/{namespace}/{locale}/{group}.php` | | A package's sentence-keyed (JSON) line | your own `lang/{locale}.json`, same sentence as key | | A framework line such as a validation message | `lang/{locale}/validation.php` after `lang:publish` | The JSON row works because the file loader merges JSON files from package-registered JSON paths first and the application's `lang` directory last, so your entry for the same sentence wins. The framework row differs because Laravel's own lines are not namespaced: its `lang` directory is simply the first base path, and your `lang` directory is merged over it. ## Pitfalls 1. **Copying the whole package file.** It works, but freezes every line at today's wording; later package fixes never reach you. 2. **Using the Composer vendor name as the directory.** If the package registered a different namespace, your override is never read. 3. **Expecting lang:publish to copy package files.** It copies only the framework's English files; packages that want you to customise more offer their own publish step. 4. **Forgetting the locale level.** `lang/vendor/billing/invoices.php` without a locale directory is never loaded. ## A worked example A charity site uses a donations-billing package whose Spanish receipt line reads `Pago recibido`; the charity wants `Donación recibida`, and a Polish wording of its own. The steps: 1. Find the namespace in a key the package uses, for instance `billing::invoices.paid`, which gives the namespace `billing` and the group `invoices`. 2. Create `lang/vendor/billing/es/invoices.php` returning `['paid' => 'Donación recibida']`. 3. Create `lang/vendor/billing/pl/invoices.php` with the Polish line. 4. Leave every other key out of both files. ## Checking the result - Call `__('billing::invoices.paid')` from `php artisan tinker` with the locale set, and compare it with a key you did not override. - In a test, assert that `Lang::get('billing::invoices.paid', [], 'es')` returns the new text, so a package update that renames the key fails the build instead of silently reverting the wording. ## Keeping overrides healthy on upgrades - Read the package's changelog for renamed translation keys; an override for a key the package no longer reads has no effect. - Keep a short comment in each override file explaining why the wording differs, so the next developer does not delete it as noise. - Review overrides when the package adds or improves a locale you already override, since its new lines may now cover what you wrote.

  • The override file exists but the package's text still shows. What do you check first?
    That the directory uses the namespace from the key (`billing` in `billing::invoices.paid`), that there is a locale level matching the active locale, and that the file name matches the group. A Composer vendor name in place of the registered namespace is the usual cause.
  • How do you override a package line that the package translates with sentence keys?
    Add the same sentence as a key in your own `lang/{locale}.json`. The loader merges package JSON paths first and your `lang` directory last, so your entry replaces the package's for that sentence.

saying these in an interview costs you the question

  • Copy the whole package file or unchanged lines will disappear
  • Edit the file under vendor/ directly; Composer keeps local changes
  • The override directory is named after the Composer vendor, like lang/vendor/acme
  • lang:publish copies every installed package's translation files
  • Framework validation lines are overridden under lang/vendor/laravel