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

Translation Laravel Package

arbory/translation

Arbory Translation is a Laravel package for managing application translations from the database. It provides a practical workflow for editing, organizing, and maintaining multilingual strings, helping teams centralize localization and keep language content consistent across projects.

View on GitHub
Deep Wiki
Context7

Getting Started

This release is a patch-level 3.1.0 on top of a package whose job is to sit between your translation lookups (__(), trans(), @lang) and whichever backing store holds the strings, caching the resolved values so you aren't hitting the database or disk on every render.

Minimal setup

  1. Install and publish config:
    composer require <vendor>/<package>
    php artisan vendor:publish --tag=<package>-config
    
  2. Open the published config first — this is where the three things fixed in 3.1.0 live: the cache.store, the cache.ttl, and the tag/grouping behaviour. Read it before reading any source.
  3. If the package is DB-backed, run migrations and confirm the translation repository resolves (check the container binding in the service provider).
  4. Confirm your cache store supports tags if you intend to use them — redis, memcached, and array do; file and database do not.

First use case

The entry-level win is: put a cached lookup in front of a DB-backed translation table so a page with 200 strings doesn't issue 200 queries. Configure the store, point the package at your translations table, load a page, and verify with DB::listen() or Telescope that the second request produces zero translation queries.

Where to look first

  • The config file — for ttl, store, and tag settings.
  • The cache repository / resolver class — to see how keys are built.
  • The flush command or event listener — this is what 3.1.0 repaired, so understand what triggers invalidation before you rely on it.

Implementation Patterns

Flush on write, read through cache

The core workflow is write-through invalidation: whenever a translation row is created, updated, or deleted, flush the affected cache entry (or tag). 3.1.0 fixes the flushing path, so if you previously worked around broken invalidation with manual Cache::forget() calls, custom versioned key prefixes, or a nightly cache:clear, you can delete those workarounds now.

// Typical pattern: observer or model event
protected static function booted(): void
{
    static::saved(fn ($t) => TranslationCache::flush($t->locale, $t->group));
    static::deleted(fn ($t) => TranslationCache::flush($t->locale, $t->group));
}

Tag-scoped invalidation

Use tags to invalidate by locale or by translation group rather than nuking everything:

Cache::tags(['translations', "locale:{$locale}"])->flush();

Because 3.1.0 fixes taggable-store detection, this is now safe to write unconditionally — the package should degrade to a plain flush on stores without tag support instead of throwing or silently no-oping. Verify the behaviour on your configured store rather than assuming.

TTL tuning by content class

Split your keys into two mental buckets:

  • UI chrome / static strings — long TTL or null (forever), flushed explicitly on write. These rarely change and are the bulk of the benefit.
  • Dynamic / CMS-edited content — short TTL (60–300s) as a safety net, plus explicit flush on write.

Warm the cache on deploy

Add a cache-warm step to your deploy pipeline so the first request after a release isn't a cold-cache stampede. If the package exposes an artisan warm command, call it after migrate; otherwise iterate locales and groups and prime them.

Locale and fallback handling

Cache keys must include the resolved locale — otherwise a request in fr can poison the cache for en. If you support a fallback locale, make sure the fallback resolution happens before the cache write, or you'll cache the fallback string under the primary locale key.

Testing

Bind array as the cache store in phpunit.xml so tests exercise the real code path without Redis. Assert both the hit path (query count stays at zero) and the invalidation path (write a translation, assert the old value is gone).

Gotchas and Tips

TTL units changed meaningfully — audit your config after upgrading. The 3.1.0 fix corrects TTL unit handling. If you had previously compensated for the bug by multiplying values (e.g. storing 3600 * 60 because a "minutes" value was being read as seconds, or vice versa), that compensation is now wrong. Re-read the config docblock, set a value, and assert the expiry empirically with Cache::flush() + sleep() rather than trusting the comment.

Taggable detection changes behaviour on non-taggable stores. Previously you may have been getting an exception, a silent no-op, or over-broad flushing on file/database cache drivers. After 3.1.0 the detection is corrected, so flush granularity on those stores may change. If you run file cache in local dev and Redis in production, test invalidation in both — the fix is precisely in the branch that differs between them.

Drop your flushing workarounds. Anything you added to compensate for the broken flush — a cache.version key bumped on save, a queue job that clears the whole store, a cron — is now redundant and may actively conflict (e.g. double-flushing, or a version bump that orphans old keys until TTL). Remove them deliberately rather than leaving them as belt-and-braces; orphaned keys will quietly consume memory.

Deprecation warnings fixed — the package is clean on modern PHP/Laravel, but if you've subclassed the cache repository or overridden methods to suppress warnings, re-check those overrides against the new signatures. Your subclass is the thing most likely to still emit deprecations.

Debugging a suspected cache miss

  • Dump the store and key: the key almost always encodes locale + group + a hash of the source string. Mismatched key construction is the #1 cause of "cache isn't working."
  • Cache::tags([...])->flush() on a non-taggable store is the #2 cause.
  • Confirm the package is actually bound — a stray config('app.debug') branch sometimes bypasses caching in local environments, which makes it look broken in dev and fine in prod (or the reverse).

Extension points worth knowing

  • Override the cache store binding in a service provider rather than editing the config if you need per-environment stores.
  • The repository/resolver is usually swappable via container binding — useful for injecting a decorator that logs hits and misses in staging.
  • Model events on the translation model are the natural invalidation hook; if your translations are edited through a CMS rather than Eloquent, fire the equivalent flush manually from that write path, otherwise you're relying on TTL alone.

Upgrade risk assessment: low. No breaking API changes indicated; the risk is confined to behaviour changes where you had compensated for the bugs. Diff your config against the published stub and re-test invalidation on the cache driver you actually deploy with.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource