diff --git a/config/translation-manager.php b/config/translation-manager.php index 6411f6d4..a552a080 100644 --- a/config/translation-manager.php +++ b/config/translation-manager.php @@ -66,4 +66,31 @@ '$trans.get', ], + /** + * if true finding new translations will be disabled while users browse application + */ + 'ignore_new_trans' => false, + + /** + * When project does not user JSON alternative it can be ignored + */ + 'ignore_json' => true, + + /** + * Translations without source position will be marked as red + */ + 'warn_in_code' => false, + + /* + |-------------------------------------------------------------------------- + | DEBUG + |-------------------------------------------------------------------------- + | + | After every translation will be placed original key in square brackets + | e.g.: trans('auth.login') -> Login [auth.login] + | NOTE: only when translation exists! + | + */ + 'debug' => false, + ]; diff --git a/database/migrations/2014_04_02_193005_create_translations_table.php b/database/migrations/2014_04_02_193005_create_translations_table.php index 053d09c2..3f8d92a6 100644 --- a/database/migrations/2014_04_02_193005_create_translations_table.php +++ b/database/migrations/2014_04_02_193005_create_translations_table.php @@ -1,20 +1,22 @@ collation = 'utf8mb4_bin'; + $table->collation = 'utf8mb4_bin'; $table->bigIncrements('id'); $table->integer('status')->default(0); $table->string('locale'); @@ -22,17 +24,20 @@ public function up() $table->text('key'); $table->text('value')->nullable(); $table->timestamps(); + + $table->index(['group']); + $table->index(['locale']); }); - } + } - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { Schema::drop('ltm_translations'); - } + } } diff --git a/database/migrations/2021_05_04_201011_create_ltm_translations_sources_table.php b/database/migrations/2021_05_04_201011_create_ltm_translations_sources_table.php new file mode 100644 index 00000000..78f6e760 --- /dev/null +++ b/database/migrations/2021_05_04_201011_create_ltm_translations_sources_table.php @@ -0,0 +1,40 @@ +bigIncrements('id'); + + $table->string('group'); + $table->text('key'); + + $table->string('file_path'); + $table->integer('file_line'); + + $table->index(['group', 'key']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ltm_translation_sources'); + } +} diff --git a/database/migrations/2021_05_04_201011_create_ltm_translations_urls_table.php b/database/migrations/2021_05_04_201011_create_ltm_translations_urls_table.php new file mode 100644 index 00000000..eecf23be --- /dev/null +++ b/database/migrations/2021_05_04_201011_create_ltm_translations_urls_table.php @@ -0,0 +1,39 @@ +bigIncrements('id'); + + $table->string('group'); + $table->text('key'); + + $table->string('url'); + + $table->index(['group', 'key']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ltm_translation_urls'); + } +} diff --git a/database/migrations/2021_05_04_201011_create_ltm_translations_variables_table.php b/database/migrations/2021_05_04_201011_create_ltm_translations_variables_table.php new file mode 100644 index 00000000..9ec7cba7 --- /dev/null +++ b/database/migrations/2021_05_04_201011_create_ltm_translations_variables_table.php @@ -0,0 +1,39 @@ +bigIncrements('id'); + + $table->string('group'); + $table->text('key'); + + $table->string('attribute'); + + $table->index(['group', 'key']); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ltm_translation_variables'); + } +} diff --git a/readme.md b/readme.md index 3de3adc1..cbc919c6 100644 --- a/readme.md +++ b/readme.md @@ -15,6 +15,7 @@ The workflow would be: This way, translations can be saved in git history and no overhead is introduced in production. ![Screenshot](http://i.imgur.com/4th2krf.png) +![Screenshot](screenshot2.png) ## Installation diff --git a/resources/views/components/locales_list.blade.php b/resources/views/components/locales_list.blade.php new file mode 100644 index 00000000..d62bed86 --- /dev/null +++ b/resources/views/components/locales_list.blade.php @@ -0,0 +1,45 @@ +
+ \ No newline at end of file diff --git a/resources/views/components/post_import.blade.php b/resources/views/components/post_import.blade.php new file mode 100644 index 00000000..543012ec --- /dev/null +++ b/resources/views/components/post_import.blade.php @@ -0,0 +1,22 @@ + + \ No newline at end of file diff --git a/resources/views/components/post_publish.blade.php b/resources/views/components/post_publish.blade.php new file mode 100644 index 00000000..ae020fb4 --- /dev/null +++ b/resources/views/components/post_publish.blade.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/resources/views/components/search.blade.php b/resources/views/components/search.blade.php new file mode 100644 index 00000000..8e07bee8 --- /dev/null +++ b/resources/views/components/search.blade.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/resources/views/components/translation_detail.blade.php b/resources/views/components/translation_detail.blade.php new file mode 100644 index 00000000..4568444e --- /dev/null +++ b/resources/views/components/translation_detail.blade.php @@ -0,0 +1,79 @@ + +Key | + @foreach ($locales as $locale) +{{ $locale }} | + @endforeach + @if ($deleteEnabled) ++ @endif + |
---|---|---|
count() == 0 ) class="danger" @endif>{!! htmlentities($key, ENT_QUOTES, 'UTF-8', false) !!} + $group, "translationKey" => $key ]) }}"> + | + @foreach ($locales as $locale) + + ++ $group]) }}" + data-title="Enter translation">{!! $t ? htmlentities($t->value, ENT_QUOTES, 'UTF-8', + false) : '' !!} + | + @endforeach + @if ($deleteEnabled) ++ + | + @endif +
Warning, translations are not visible until they are exported back to the app/lang file, using php artisan
+ translation:export
command or publish button.
+ @if($group) + @include( 'translation-manager::components.post_publish' ) + @else + @include( 'translation-manager::components.post_import' ) + @endif +
+ @else + Back + @endif + @if($group || $q) + @if($key) + @include( 'translation-manager::components.translation_detail' ) + @else + @if($q) + @include( 'translation-manager::components.search' ) + @else + + +Warning, translations are not visible until they are exported back to the app/lang file, using php artisan translation:export
command or publish button.
- -
- - - - - - - - - -Key | - -= $locale ?> | - - -- - |
---|---|---|
- - - - | - " - id="username" data-type="textarea" data-pk="id : 0 ?>" - data-url="" - data-title="Enter translation">value, ENT_QUOTES, 'UTF-8', false) : '' ?> - | - - -- - | - -
['\"])". // Match " or ' and store in {quote} "(?P(?:\\\k{quote}|(?!\k{quote}).)*)". // Match any string that can be {quote} escaped @@ -190,72 +195,232 @@ public function findTranslations($path = null) $finder = new Finder(); $finder->in($path)->exclude('storage')->exclude('vendor')->name('*.php')->name('*.twig')->name('*.vue')->files(); + $section = null; + if (app()->runningInConsole()) { + $output = new ConsoleOutput(); + $section = $output->section(); + + $bar = new ProgressBar($section); + $bar->setFormat("%message%\n %current%/%max% [%bar%] %percent:3s%%"); + $bar->setMessage('Files'); + $bar->start(count($finder)); + } + /** @var \Symfony\Component\Finder\SplFileInfo $file */ foreach ($finder as $file) { + if ($section != null) { + $bar->advance(); + } + // Search the current file for the pattern - if (preg_match_all("/$groupPattern/siU", $file->getContents(), $matches)) { + if (preg_match_all("/$groupPattern/si", $file->getContents(), $matches)) { // Get all matches - foreach ($matches[2] as $key) { - $groupKeys[] = $key; + foreach ($matches[ 2 ] as $i => $key) { + $found++; + if (!isset($groupKeys[ $key ])) { + $groupKeys[ $key ] = [ + "sources" => [], + "variables" => [], + ]; + } + $groupKeys[ $key ][ "sources" ] = array_merge($groupKeys[ $key ][ "sources" ], $this->findLineNumber($file, $key)); + if (isset($matches[ 5 ]) && isset($matches[ 5 ][ $i ]) && $matches[ 5 ][ $i ] != "") { + $attributes = explode(",", static::str_strip_whitespace($matches[ 5 ][ $i ])); + foreach ($attributes as $attribute) { + list($item, $_rest) = explode("=", $attribute, 2); + $groupKeys[ $key ][ "variables" ][] = str_replace(['"', "'"], "", $item); + } + } } } - if (preg_match_all("/$stringPattern/siU", $file->getContents(), $matches)) { - foreach ($matches['string'] as $key) { - if (preg_match("/(^[a-zA-Z0-9_-]+([.][^\1)\ ]+)+$)/siU", $key, $groupMatches)) { - // group{.group}.key format, already in $groupKeys but also matched here - // do nothing, it has to be treated as a group - continue; - } + if (!$this->config[ 'ignore_json' ]) { + if (preg_match_all("/$stringPattern/siU", $file->getContents(), $matches)) { + foreach ($matches[ 'string' ] as $key) { + if (preg_match("/(^[a-zA-Z0-9_-]+([.][^\1)\ ]+)+$)/siU", $key, $groupMatches)) { + // group{.group}.key format, already in $groupKeys but also matched here + // do nothing, it has to be treated as a group + continue; + } - //TODO: This can probably be done in the regex, but I couldn't do it. - //skip keys which contain namespacing characters, unless they also contain a - //space, which makes it JSON. - if (! (Str::contains($key, '::') && Str::contains($key, '.')) - || Str::contains($key, ' ')) { - $stringKeys[] = $key; + //TODO: This can probably be done in the regex, but I couldn't do it. + //skip keys which contain namespacing characters, unless they also contain a + //space, which makes it JSON. + if (!(Str::contains($key, '::') && Str::contains($key, '.')) + || Str::contains($key, ' ')) { + $stringKeys[] = $key; + } } } } } // Remove duplicates - $groupKeys = array_unique($groupKeys); - $stringKeys = array_unique($stringKeys); + ksort($groupKeys); + + if ($section != null) { + $bar->finish(); + + $bar2 = new ProgressBar($section); + $bar2->setFormat("%message%\n %current%/%max% [%bar%] %percent:3s%%"); + $bar2->setMessage("Keys"); + $bar2->start(count($groupKeys)); + } + + //clean variables and sources + \Illuminate\Support\Facades\DB::statement('TRUNCATE TABLE `ltm_translation_sources`'); // Add the translations to the database, if not existing. - foreach ($groupKeys as $key) { + foreach ($groupKeys as $key => $data) { + if ($section != null) { + $bar2->advance(); + } + // Split the group and item list($group, $item) = explode('.', $key, 2); - $this->missingKey('', $group, $item); + $this->missingKey('', $group, $item, array_unique($data[ 'variables' ])); + + // save location in strings + $files = array_unique($data[ 'sources' ]); + foreach ($files as $file) { + list($path, $line) = explode(':', $file); + \Illuminate\Support\Facades\DB::table('ltm_translation_sources')->insert([ + "group" => $group, + "key" => $item, + "file_path" => $path, + "file_line" => $line, + ]); + } + + $counter++; } - foreach ($stringKeys as $key) { - $group = self::JSON_GROUP; - $item = $key; - $this->missingKey('', $group, $item); + if ($section != null) { + $bar2->finish(); + } + + if (!$this->config[ 'ignore_json' ]) { + $stringKeys = array_unique($stringKeys); + + if ($section != null) { + $bar3 = new ProgressBar($section); + $bar3->setFormat("%message%\n %current%/%max% [%bar%] %percent:3s%%"); + $bar3->setMessage("JSON"); + $bar3->start(count($groupKeys)); + } + + foreach ($stringKeys as $key) { + if ($bar3 != null) { + $bar3->advance(); + } + + $group = Manager::JSON_GROUP; + $item = $key; + $this->missingKey('', $group, $item); + } + + if ($section != null) { + $bar3->finish(); + } } // Return the number of found translations return count($groupKeys + $stringKeys); } - public function missingKey($namespace, $group, $key) + /** + * return list of line_numbers + * + * @param \Symfony\Component\Finder\SplFileInfo $file + * @param $search + * + * @return array + */ + private function findLineNumber(\Symfony\Component\Finder\SplFileInfo $file, $search) + { + $lines = file($file->getRealPath()); + $line_numbers = []; + + foreach ($lines as $key => $line) { + if (strpos($line, $search) !== false) { + $line_numbers[] = $file->getRelativePath()."/".$file->getFilename().":".($key + 1); + } + } + + return $line_numbers; + } + + /** + * Strp all whitespaces inside of string + * + * @param $string + * + * @return string|string[]|null + */ + public static function str_strip_whitespace($string) { - if (! in_array($group, $this->config['exclude_groups'])) { + return preg_replace('/\s+/', '', $string); + } + + public function missingKey($namespace, $group, $key, $parameters = []) + { + if (!in_array($group, $this->config[ 'exclude_groups' ])) { + if ($this->config[ 'ignore_json' ]) { + //ignore all non alphanumeric strings + if (preg_match("/[a-zA-Z0-9-_\.]*/", $key, $groupMatches)) { + if ($groupMatches[ 0 ] != $key) { + return; + } + } + } + Translation::firstOrCreate([ - 'locale' => $this->app['config']['app.locale'], - 'group' => $group, - 'key' => $key, + 'locale' => $this->app[ 'config' ][ 'app.locale' ], + 'group' => $group, + 'key' => $key, ]); + + if (count($parameters) > 0) { + Translation::possibleVariables($group, $key)->delete(); + + // save possible variables + foreach ($parameters as $parameter) { + \Illuminate\Support\Facades\DB::table('ltm_translation_variables')->insert([ + "group" => $group, + "key" => $key, + "attribute" => $parameter, + ]); + } + } + + if (!app()->runningInConsole()) { + $url = request()->getRequestUri(); + + // ignore url when part of config->route->prefix + if (!Str::contains($url, $this->config[ 'route' ][ 'prefix' ])) { + // save URL with translation key + $_testUrl = DB::table('ltm_translation_urls') + ->where('group', $group) + ->where('key', $key) + ->where('url', $url); + + if ($_testUrl->count() == 0) { + DB::table('ltm_translation_urls')->insert([ + 'group' => $group, + 'key' => $key, + 'url' => $url, + ]); + } + } + } } } public function exportTranslations($group = null, $json = false) { - $basePath = $this->app['path.lang']; + $basePath = $this->app[ 'path.lang' ]; - if (! is_null($group) && ! $json) { - if (! in_array($group, $this->config['exclude_groups'])) { + if (!is_null($group) && !$json) { + if (!in_array($group, $this->config[ 'exclude_groups' ])) { $vendor = false; if ($group == '*') { return $this->exportAllTranslations(); @@ -266,13 +431,13 @@ public function exportTranslations($group = null, $json = false) } $tree = $this->makeTree(Translation::ofTranslatedGroup($group) - ->orderByGroupKeys(Arr::get($this->config, 'sort_keys', false)) - ->get()); + ->orderByGroupKeys(Arr::get($this->config, 'sort_keys', false)) + ->get()); foreach ($tree as $locale => $groups) { - if (isset($groups[$group])) { - $translations = $groups[$group]; - $path = $this->app['path.lang']; + if (isset($groups[ $group ])) { + $translations = $groups[ $group ]; + $path = $this->app[ 'path.lang' ]; $locale_path = $locale.DIRECTORY_SEPARATOR.$group; if ($vendor) { @@ -287,7 +452,7 @@ public function exportTranslations($group = null, $json = false) $subfolder_level = $subfolder_level.$subfolder.DIRECTORY_SEPARATOR; $temp_path = rtrim($path.DIRECTORY_SEPARATOR.$subfolder_level, DIRECTORY_SEPARATOR); - if (! is_dir($temp_path)) { + if (!is_dir($temp_path)) { mkdir($temp_path, 0777, true); } } @@ -304,13 +469,13 @@ public function exportTranslations($group = null, $json = false) if ($json) { $tree = $this->makeTree(Translation::ofTranslatedGroup(self::JSON_GROUP) - ->orderByGroupKeys(Arr::get($this->config, 'sort_keys', false)) - ->get(), true); + ->orderByGroupKeys(Arr::get($this->config, 'sort_keys', false)) + ->get(), true); foreach ($tree as $locale => $groups) { - if (isset($groups[self::JSON_GROUP])) { - $translations = $groups[self::JSON_GROUP]; - $path = $this->app['path.lang'].'/'.$locale.'.json'; + if (isset($groups[ self::JSON_GROUP ])) { + $translations = $groups[ self::JSON_GROUP ]; + $path = $this->app[ 'path.lang' ].'/'.$locale.'.json'; $output = json_encode($translations, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE); $this->files->put($path, $output); } @@ -342,10 +507,10 @@ protected function makeTree($translations, $json = false) $array = []; foreach ($translations as $translation) { if ($json) { - $this->jsonSet($array[$translation->locale][$translation->group], $translation->key, + $this->jsonSet($array[ $translation->locale ][ $translation->group ], $translation->key, $translation->value); - } else { - Arr::set($array[$translation->locale][$translation->group], $translation->key, + } else if( isset($translation->value) && $translation->value != "" ){ + Arr::set($array[ $translation->locale ][ $translation->group ], $translation->key, $translation->value); } } @@ -358,7 +523,7 @@ public function jsonSet(&$array, $key, $value) if (is_null($key)) { return $array = $value; } - $array[$key] = $value; + $array[ $key ] = $value; return $array; } @@ -399,7 +564,7 @@ public function addLocale($locale) $this->saveIgnoredLocales(); $this->ignoreLocales = $this->getIgnoredLocales(); - if (! $this->files->exists($localeDir) || ! $this->files->isDirectory($localeDir)) { + if (!$this->files->exists($localeDir) || !$this->files->isDirectory($localeDir)) { return $this->files->makeDirectory($localeDir); } @@ -413,7 +578,7 @@ protected function saveIgnoredLocales() public function removeLocale($locale) { - if (! $locale) { + if (!$locale) { return false; } $this->ignoreLocales = array_merge($this->ignoreLocales, [$locale]); @@ -428,7 +593,7 @@ public function getConfig($key = null) if ($key == null) { return $this->config; } else { - return $this->config[$key]; + return $this->config[ $key ]; } } } diff --git a/src/ManagerServiceProvider.php b/src/ManagerServiceProvider.php index daef4bd4..c4d92702 100644 --- a/src/ManagerServiceProvider.php +++ b/src/ManagerServiceProvider.php @@ -1,25 +1,25 @@ mergeConfigFrom($configPath, 'translation-manager'); $this->publishes([$configPath => config_path('translation-manager.php')], 'config'); @@ -52,15 +52,15 @@ public function register() return new Console\CleanCommand($app['translation-manager']); }); $this->commands('command.translation-manager.clean'); - } + } /** - * Bootstrap the application events. - * - * @return void - */ - public function boot() - { + * Bootstrap the application events. + * + * @return void + */ + public function boot() + { $viewPath = __DIR__.'/../resources/views'; $this->loadViewsFrom($viewPath, 'translation-manager'); $this->publishes([ @@ -73,22 +73,23 @@ public function boot() ], 'migrations'); $this->loadRoutesFrom(__DIR__.'/routes.php'); - } + } - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return array('translation-manager', + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array( + 'translation-manager', 'command.translation-manager.reset', 'command.translation-manager.import', 'command.translation-manager.find', 'command.translation-manager.export', 'command.translation-manager.clean' ); - } + } } diff --git a/src/Models/Translation.php b/src/Models/Translation.php index 60a744a4..246ab26e 100644 --- a/src/Models/Translation.php +++ b/src/Models/Translation.php @@ -1,21 +1,23 @@ where('group', $group)->whereNotNull('value'); } - public function scopeOrderByGroupKeys($query, $ordered) { + public function scopeOrderByGroupKeys($query, $ordered) + { if ($ordered) { $query->orderBy('group')->orderBy('key'); } @@ -40,7 +43,7 @@ public function scopeSelectDistinctGroup($query) { $select = ''; - switch (DB::getDriverName()){ + switch (DB::getDriverName()) { case 'mysql': $select = 'DISTINCT `group`'; break; @@ -52,4 +55,37 @@ public function scopeSelectDistinctGroup($query) return $query->select(DB::raw($select)); } + /** + * @param $group + * @param $key + * + * @return Builder + */ + public static function sourceLocations( $group, $key ) + { + return \Illuminate\Support\Facades\DB::table('ltm_translation_sources')->where('group', $group)->where('key', $key); + } + + /** + * @param $group + * @param $key + * + * @return Builder + */ + public static function urls( $group, $key ) + { + return \Illuminate\Support\Facades\DB::table('ltm_translation_urls')->where('group', $group)->where('key', $key); + } + + /** + * @param $group + * @param $key + * + * @return Builder + */ + public static function possibleVariables( $group, $key ) + { + return \Illuminate\Support\Facades\DB::table('ltm_translation_variables')->where('group', $group)->where('key', $key); + } + } diff --git a/src/Translator.php b/src/Translator.php index 28ed72f6..a3cb0160 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -8,6 +8,9 @@ class Translator extends LaravelTranslator { /** @var Dispatcher */ protected $events; + /** @var Manager */ + protected $manager; + /** * Get the translation for the given key. * @@ -20,14 +23,17 @@ public function get($key, array $replace = array(), $locale = null, $fallback = { // Get without fallback $result = parent::get($key, $replace, $locale, false); - if($result === $key){ - $this->notifyMissingKey($key); + if($result === $key && config( 'translation-manager.ignore_new_trans', false )){ + $this->notifyMissingKey($key, array_keys( $replace )); // Reget with fallback $result = parent::get($key, $replace, $locale, $fallback); } + if( config( 'translation-manager.debug', false ) && $result != $key ) + $result .= " [" . $key . "]"; + return $result; } @@ -36,11 +42,14 @@ public function setTranslationManager(Manager $manager) $this->manager = $manager; } - protected function notifyMissingKey($key) + protected function notifyMissingKey($key, $parameters) { + if( config('translation-manager.ignore_new_trans', false ) ) + return ; + list($namespace, $group, $item) = $this->parseKey($key); if($this->manager && $namespace === '*' && $group && $item ){ - $this->manager->missingKey($namespace, $group, $item); + $this->manager->missingKey($namespace, $group, $item, $parameters); } } diff --git a/src/routes.php b/src/routes.php index fefb399d..99883868 100644 --- a/src/routes.php +++ b/src/routes.php @@ -2,19 +2,25 @@ declare(strict_types=1); -$config = array_merge(config('translation-manager.route'), ['namespace' => 'Barryvdh\TranslationManager']); -Route::group($config, function($router) -{ - $router->get('view/{groupKey?}', 'Controller@getView')->where('groupKey', '.*'); - $router->get('/{groupKey?}', 'Controller@getIndex')->where('groupKey', '.*'); - $router->post('/add/{groupKey}', 'Controller@postAdd')->where('groupKey', '.*'); - $router->post('/edit/{groupKey}', 'Controller@postEdit')->where('groupKey', '.*'); - $router->post('/groups/add', 'Controller@postAddGroup'); - $router->post('/delete/{groupKey}/{translationKey}', 'Controller@postDelete')->where('groupKey', '.*'); - $router->post('/import', 'Controller@postImport'); - $router->post('/find', 'Controller@postFind'); - $router->post('/locales/add', 'Controller@postAddLocale'); - $router->post('/locales/remove', 'Controller@postRemoveLocale'); - $router->post('/publish/{groupKey}', 'Controller@postPublish')->where('groupKey', '.*'); - $router->post('/translate-missing', 'Controller@postTranslateMissing'); +use Barryvdh\TranslationManager\Controller; + +Route::group(config('translation-manager.route'), function ($router) { + $router->get('/view/{groupKey?}', [Controller::class, 'getView'])->where('groupKey', '.*')->name( 'translation-manager.group.list' ); + $router->get('/search', [Controller::class, 'getSearchResults'])->name( 'translation-manager.search' ); + $router->get('/detail/{groupKey}/{translationKey}', [Controller::class, 'getDetail'])->name( 'translation-manager.translation' ); + $router->get('/{groupKey?}', [Controller::class, 'getIndex'])->where('groupKey', '.*')->name( 'translation-manager.index'); + + + $router->post('/add/{groupKey}', [Controller::class, 'postAdd'])->where('groupKey', '.*')->name('translation-manager.translation.add'); + $router->post('/edit/{groupKey}', [Controller::class, 'postEdit'])->where('groupKey', '.*')->name('translation-manager.translation.edit'); + $router->post('/edit-all/{groupKey}/{translationKey}', [Controller::class, 'postEditAll'])->name('translation-manager.translation.edit-all'); + + $router->post('/groups/add', [Controller::class, 'postAddGroup']); + $router->post('/delete/{groupKey}/{translationKey}', [Controller::class, 'postDelete'])->where('groupKey', '.*'); + $router->post('/import', [Controller::class, 'postImport']); + $router->post('/find', [Controller::class, 'postFind']); + $router->post('/locales/add', [Controller::class, 'postAddLocale']); + $router->post('/locales/remove', [Controller::class, 'postRemoveLocale']); + $router->post('/publish/{groupKey}', [Controller::class, 'postPublish'])->where('groupKey', '.*'); + $router->post('/translate-missing', [Controller::class, 'postTranslateMissing']); }); diff --git a/tests/Unit/ValidateTranslationKeysTest.php b/tests/Unit/ValidateTranslationKeysTest.php new file mode 100644 index 00000000..d8ffc307 --- /dev/null +++ b/tests/Unit/ValidateTranslationKeysTest.php @@ -0,0 +1,32 @@ +truncate(); + $this->artisan( 'translations:find' ); + + DB::table( 'ltm_translations' )->whereNotNull( 'key' )->update( [ 'value' => 'test' ] ); + + Artisan::command( 'translations:export --all', function () {} ); + + $translations = DB::table( 'ltm_translations' )->whereNotNull( 'value' )->get(); + foreach ( $translations as $translation ){ + $this->assertIsString( trans( $translation->group . "." . $translation->key ), "Key[" . $translation->group . "." . $translation->key . "] has more than one result!" ); + } + } +}