Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature namespaces #178

Closed
wants to merge 41 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
be19111
added .idea folder into .gitignore
Jul 19, 2024
57f6f6b
added .phpunit.cache folder into .gitignore
Jul 19, 2024
04024e7
phpunit.xml.dist schema updated
Jul 19, 2024
caf72c8
code style
Jul 19, 2024
188967c
PHPUnit replaced by Pest
Jul 19, 2024
6a62ee8
Tests refactoring
Jul 19, 2024
cfd30e9
PHP 8.0 compatibility
Jul 19, 2024
450db0d
github test runner fixed
Jul 19, 2024
9e4153b
PHP CS Fixer added and reconfigured
Jul 19, 2024
2e60a98
PHP CS Fixer configuration rules fix
Jul 19, 2024
83e3070
PHP CS Fixer configuration rules fixed
Jul 19, 2024
df9b024
Update composer.json
Jul 22, 2024
a3d8682
Update composer.json and run-tests.yml
Jul 22, 2024
394e2af
Revert "Update composer.json and run-tests.yml"
Jul 22, 2024
a5d5cc2
Update composer.json
Jul 22, 2024
ee48bd4
removed unused migration
Jul 22, 2024
5cce48e
removed constants and comments in test files
Jul 22, 2024
e03d396
removed DOCBlocks form Pest.php
Jul 22, 2024
9c5f657
removed declare(strict_types=1);
Jul 22, 2024
0fa8621
Fix styling
aon4o Jul 22, 2024
3b436f8
package ServiceProvider updated with spatie/laravel-package-tools
aon4o Jul 22, 2024
42f1b7f
updated README.md
aon4o Jul 22, 2024
76e2d5b
Fix Laravel 8 compatibility
aon4o Jul 22, 2024
b077803
grammar fix README.md
aon4o Jul 22, 2024
66f23fa
Namespaced translations feature implemented
aon4o Jul 23, 2024
c8d8544
updated CHANGELOG.md
aon4o Jul 23, 2024
8317a9c
tests fixed
aon4o Jul 23, 2024
6a52598
backwards compatibility for migrations fixed
aon4o Jul 23, 2024
e9fcb51
backwards compatibility for migrations fixed
aon4o Jul 23, 2024
263cd2e
LanguageLine caching updated
aon4o Jul 23, 2024
e0c07e5
Added tests for namespaced translations
aon4o Jul 23, 2024
57e03e4
Translation loading fix
aon4o Jul 25, 2024
a43c7b5
Fix styling
aon4o Jul 25, 2024
13f0c83
Revert "Fix styling"
aon4o Jul 25, 2024
d8025b4
Revert "Translation loading fix"
aon4o Jul 25, 2024
9239886
Translation loading fix
aon4o Jul 25, 2024
dc62cbb
Fix styling
aon4o Jul 25, 2024
fd3d116
$this->app['config'] -> config()
aon4o Aug 26, 2024
f0ced13
Merge branch 'main' into main
aon4o Dec 9, 2024
dc7312f
Merge branch 'spatie:main' into main
aon4o Dec 13, 2024
162bd63
Merge pull request #1 from aon4o/feature-namespaces
aon4o Dec 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to `laravel-translation-loader` will be documented in this file

