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

Laratext Laravel Package

edulazaro/laratext

Laratext lets Laravel apps define translations with both a key and readable text, keeping language files clear and stable. It auto-translates missing keys via OpenAI or Google Translate, plus offers a @text Blade directive, text() helper, and scan/update commands.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. Laratext is an idiomatic Laravel package: a service provider, a publishable config/texts.php, a global text() helper, a @text Blade directive, an Artisan command (laratext:scan), and a strategy-style translator layer (OpenAITranslator, ClaudeTranslator, GoogleTranslator behind a default_translator config key). That is the shape a Laravel team expects, and the translator interface is the part that matters most architecturally — it gives us a seam to swap in DeepL, a self-hosted model, or an internal MT gateway without touching call sites.

The core design bet is the text('key.name', 'Default text') signature: it keeps the key stable for lookups while keeping the source string readable in code and diffable in review. That is a genuine improvement over Laravel's __() dilemma (keys-only = unreadable files; text-only = brittle on any character change), and it is the reason to consider the package at all.

Everything else is a build-and-integrate exercise: the scan command, the auto-translation loop, and the JSON language-file writer.

Integration feasibility. Low ceremony on paper — composer require, publish the config, set API keys in .env, run laratext:scan --dry to see scope. The stated compatibility matrix (PHP 8.2–8.5 × Laravel 10–13, every combination in CI, plus a weekly scheduled run against new framework releases) is better than most packages of this size and materially reduces the "will it break on the next Laravel minor" risk.

Two integration hazards need to be validated in a spike rather than assumed:

  1. Global namespace collisions. text() is a very generic helper name, and @text a very generic directive name. Any existing app helper, any other package helper, and any future Laravel addition collide here. We need to confirm the package guards with function_exists() and that we are comfortable reserving that name permanently.
  2. Coexistence with __(). The README never states whether Laratext registers itself into Laravel's translator such that __('key.name') resolves the same JSON entries, or whether it is a parallel lookup path. If it is parallel, we have two translation systems in one codebase and a migration that touches every view and controller. This is the single most important unknown.

Technical risk.

  • Nondeterministic output in a deterministic artifact. Machine-translated strings become committed source files. Re-running with a different model or prompt produces different files, which means noisy diffs and unstable review. --resync exists precisely because this is expected; that is a tell.
  • No translation memory or human review workflow. There is a laratext:lock escape hatch and a --resync, but no concept of a reviewer, an approval state, or a glossary beyond a free-text context string. For any product with brand terms, legal copy, or regulated content, the review loop is ours to build.
  • Static scanner blind spots are silent. text("activity.{$this->type->value}") is never seen by the scanner, is never created, and — per the README's own admission — surfaces the raw key to end users with nothing warning us. This is a production-visible failure class, not a lint nit.
  • Destructive flags. --prune deletes keys from language files based on static reference analysis. Combined with the silent-interpolation problem, a prune run can delete a key that is used but is dynamically constructed. Git review is mandatory, not optional.
  • Data egress. Source strings and the context blob (which the README explicitly encourages you to fill with product description, register, and do-not-translate terms) are sent to OpenAI, Anthropic, or Google. That is a third-party data-processing review, and possibly a DPA, in any organization with compliance obligations.
  • Adoption signal is weak. 77 stars, 0 dependents, a composite score in the mid-20s, and a single maintainer. This is not disqualifying for an MIT-licensed, small-surface package — but it means we are the de facto QA for anything beyond the tested happy path.

Key questions to answer before committing.

  • Does Laratext hook into Laravel's translator, or is it a parallel resolution path? Can __() and text() read the same JSON files?
  • Does the scan write per-language atomically, or can an API failure mid-run leave a half-written lang/es.json?
  • What is the actual behavior on HTTP 429 / 500 from the provider — does it abort the run, skip the key, or write the source text as a fallback? The retries config implies the latter is configurable, but the failure semantics matter more than the retry count.
  • Are JSON language files the only supported format, or can it read/write nested PHP array files (lang/es/messages.php)?
  • Is there any batching/rate-limit control, or does a 5,000-key project fire requests as fast as the provider accepts them?
  • Can the translator interface be stubbed for offline/CI runs, so the scan is testable without spending tokens?
  • How does the plural path behave when the translator returns fewer forms than the target language requires — does it fail loudly or produce a one-form file that silently mis-selects?

Integration Approach

Stack fit. Best fit is a Laravel 10+ application with ad-hoc, inconsistent copy handling — strings inline in Blade, some __() calls, some hardcoded English — and a small number of target locales (say 2–5) where machine translation plus light human review is an acceptable quality bar. Poor fit for anything with an established TMS/vendor relationship, per-locale professional translators, or compliance obligations around copy (medical, financial, legal). Those teams should take the key+text convention as a good idea and skip the auto-translation half.

