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]);
+ }
+
+ }
+}