diff --git a/README.md b/README.md index e677138..1a71710 100644 --- a/README.md +++ b/README.md @@ -206,11 +206,6 @@ This way only the current translations are pushed to the window object and not a > Note: On local environment the cached views are getting cleared to keep translations fresh. -## Limitations - -The JSON file translations are not supported! Only the PHP array based key - value translations can be used. -Also, please pay attention to place and use the tranlations the way Laravel requires it. - ## Contribute If you found a bug or you have an idea connecting the package, feel free to open an issue. diff --git a/src/I18nServiceProvider.php b/src/I18nServiceProvider.php index 0d99212..927213f 100644 --- a/src/I18nServiceProvider.php +++ b/src/I18nServiceProvider.php @@ -66,17 +66,50 @@ protected function translations(): Collection ]; }); + $jsonTranslations = $this->jsonTranslations($path); + $packageTranslations = $this->packageTranslations(); return $translations->keys() ->merge($packageTranslations->keys()) + ->merge($jsonTranslations->keys()) ->unique() ->values() - ->mapWithKeys(function ($locale) use ($translations, $packageTranslations) { + ->mapWithKeys(function ($locale) use ($translations, $jsonTranslations, $packageTranslations) { + $locales = array_unique([ + $locale, + config('app.fallback_locale'), + ]); + + /* + * Laravel docs describe the following behavior: + * + * - Package translations may be overridden with app translations: + * https://laravel.com/docs/10.x/localization#overriding-package-language-files + * - Does a JSON translation file redefine a translation key used by a package or a + * PHP defined translation, the package defined or PHP defined tarnslation will be + * overridden: + * https://laravel.com/docs/10.x/localization#using-translation-strings-as-keys + * (Paragraph "Key / File conflicts") + */ + $prioritizedTranslations = [ + $packageTranslations, + $translations, + $jsonTranslations, + ]; + + $fullTranslations = collect(); + foreach ($prioritizedTranslations as $t) { + foreach ($locales as $l) { + if ($t->has($l)) { + $fullTranslations = $fullTranslations->replace($t->get($l)); + break; + } + } + } + return [ - $locale => $translations->has($locale) - ? $translations->get($locale)->merge($packageTranslations->get($locale)) - : $packageTranslations->get($locale)->merge($translations->get(config('app.fallback_locale'))), + $locale => $fullTranslations, ]; }); } @@ -109,6 +142,26 @@ protected function packageTranslations() }); } + /** + * Get the application json translation files. + * + * @param string $dir Path to the application active lang dir. + * @return \Illuminate\Support\Collection + */ + protected function jsonTranslations($dir) + { + return collect(File::glob($dir . '/*.json')) + ->mapWithKeys(function ($path) { + return [ + basename($path, '.json') => json_decode( + json: file_get_contents($path), + associative: true, + flags: JSON_THROW_ON_ERROR, + ), + ]; + }); + } + /** * Get the files of the given directory. * diff --git a/tests/I18nTest.php b/tests/I18nTest.php index fd20edc..d97bfb3 100644 --- a/tests/I18nTest.php +++ b/tests/I18nTest.php @@ -9,13 +9,16 @@ class I18nTest extends TestCase /** @test */ public function translations_can_be_printed_via_blade_directive() { - $this->get('/i18n/translations')->assertSee(json_encode(trans('auth')), false); + $this->get('/i18n/translations') + ->assertSee(json_encode(trans('auth')), false) + ->assertSee('"This is a JSON test":"This is a JSON test"', false); } /** @test */ public function translations_can_have_custom_key() { - $this->get('/i18n/custom-key')->assertSee("window['custom'] = ", false); + $this->get('/i18n/custom-key') + ->assertSee("window['custom'] = ", false); } /** @test */ @@ -24,11 +27,13 @@ public function translations_can_be_multilocale() App::setLocale('hu'); $this->get('/i18n/translations') ->assertSee(json_encode(trans('auth')), false) + ->assertSee('"This is a JSON test":"Ez egy JSON teszt"', false) ->assertSee('"i18n::":{"messages":{"test":"Teszt"}', false); App::setLocale('en'); $this->get('/i18n/translations') ->assertSee(json_encode(trans('auth')), false) + ->assertSee('"This is a JSON test":"This is a JSON test"', false) ->assertSee('"i18n::":{"messages":{"test":"Test"}', false); } @@ -38,6 +43,7 @@ public function translations_can_fallback_if_locale_does_not_exists() App::setLocale('fr'); $this->get('/i18n/translations') ->assertSee(json_encode(trans('auth')), false) + ->assertSee('"This is a JSON test":"This is a JSON test"', false) ->assertSee('"i18n::":{"messages":{"test":"Test"}', false); } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 2864537..0ef6fd4 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\View; use Pine\I18n\Tests\CreatesApplication; @@ -16,8 +17,16 @@ public function setUp(): void { parent::setUp(); + app()->setBasePath(__DIR__ . '/app'); + + // clean up default language dir that laravel publishes + File::deleteDirectory(base_path('/lang/en')); + $this->app['translator']->addNamespace('i18n', __DIR__.'/lang'); + Artisan::call('cache:clear'); + Artisan::call('view:clear'); + Artisan::call('lang:publish'); View::addNamespace('i18n', __DIR__.'/views'); @@ -26,4 +35,12 @@ public function setUp(): void return view("i18n::{$view}"); }); } + + public function tearDown(): void + { + parent::tearDown(); + + // clean up default language dir that laravel publishes + File::deleteDirectory(base_path('/lang/en')); + } } diff --git a/tests/app/lang/en.json b/tests/app/lang/en.json new file mode 100644 index 0000000..5ad22c2 --- /dev/null +++ b/tests/app/lang/en.json @@ -0,0 +1,3 @@ +{ + "This is a JSON test": "This is a JSON test" +} diff --git a/tests/app/lang/hu.json b/tests/app/lang/hu.json new file mode 100644 index 0000000..854875d --- /dev/null +++ b/tests/app/lang/hu.json @@ -0,0 +1,3 @@ +{ + "This is a JSON test": "Ez egy JSON teszt" +}