[//]: # (TODO: Update version when decided)

## 3.0.0 - 2024-07-23

- added support for namespaced database translations

## 2.7.0 - 2020-12-04

- add support for php 8.0
Expand Down Expand Up @@ -41,7 +47,9 @@ All notable changes to `laravel-translation-loader` will be documented in this f
- add support for Laravel 5.8

## 2.2.3 - 2019-02-01

``

- use Arr:: and Str:: functions

## 2.2.2 - 2018-10-25
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ This is the contents of the published config file:
```php
return [

/*
* The name of the table in which the language lines are stored.
*/
'table_name' => 'language_lines',

/*
* Language lines will be fetched by these loaders. You can put any class here that implements
* the Spatie\TranslationLoader\TranslationLoaders\TranslationLoader-interface.
Expand All @@ -88,7 +93,7 @@ return [
'model' => Spatie\TranslationLoader\LanguageLine::class,

/*
* This is the translation manager that overrides the default Laravel `translation.loader`
* This is the translation manager which overrides the default Laravel `translation.loader`
*/
'translation_manager' => Spatie\TranslationLoader\TranslationLoaderManager::class,

Expand All @@ -106,9 +111,10 @@ the `Spatie\TranslationLoader\LanguageLine`-model:
use Spatie\TranslationLoader\LanguageLine;

LanguageLine::create([
'group' => 'validation',
'key' => 'required',
'text' => ['en' => 'This is a required field', 'nl' => 'Dit is een verplicht veld'],
'namespace' => '*',
'group' => 'validation',
'key' => 'required',
'text' => ['en' => 'This is a required field', 'nl' => 'Dit is een verplicht veld'],
]);
```

Expand Down Expand Up @@ -145,7 +151,7 @@ interface TranslationLoader
/*
* Returns all translations for the given locale and group.
*/
public function loadTranslations(string $locale, string $group): array;
public function loadTranslations(string $locale, string $group, string|null $namespace = null): array;
}
```

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
],
"require": {
"php": "^8.0",
"doctrine/dbal": "^3.0",
"illuminate/translation": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"spatie/laravel-package-tools": "^1.12"
},
Expand Down
5 changes: 5 additions & 0 deletions config/translation-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

return [

/*
* The name of the table in which the language lines are stored.
*/
'table_name' => 'language_lines',

/*
* Language lines will be fetched by these loaders. You can put any class here that implements
* the Spatie\TranslationLoader\TranslationLoaders\TranslationLoader-interface.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table(config('translation-loader.table_name'), function (Blueprint $table) {
$table->string('namespace')->default('*')->after('id');
$table->string('group')->default('*')->change();
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table(config('translation-loader.table_name'), function (Blueprint $table) {
$table->dropColumn('namespace');
$table->string('group')->change();
});
}
};
4 changes: 2 additions & 2 deletions database/migrations/create_language_lines_table.php.stub
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ return new class extends Migration
*/
public function up(): void
{
Schema::create('language_lines', function (Blueprint $table) {
Schema::create(config('translation-loader.table_name'), function (Blueprint $table) {
$table->id();
$table->string('group')->index();
$table->string('key');
Expand All @@ -29,6 +29,6 @@ return new class extends Migration
*/
public function down(): void
{
Schema::dropIfExists('language_lines');
Schema::dropIfExists(config('translation-loader.table_name'));
}
};
31 changes: 26 additions & 5 deletions src/LanguageLine.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use PDOException;

class LanguageLine extends Model
{
Expand Down Expand Up @@ -35,10 +38,28 @@ public static function boot(): void
static::deleted($flushGroupCache);
}

public static function getTranslationsForGroup(string $locale, string $group): array
public static function getTranslationsForGroup(string $locale, string $group, string|null $namespace = null): array
{
return Cache::rememberForever(static::getCacheKey($group, $locale), function () use ($group, $locale) {
// When the app uses laravel-sail the package breaks every artisan command ran outside sail context.
// That's beacuse artisan starts an app and registers all service providers,
// but the cache store and/or database is unavailable beacause the hostname
// (e.g. redis/mysql) is unresolvable.
try {
DB::connection()->getPdo();
Cache::get('laravel-translation-loader');
} catch (PDOException $exception) {
Log::error('laravel-translation-loader: Could not connect to the database, falling back to file translations');

return [];
} catch (RedisException $exception) {
Log::error('laravel-translation-loader: Could not connect to the cache store, falling back to file translations');

return [];
}

return Cache::rememberForever(static::getCacheKey($namespace, $group, $locale), function () use ($namespace, $group, $locale) {
return static::query()
->where('namespace', $namespace)
->where('group', $group)
->get()
->reduce(function ($lines, self $languageLine) use ($locale, $group) {
Expand All @@ -57,9 +78,9 @@ public static function getTranslationsForGroup(string $locale, string $group): a
});
}

public static function getCacheKey(string $group, string $locale): string
public static function getCacheKey(string $namespace, string $group, string $locale): string
{
return "spatie.translation-loader.{$group}.{$locale}";
return "spatie.translation-loader.$namespace.$group.$locale";
}

public function getTranslation(string $locale): string|null
Expand All @@ -83,7 +104,7 @@ public function setTranslation(string $locale, string $value): static
public function flushGroupCache(): void
{
foreach ($this->getTranslatedLocales() as $locale) {
Cache::forget(static::getCacheKey($this->group, $locale));
Cache::forget(static::getCacheKey($this->namespace, $this->group, $locale));
}
}

Expand Down
5 changes: 1 addition & 4 deletions src/TranslationLoaderManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,12 @@ public function load($locale, $group, $namespace = null): array
try {
$fileTranslations = parent::load($locale, $group, $namespace);

if (! is_null($namespace) && $namespace !== '*') {
return $fileTranslations;
}

$loaderTranslations = $this->getTranslationsForTranslationLoaders($locale, $group, $namespace);

return array_replace_recursive($fileTranslations, $loaderTranslations);
} catch (QueryException $exception) {
$modelClass = config('translation-loader.model');

$model = new $modelClass();

if (is_a($model, LanguageLine::class) && ! Schema::hasTable($model->getTable())) {
Expand Down
4 changes: 2 additions & 2 deletions src/TranslationLoaders/Db.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

class Db implements TranslationLoader
{
public function loadTranslations(string $locale, string $group): array
public function loadTranslations(string $locale, string $group, string|null $namespace = null): array
{
$model = $this->getConfiguredModelClass();

return $model::getTranslationsForGroup($locale, $group);
return $model::getTranslationsForGroup($locale, $group, $namespace);
}

protected function getConfiguredModelClass(): string
Expand Down
3 changes: 2 additions & 1 deletion src/TranslationLoaders/TranslationLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ interface TranslationLoader
*
* @param string $locale
* @param string $group
* @param string|null $namespace
*
* @return array
*/
public function loadTranslations(string $locale, string $group): array;
public function loadTranslations(string $locale, string $group, string|null $namespace = null): array;
}
5 changes: 4 additions & 1 deletion src/TranslationServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ public function configurePackage(Package $package): void
$package
->name('laravel-translation-loader')
->hasConfigFile()
->hasMigrations('create_language_lines_table');
->hasMigrations(
'create_language_lines_table',
'alter_language_lines_table_add_column_namespace'
);

$this->registerLoader();
$this->registerTranslator();
Expand Down
6 changes: 3 additions & 3 deletions tests/Feature/DummyManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
});

it('can translate using dummy manager using db', function () {
createLanguageLine('file', 'key', ['en' => 'en value from db']);
createLanguageLine('*', 'file', 'key', ['en' => 'en value from db']);
expect(trans('file.key'))->toEqual('en value from db');
});

it('can translate using dummy manager using file with incomplete db', function () {
createLanguageLine('file', 'key', ['nl' => 'nl value from db']);
createLanguageLine('*', 'file', 'key', ['nl' => 'nl value from db']);
expect(trans('file.key'))->toEqual('en value');
});

it('can translate using dummy manager using empty translation in db', function () {
createLanguageLine('file', 'key', ['en' => '']);
createLanguageLine('*', 'file', 'key', ['en' => '']);

// Some versions of Laravel changed the behaviour of what an empty "" translation value returns: the key name or an empty value
// @see https://github.com/laravel/framework/issues/34218
Expand Down
8 changes: 4 additions & 4 deletions tests/Feature/JsonTransTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
->and(__($this->term2))->toEqual($this->term2Nl);
});

test('by default it will prefer a db translation over a file translation', function () {
createLanguageLine('*', $this->term1, ['en' => $this->term1EnDb]);
createLanguageLine('*', $this->term2, ['en' => $this->term2EnDb]);
it('it will prefer a db translation over a file translation by default', function () {
createLanguageLine('*', '*', $this->term1, ['en' => $this->term1EnDb]);
createLanguageLine('*', '*', $this->term2, ['en' => $this->term2EnDb]);

expect(__($this->term1))->toEqual($this->term1EnDb)
->and(__($this->term2))->toEqual($this->term2EnDb);
});

it('will default to fallback if locale is missing', function () {
app()->setLocale('de');
createLanguageLine('*', $this->term1, ['en' => $this->term1EnDb]);
createLanguageLine('*', '*', $this->term1, ['en' => $this->term1EnDb]);

expect(__($this->term1))->toEqual($this->term1EnDb);
});
10 changes: 5 additions & 5 deletions tests/Feature/LanguageLineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
use Spatie\TranslationLoader\LanguageLine;

it('can get a translation', function () {
$languageLine = createLanguageLine('group', 'new', ['en' => 'english', 'nl' => 'nederlands']);
$languageLine = createLanguageLine('*', 'group', 'new', ['en' => 'english', 'nl' => 'nederlands']);

expect($languageLine->getTranslation('en'))->toEqual('english')
->and($languageLine->getTranslation('nl'))->toEqual('nederlands');
});

it('can set a translation', function () {
$languageLine = createLanguageLine('group', 'new', ['en' => 'english']);
$languageLine = createLanguageLine('*', 'group', 'new', ['en' => 'english']);

$languageLine->setTranslation('nl', 'nederlands');

Expand All @@ -27,11 +27,11 @@
});

it('doesnt show error when getting nonexistent translation', function () {
$languageLine = createLanguageLine('group', 'new', ['nl' => 'nederlands']);
$languageLine = createLanguageLine('*', 'group', 'new', ['nl' => 'nederlands']);
expect($languageLine->getTranslation('en'))->toBeNull();
});

test('get fallback locale if doesnt exists', function () {
$languageLine = createLanguageLine('group', 'new', ['en' => 'English']);
it('will get a fallback locale if doesnt exists', function () {
$languageLine = createLanguageLine('*', 'group', 'new', ['en' => 'English']);
expect($languageLine->getTranslation('es'))->toEqual('English');
});
32 changes: 20 additions & 12 deletions tests/Feature/TransTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,52 @@
->and(trans('file.404.message'))->toEqual('Deze pagina bestaat niet');
});

test('by default it will prefer a db translation over a file translation', function () {
createLanguageLine('file', 'key', ['en' => 'en value from db']);
createLanguageLine('file', '404.title', ['en' => 'page not found from db']);
it('it will prefer a db translation over a file translation by default', function () {
createLanguageLine('*', 'file', 'key', ['en' => 'en value from db']);
createLanguageLine('*', 'file', '404.title', ['en' => 'page not found from db']);
createLanguageLine('validation', 'string', 'required', ['en' => 'The filed is required']);

expect(trans('file.key'))->toEqual('en value from db')
->and(trans('file.404.title'))->toEqual('page not found from db')
->and(trans('file.404.message'))->toEqual('This page does not exists');
->and(trans('file.404.message'))->toEqual('This page does not exists')
->and(trans('validation::string.required'))->toEqual('The filed is required');
});

it('will return array if the given translation is nested', function () {
foreach (Arr::dot($this->nested) as $key => $text) {
createLanguageLine('nested', $key, ['en' => $text]);
createLanguageLine('*', 'nested', $key, ['en' => $text]);
createLanguageLine('namespace', 'nested', $key, ['en' => $text]);
}

expect(trans('nested.bool'))->toEqualCanonicalizing($this->nested['bool'], '$canonicalize = true', $delta = 0.0, $maxDepth = 10, $canonicalize = true);
expect(trans('nested.bool'))->toEqualCanonicalizing($this->nested['bool'], '$canonicalize = true')
->and(trans('namespace::nested.bool'))->toEqualCanonicalizing($this->nested['bool'], '$canonicalize = true');
});

it('will return the translation string if max nested level is reached', function () {
foreach (Arr::dot($this->nested) as $key => $text) {
createLanguageLine('nested', $key, ['en' => $text]);
createLanguageLine('*', 'nested', $key, ['en' => $text]);
createLanguageLine('namespace', 'nested', $key, ['en' => $text]);
}

expect(trans('nested.bool.1'))->toEqual($this->nested['bool'][1]);
});
expect(trans('nested.bool.1'))->toEqual($this->nested['bool'][1])
->and(trans('namespace::nested.bool.1'))->toEqual($this->nested['bool'][1]);
})->only();

it('will return the dotted translation key if no translation found', function () {
$notFoundKey = 'nested.bool.3';

foreach (Arr::dot($this->nested) as $key => $text) {
createLanguageLine('nested', $key, ['en' => $text]);
createLanguageLine('*', 'nested', $key, ['en' => $text]);
}

expect(trans($notFoundKey))->toEqual($notFoundKey);
});

it('will default to fallback if locale is missing', function () {
app()->setLocale('de');
createLanguageLine('missing_locale', 'key', ['en' => 'en value from db']);
createLanguageLine('*', 'missing_locale', 'key', ['en' => 'en value from db']);
createLanguageLine('missing_namespace', 'missing_locale', 'key', ['en' => 'en value from db']);

expect(trans('missing_locale.key'))->toEqual('en value from db');
expect(trans('missing_locale.key'))->toEqual('en value from db')
->and(trans('missing_namespace::missing_locale.key'))->toEqual('en value from db');
});
Loading
Loading