Migration path. Do not adopt the package and the convention simultaneously across the codebase. Sequence it:

  1. Spike branch. Install, publish config, point at a single locale, run laratext:scan --dry. The dry run is the value here: it inventories new keys, drifted source texts, and orphan keys and costs nothing. That output is our scope estimate and our cost model.
  2. Decide the coexistence question. Confirm __() interop. If it does not exist, plan for a translation-layer shim so existing calls keep resolving while new code uses text().
  3. Migrate incrementally by surface, not by grep. Convert one Blade layout/nav, one email template set, one feature area. The @text directive means view changes are mechanical; the risk is concentrated in dynamic keys, so audit each migrated surface for interpolation before and after.
  4. Pilot one locale end-to-end with human review of every string, then laratext:lock the keys that pass. That gives a reviewed baseline that future scans will not overwrite.
  5. Only then add locales. Each new locale multiplies spend and review load; add them one at a time.

Compatibility and sequencing concerns.

  • Secrets management. API keys go in the secret store, not in a committed .env or in the app server's environment for production. Scans should run from a CI job or a dedicated worker with the key injected, not from developer laptops with a shared key.
  • Format alignment. Confirm the JSON output path works with our lang/ layout and any existing files. If we already have lang/es.json, establish whether Laratext merges into it or replaces it, and test on a copy first.
  • Model pinning. The config hardcodes model identifiers (gpt-5.4-nano, claude-haiku-4-5). Those names have a shelf life. Treat the model string as a managed config value with an owner and a review date, or a provider deprecation will silently change translation quality across the whole product.
  • Version pinning. Pin to an exact version (composer require edulazaro/laratext:1.x.y) and upgrade deliberately. With one maintainer and near-zero dependents, an upstream regression will not be caught by anyone but us.
  • CI ordering. Run laratext:scan --dry as a non-blocking report initially, then as a gate once we trust the output. Never let --write run in CI unattended until we have a reviewed, locked baseline and a diff-review step.

Operational Impact

Maintenance. The package is small and the surface is well-tested, but the maintenance burden is unevenly distributed: the code is low-touch, the outputs are high-touch. Every scan run produces a content diff that someone must review, and that reviewer needs enough language competence to catch a wrong-gender noun or a placeholder that survived translation but moved. Budget for a translation owner, not just a package owner.

The MIT license and the clean translator interface mean the exit cost is low: forking or replacing the scan loop with an in-house Artisan command is a few days of work, not a quarter. That materially de-risks the 0-dependents signal. What we cannot cheaply replace is the accumulated reviewed translation corpus — so keep it in a format we control and back it up like any other data asset.

Support. Community-only. No SLA, no vendor. Issues land with the single maintainer. Internalize that: we are the escalation path, and we should be prepared to patch a fork. The changelog and release history are present and the test matrix is genuinely good, which raises confidence in the happy path but says nothing about our specific provider, model, or file layout.

Scaling. Cost and runtime scale as keys × locales × run frequency, and the multiplier that bites is retranslation, not initial translation:

  • A first full run on a few thousand keys across several locales is a real but one-time spend.
  • Drift detection is the recurring cost. If source_locale is misconfigured relative to config('app.locale'), the README's own example shows the entire project being retranslated in a single --write. That misconfiguration will happen to someone. Set source_locale explicitly on day one, even when it matches the default.
  • The footgun to document loudly: omitting --write does not save money. Only --dry avoids provider calls. Anyone assuming "no --write = preview" will burn budget in a loop.
  • Rate limits and timeouts: retries defaults to 3 and timeouts to 20–60s. On a large project a single scan can run for a long time; run it as a scheduled job with a log, not interactively. Watch for provider 429s at scale — batching behavior is unverified.
  • --resync should be a break-glass operation requiring sign-off, not a routine command. It ignores existing translations and retranslates everything.

Failure modes to instrument.

Failure Blast radius Mitigation
Interpolated key never scanned Raw key shown to users in one locale Style rule banning text() with interpolated keys; review checklist; a runtime fallback that logs missing keys
Provider outage mid-scan Partial or truncated language file Verify atomic writes; always run on a branch; diff before merge
--prune removes a live dynamic key Missing translation in production Never prune without a full git diff review and a staging smoke test
Provider deprecates the pinned model Silent quality shift across all locales Scheduled config review; diff a sample of --resync output against locked strings
Placeholder or plural form corrupted Broken UI strings, wrong plural selection Lock reviewed keys; spot-check :placeholder presence per locale after each write
Key leaked / expired Scan fails, no user-facing impact if scans are decoupled from deploys Keep the scan job out of the deploy path so translation failures never block a release

The last row is an architectural decision worth making explicitly: translation is a batch process, not a request-time dependency. If a provider is down, deploys must still ship with the last committed language files.

Ramp-up. Developer ramp is small — text() and @text are self-explanatory, and the key+text convention is learnable in a code review. The organizational ramp is the real work:

  • A written key-naming convention (nav.*, errors.*, emails.*) so keys double as context for the translator, as the README intends.
  • A maintained context string — product description, register, and a do-not-translate list — owned by someone and reviewed when the product changes.
  • A review workflow for machine output: who reads the Spanish, who signs off, when a key gets locked.
  • Runbook entries for the scan job: how to dry-run, how to interpret drift warnings, how to roll back a bad --write, and what to do when the provider 429s.

Realistically: one engineer-day for the spike and scope inventory, two to three days for the first surface migration plus config and secrets wiring, and then a recurring half-day per locale per release cycle for review. That recurring cost is the honest number to put in front of stakeholders — the package is cheap, the review is not.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin