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

Laminas I18N Laravel Package

laminas/laminas-i18n

laminas-i18n provides internationalization tools for PHP applications: translations and plurals, locale-aware number, currency, date and time formatting, plus validators and view helpers for country and phone data.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. Low-to-moderate, and narrower than the package description implies. The description advertises "filter and validate internationalized values," but v3.0.0 explicitly removed all filters, all validators, all view helpers, the MVC module, and TranslatorInterface/TranslatorAwareInterface (PRs #167, #170, #176, #175). What remains for a Laravel consumer is essentially three things: (1) a standalone Translator with loader/plural-rule machinery, (2) message collectors with PSR-6/PSR-16 caching, and (3) i18n value objects such as CountryCode, DefaultLocale, and TextDomain. That is a real but thin surface, and it overlaps heavily with Illuminate\Translation plus ext-intl.

The genuine capability gap worth evaluating is CLDR-based plural rules and text domains. Laravel's MessageSelector handles {0}, {1}, [2,*] style choice ranges but does not implement full CLDR cardinal/ordinal rules per locale. laminas-i18n does, and supports gettext/INI/PHP-array/TMX/XLIFF loaders with plural forms (including the INI plural-loading bug fixed in #212). If our roadmap includes locales with 3+ plural forms (pl, ru, ar, cs, ga), that is the only defensible reason to adopt this package inside a Laravel codebase.

Integration feasibility. Mechanically feasible, architecturally awkward. The translator is described as "mostly dependency free" apart from an ext-intl fallback for default-locale resolution, so it can be constructed by hand. But the shipped factories are marked @internal (PR #191), meaning the supported composition path — laminas-servicemanager — is not something we should bind our DI to. We would write our own Laravel service provider and factory, which is straightforward but signals we are off the library's paved road. Two interop seams need adapters: v3 dispatches PSR-14 events (PR #204), while Laravel's event dispatcher is not PSR-14-compliant, and v3 caching is PSR-6/PSR-16 — Laravel's Cache\Repository satisfies PSR-16 but not PSR-6, so a PSR-6 path requires symfony/cache.

Technical risk.

  • Description drift. The package metadata still sells validators/filters that no longer exist. Any estimate built from the README without reading the 3.0.0 notes will be wrong. If we need the removed filters/validators, we must confirm whether the split-out packages from RFC #145 actually shipped and are maintained — do not assume.
  • Bus factor. Release notes show one contributor (@gsteel) dominating nearly every substantive PR across 2.31–3.0.0. Contributor count per release is 1–8. Laminas is a post-Zend community with declining gravity.
  • Signal quality. 83 stars, a composite score of 31/100, and 0 reported dependents in the dataset. The zero is almost certainly a snapshot artifact (Laminas MVC apps consume this transitively via the metapackage), but it means we cannot lean on ecosystem evidence and must do our own due diligence.
  • Metadata anomaly. The dataset's latest release date is 2026-09-14, which is future-dated relative to this assessment. Treat as snapshot drift or a back-dated tag and verify against Packagist before pinning a constraint.
  • Silent misconfiguration. The README states a translator with no translations "will do nothing but just return the given message IDs." That is a silent-failure default: a misconfigured cache or loader path yields raw keys in production with no error. This must be paired with missing-key instrumentation.

Key questions.

  1. Which locales actually require >2 plural forms in the next 12 months? If none, the case for adoption largely evaporates.
  2. Do we need text domains, or is Laravel's flat lang/{locale}/{file}.php namespace (file.key) sufficient?
  3. Are the filters/validators removed in v3 available as separately maintained packages, and are they PHP 8.2+/8.3+ compatible?
  4. Does the licensing posture (BSD-3-Clause) require any attribution action in our distribution or NOTICE file?
  5. What is the verified current stable tag and its PHP constraint on Packagist, given the anomalous release date?

Integration Approach

Stack fit. Poor as a primary translation layer, acceptable as a narrow supplement. Laravel's Lang/trans/trans_choice, setLocale, middleware-driven locale negotiation, and lang/ publishing are deeply woven into Blade, validation messages, and package translations. Replacing that with a Laminas Translator buys us almost nothing and costs us the entire Laravel ergonomic surface. The correct posture is coexistence, not replacement: Laravel stays the system of record for UI strings; laminas-i18n is introduced only for the specific gap (CLDR pluralization and/or i18n value objects).

Migration path. There is no migration. That is the point. Frame this as an additive capability: a LaminasI18nServiceProvider registering a singleton translator with an explicit loader chain and cache, plus a thin application-owned port (e.g. PluralMessageFormatter) that the rest of the codebase depends on. No existing lang/ files move. If the pilot fails, deleting the provider and the port removes the dependency entirely — that reversibility is the main reason to accept the risk.

Compatibility. Pin to v3.x only; v2 and v3 are not source-compatible and the v2 documentation tree is frozen. Confirm PHP constraint against our platform (v2.31.0 dropped 8.1 and added 8.5 support, so v3 likely requires ≥8.2), and confirm ext-intl is present in every runtime image including queue workers and CI — the fallback-locale path depends on it. Laravel cache binding satisfies PSR-16 directly; if we want the PSR-6 collector path, we add symfony/cache and an adapter rather than pulling a second cache abstraction.

Sequencing.

  1. Week 1 — spike. Construct the translator manually in a tinker/artisan command against a real locale set. Verify plural output for ru, ar, pl versus Laravel's trans_choice. This single test decides go/no-go.
  2. Week 2 — isolation. Write the port interface and the provider; add a PSR-14 bridge or explicitly disable events if unused (most likely: disable — we do not need the dispatcher for translation loading).
  3. Week 3 — pilot. One surface only (e.g. notification/email templates across the 3+ plural locales). No user-facing web strings in phase one.
  4. Week 4 — hardening. Cache warm in the deploy pipeline, missing-key logging, and a documented escape hatch.
  5. Deferred. CountryCode — evaluate league/iso3166 or symfony/intl first, since adopting a second package for value objects multiplies surface area for no pluralization benefit.

Operational Impact

Maintenance. Low code volume, non-trivial cognitive overhead. We inherit a dependency whose supported composition model (servicemanager, @internal factories) is not ours, whose docs are split across two versioned trees, and whose maintainer concentration is high. Budget roughly a half-day per major release to review BC breaks — 3.0.0 shipped ~14 issues and 36 PRs of breaking change, which is not a "bump and forget" cadence. Add a Renovate/Dependabot rule that flags major bumps for human review rather than auto-merge.

Support. No commercial backing. Upstream support is GitHub issues only. Our internal support path is the port interface: any engineer debugging a translation bug should hit our adapter first, not the vendor. Document explicitly in the ADR that escalations beyond our adapter have no SLA.

Scaling. File-based message loading is per-request unless cached; the PSR-6/PSR-16 collectors (PRs #194, #195, #198) exist precisely for this. For our catalog size this is fine, but two mandates follow: (a) warm translation caches during deploy, not on first request, to avoid a cold-start thundering herd after every release; (b) key the cache by locale + text domain + file mtime so a bad deploy cannot serve stale strings indefinitely. Memory footprint is modest; the translator is not a scaling bottleneck.

Failure modes.

  • Silent no-op. Missing loader or empty catalog returns raw message IDs to users with no exception. Mitigation: assert non-empty catalog at boot in non-production, and emit a metric for untranslated-key passes.
  • Missing ext-intl. Default-locale fallback degrades. Mitigation: platform check in the health endpoint and a CI matrix assertion.
  • Cache poisoning / staleness. Wrong cache keys serve the wrong locale's strings — a correctness bug that surfaces as customer-visible language mixing. Mitigation: locale in the key, versioned namespace, flush on deploy.
  • Upstream major break. v3 already removed filters/validators/view helpers. A v4 could remove the translator's collector API. Mitigation: the port interface plus a single-file adapter bounds the blast radius to one class.
  • PSR-6/PSR-14 adapter drift. Laravel upgrades could break our bridges. Mitigation: only build the bridges we actually need; prefer PSR-16 and no event dispatcher.

Ramp-up. One engineer, two to three days to reach productive velocity, dominated by reading two doc trees and reconciling the stale package description against v3 reality — not by the API itself. The team's existing Laravel i18n knowledge does not transfer to Laminas loader/collector composition, so plan for a short internal write-up. Net recommendation: conditional approval, scoped to pluralization only, behind an application-owned interface, with a one-week kill criterion. If the plural-rules spike shows Laravel's trans_choice is adequate for our actual locale roadmap, decline and spend the integration budget elsewhere.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package