fix(i18n): include namespace in cache key/query, clean up singleton, test isolation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
boban 2026-05-16 01:27:10 +02:00
parent ce9e53d206
commit b121520ed6
3 changed files with 12 additions and 8 deletions

View File

@ -10,7 +10,8 @@ class DatabaseTranslationLoader implements Loader
{ {
public function load($locale, $group, $namespace = null): array public function load($locale, $group, $namespace = null): array
{ {
$cacheKey = "lang:{$locale}:{$group}"; $ns = $namespace ?? '*';
$cacheKey = "lang:{$ns}:{$locale}:{$group}";
$cached = Redis::hGetAll($cacheKey); $cached = Redis::hGetAll($cacheKey);
if (! empty($cached)) { if (! empty($cached)) {
@ -19,6 +20,7 @@ class DatabaseTranslationLoader implements Loader
$translations = Translation::where('locale', $locale) $translations = Translation::where('locale', $locale)
->where('group', $group) ->where('group', $group)
->where('namespace', $namespace ?? '*')
->pluck('value', 'key') ->pluck('value', 'key')
->toArray(); ->toArray();
@ -31,7 +33,6 @@ class DatabaseTranslationLoader implements Loader
} }
public function addNamespace($namespace, $hint): void {} public function addNamespace($namespace, $hint): void {}
public function addPath($path): void {}
public function addJsonPath($path): void {} public function addJsonPath($path): void {}
public function namespaces(): array public function namespaces(): array

View File

@ -15,18 +15,17 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
$this->app->singleton('translation.loader', function ($app) { $this->app->singleton('translation.loader', fn() => new DatabaseTranslationLoader);
return new DatabaseTranslationLoader;
});
// TranslationServiceProvider is deferred and re-binds translation.loader // TranslationServiceProvider is deferred and re-binds translation.loader
// when it resolves. We extend 'translator' to swap in our DB loader after // when it resolves. We extend 'translator' to swap in our DB loader after
// the translator instance is created. // the translator instance is created.
$this->app->extend('translator', function ($translator, $app) { $this->app->extend('translator', function ($translator, $app) {
$loader = new DatabaseTranslationLoader; $loader = new DatabaseTranslationLoader;
$reflection = new \ReflectionProperty($translator, 'loader'); // Keep the singleton up-to-date so app('translation.loader') returns ours.
$reflection->setAccessible(true); $app->instance('translation.loader', $loader);
$reflection->setValue($translator, $loader); $prop = new \ReflectionProperty($translator, 'loader');
$prop->setValue($translator, $loader);
return $translator; return $translator;
}); });

View File

@ -5,10 +5,14 @@ use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
beforeEach(function () { beforeEach(function () {
Redis::del('lang:*:de:auth');
Redis::del('lang:de:auth'); Redis::del('lang:de:auth');
app('translator')->setLoaded([]);
}); });
afterEach(function () { afterEach(function () {
app('translator')->setLoaded([]);
Redis::del('lang:*:de:auth');
Redis::del('lang:de:auth'); Redis::del('lang:de:auth');
}); });