├── .gitignore
├── README.md
├── src
├── TranslationServiceProvider.php
├── TranslationModel.php
├── Console
│ ├── ResetCommand.php
│ ├── ExportCommand.php
│ └── ImportCommand.php
├── ExportButton.php
├── Translation.php
└── TranslationController.php
├── composer.json
├── LICENSE
├── database
└── migrations
│ └── 2017_07_17_040159_create_translations_table.php
└── resources
└── views
└── edit.blade.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | phpunit.phar
3 | /vendor
4 | composer.phar
5 | composer.lock
6 | *.project
7 | .idea/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | laravel-admin-ext/translation
2 | =============================
3 |
4 | License
5 | ------------
6 | Licensed under [The MIT License (MIT)](LICENSE).
7 |
--------------------------------------------------------------------------------
/src/TranslationServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadViewsFrom(__DIR__.'/../resources/views', 'laravel-admin-translations');
15 |
16 | if ($this->app->runningInConsole()) {
17 | $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
18 |
19 | $this->commands([
20 | Console\ImportCommand::class,
21 | Console\ExportCommand::class,
22 | Console\ResetCommand::class,
23 | ]);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravel-admin-ext/translation",
3 | "description": "Translation manager for laravel",
4 | "type": "library",
5 | "keywords": ["laravel-admin", "translation", "lang"],
6 | "homepage": "https://github.com/laravel-admin-extensions/translation",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "z-song",
11 | "email": "zosong@126.com"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=7.0.0",
16 | "laravel/framework": "~5.5",
17 | "encore/laravel-admin": "~1.5"
18 | },
19 | "require-dev": {
20 | "phpunit/phpunit": "~6.0",
21 | "laravel/laravel": "~5.5"
22 | },
23 | "autoload": {
24 | "psr-4": {
25 | "Encore\\Admin\\Translation\\": "src/"
26 | }
27 | },
28 | "extra": {
29 | "laravel": {
30 | "providers": [
31 | "Encore\\Admin\\Translation\\TranslationServiceProvider"
32 | ]
33 |
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/TranslationModel.php:
--------------------------------------------------------------------------------
1 | setConnection(config('admin.database.connection') ?: config('database.default'));
25 |
26 | $this->setTable(config('admin.extensions.translation.table', 'laravel_translations'));
27 | }
28 |
29 | public static function boot()
30 | {
31 | parent::boot();
32 |
33 | static::updating(function (Model $model) {
34 | if ($model->attributes['value'] != $model->original['value']) {
35 | $model->status = static::STATUS_CHANGED;
36 | }
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jens Segers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/src/Console/ResetCommand.php:
--------------------------------------------------------------------------------
1 | manager = $manager;
37 |
38 | parent::__construct();
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function handle()
45 | {
46 | $this->manager->resetTranslations($this->argument('locale'));
47 |
48 | $this->info('All translations are deleted');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Console/ExportCommand.php:
--------------------------------------------------------------------------------
1 | manager = $manager;
37 |
38 | parent::__construct();
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function handle()
45 | {
46 | if ($group = $this->argument('group')) {
47 | $this->manager->exportTranslations($group);
48 | } else {
49 | $this->manager->exportAllTranslations();
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Console/ImportCommand.php:
--------------------------------------------------------------------------------
1 | manager = $manager;
37 |
38 | parent::__construct();
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function handle()
45 | {
46 | $counter = $this->manager->importTranslations($this->argument('locale'), $this->option('force'));
47 |
48 | $this->info('Done importing, processed '.$counter.' items!');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/database/migrations/2017_07_17_040159_create_translations_table.php:
--------------------------------------------------------------------------------
1 | create($table, function (Blueprint $table) {
24 | $table->increments('id');
25 | $table->integer('status')->default(0);
26 | $table->string('locale');
27 | $table->string('group');
28 | $table->string('key');
29 | $table->text('value')->nullable();
30 | $table->timestamps();
31 | });
32 | }
33 |
34 | /**
35 | * Reverse the migrations.
36 | *
37 | * @return void
38 | */
39 | public function down()
40 | {
41 | $connection = config('admin.database.connection') ?: config('database.default');
42 |
43 | $table = config('admin.extensions.translations.table', 'laravel_translations');
44 |
45 | Schema::connection($connection)->dropIfExists($table);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/ExportButton.php:
--------------------------------------------------------------------------------
1 | get()->toarray();
14 |
15 | return array_flatten($groups);
16 | }
17 |
18 | protected function setupScript()
19 | {
20 | $script = <<<'SCRIPT'
21 |
22 | $('._export').click(function () {
23 | $.ajax({
24 | url: $(this).attr('href'),
25 | type: "GET",
26 | success: function (data) {
27 | $.pjax.reload('#pjax-container');
28 | toastr.success(data.message);
29 | }
30 | });
31 |
32 | return false;
33 | });
34 |
35 | SCRIPT;
36 |
37 | Admin::script($script);
38 | }
39 |
40 | /**
41 | * Render Export button.
42 | *
43 | * @return string
44 | */
45 | public function render()
46 | {
47 | $this->setupScript();
48 |
49 | $export = trans('admin.export');
50 |
51 | $links = '';
52 |
53 | foreach ($this->getGroups() as $group) {
54 | $action = route('translations.index', ['export' => $group]);
55 | $links .= "
$group";
56 | }
57 |
58 | return <<
61 |
62 |
66 |
69 |
70 |
71 |
72 | EOT;
73 | }
74 |
75 | public function __toString()
76 | {
77 | return $this->render();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Translation.php:
--------------------------------------------------------------------------------
1 | directories(resource_path('lang')));
24 |
25 | return array_unique(array_merge($exists, static::config('locales', [])));
26 | }
27 |
28 | public static function getGroups()
29 | {
30 | $groups = TranslationModel::select(DB::raw('DISTINCT `group`'))->get()->toarray();
31 |
32 | $groups = array_flatten($groups);
33 |
34 | return array_unique(array_merge($groups, static::config('groups', [])));
35 | }
36 |
37 | public function importTranslations($locale = null, $force = false)
38 | {
39 | if (is_null($locale)) {
40 | $locale = config('app.locale');
41 | }
42 |
43 | $langPath = resource_path('lang').'/'.$locale;
44 |
45 | $counter = 0;
46 |
47 | foreach (app('files')->allfiles($langPath) as $file) {
48 | $info = pathinfo($file);
49 | $group = $info['filename'];
50 |
51 | $translations = \Lang::getLoader()->load($locale, $group);
52 | if ($translations && is_array($translations)) {
53 | foreach (array_dot($translations) as $key => $value) {
54 | $importedTranslation = $this->importTranslation($key, $value, $locale, $group, $force);
55 | $counter += $importedTranslation ? 1 : 0;
56 | }
57 | }
58 | }
59 |
60 | return $counter;
61 | }
62 |
63 | public function importTranslation($key, $value, $locale, $group, $force = false)
64 | {
65 | if (is_array($value)) {
66 | return false;
67 | }
68 |
69 | $translation = TranslationModel::firstOrNew([
70 | 'locale' => $locale,
71 | 'group' => $group,
72 | 'key' => $key,
73 | ]);
74 |
75 | if ($force || !$translation->value) {
76 | $translation->value = (string) $value;
77 | $translation->status = TranslationModel::STATUS_SAVED;
78 | $translation->save();
79 |
80 | return true;
81 | }
82 |
83 | return false;
84 | }
85 |
86 | /**
87 | * @param $locale
88 | */
89 | public function resetTranslations($locale)
90 | {
91 | TranslationModel::where('locale', $locale)->delete();
92 | }
93 |
94 | public function exportAllTranslations()
95 | {
96 | $groups = TranslationModel::select(DB::raw('DISTINCT `group`'))->get();
97 |
98 | foreach ($groups as $group) {
99 | $this->exportTranslations($group->group);
100 | }
101 | }
102 |
103 | public function exportTranslations($group)
104 | {
105 | $translations = TranslationModel::where('group', $group)->get();
106 |
107 | $tree = [];
108 |
109 | foreach ($translations as $translation) {
110 | array_set($tree[$translation->locale][$translation->group], $translation->key, $translation->value);
111 | }
112 |
113 | foreach ($tree as $locale => $groups) {
114 | if (isset($groups[$group])) {
115 | $translations = $groups[$group];
116 | $path = resource_path('lang/'.$locale.'/'.$group.'.php');
117 | $output = "put($path, $output);
119 | }
120 | }
121 |
122 | TranslationModel::where('group', $group)->update(['status' => TranslationModel::STATUS_SAVED]);
123 | }
124 |
125 | /**
126 | * Register routes for laravel-admin.
127 | *
128 | * @return void
129 | */
130 | public static function registerRoutes()
131 | {
132 | /* @var \Illuminate\Routing\Router $router */
133 | Route::group(['prefix' => config('admin.route.prefix')], function ($router) {
134 | $attributes = array_merge([
135 | 'middleware' => config('admin.route.middleware'),
136 | ], static::config('route', []));
137 |
138 | Route::group($attributes, function ($router) {
139 |
140 | /* @var \Illuminate\Routing\Router $router */
141 | $router->resource('translations', 'Encore\Admin\Translation\TranslationController');
142 | });
143 | });
144 | }
145 |
146 | /**
147 | * {@inheritdoc}
148 | */
149 | public static function import()
150 | {
151 | parent::createMenu('Translations', 'translations', 'fa-lang');
152 |
153 | parent::createPermission('Translations', 'ext.translations', 'translations*');
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/TranslationController.php:
--------------------------------------------------------------------------------
1 | get('export')) {
23 | $translation = new Translation();
24 |
25 | $translation->exportTranslations($group);
26 |
27 | return response()->json([
28 | 'status' => true,
29 | 'message' => trans('admin.export_success'),
30 | ]);
31 | }
32 |
33 | return Admin::content(function (Content $content) {
34 | $content->header('Translations');
35 | $content->description('Translation list.');
36 |
37 | $content->body($this->grid());
38 | });
39 | }
40 |
41 | /**
42 | * Edit interface.
43 | *
44 | * @param $id
45 | *
46 | * @return Content
47 | */
48 | public function edit($id)
49 | {
50 | return Admin::content(function (Content $content) use ($id) {
51 | $content->header('Edit translations');
52 |
53 | list($group, $key) = explode('.', $id);
54 |
55 | $locales = $this->getLocaleOptions();
56 |
57 | $translations = TranslationModel::where(compact('group', 'key'))->get();
58 |
59 | $content->body(view('laravel-admin-translations::edit', compact('translations', 'locales', 'id', 'group', 'key')));
60 | });
61 | }
62 |
63 | public function update($id, Request $request)
64 | {
65 | list($group, $key) = explode('.', $id);
66 |
67 | $translations = TranslationModel::where(compact('group', 'key'))->get();
68 |
69 | foreach ($request->input('values') as $id => $item) {
70 | if (is_int($id)) {
71 | $model = $translations->find($id);
72 | } else {
73 | $model = new TranslationModel();
74 | $model->group = $group;
75 | $model->key = $key;
76 | }
77 |
78 | if (empty($model)) {
79 | continue;
80 | }
81 |
82 | if ($item['_remove_'] == 1) {
83 | $model->delete();
84 | continue;
85 | }
86 |
87 | $model->locale = $item['locale'];
88 | $model->value = $item['value'];
89 |
90 | $model->save();
91 | }
92 |
93 | admin_toastr(trans('admin.update_succeeded'));
94 |
95 | return redirect(route('translations.index'));
96 | }
97 |
98 | /**
99 | * Create interface.
100 | *
101 | * @return Content
102 | */
103 | public function create()
104 | {
105 | return Admin::content(function (Content $content) {
106 | $content->header('Create new translation');
107 |
108 | $form = new \Encore\Admin\Widgets\Form(compact('group', 'key'));
109 |
110 | $form->select('group')->options($this->getGroupOptions());
111 | $form->text('key');
112 |
113 | $form->divider();
114 |
115 | $form->hasMany('locales', function (Form\NestedForm $form) {
116 | $form->select('locale', 'Locale')->options($this->getLocaleOptions());
117 | $form->textarea('value', 'Text');
118 | });
119 |
120 | $form->action(route('translations.store'));
121 |
122 | $content->body(new Box('', $form));
123 | });
124 | }
125 |
126 | public function store(Request $request)
127 | {
128 | $translation = new Translation();
129 |
130 | foreach ($request->input('locales') as $item) {
131 | $translation->importTranslation(
132 | $request->input('key'),
133 | $item['value'],
134 | $item['locale'],
135 | $request->input('group')
136 | );
137 | }
138 |
139 | admin_toastr(trans('admin.save_succeeded'));
140 |
141 | return redirect(route('translations.index'));
142 | }
143 |
144 | public function grid()
145 | {
146 | return Admin::grid(TranslationModel::class, function (Grid $grid) {
147 | $grid->model()->groupBy(['group', 'key'])->select(['id', 'group', 'key', DB::raw('GROUP_CONCAT(status SEPARATOR \',\') as status'), DB::raw('GROUP_CONCAT(DISTINCT CONCAT(locale,\'###\',value) ORDER BY locale ASC SEPARATOR \'|||\') as value'), 'created_at', 'updated_at']);
148 |
149 | $grid->column('usage')->display(function () {
150 | return "trans('{$this->group}.{$this->key}')";
151 | });
152 |
153 | $grid->value('Locale : Text')->display(function ($value) {
154 | $html = '';
155 |
156 | foreach (explode('|||', $value) as $value) {
157 | list($locale, $value) = explode('###', $value);
158 | $html .= "- $locale:
- $value
";
159 | }
160 |
161 | return $html.'
';
162 | });
163 |
164 | $grid->group();
165 | $grid->key();
166 |
167 | $grid->status()->display(function ($status) {
168 | $status = explode(',', $status);
169 |
170 | $changed = in_array(TranslationModel::STATUS_CHANGED, $status);
171 |
172 | return $changed ? 'changed'
173 | : 'saved';
174 | });
175 |
176 | $grid->updated_at();
177 |
178 | $grid->filter(function (Grid\Filter $filter) {
179 | $filter->disableIdFilter();
180 |
181 | $filter->equal('group')->select($this->getGroupOptions());
182 | $filter->equal('key');
183 | $filter->equal('locale')->select($this->getLocaleOptions());
184 | });
185 |
186 | $grid->actions(function (Grid\Displayers\Actions $actions) {
187 | $actions->setKey($this->row->group.'.'.$this->row->key);
188 | });
189 |
190 | $grid->tools(function (Grid\Tools $tools) {
191 | $tools->append(new ExportButton());
192 | });
193 |
194 | $grid->disableExport();
195 | $grid->disableRowSelector();
196 | });
197 | }
198 |
199 | /**
200 | * Make a form builder.
201 | *
202 | * @return Form
203 | */
204 | protected function form()
205 | {
206 | return Admin::form(TranslationModel::class, function (Form $form) {
207 | $form->display('id', 'ID');
208 |
209 | $form->text('group');
210 | $form->text('key');
211 |
212 | $form->divider();
213 |
214 | $form->select('locale')->options($this->getLocaleOptions())->default(config('app.locale'));
215 | $form->textarea('value');
216 |
217 | $form->divider();
218 |
219 | $form->select('locale')->options($this->getLocaleOptions())->default(config('app.locale'));
220 | $form->textarea('value');
221 |
222 | $form->divider();
223 |
224 | $form->display('created_at', 'Created At');
225 | $form->display('updated_at', 'Updated At');
226 | });
227 | }
228 |
229 | public function destroy($id)
230 | {
231 | list($group, $key) = explode('.', $id);
232 |
233 | if (TranslationModel::where(compact('group', 'key'))->delete()) {
234 | return response()->json([
235 | 'status' => true,
236 | 'message' => trans('admin.delete_succeeded'),
237 | ]);
238 | } else {
239 | return response()->json([
240 | 'status' => false,
241 | 'message' => trans('admin.delete_failed'),
242 | ]);
243 | }
244 | }
245 |
246 | protected function getLocaleOptions()
247 | {
248 | $locales = Translation::getLocales();
249 |
250 | return array_combine($locales, $locales);
251 | }
252 |
253 | protected function getGroupOptions()
254 | {
255 | $groups = Translation::getGroups();
256 |
257 | return array_combine($groups, $groups);
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/resources/views/edit.blade.php:
--------------------------------------------------------------------------------
1 |
27 |
139 |
140 |
141 |
169 |
--------------------------------------------------------------------------------