symfony/translation
Symfony's Translation component gives you the tools to internationalize your application: translate messages using locale-aware catalogs, handle pluralization and ICU message formats, and load translations from PHP, XLIFF, YAML, JSON, or PO files. Use it standalone or with the full Symfony framework...
trans() API, the decision to enter a new market becomes a content/ops decision rather than an engineering one — a new locale is a catalog plus a config change, not a sprint. This is what lets you commit to launch dates for additional markets without re-litigating architecture each time.symfony/translation is already in the dependency tree of laravel/framework, so the question is not "adopt or not" but "where does our code call in." Default should be Laravel's __() / lang/ wrappers. Escalate to direct component use only for specific needs: XLIFF catalogs shared with a TMS, ICU message formatting for plurals/gender/currency, TranslatableMessage objects flowing through domain/queue layers, message domains for separating product vs. legal vs. notification copy, and catalogue diffing to compute translation completeness as a release gate.symfony/symfony monorepo, so stars, issue activity, and dependents all undercount. Treat this as tier-1 infrastructure and judge it on release cadence and LTS policy instead.Consider adopting (directly or via Laravel's wrappers) when:
str_replace pluralization breaks in Slavic and Arabic locales first, and finance-facing bugs are expensive.Look elsewhere, or stay with defaults, when:
lang/*.php and __() are sufficient and idiomatic; do not add an abstraction for its own sake.spatie/laravel-translatable or a dedicated content model; this component translates message IDs, not database entities, and forcing it to do so is a common and costly mistake.Adoption risks to price in: version coupling to PHP/Laravel upgrade cycles; the absence of a security policy or contribution guide in this split repo (they live in the monorepo, which slows third-party audit); and the 8.x return-type enforcement plus removals (TranslatableMessage::__toString(), CsvFileLoader::$escape) that make this a semver-major migration surface, not a drop-in.
To executives: This is free, MIT-licensed plumbing that determines how cheaply we enter new language markets. It is already inside our stack as a Laravel dependency, so the decision is about how we use it, not whether to buy it. Used well, it converts "launch in market X" from an engineering project into a content project with a predictable cost, and it lets us postpone or avoid five-figure annual translation-platform fees until volume actually justifies them. It also gives us an objective, automatable launch gate — percentage of user-facing strings translated per locale — instead of a subjective "the translations look done." The cost is an upgrade-cadence commitment: we track Symfony's LTS windows, which in practice sets when we move PHP and Laravel majors. Recommendation: adopt as standard infrastructure, no procurement needed, with a decision point to revisit paid tooling once we exceed roughly five locales or ten thousand strings.
To engineering: Use Laravel's __() and lang/ as the default surface; reach for the component directly only for XLIFF/TMS interop, ICU formatting, translatable domain objects, and catalogue diffing in CI. Never run two translation stacks on the same surface. Pin to an LTS branch, expect breaking changes at majors, and put locale propagation (queue, CLI, scheduled jobs) in the initial design rather than retrofitting it — that is where internationalization bugs actually come from. Budget a small, bounded spike to confirm the boundary before committing; this is low-risk, low-differentiation infrastructure and should not consume roadmap attention beyond that.
How can I help you explore Laravel packages today?