Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lang Laravel Package

laravel-lang/lang

Laravel Localizations provides comprehensive language files for Laravel, giving your app ready-made translations for validation, auth, and framework messages across dozens of locales. Install via Composer and ship multilingual apps faster.

View on GitHub
Deep Wiki
Context7

Getting Started

laravel-lang/lang is not a service-provider package — it ships no PHP classes, no config, no auto-discovery. It is a data repository: ~126 locale folders of auth.php, passwords.php, pagination.php, validation.php, validation-inline.php, plus a {locale}.json, and (since v10) split directories for the wider ecosystem (jetstream, fortify, breeze, cashier, nova, spark-paddle, spark-stripe).

Minimal setup

composer require --dev laravel-lang/lang

It is a dev dependency by design: you consume its files at development/build time and commit the result into your own lang/ directory. Nothing reads from vendor/laravel-lang/lang at runtime.

Where to look first

  1. vendor/laravel-lang/lang/locales/ — the actual translated files. Confirm the exact folder name before wiring it to app()->setLocale(); codes are pt_BR, zh_CN, sr_Latn, not BCP-47 pt-BR.
  2. vendor/laravel-lang/lang/source/ — the English master keys. This is your reference for "does a key exist for this new validation rule yet?"
  3. The documentation — for the recommended publisher workflow rather than manual copying.

First use case: add French to a Breeze app.

cp -r vendor/laravel-lang/lang/locales/fr lang/fr
cp vendor/laravel-lang/lang/locales/fr.json lang/fr.json
// .env
APP_LOCALE=fr
APP_FALLBACK_LOCALE=en

Then flip a request at runtime with App::setLocale('fr') and watch the login/validation/pagination strings change. If you'd rather not hand-copy, the companion laravel-lang/publisher package wraps this in artisan commands (e.g. lang:add, lang:update) — check the current docs for exact names, since the commands have evolved.

Implementation Patterns

Publish once, own it forever. The idiomatic workflow is: copy the locales you need into lang/, commit them, and treat them as project files. That gives you diffs, code review, and the ability to patch a bad machine translation.

Runtime locale switching. A middleware reading a route segment or a user column is the common shape:

// app/Http/Middleware/SetLocale.php
public function handle($request, Closure $next)
{
    $locale = $request->route('locale')
        ?? $request->user()?->locale
        ?? config('app.locale');

    if (in_array($locale, config('app.available_locales'), true)) {
        App::setLocale($locale);
    }

    return $next($request);
}

Keep the allow-list in config/app.php (or a dedicated config/localization.php) and validate against it — never pass a raw request value into setLocale().

Two different translation namespaces. PHP files under lang/fr/ are for framework messages; lang/fr.json is the flat English-string→translation map used by __('Welcome back') in your Blade templates. You will almost always need both, and forgetting the JSON file is the most common "why is only half my UI translated?" bug.

Override per-form attribute names. The attributes array at the bottom of validation.php turns "The email field is required." into "The work email address field is required." This is the highest-value customization in the whole package and the one developers skip.

Package-namespaced strings. Fortify, Jetstream, Nova and Cashier resolve translations through lang/vendor/{package}/{locale}/…. Publish those with the package's own vendor:publish after copying the laravel-lang subdirectory into place, otherwise auth emails stay English.

Key-parity tests. Because the package is a moving target, add a cheap test that walks lang/en (or source/) and asserts every key exists in each shipped locale:

public function test_locales_have_all_keys(): void
{
    $reference = Arr::dot(require lang_path('en/validation.php'));

    foreach (config('app.available_locales') as $locale) {
        $candidate = Arr::dot(require lang_path("$locale/validation.php"));
        $missing = array_diff_key($reference, $candidate);
        $this->assertEmpty($missing, "Missing in $locale: ".implode(', ', array_keys($missing)));
    }
}

Run it in CI, not on every request.

Incremental updates. After a Laravel minor upgrade introduces new validation rules, bump the dev dependency and re-run the publisher (or re-copy) — but only for files you actually use. Publishing everything on every upgrade creates noisy PRs where 40 locales change because one contributor fixed a typo in Afrikaans.

Trim what you ship. If you support three locales, don't vendor 126. Copy only what's in your allow-list and let the publisher handle the rest.

Gotchas and Tips

  • Locale code format is non-standard. pt_BR and zh_CN use underscores and script suffixes. Map them to BCP-47 (pt-BR) at the boundaries — <html lang>, Open Graph tags, Carbon::setLocale() — and keep the underscore form for lang/ paths and app()->setLocale().
  • There is no en locale. English is Laravel's built-in default (and the source/ directory). Don't try to publish en; you'll get an empty folder.
  • Don't edit inside vendor/. Any publisher re-run silently overwrites your edits. Put custom strings in lang/{locale}/custom.php and merge, or keep an explicit patch list under version control.
  • Missing keys fail silently. Laravel falls back to the key name, so an untranslated rule renders as literal validation.declined. If you see dotted keys in the UI, your language files predate the validator — bump and republish.
  • --no-dev breaks deploy-time publishing. Since it's a dev dependency, a production container built with composer install --no-dev has no vendor/laravel-lang/lang. Publish and commit during development; never rely on publishing at deploy time.
  • validation.php vs validation-inline.php. The inline variant omits attribute names; some Laravel versions no longer use it. Publishing one but not the other leaves gaps depending on which translator your validation path hits.
  • JSON keys are the English source string. Renaming copy in Blade ("Welcome" → "Welcome!") silently orphans the translation. Freeze user-facing strings before you hand them to translators.
  • Pluralization is locale-dependent. Russian and Polish need three pipe-separated segments in trans_choice where English needs two. Translators get this wrong; trans_choice on a two-segment Russian string produces grammatically wrong output.
  • RTL is your job. ar, he, fa, ur are translated but nothing sets dir="rtl" or flips Tailwind logical properties. The package gives you text, not layout.
  • Machine-translated strings are flagged in the repo. Treat auth emails, billing, and legal copy as drafts requiring human review — the release notes repeatedly show bulk machine-translation passes.
  • Major-version layout changes. v9 moved to source/ + locales/{locale}; v10 split keys per Laravel package. Pin the major version and read the upgrade notes before composer update, or your publish script will look in a directory that no longer exists.
  • Don't confuse it with php artisan lang:publish. Laravel's built-in command only scaffolds the English lang/ directory. The locale content comes from this package (and its publisher).
  • Debugging tip: wrap __() in a local-only helper that logs when the returned value equals the input key. It surfaces untranslated strings across an entire click-through far faster than eyeballing each screen.
  • Extension point: the useful customization surface is entirely in the arrays — validation.php attributes and custom, and your own lang/{locale}/*.php for domain vocabulary. There are no service-container bindings, events, or config keys to hook into; if you need programmatic behavior, the publisher package is the layer that provides it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib