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.
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.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.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.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.user negotiation source and avoids re-deriving locale from headers on every request.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.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.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.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.config/localization.php, run php artisan config:clear. Locale-order changes that "don't take effect" are almost always config cache.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.withHeader('Accept-Language', ...) and assert the resolved locale before asserting translated output.__() 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.How can I help you explore Laravel packages today?