diff --git a/composer.json b/composer.json index 1778075..02a764f 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ "require": { "php": "^8.0|^8.1|^8.2", "laravel/framework": "^9.21|^10.0", + "moonshine/moonshine": "^1.60", "ext-json": "*" }, "require-dev": { diff --git a/config/moonshine-laravel-translations.php b/config/moonshine-laravel-translations.php new file mode 100644 index 0000000..b19e805 --- /dev/null +++ b/config/moonshine-laravel-translations.php @@ -0,0 +1,23 @@ + [ + + //'auth', + //'http-statuses', + //'pagination', + //'passwords', + //'validation', + + //'json', // .json language files + + ], + + 'main-locale' => config('app.fallback_locale'), + + 'locales' => [ + // 'en', + // 'ru', + ], +]; diff --git a/config/package_name.php b/config/package_name.php deleted file mode 100644 index ca5d8ed..0000000 --- a/config/package_name.php +++ /dev/null @@ -1,5 +0,0 @@ -id(); + $table->string('group'); + $table->unsignedSmallInteger('list_order')->default(65535); + $table->text('key'); + $table->string('locale'); + $table->text('value')->nullable(); + $table->boolean('is_changed')->default(false)->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('moonshine_laravel_translations'); + } +}; diff --git a/src/Actions/.gitignore b/src/Actions/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/src/Actions/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/src/Actions/ExportTranslationsAction.php b/src/Actions/ExportTranslationsAction.php new file mode 100644 index 0000000..7994cc8 --- /dev/null +++ b/src/Actions/ExportTranslationsAction.php @@ -0,0 +1,81 @@ + 'local', + 'root' => lang_path(), + ]); + + $translations = MoonshineLaravelTranslation::toBase()->get(); + + foreach ($translations->groupBy('locale') as $locale => $localeData) { + foreach ($localeData->groupBy('group') as $group => $groupData) { + + $groupData = $groupData->sortBy('list_order')->pluck('value', 'key')->toArray(); + + $groupData = Arr::undot($groupData); + + if ($group == 'json') { + + $langDisk->put($locale.'.json', + json_encode($groupData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); + + continue; + } + + $langDisk->put($locale.'/'.$group.'.php', "prettyVarExport($groupData).";\n"); + + } + } + + MoonshineLaravelTranslation::query()->update(['is_changed'=>false]); + + MoonShineUI::toast( + 'Экспортировано', + 'success' + ); + + return back(); + + } + + protected function prettyVarExport($expression): string + { + $export = var_export($expression, true); + $patterns = [ + "/array \(/" => '[', + "/^([ ]*)\)(,?)$/m" => '$1]$2', + "/([\s]+)\n([\s]+)\[/ui" => ' [', + "/\n([\s ]{8})(['|\]])/ui" => "\n $2", + "/\n([\s ]{6})(['|\]])/ui" => "\n $2", + "/\n([\s ]{4})(['|\]])/ui" => "\n $2", + "/\n([\s ]{2})(['|\]])/ui" => "\n $2", + ]; + $output = preg_replace(array_keys($patterns), array_values($patterns), $export); + + + + return trim($output); + } + +} diff --git a/src/Actions/ImportTranslationsAction.php b/src/Actions/ImportTranslationsAction.php new file mode 100644 index 0000000..1e09089 --- /dev/null +++ b/src/Actions/ImportTranslationsAction.php @@ -0,0 +1,110 @@ + 'local', + 'root' => lang_path(), + ]); + + collect($langDisk->allFiles())->each(function (string $fileName) use ($langDisk) { + + $fileName = str($fileName); + + if ($fileName->startsWith('vendor')) { + return; + } + + if ($fileName->endsWith('.json')) { + $locale = $fileName->replaceLast('.json', '')->toString(); + $groupName = 'json'; + $arrayTranslations = Arr::dot(json_decode($langDisk->get($fileName->toString()), true)); + $i = 0; + foreach ($arrayTranslations as $key => $value) { + $this->updateOrCreateTranslation([ + 'group' => $groupName, + 'list_order' => $i, + 'key' => $key, + 'locale' => $locale, + 'value' => $value, + ]); + $i++; + } + return; + } + + if ($fileName->endsWith('.php')) { + $locale = $fileName->substr(0, 2)->toString(); + $groupName = $fileName->replaceFirst($locale.'/', '')->replaceLast('.php', ''); + $arrayTranslations = include lang_path($fileName->toString()); + $arrayTranslations = Arr::dot($arrayTranslations); + $i = 0; + foreach ($arrayTranslations as $key => $value) { + $this->updateOrCreateTranslation([ + 'group' => $groupName, + 'list_order' => $i, + 'key' => $key, + 'locale' => $locale, + 'value' => $value, + ]); + $i++; + } + return; + } + + //dd($fileName); + }); + + MoonShineUI::toast( + 'Импортировано', + 'success' + ); + + return back(); + + } + + protected function updateOrCreateTranslation(array $data) + { + + if (!empty(config('moonshine-laravel-translations.locales')) && !in_array($data['locale'],config('moonshine-laravel-translations.locales'))) { + return; + } + + if (!empty(config('moonshine-laravel-translations.ignored')) && in_array($data['group'],config('moonshine-laravel-translations.ignored'))) { + return; + } + + MoonshineLaravelTranslation::updateOrCreate([ + 'group' => $data['group'], + 'key' => $data['key'], + 'locale' => $data['locale'], + ], [ + 'list_order' => $data['list_order'] ?? 0, + 'value' => $data['value'], + 'is_changed' => false, + ]); + } + +} diff --git a/src/Models/.gitignore b/src/Models/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/src/Models/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/src/Models/MoonshineLaravelTranslation.php b/src/Models/MoonshineLaravelTranslation.php new file mode 100644 index 0000000..5b5ea65 --- /dev/null +++ b/src/Models/MoonshineLaravelTranslation.php @@ -0,0 +1,50 @@ + 'boolean', + ]; + + public static function getCountChanged(): int + { + return static::where('is_changed', true)->count(); + } + + public static function getLocalesList(): array + { + + if (is_null(static::$localesList)) { + static::$localesList = static::groupBy('locale')->orderBy('locale')->pluck('locale')->toArray(); + } + + return static::$localesList; + } + + public static function getGroupsList(): array + { + + if (is_null(static::$groupsList)) { + static::$groupsList = static::groupBy('group')->orderBy('group')->pluck('group')->toArray(); + } + + return static::$groupsList; + } +} diff --git a/src/Providers/MoonShineLaravelTranslationsServiceProvider.php b/src/Providers/MoonShineLaravelTranslationsServiceProvider.php new file mode 100644 index 0000000..dd78069 --- /dev/null +++ b/src/Providers/MoonShineLaravelTranslationsServiceProvider.php @@ -0,0 +1,36 @@ +loadMigrationsFrom(__DIR__ . '/../../database/migrations'); + $this->loadTranslationsFrom(__DIR__ . '/../../lang', 'moonshine-laravel-translations'); + + $this->publishes([ + __DIR__ . '/../../config/moonshine-laravel-translations.php' => config_path('moonshine-laravel-translations.php'), + ]); + + $this->mergeConfigFrom( + __DIR__ . '/../../config/moonshine-laravel-translations.php', + 'moonshine-laravel-translations' + ); + + $this->publishes([ + __DIR__ . '/../../lang' => $this->app->langPath('vendor/moonshine-laravel-translations'), + ]); + + $this->commands([]); + } +} diff --git a/src/Providers/PackageNameServiceProvider.php b/src/Providers/PackageNameServiceProvider.php deleted file mode 100644 index e03da0f..0000000 --- a/src/Providers/PackageNameServiceProvider.php +++ /dev/null @@ -1,42 +0,0 @@ -loadMigrationsFrom(__DIR__ . '/../../database/migrations'); - $this->loadTranslationsFrom(__DIR__ . '/../../lang', 'package_name'); - $this->loadViewsFrom(__DIR__ . '/../../resources/views', 'package_name'); - $this->loadRoutesFrom(__DIR__ . '/../../routes'); - - $this->publishes([ - __DIR__ . '/../../config/package_name.php' => config_path('package_name.php'), - ]); - - $this->mergeConfigFrom( - __DIR__ . '/../../config/package_name.php', - 'package_name' - ); - - $this->publishes([ - __DIR__ . '/../../public' => public_path('vendor/package_name'), - ], ['package_name-assets', 'laravel-assets']); - - $this->publishes([ - __DIR__ . '/../../lang' => $this->app->langPath('vendor/package_name'), - ]); - - $this->commands([]); - } -} diff --git a/src/Resources/.gitignore b/src/Resources/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/src/Resources/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/src/Resources/MoonShineLaravelTranslationResource.php b/src/Resources/MoonShineLaravelTranslationResource.php new file mode 100644 index 0000000..c71f5d9 --- /dev/null +++ b/src/Resources/MoonShineLaravelTranslationResource.php @@ -0,0 +1,181 @@ +is_changed) { + return 'yellow'; + } + + return parent::trClass($item, $index); + } + + public function fields(): array + { + return [ + ID::make() + ->sortable() + ->hideOnDetail(), + + NoInput::make('Изменено', 'is_changed') + ->boolean(false, true) + ->sortable() + ->hideOnForm(), + + NoInput::make('Локаль', 'locale', fn(MoonshineLaravelTranslation $moonshineLaravelTranslation + ) => Str::upper($moonshineLaravelTranslation->locale)) + ->badge('pink') + ->sortable(), + + StackFields::make('Группа/Ключ', 'group') + ->fields([ + + NoInput::make('Группа', 'group') + ->badge('blue'), + + NoInput::make('Ключ', 'key', + fn(MoonshineLaravelTranslation $moonshineLaravelTranslation + ) => str($moonshineLaravelTranslation->key) + ->replaceMatches('/:([a-z\_]+)/ui', + '$0') + ->toString()), + + ]) + ->sortable() + ->hideOnForm() + ->hideOnDetail(), + + + NoInput::make('Группа', 'group') + ->badge('blue') + ->hideOnIndex(), + + NoInput::make('Ключ', 'key', + fn(MoonshineLaravelTranslation $moonshineLaravelTranslation + ) => str($moonshineLaravelTranslation->key) + ->replaceMatches('/:([a-z\_]+)/ui', + '$0') + ->toString()) + ->hideOnIndex(), + + + NoInput::make('Значение', 'value', + fn(MoonshineLaravelTranslation $moonshineLaravelTranslation) => str($moonshineLaravelTranslation->value) + ->replaceMatches('/:([a-z\_]+)/ui', '$0') + ->toString()) + ->sortable() + ->hideOnForm(), + + TextArea::make('Значение', 'value') + ->hideOnIndex() + ->hideOnDetail(), + ]; + } + + /** + * @return array{name: string[]} + */ + public function rules($item): array + { + return [ + 'value' => ['nullable', 'string', ''], + ]; + } + + public function search(): array + { + return ['id', 'group', 'key', 'locale', 'value']; + } + + public function filters(): array + { + return [ + + SelectFilter::make('Локали', 'locale') + ->nullable() + ->options(array_combine(MoonshineLaravelTranslation::getLocalesList(), + MoonshineLaravelTranslation::getLocalesList())), + + SelectFilter::make('Группы', 'group') + ->nullable() + ->options(array_combine(MoonshineLaravelTranslation::getGroupsList(), + MoonshineLaravelTranslation::getGroupsList())), + + ]; + } + + public function actions(): array + { + return [ + + ImportTranslationsAction::make('Импортировать переводы') + ->showInLine(), + + ExportTranslationsAction::make('Экспортировать переводы')->showInLine(), + + ]; + } + + public function queryTags(): array + { + + $tags = []; + + //foreach (MoonshineLaravelTranslation::getGroupsList() as $groupList) { + // $tags[] = QueryTag::make( + // $groupList, // Tag Title + // fn(Builder $query) => $query->where('group', $groupList) // Query builder + // ); + //} + + return $tags; + } + + public function afterUpdated(MoonshineLaravelTranslation $moonshineLaravelTranslation): void + { + + if ($moonshineLaravelTranslation->wasChanged('value')) { + $moonshineLaravelTranslation->update(['is_changed' => true]); + } + + } +}