diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 88af8977..ea04697c 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -249,6 +249,18 @@ class HandleInertiaRequests extends Middleware * App strings use English text as the translation key, so "en" ships no * messages — the key itself is the fallback. * + * Asked of the framework's own loader rather than read out of + * lang/{locale}.json directly, so that a package which registers its + * catalogue with loadJsonTranslationsFrom() reaches the frontend too. + * Reading the file worked for as long as every translatable string + * belonged to this repository; the companion packages own screens of + * their own, and theirs were rendering in English in every language + * because their catalogue never got this far. + * + * Precedence comes from the loader and is the useful way round: an + * installation's own lang/{locale}.json is merged last and therefore + * wins, so a package string can be overridden locally. + * * @return array */ protected function translations(string $locale): array @@ -257,13 +269,7 @@ class HandleInertiaRequests extends Middleware return []; } - $path = lang_path("{$locale}.json"); - - if (! is_file($path)) { - return []; - } - /** @var array */ - return json_decode((string) file_get_contents($path), true, flags: JSON_THROW_ON_ERROR); + return app('translator')->getLoader()->load($locale, '*', '*'); } } diff --git a/tests/Feature/Platform/PackageTranslationsTest.php b/tests/Feature/Platform/PackageTranslationsTest.php new file mode 100644 index 00000000..73bde99f --- /dev/null +++ b/tests/Feature/Platform/PackageTranslationsTest.php @@ -0,0 +1,76 @@ +set(Setting::EnabledLocales, ['en', 'es']); + + $this->admin = User::factory()->create(['locale' => 'es']); + + $this->packageLang = sys_get_temp_dir().'/package-lang-'.bin2hex(random_bytes(6)); + File::makeDirectory($this->packageLang); +}); + +afterEach(function () { + File::deleteDirectory($this->packageLang); +}); + +function packageCatalogue(string $dir, array $entries): void +{ + File::put($dir.'/es.json', json_encode($entries, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); + + app('translator')->getLoader()->addJsonPath($dir); +} + +function sharedTranslations(User $user): array +{ + return test()->actingAs($user)->get('/dashboard')->viewData('page')['props']['translations']; +} + +test('a package catalogue reaches the frontend', function () { + packageCatalogue($this->packageLang, ['Attribution settings saved.' => 'Ajustes de atribución guardados.']); + + expect(sharedTranslations($this->admin)) + ->toHaveKey('Attribution settings saved.', 'Ajustes de atribución guardados.'); +}); + +test('the application keeps its own strings', function () { + packageCatalogue($this->packageLang, ['Something from a package' => 'Algo de un paquete']); + + $translations = sharedTranslations($this->admin); + + expect($translations)->toHaveKey('Something from a package') + // Any string this repository owns, still translated as before. + ->and($translations['Dashboard'] ?? null)->toBe('Panel de control'); +}); + +// The useful way round: an installation can override a package's wording +// in its own catalogue without editing the package. +test('the installation wins when both define the same key', function () { + packageCatalogue($this->packageLang, ['Dashboard' => 'From the package']); + + expect(sharedTranslations($this->admin)['Dashboard'])->toBe('Panel de control'); +}); + +// English is the key, so there is nothing to send and nothing to merge. +test('english still ships no catalogue at all', function () { + packageCatalogue($this->packageLang, ['Attribution' => 'Should never be sent']); + + $english = User::factory()->create(['locale' => 'en']); + + expect(sharedTranslations($english))->toBe([]); +});