From b8b8a0ab1ae5a4551080594680850d9df25b1fb1 Mon Sep 17 00:00:00 2001 From: Fady Mondy Date: Tue, 20 Dec 2022 17:46:54 +0200 Subject: [PATCH] update file system to be json files remove translation-loader --- Config/config.php | 2 +- Console/InstallTranslation.php | 7 +- Database/.DS_Store | Bin 6148 -> 0 bytes ..._25_010712_create_language_lines_table.php | 51 ------- ...php => TranslationsjsonDatabaseSeeder.php} | 4 +- Entities/Translation.php | 57 ++------ Exports/TranslationsExport.php | 25 +--- Http/Controllers/.gitkeep | 0 .../TranslationsjsonController.php | 79 +++++++++++ Http/Middleware/.gitkeep | 0 Http/Requests/.gitkeep | 0 Imports/TranslationsImport.php | 17 +-- Providers/TranslationsServiceProvider.php | 71 +++++++++- Resources/assets/.gitkeep | 0 Resources/assets/js/app.js | 0 Resources/assets/sass/app.scss | 0 Resources/lang/.gitkeep | 0 Resources/views/.gitkeep | 0 Resources/views/index.blade.php | 0 Resources/views/layouts/master.blade.php | 0 Routes/web.php | 7 +- Services/SaveScan.php | 124 ++++++------------ Tests/.DS_Store | Bin 6148 -> 0 bytes Vilt/Resources/TranslationsResource.php | 55 +++++--- .../Actions/ExportAction.php | 2 +- .../Actions/ScanAction.php | 2 +- .../Actions/TranslateAction.php | 2 +- .../TranslationsResource/Components.php | 2 +- .../TranslationsResource/Methods.php | 2 +- .../Modals/ImportModal.php | 2 +- .../Routes/TranslateRoute.php | 2 +- composer.json | 1 - 32 files changed, 255 insertions(+), 259 deletions(-) delete mode 100644 Database/.DS_Store delete mode 100644 Database/Migrations/2022_01_25_010712_create_language_lines_table.php rename Database/Seeders/{TranslationsDatabaseSeeder.php => TranslationsjsonDatabaseSeeder.php} (71%) create mode 100644 Http/Controllers/.gitkeep create mode 100644 Http/Controllers/TranslationsjsonController.php create mode 100644 Http/Middleware/.gitkeep create mode 100644 Http/Requests/.gitkeep create mode 100644 Resources/assets/.gitkeep create mode 100644 Resources/assets/js/app.js create mode 100644 Resources/assets/sass/app.scss create mode 100644 Resources/lang/.gitkeep create mode 100644 Resources/views/.gitkeep create mode 100644 Resources/views/index.blade.php create mode 100644 Resources/views/layouts/master.blade.php delete mode 100644 Tests/.DS_Store diff --git a/Config/config.php b/Config/config.php index ae5d93a..b8a31c2 100644 --- a/Config/config.php +++ b/Config/config.php @@ -1,7 +1,7 @@ "Translations", + "name" => "Translationsjson", /* |-------------------------------------------------------------------------- | Paths diff --git a/Console/InstallTranslation.php b/Console/InstallTranslation.php index 2c43d69..47f5982 100644 --- a/Console/InstallTranslation.php +++ b/Console/InstallTranslation.php @@ -4,11 +4,6 @@ use Illuminate\Console\Command; use Illuminate\Support\Facades\Artisan; -use Modules\Base\Helpers\Resources\VILT; -use Modules\Menu\Entities\Menus; -use Modules\Menu\Entities\MenusGroups; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Input\InputArgument; class InstallTranslation extends Command { @@ -44,7 +39,7 @@ public function __construct() public function handle() { $this->info('Install Permissions'); - Artisan::call('roles:generate language_lines'); + Artisan::call('roles:generate translations'); $this->info('Your Translations is ready now'); return Command::SUCCESS; diff --git a/Database/.DS_Store b/Database/.DS_Store deleted file mode 100644 index 35f554cb9229549291f734b68c31b77a9f3072a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKu};G<5IvUyDKJ!G>1aNoGfSw#$drwVwkf4j(o_Y+gp@DftN0W?gm*qGq>Uq( z5JGp-`8n5T`|{%EVj?n==WI+gB%%n;*c+e;82`@RvYwBe0gaoZq%EyzL67;e#n!-I zRDhq|fbMBZb9$o2^Bbr0ve-0I4zP+cNoILD11rj3k6zAi-><^+6=n8A=H{Wb@(JBw zS1qx(rpR%HEZ4lln!LjctQ+JtbBsRf96PvQS$@ae&6EE>bWADs{(>_2c0WV2ZZn#> zy1yli5Afy0lHd}NmsI4lX3aA3L0{nikaK_kUCFouqXxtG1 z*hknI`tDl@XgL6kJywEEw9%24Bv7)FL8ZapvdSP9B-GHzwe<5xERgks$4h+9V| z6ANmq3aA3R3LMDgkn8_-_xXQ!kY1?*s=&We!1R+enc$XuZEf8g*IEm|gR?QO60C=y h<4Q5OT#83bigIncrements('id'); - $table->string('group'); - $table->index('group'); - $table->text('key'); - $table->jsonb('text'); - $table->jsonb('metadata')->nullable(); - $table->string('namespace')->default('*'); - $table->index('namespace'); - $table->softDeletes(); - $table->timestamps(); - }); - - if (!Schema::hasColumn('users', 'lang')) { - Schema::table('users', function (Blueprint $table) { - $table->string('lang')->default('en'); - }); - } - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::drop('language_lines'); - - if (Schema::hasColumn('users', 'lang')) { - Schema::table('users', function (Blueprint $table) { - $table->dropColumn('lang'); - }); - } - } -} diff --git a/Database/Seeders/TranslationsDatabaseSeeder.php b/Database/Seeders/TranslationsjsonDatabaseSeeder.php similarity index 71% rename from Database/Seeders/TranslationsDatabaseSeeder.php rename to Database/Seeders/TranslationsjsonDatabaseSeeder.php index 14fcdf2..dbf0cce 100644 --- a/Database/Seeders/TranslationsDatabaseSeeder.php +++ b/Database/Seeders/TranslationsjsonDatabaseSeeder.php @@ -1,11 +1,11 @@ 'array']; + protected ?array $rows = []; - protected $table = "language_lines"; - - protected $fillable = [ - "group", - "key", - "text" - ]; - - - public static function getTranslatableLocales(): array + public function getRows() { - return config('filament-translations.locals'); - } - - public function getTranslation(string $locale, string $group = null): string - { - if ($group === '*' && !isset($this->text[$locale])) { - $fallback = config('app.fallback_locale'); - - return $this->text[$fallback] ?? $this->key; - } - return $this->text[$locale] ?? ''; - } - - public function setTranslation(string $locale, string $value): self - { - $this->text = array_merge($this->text ?? [], [$locale => $value]); - - return $this; + return (new SaveScan())->getKeys(); } - protected function getTranslatedLocales(): array + protected function sushiShouldCache() { - return array_keys($this->text); + return false; } } diff --git a/Exports/TranslationsExport.php b/Exports/TranslationsExport.php index 76f6e17..ba9dc31 100644 --- a/Exports/TranslationsExport.php +++ b/Exports/TranslationsExport.php @@ -2,6 +2,7 @@ namespace Modules\Translations\Exports; +use Illuminate\Support\Facades\File; use Maatwebsite\Excel\Concerns\WithHeadings; use Modules\Translations\Entities\Translation; use Maatwebsite\Excel\Concerns\FromCollection; @@ -13,31 +14,15 @@ class TranslationsExport implements FromCollection, WithHeadings */ public function collection() { - $transaltions = Translation::all(); - - foreach ($transaltions as $item) { - foreach ($item->text as $key => $lang) { - $item->{$key} = $lang; - } - unset($item->group); - unset($item->metadata); - unset($item->namespace); - unset($item->text); - unset($item->created_at); - unset($item->updated_at); - unset($item->deleted_at); - } - - return $transaltions; + return Translation::all(); } public function headings(): array { - $loadLocals = []; - $getLocals = config('translations.locals'); - foreach ($getLocals as $key => $value) { - array_push($loadLocals, $key); + $jsonFolder = File::files(lang_path()); + foreach ($jsonFolder as $key => $value) { + $loadLocals[] = str_replace('.json', '', $value->getFilename()); } return array_merge(["id", "key"], $loadLocals); diff --git a/Http/Controllers/.gitkeep b/Http/Controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Http/Controllers/TranslationsjsonController.php b/Http/Controllers/TranslationsjsonController.php new file mode 100644 index 0000000..0c1a94f --- /dev/null +++ b/Http/Controllers/TranslationsjsonController.php @@ -0,0 +1,79 @@ +where('group', '*')->first(); - if ($trans) { - $text = []; - $i = 2; - foreach ($getLocals as $lang => $value) { - $text[$lang] = $row[$i]; - $i++; - } - - $trans->text = $text; - $trans->save(); + $jsonFolder = File::files(lang_path()); + foreach($jsonFolder as $key=>$getLangName){ + $fileContent = json_decode(File::get(lang_path($getLangName->getFilename()))); + $fileContent->{$row[1]} = $row[$key+2]; + File::put(lang_path($getLangName->getFilename()), json_encode($fileContent, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); } } } diff --git a/Providers/TranslationsServiceProvider.php b/Providers/TranslationsServiceProvider.php index 0f7fc95..e799f83 100644 --- a/Providers/TranslationsServiceProvider.php +++ b/Providers/TranslationsServiceProvider.php @@ -2,8 +2,8 @@ namespace Modules\Translations\Providers; -use Illuminate\Database\Eloquent\Factory; use Illuminate\Support\ServiceProvider; +use Illuminate\Database\Eloquent\Factory; use Modules\Base\Services\Components\Base\Lang; use Modules\Base\Services\Core\VILT; use Modules\Translations\Console\InstallTranslation; @@ -27,12 +27,13 @@ class TranslationsServiceProvider extends ServiceProvider */ public function boot() { - VILT::registerTranslation(Lang::make('language_lines.sidebar')->label(__('Translations'))); - + $this->registerTranslations(); $this->registerConfig(); + $this->registerViews(); $this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations')); VILT::loadResources($this->moduleName); - + VILT::loadPages($this->moduleName); + VILT::registerTranslation(Lang::make('translations.sidebar')->label(__('Translations'))); $this->commands([ InstallTranslation::class @@ -60,8 +61,64 @@ protected function registerConfig() module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'), ], 'config'); $this->mergeConfigFrom( - module_path($this->moduleName, 'Config/config.php'), - $this->moduleNameLower + module_path($this->moduleName, 'Config/config.php'), $this->moduleNameLower ); } -} \ No newline at end of file + + /** + * Register views. + * + * @return void + */ + public function registerViews() + { + $viewPath = resource_path('views/modules/' . $this->moduleNameLower); + + $sourcePath = module_path($this->moduleName, 'Resources/views'); + + $this->publishes([ + $sourcePath => $viewPath + ], ['views', $this->moduleNameLower . '-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + } + + /** + * Register translations. + * + * @return void + */ + public function registerTranslations() + { + $langPath = resource_path('lang/modules/' . $this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath, $this->moduleNameLower); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower); + } + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return []; + } + + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (\Config::get('view.paths') as $path) { + if (is_dir($path . '/modules/' . $this->moduleNameLower)) { + $paths[] = $path . '/modules/' . $this->moduleNameLower; + } + } + return $paths; + } +} diff --git a/Resources/assets/.gitkeep b/Resources/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Resources/assets/js/app.js b/Resources/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/Resources/assets/sass/app.scss b/Resources/assets/sass/app.scss new file mode 100644 index 0000000..e69de29 diff --git a/Resources/lang/.gitkeep b/Resources/lang/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Resources/views/.gitkeep b/Resources/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Resources/views/index.blade.php b/Resources/views/index.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/Resources/views/layouts/master.blade.php b/Resources/views/layouts/master.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/Routes/web.php b/Routes/web.php index 2e3d391..a69908e 100644 --- a/Routes/web.php +++ b/Routes/web.php @@ -1,9 +1,5 @@ name('translations.switch'); diff --git a/Services/SaveScan.php b/Services/SaveScan.php index b08afc6..24f2db4 100644 --- a/Services/SaveScan.php +++ b/Services/SaveScan.php @@ -2,10 +2,7 @@ namespace Modules\Translations\Services; -use Carbon\Carbon; -use Illuminate\Support\Facades\DB; -use Modules\Translations\Services\Scan; -use Google\Cloud\Translate\V2\TranslateClient; +use Illuminate\Support\Facades\File; use Modules\Translations\Entities\Translation; class SaveScan @@ -26,94 +23,57 @@ public function save() list($trans, $__) = $scanner->getAllViewFilesWithTranslations(); - /** @var Collection $trans */ /** @var Collection $__ */ - DB::transaction(function () use ($trans, $__) { - Translation::query() - ->whereNull('deleted_at') - ->update([ - 'deleted_at' => Carbon::now() - ]); - - $trans->each(function ($trans) { - list($group, $key) = explode('.', $trans, 2); - $namespaceAndGroup = explode('::', $group, 2); - if (count($namespaceAndGroup) === 1) { - $namespace = '*'; - $group = $namespaceAndGroup[0]; - } else { - list($namespace, $group) = $namespaceAndGroup; - } - $this->createOrUpdate($namespace, $group, $key); - }); - - $__->each(function ($default) { - $this->createOrUpdate('*', '*', $default); - }); - }); - } - - /** - * @param $namespace - * @param $group - * @param $key - */ - protected function createOrUpdate($namespace, $group, $key): void - { - $getLocals = config('translations.locals'); - - /** @var Translation $translation */ - $translation = Translation::withTrashed() - ->where('namespace', $namespace) - ->where('group', $group) - ->where('key', $key) - ->first(); - - $defaultLocale = config('app.locale'); - - $text = []; - foreach ($getLocals as $lang => $value) { - $text[$lang] = ""; - } - - if ($translation) { - if (!$this->isCurrentTransForTranslationArray($translation, $defaultLocale)) { - $translation->restore(); - } - } else { - $ifEx = Translation::where('key' , $key)->first(); - if(!$ifEx){ - $translation = Translation::make([ - 'namespace' => $namespace, - 'group' => $group, - 'key' => $key, - 'text' => $text, - ]); - - if (!$this->isCurrentTransForTranslationArray($translation, $defaultLocale)) { - $translation->save(); - } + $collectKeys = collect([]); + $__->each(function ($default) use ($collectKeys) { + if(!Translation::where('key', $default)->first() && ((!str_contains($default, '{{')) && (!str_contains($default, '}}')) && (!str_contains($default, '::')) && (!str_contains($default, '.$')))){ + $collectKeys->put($default, $default); } + }); + $jsonFolder = File::files(lang_path()); + foreach($jsonFolder as $getLangName){ + $fileContent = json_decode(File::get(lang_path($getLangName->getFilename()))); + $jsonCollection = collect($fileContent); + $lastJson = array_merge($jsonCollection->toArray(), $collectKeys->toArray()); + File::put(lang_path($getLangName->getFilename()), json_encode($lastJson, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); } } - /** - * @param Translation $translation - * @param $locale - * @return bool - */ - private function isCurrentTransForTranslationArray(Translation $translation, $locale): bool + public function getKeys(): array { - if ($translation->group === '*') { - return is_array(__($translation->key, [], $locale)); + $langCollection = collect([]); + $jsonFolder = File::files(lang_path()); + $counter = 1; + $langNames = []; + foreach($jsonFolder as $getLangName){ + $langNames[] = str_replace('.json', '', $getLangName->getFilename()); } - - if ($translation->namespace === '*') { - return is_array(trans($translation->group . '.' . $translation->key, [], $locale)); + foreach($jsonFolder as $getLang){ + $fetchLang = json_decode(File::get(lang_path($getLang->getFilename()))); + $lang = str_replace('.json', '', $getLang->getFilename()); + foreach($fetchLang as $index=>$langItem){ + if((!$langCollection->where('id', $counter)->first())){ + $catchByKey = $langCollection->where('key', $index)->first(); + if($catchByKey){ + $catchByKey->put($lang, $langItem); + } + else { + $initCollection = collect([]); + $initCollection->put("id", $counter); + $initCollection->put("key", $index); + foreach($langNames as $item){ + $initCollection->put($item, $langItem); + } + $langCollection->push($initCollection); + } + + $counter++; + } + } } - return is_array(trans($translation->namespace . '::' . $translation->group . '.' . $translation->key, [], $locale)); + return $langCollection->toArray(); } } diff --git a/Tests/.DS_Store b/Tests/.DS_Store deleted file mode 100644 index c0877870428575741a3786bd8c8e4836afb86eb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5T0oRM!Xcf2wwIT`UbH?U!ZvLq_stbrYWVx>pq+hUW_D(Oz73fS5%KJ>oDxllD2ED;PSFgAjEnYUWEL6ZG{=EkK#p&OpO}d>;Z- zurQ2@>C=HGl>opD<|OD#Eg><%urQ2@7=f^c0yUJa#9$4FJ(yo%7!@^~*oqIfmDws3 zPOD@7klcw2MQ@z}XQ0c#SRW^n|F3_p|GPo{table = "translations"; + $this->view = "Resource"; + } + public function rows(): array { $this->canCreate = false; + $this->canDelete = false; + $this->canDeleteAny = false; - return [ + $jsonFolder = File::files(lang_path()); + $counter = 0; + $langRows = []; + foreach($jsonFolder as $getLangName){ + $langName = str_replace('.json', '', $getLangName->getFilename()); + $langRows[] = Text::make($langName); + } + + return array_merge([ Text::make('id') ->label(__('ID')) ->edit(false) ->create(false), - Text::make('group') - ->label(__('Group')), - - Text::make('namespace') - ->label(__('Namespace')), - Text::make('key') ->label(__('Key')) ->searchable(), + ], $langRows); + } + + public function update(Request $request, $id) + { + $request->validate([ + "key" => "required|string" + ]); + + $jsonFolder = File::files(lang_path()); + foreach($jsonFolder as $getLangName){ + $fileContent = json_decode(File::get(lang_path($getLangName->getFilename()))); + $fileContent->{$request->get('key')} = $request->get(str_replace('.json', '', $getLangName->getFilename())); + File::put(lang_path($getLangName->getFilename()), json_encode($fileContent, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE)); + } - Schema::make('text') - ->label(__('Text')) - ->sortable(false) - ->list('false') - ->options($this->getLocals()), - ]; + return Alert::make(__('Translation Updated Success'))->fire(); } } diff --git a/Vilt/Resources/TranslationsResource/Actions/ExportAction.php b/Vilt/Resources/TranslationsResource/Actions/ExportAction.php index 82e41fa..39cb511 100644 --- a/Vilt/Resources/TranslationsResource/Actions/ExportAction.php +++ b/Vilt/Resources/TranslationsResource/Actions/ExportAction.php @@ -13,7 +13,7 @@ public function setup(): void $this->label(__('Export')); $this->type("success"); $this->icon('bx bxs-spreadsheet'); - $this->url(url('admin/language-lines/export')); + $this->url(url('admin/translations/export')); $this->can(true); } } diff --git a/Vilt/Resources/TranslationsResource/Actions/ScanAction.php b/Vilt/Resources/TranslationsResource/Actions/ScanAction.php index bc751b1..416865c 100644 --- a/Vilt/Resources/TranslationsResource/Actions/ScanAction.php +++ b/Vilt/Resources/TranslationsResource/Actions/ScanAction.php @@ -13,7 +13,7 @@ public function setup(): void $this->label(__('Scan')); $this->type("success"); $this->icon('bx bx-search-alt'); - $this->action('language_lines.scan'); + $this->action('translations.scan'); $this->can(true); } } diff --git a/Vilt/Resources/TranslationsResource/Actions/TranslateAction.php b/Vilt/Resources/TranslationsResource/Actions/TranslateAction.php index b1e35d9..91f8be9 100644 --- a/Vilt/Resources/TranslationsResource/Actions/TranslateAction.php +++ b/Vilt/Resources/TranslationsResource/Actions/TranslateAction.php @@ -13,7 +13,7 @@ public function setup(): void $this->label(__('Translate')); $this->type("success"); $this->icon('bx bxs-cloud'); - $this->action('language_lines.translate'); + $this->action('translations.translate'); $this->can(true); } } diff --git a/Vilt/Resources/TranslationsResource/Components.php b/Vilt/Resources/TranslationsResource/Components.php index 3408847..6c38d71 100644 --- a/Vilt/Resources/TranslationsResource/Components.php +++ b/Vilt/Resources/TranslationsResource/Components.php @@ -17,7 +17,7 @@ public function components():array { return [ Component::make(ScanAction::class)->action(), - Component::make(TranslateAction::class)->action(), +// Component::make(TranslateAction::class)->action(), Component::make(ImportAction::class)->action(), Component::make(ExportAction::class)->action(), Component::make(ImportModal::class)->modal(), diff --git a/Vilt/Resources/TranslationsResource/Methods.php b/Vilt/Resources/TranslationsResource/Methods.php index 455006a..7379d19 100644 --- a/Vilt/Resources/TranslationsResource/Methods.php +++ b/Vilt/Resources/TranslationsResource/Methods.php @@ -110,4 +110,4 @@ public function getLocals(): array return $loadLocals; } -} \ No newline at end of file +} diff --git a/Vilt/Resources/TranslationsResource/Modals/ImportModal.php b/Vilt/Resources/TranslationsResource/Modals/ImportModal.php index 6d36354..fa8be2d 100644 --- a/Vilt/Resources/TranslationsResource/Modals/ImportModal.php +++ b/Vilt/Resources/TranslationsResource/Modals/ImportModal.php @@ -17,7 +17,7 @@ public function setup(): void Media::make('excel')->label(__('Excel File')) ]); $this->buttons([ - Action::make('upload')->label(__('Upload'))->action('language_lines.import') + Action::make('upload')->label(__('Upload'))->action('translations.import') ]); } } diff --git a/Vilt/Resources/TranslationsResource/Routes/TranslateRoute.php b/Vilt/Resources/TranslationsResource/Routes/TranslateRoute.php index ecc4ff4..538748d 100644 --- a/Vilt/Resources/TranslationsResource/Routes/TranslateRoute.php +++ b/Vilt/Resources/TranslationsResource/Routes/TranslateRoute.php @@ -2,7 +2,7 @@ namespace Modules\Translations\Vilt\Resources\TranslationsResource\Routes; -use Modules\Translations\Vilt\Resources\TranslationsResource; +use Modules\Translationsjson\Vilt\Resources\TranslationsResource; use Modules\Base\Services\Components\Routes; diff --git a/composer.json b/composer.json index 1c75b39..dc55f31 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,6 @@ "require": { "queents/vilt": "^1.0", "spatie/laravel-translatable": "^6.0", - "spatie/laravel-translation-loader": "^2.7", "google/cloud-translate": "^1.12" }, "extra": {