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

Localization Laravel Package

atannex/localization

A Laravel localization package that simplifies multilingual apps: manage translations, switch locales, and serve language-aware routes with lightweight helpers and sensible defaults.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install & publish. composer require atannex/localization, then publish the config (php artisan vendor:publish --tag=localization-config) and any migrations for the fallback/translation tables. Register the package's middleware in your HTTP kernel or bootstrap file — locale negotiation typically needs to run early in the global stack, before route resolution.
  • Look first at config/localization.php. This is the control surface: the default and fallback locales, the supported-locale subset of the ~110-locale catalog, the negotiation driver order, and the URL strategy (prefix vs. domain). Read the catalog once — you rarely need all 110, and trimming the supported set to your actual markets keeps switchers, hreflang, and coverage reports honest.
  • Fastest first use case: URL prefix + language switcher. Configure en, es, fr as supported, wrap a small route group in the localized-prefix middleware, drop the language-switcher component into your layout, and verify that hitting /es/... renders Spanish, that the switcher preserves the current path, and that Accept-Language fallback works when no prefix is present. That single loop exercises negotiation, URL generation, and the switcher at once — the three things most apps need on day one.

Implementation Patterns

  • Negotiation as an ordered chain. The package composes sources (URL prefix, domain, query, authenticated user, session, cookie, Accept-Language, geo). Treat the order in config as your product decision: URL should usually win (it's explicit and shareable), then the persisted user/session preference, then cookie/session, then Accept-Language, then geo as a weak default. Don't rely on geo silently — it surprises users on VPNs and in border regions.
  • Persist the choice, not just the redirect. When a user switches languages, store the preference against the user (or a long-lived cookie) so subsequent visits and authenticated requests resolve consistently. This pairs naturally with the user negotiation source and avoids re-deriving locale from headers on every request.
  • Localized routes and links. Generate links through the package's route helpers rather than concatenating prefixes, so the URL strategy (prefix/domain) stays swappable. hreflang tags should be emitted from the same resolution logic — one canonical per locale — so SEO output can't drift from what the router actually serves.
  • Translatable attributes on JSON columns. Mark model attributes translatable (e.g. title, slug, body) and let the package read/write per-locale values into a JSON column. The driver-aware JSON scopes are the real leverage: query with whereTranslation('title', $q)-style scopes and get correct behavior across MySQL, PostgreSQL, and SQLite instead of hand-rolling JSON_EXTRACT per driver. Use this for content models; keep operational fields (status, price) untranslated.
  • Locale-aware formatting. Route dates, numbers, and currency through the package's formatter rather than number_format/Carbon::format. This is where localization bugs hide — decimal separators, currency placement, and date ordering differ per locale, and centralizing it means a locale change is a config change, not a grep.
  • Queue locale propagation. Long-running or deferred work (mail, PDFs, notifications) must render in the requester's locale, not the worker's default. Capture the locale when dispatching and let the package restore it inside the job. Apply this to any job that formats user-facing output; jobs that only touch data don't need it.
  • Missing-key tracking + coverage in the dev loop. Run with missing-key tracking enabled in local/staging, then use the coverage command in CI or a pre-release check to see which locales are under-translated. The export/import commands are the bridge to translators: export keys, hand off, import back, re-run coverage. This is the workflow that keeps translation debt from accumulating silently.
  • Validation rules for localized input. When a form accepts per-locale content, the package's validation rules let you require translations for specific locales (e.g. required in the default locale, optional elsewhere) without writing bespoke rules.
  • Filament plugin for admin surfaces. If you use Filament, the plugin gives locale-aware form fields and switching in the panel — the cheapest way to let editors manage translations without a separate admin build.

Gotchas and Tips

  • JSON column behavior varies by driver. The scopes paper over most differences, but case sensitivity, collation, and how null/missing keys are treated still differ between MySQL, PostgreSQL, and SQLite. Test the same query on your production driver — passing under SQLite in tests does not guarantee MySQL parity.
  • Route caching and localized prefixes. After changing supported locales or URL strategy, php artisan route:clear / re-cache. Stale route caches are the most common cause of "the switcher generates the wrong URL."
  • Accept-Language quality values and ordering. Header parsing must respect q= weights, not just the first tag. Also decide explicitly what happens when the header requests an unsupported locale — fall back to default, don't error.
  • Queue propagation is not universal. Only jobs that opt into the locale middleware get it. Audit notification/mailable dispatch paths; a missing capture shows up as English emails to Spanish users, which is easy to miss in testing.
  • Config cache. After publishing or editing config/localization.php, run php artisan config:clear. Locale-order changes that "don't take effect" are almost always config cache.
  • Missing-key tracking has a cost. It writes as it goes — keep it off in production, or point it at a low-traffic store. Treat it as a dev/staging instrument.
  • hreflang and canonical must agree. Emit x-default deliberately, and make sure the canonical URL for a page matches the locale the router actually serves — mismatches are an SEO penalty, not a cosmetic bug.
  • Testing. Set the locale explicitly in tests and assert negotiation separately from rendering: one test per source (prefix, header, user, cookie), plus a priority-order test. Use withHeader('Accept-Language', ...) and assert the resolved locale before asserting translated output.
  • Extension points to know. The locale repository (add custom locales or metadata), the negotiation drivers (add a source like a tenant's default locale), and the formatter (override currency/date output) are the three places to reach when the defaults don't fit. Check these before forking behavior into your app.
  • Migration reality. If you're coming from hardcoded __() strings, the package is a complement, not a replacement — decide per domain whether content lives in translation files or translatable model attributes, and don't mix both for the same field.
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.
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
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle