mirror of
https://github.com/bs-community/blessing-skin-server.git
synced 2024-12-15 06:09:58 +08:00
Add support of customizing UI text
This commit is contained in:
parent
a72d46d2f2
commit
54d3b76c13
72
app/Http/Controllers/TranslationsController.php
Normal file
72
app/Http/Controllers/TranslationsController.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Foundation\Application;
|
||||
use App\Services\Translations\JavaScript;
|
||||
use Spatie\TranslationLoader\LanguageLine;
|
||||
|
||||
class TranslationsController extends Controller
|
||||
{
|
||||
public function list(Application $app)
|
||||
{
|
||||
return LanguageLine::all()->map(function ($line) use ($app) {
|
||||
$line->text = $line->getTranslation($app->getLocale());
|
||||
return $line;
|
||||
});
|
||||
}
|
||||
|
||||
public function create(Request $request, Application $app, JavaScript $js)
|
||||
{
|
||||
$data = $this->validate($request, [
|
||||
'group' => 'required|string',
|
||||
'key' => 'required|string',
|
||||
'text' => 'required|string',
|
||||
]);
|
||||
|
||||
$line = new LanguageLine();
|
||||
$line->group = $data['group'];
|
||||
$line->key = $data['key'];
|
||||
$line->setTranslation($app->getLocale(), $data['text']);
|
||||
$line->save();
|
||||
|
||||
if ($data['group'] === 'front-end') {
|
||||
$js->resetTime($app->getLocale());
|
||||
}
|
||||
$request->session()->put('success', true);
|
||||
|
||||
return redirect('/admin/i18n');
|
||||
}
|
||||
|
||||
public function update(Request $request, Application $app, JavaScript $js)
|
||||
{
|
||||
$data = $this->validate($request, [
|
||||
'id' => 'required|integer',
|
||||
'text' => 'required|string',
|
||||
]);
|
||||
|
||||
$line = LanguageLine::findOrFail($data['id']);
|
||||
$line->setTranslation($app->getLocale(), $data['text']);
|
||||
$line->save();
|
||||
|
||||
if ($line->group === 'front-end') {
|
||||
$js->resetTime($app->getLocale());
|
||||
}
|
||||
|
||||
return json(trans('admin.i18n.updated'), 0);
|
||||
}
|
||||
|
||||
public function delete(Request $request, Application $app, JavaScript $js)
|
||||
{
|
||||
['id' => $id] = $this->validate($request, ['id' => 'required|integer']);
|
||||
$line = LanguageLine::findOrFail($id);
|
||||
$line->delete();
|
||||
|
||||
if ($line->group === 'front-end') {
|
||||
$js->resetTime($app->getLocale());
|
||||
}
|
||||
|
||||
return json(trans('admin.i18n.deleted'), 0);
|
||||
}
|
||||
}
|
@ -39,6 +39,11 @@ class JavaScript
|
||||
return url("lang/$locale.js?t=$compiledModified");
|
||||
}
|
||||
|
||||
public function resetTime(string $locale): void
|
||||
{
|
||||
$this->cache->put($this->prefix.$locale, 0);
|
||||
}
|
||||
|
||||
public function plugin(string $locale): string
|
||||
{
|
||||
$path = public_path("lang/${locale}_plugin.js");
|
||||
|
@ -29,6 +29,7 @@ $menu['admin'] = [
|
||||
['title' => 'general.player-manage', 'link' => 'admin/players', 'icon' => 'fa-gamepad'],
|
||||
['title' => 'general.report-manage', 'link' => 'admin/reports', 'icon' => 'fa-flag'],
|
||||
['title' => 'general.customize', 'link' => 'admin/customize', 'icon' => 'fa-paint-brush'],
|
||||
['title' => 'general.i18n', 'link' => 'admin/i18n', 'icon' => 'fa-globe'],
|
||||
['title' => 'general.score-options', 'link' => 'admin/score', 'icon' => 'fa-credit-card'],
|
||||
['title' => 'general.options', 'link' => 'admin/options', 'icon' => 'fa-cog'],
|
||||
['title' => 'general.res-options', 'link' => 'admin/resource', 'icon' => 'fa-atom'],
|
||||
|
@ -66,6 +66,11 @@ export default [
|
||||
() => import('../views/admin/Customization'),
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'admin/i18n',
|
||||
component: () => import('../views/admin/Translations.vue'),
|
||||
el: '#table',
|
||||
},
|
||||
{
|
||||
path: 'admin/plugins/manage',
|
||||
component: () => import('../views/admin/Plugins.vue'),
|
||||
|
103
resources/assets/src/views/admin/Translations.vue
Normal file
103
resources/assets/src/views/admin/Translations.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div>
|
||||
<vue-good-table
|
||||
:rows="lines"
|
||||
:columns="columns"
|
||||
:search-options="tableOptions.search"
|
||||
:pagination-options="tableOptions.pagination"
|
||||
style-class="vgt-table striped"
|
||||
>
|
||||
<template #table-row="props">
|
||||
<span v-if="props.column.field === 'operations'">
|
||||
<el-button size="medium" @click="modify(props.row)">
|
||||
{{ $t('admin.i18n.modify') }}
|
||||
</el-button>
|
||||
<el-button type="danger" size="medium" @click="remove(props.row)">
|
||||
{{ $t('admin.i18n.delete') }}
|
||||
</el-button>
|
||||
</span>
|
||||
<span v-else-if="props.column.field === 'text'">
|
||||
<span v-if="props.row.text" v-text="props.formattedRow[props.column.field]" />
|
||||
<i v-else>{{ $t('admin.i18n.empty') }}</i>
|
||||
</span>
|
||||
<span v-else v-text="props.formattedRow[props.column.field]" />
|
||||
</template>
|
||||
</vue-good-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { VueGoodTable } from 'vue-good-table'
|
||||
import 'vue-good-table/dist/vue-good-table.min.css'
|
||||
import tableOptions from '../../components/mixins/tableOptions'
|
||||
import emitMounted from '../../components/mixins/emitMounted'
|
||||
|
||||
export default {
|
||||
name: 'Translations',
|
||||
components: {
|
||||
VueGoodTable,
|
||||
},
|
||||
mixins: [
|
||||
emitMounted,
|
||||
tableOptions,
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
lines: [],
|
||||
columns: [
|
||||
{ field: 'group', label: this.$t('admin.i18n.group') },
|
||||
{ field: 'key', label: this.$t('admin.i18n.key') },
|
||||
{ field: 'text', label: this.$t('admin.i18n.text') },
|
||||
{
|
||||
field: 'operations',
|
||||
label: this.$t('admin.operationsTitle'),
|
||||
sortable: false,
|
||||
globalSearchDisabled: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
this.lines = await this.$http.get('/admin/i18n/list')
|
||||
},
|
||||
async modify(line) {
|
||||
let text = null
|
||||
try {
|
||||
({ value: text } = await this.$prompt(this.$t('admin.i18n.updating'), {
|
||||
inputValue: line.text,
|
||||
}))
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
const { code, message } = await this.$http.put(
|
||||
'/admin/i18n',
|
||||
{ id: line.id, text }
|
||||
)
|
||||
if (code === 0) {
|
||||
line.text = text
|
||||
this.$message.success(message)
|
||||
} else {
|
||||
this.$message.warning(message)
|
||||
}
|
||||
},
|
||||
async remove({ id, originalIndex }) {
|
||||
try {
|
||||
await this.$confirm(this.$t('admin.i18n.confirmDelete'), {
|
||||
type: 'warning',
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
const { message } = await this.$http.del('/admin/i18n', { id })
|
||||
this.$delete(this.lines, originalIndex)
|
||||
this.$message.success(message)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -49,6 +49,8 @@ Vue.directive('t', (el, { value }) => {
|
||||
Vue.prototype.$http = {
|
||||
get: jest.fn(),
|
||||
post: jest.fn(),
|
||||
put: jest.fn(),
|
||||
del: jest.fn(),
|
||||
}
|
||||
|
||||
Vue.use(Button)
|
||||
|
86
resources/assets/tests/views/admin/Translations.test.ts
Normal file
86
resources/assets/tests/views/admin/Translations.test.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import Vue from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { Button } from 'element-ui'
|
||||
import { MessageBoxData } from 'element-ui/types/message-box'
|
||||
import { flushPromises } from '../../utils'
|
||||
import Translations from '@/views/admin/Translations.vue'
|
||||
|
||||
test('fetch data', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{
|
||||
id: 1, group: 'general', key: 'submit', text: '',
|
||||
},
|
||||
])
|
||||
|
||||
const wrapper = mount(Translations)
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.get).toBeCalledWith('/admin/i18n/list')
|
||||
expect(wrapper.text()).toContain('admin.i18n.empty')
|
||||
})
|
||||
|
||||
test('modify line', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{
|
||||
id: 1, group: 'general', key: 'submit', text: '',
|
||||
},
|
||||
])
|
||||
Vue.prototype.$http.put
|
||||
.mockResolvedValueOnce({ code: 1, message: 'failed' })
|
||||
.mockResolvedValueOnce({ code: 0, message: 'ok' })
|
||||
Vue.prototype.$prompt
|
||||
.mockRejectedValueOnce(null)
|
||||
.mockResolvedValueOnce({ value: '' } as MessageBoxData)
|
||||
.mockResolvedValueOnce({ value: 'wanshengwei' } as MessageBoxData)
|
||||
|
||||
const wrapper = mount(Translations)
|
||||
await flushPromises()
|
||||
const button = wrapper.findAll(Button).at(0)
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.put).not.toBeCalled()
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.put).toBeCalledWith(
|
||||
'/admin/i18n',
|
||||
{ id: 1, text: '' }
|
||||
)
|
||||
expect(Vue.prototype.$message.warning).toBeCalledWith('failed')
|
||||
expect(wrapper.text()).not.toContain('wanshengwei')
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.put).toBeCalledWith(
|
||||
'/admin/i18n',
|
||||
{ id: 1, text: 'wanshengwei' }
|
||||
)
|
||||
expect(Vue.prototype.$message.success).toBeCalledWith('ok')
|
||||
expect(wrapper.text()).toContain('wanshengwei')
|
||||
})
|
||||
|
||||
test('delete line', async () => {
|
||||
Vue.prototype.$http.get.mockResolvedValue([
|
||||
{
|
||||
id: 1, group: 'general', key: 'submit', text: '',
|
||||
},
|
||||
])
|
||||
Vue.prototype.$http.del.mockResolvedValueOnce({ message: 'ok' })
|
||||
Vue.prototype.$confirm
|
||||
.mockRejectedValueOnce(null)
|
||||
.mockResolvedValueOnce('confirm')
|
||||
|
||||
const wrapper = mount(Translations)
|
||||
await flushPromises()
|
||||
const button = wrapper.findAll(Button).at(1)
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.del).not.toBeCalled()
|
||||
|
||||
button.trigger('click')
|
||||
await flushPromises()
|
||||
expect(Vue.prototype.$http.del).toBeCalledWith('/admin/i18n', { id: 1 })
|
||||
expect(Vue.prototype.$message.success).toBeCalledWith('ok')
|
||||
expect(wrapper.text()).not.toContain('general')
|
||||
})
|
@ -84,6 +84,16 @@ customize:
|
||||
black: Black
|
||||
black-light: Black Light
|
||||
|
||||
i18n:
|
||||
add: Add New Language Line
|
||||
added: Language line added.
|
||||
updated: Language line updated.
|
||||
deleted: Language line deleted.
|
||||
group: Group
|
||||
key: Key
|
||||
text: Text
|
||||
tip: How can I use this page?
|
||||
|
||||
status:
|
||||
info: Information
|
||||
health: Health
|
||||
|
@ -296,6 +296,15 @@ admin:
|
||||
updateButton: Update Now
|
||||
downloading: Downloading...
|
||||
updateCompleted: Update completed.
|
||||
i18n:
|
||||
group: Group
|
||||
key: Key
|
||||
text: Text
|
||||
empty: (Empty)
|
||||
modify: Modify
|
||||
delete: Delete
|
||||
updating: 'Please type new text:'
|
||||
confirmDelete: Are you sure? This is irreversible.
|
||||
|
||||
report:
|
||||
tid: Texture ID
|
||||
|
@ -22,6 +22,7 @@ plugin-manage: Plugins
|
||||
plugin-market: Plugin Market
|
||||
plugin-configs: Plugin Configs
|
||||
customize: Customize
|
||||
i18n: Internationalization
|
||||
options: Options
|
||||
score-options: Score Options
|
||||
res-options: Resource Options
|
||||
|
@ -84,6 +84,16 @@ customize:
|
||||
black: 黑色主题
|
||||
black-light: 黑色主题 - 白色侧边栏
|
||||
|
||||
i18n:
|
||||
add: 添加新条目
|
||||
added: 条目增加成功
|
||||
updated: 条目更新成功
|
||||
deleted: 条目已删除
|
||||
group: 分组
|
||||
key: 键
|
||||
text: 文本
|
||||
tip: 如何使用本页面的功能?
|
||||
|
||||
status:
|
||||
info: 信息
|
||||
health: 健康
|
||||
|
@ -288,6 +288,15 @@ admin:
|
||||
updateButton: 马上升级
|
||||
downloading: 正在下载更新包
|
||||
updateCompleted: 更新完成
|
||||
i18n:
|
||||
group: 分组
|
||||
key: 键
|
||||
text: 文本
|
||||
empty: (空)
|
||||
modify: 修改
|
||||
delete: 删除
|
||||
updating: 请输入新的文本内容:
|
||||
confirmDelete: 确认删除吗?此操作不可恢复。
|
||||
|
||||
report:
|
||||
tid: 材质 ID
|
||||
|
@ -22,6 +22,7 @@ plugin-manage: 插件管理
|
||||
plugin-market: 插件市场
|
||||
plugin-configs: 插件配置
|
||||
customize: 个性化
|
||||
i18n: 多语言
|
||||
options: 站点配置
|
||||
score-options: 积分配置
|
||||
res-options: 资源配置
|
||||
|
@ -8,6 +8,7 @@
|
||||
- Allow to cache options by running `php artisan options:cache`.
|
||||
- Support multiple plugins directories. (Splited by comma in ".env" file.)
|
||||
- Added "Status" page.
|
||||
- Added support of customizing UI text.
|
||||
|
||||
## Tweaked
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
- 允许通过 `php artisan options:cache` 命令缓存站点选项
|
||||
- 支持指定多个插件目录(在 .env 文件中以逗号分隔)
|
||||
- 新增「运行状态」页面
|
||||
- 支持自定义 UI 文本
|
||||
|
||||
## 调整
|
||||
|
||||
|
67
resources/views/admin/i18n.blade.php
Normal file
67
resources/views/admin/i18n.blade.php
Normal file
@ -0,0 +1,67 @@
|
||||
@extends('admin.master')
|
||||
|
||||
@section('title', trans('general.i18n'))
|
||||
|
||||
@section('content')
|
||||
<div class="content-wrapper">
|
||||
<section class="content-header">
|
||||
<h1>@lang('general.i18n')</h1>
|
||||
</section>
|
||||
|
||||
<section class="content">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div id="table"></div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<form action="{{ url('/admin/i18n') }}" method="post">
|
||||
<div class="box box-primary">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">@lang('admin.i18n.add')</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
@if (session()->pull('success'))
|
||||
<div class="callout callout-success">@lang('admin.i18n.added')</div>
|
||||
@endif
|
||||
@if ($errors->any())
|
||||
<div class="callout callout-danger">{{ $errors->first() }}</div>
|
||||
@endif
|
||||
@csrf
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>@lang('admin.i18n.group')</td>
|
||||
<td>
|
||||
<input type="text" class="form-control" name="group" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('admin.i18n.key')</td>
|
||||
<td>
|
||||
<input type="text" class="form-control" name="key" required>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@lang('admin.i18n.text')</td>
|
||||
<td>
|
||||
<input type="text" class="form-control" name="text" required>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<input type="submit" value="@lang('general.submit')" class="el-button el-button--primary">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="callout callout-info">
|
||||
<a href="https://blessing.netlify.com/ui-text.html" target="_blank">
|
||||
@lang('admin.i18n.tip')
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@endsection
|
@ -137,6 +137,14 @@ Route::group(['middleware' => ['authorize', 'admin'], 'prefix' => 'admin'], func
|
||||
Route::post('/reports', 'ReportController@review');
|
||||
Route::any('/report-data', 'ReportController@manage');
|
||||
|
||||
Route::group(['prefix' => 'i18n'], function () {
|
||||
Route::view('', 'admin.i18n');
|
||||
Route::get('list', 'TranslationsController@list');
|
||||
Route::post('', 'TranslationsController@create');
|
||||
Route::put('', 'TranslationsController@update');
|
||||
Route::delete('', 'TranslationsController@delete');
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'plugins', 'middleware' => 'super-admin'], function () {
|
||||
Route::get('/data', 'PluginController@getPluginData');
|
||||
|
||||
|
@ -66,6 +66,17 @@ class JavaScriptTest extends TestCase
|
||||
$this->assertEquals(url('lang/en.js?t=1'), resolve(JavaScript::class)->generate('en'));
|
||||
}
|
||||
|
||||
public function testResetTime()
|
||||
{
|
||||
$this->spy(Repository::class, function ($spy) {
|
||||
$spy->shouldReceive('put')
|
||||
->with('front-end-trans-en', 0)
|
||||
->once();
|
||||
});
|
||||
|
||||
resolve(JavaScript::class)->resetTime('en');
|
||||
}
|
||||
|
||||
public function testPlugin()
|
||||
{
|
||||
$this->mock(Filesystem::class, function ($mock) {
|
||||
|
127
tests/TranslationsControllerTest.php
Normal file
127
tests/TranslationsControllerTest.php
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use App\Services\Translations\JavaScript;
|
||||
use Spatie\TranslationLoader\LanguageLine;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
|
||||
class TranslationsControllerTest extends TestCase
|
||||
{
|
||||
use DatabaseTransactions;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->actAs('admin');
|
||||
}
|
||||
|
||||
public function testList()
|
||||
{
|
||||
LanguageLine::create([
|
||||
'group' => 'general',
|
||||
'key' => 'submit',
|
||||
'text' => ['en' => 'submit'],
|
||||
]);
|
||||
|
||||
$this->getJson('/admin/i18n/list')
|
||||
->assertJson([
|
||||
[
|
||||
'group' => 'general',
|
||||
'key' => 'submit',
|
||||
'text' => 'submit',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCreate()
|
||||
{
|
||||
// Request validation
|
||||
$this->post('/admin/i18n', [])->assertRedirect('/');
|
||||
$this->post('/admin/i18n', ['group' => 'general'])
|
||||
->assertRedirect('/');
|
||||
$this->post('/admin/i18n', ['group' => 'general', 'key' => 'submit'])
|
||||
->assertRedirect('/');
|
||||
|
||||
$this->spy(JavaScript::class, function ($spy) {
|
||||
$spy->shouldReceive('resetTime')->with('en')->once();
|
||||
});
|
||||
|
||||
$this->post('/admin/i18n', [
|
||||
'group' => 'front-end',
|
||||
'key' => 'general.submit',
|
||||
'text' => 'submit',
|
||||
])->assertRedirect('/admin/i18n')->assertSessionHas('success', true);
|
||||
|
||||
$this->post('/admin/i18n', [
|
||||
'group' => 'general',
|
||||
'key' => 'submit',
|
||||
'text' => 'submit',
|
||||
])->assertRedirect('/admin/i18n');
|
||||
$this->get('/admin/i18n')->assertSee(trans('admin.i18n.added'));
|
||||
}
|
||||
|
||||
public function testUpdate()
|
||||
{
|
||||
// Request validation
|
||||
$this->putJson('/admin/i18n', [])->assertJsonValidationErrors('id');
|
||||
$this->putJson('/admin/i18n', ['id' => 'a'])
|
||||
->assertJsonValidationErrors('id');
|
||||
$this->putJson('/admin/i18n', ['id' => 1])
|
||||
->assertJsonValidationErrors('text');
|
||||
|
||||
$this->putJson('/admin/i18n', ['id' => 1, 'text' => 's'])->assertNotFound();
|
||||
|
||||
$this->spy(JavaScript::class, function ($spy) {
|
||||
$spy->shouldReceive('resetTime')->with('en')->once();
|
||||
});
|
||||
LanguageLine::create([
|
||||
'group' => 'general',
|
||||
'key' => 'submit',
|
||||
'text' => ['en' => 'submit'],
|
||||
]);
|
||||
LanguageLine::create([
|
||||
'group' => 'front-end',
|
||||
'key' => 'general.submit',
|
||||
'text' => ['en' => 'submit'],
|
||||
]);
|
||||
|
||||
$this->putJson('/admin/i18n', ['id' => 1, 'text' => 's'])
|
||||
->assertJson(['code' => 0, 'message' => trans('admin.i18n.updated')]);
|
||||
$this->putJson('/admin/i18n', ['id' => 2, 'text' => 's'])
|
||||
->assertJson(['code' => 0, 'message' => trans('admin.i18n.updated')]);
|
||||
$this->assertEquals('s', trans('general.submit'));
|
||||
$this->assertEquals('s', trans('front-end.general.submit'));
|
||||
}
|
||||
|
||||
public function testDelete()
|
||||
{
|
||||
// Request validation
|
||||
$this->deleteJson('/admin/i18n', [])->assertJsonValidationErrors('id');
|
||||
$this->deleteJson('/admin/i18n', ['id' => 'a'])
|
||||
->assertJsonValidationErrors('id');
|
||||
|
||||
$this->deleteJson('/admin/i18n', ['id' => 1])->assertNotFound();
|
||||
|
||||
$this->spy(JavaScript::class, function ($spy) {
|
||||
$spy->shouldReceive('resetTime')->with('en')->once();
|
||||
});
|
||||
LanguageLine::create([
|
||||
'group' => 'general',
|
||||
'key' => 'submit',
|
||||
'text' => ['en' => 'submit'],
|
||||
]);
|
||||
LanguageLine::create([
|
||||
'group' => 'front-end',
|
||||
'key' => 'general.submit',
|
||||
'text' => ['en' => 'submit'],
|
||||
]);
|
||||
|
||||
$this->deleteJson('/admin/i18n', ['id' => 1])
|
||||
->assertJson(['code' => 0, 'message' => trans('admin.i18n.deleted')]);
|
||||
$this->deleteJson('/admin/i18n', ['id' => 2])
|
||||
->assertJson(['code' => 0, 'message' => trans('admin.i18n.deleted')]);
|
||||
$this->assertEquals('Submit', trans('general.submit'));
|
||||
$this->assertEquals('Submit', trans('front-end.general.submit'));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user