Updated JS translations to be inserted from back-end

Removes old awkward JS translations endpoint.
New system still a little akward in code but not now in process.

Also extracted out page editors into their own files.

Closes #1258
This commit is contained in:
Dan Brown 2019-07-06 14:52:25 +01:00
parent 6fa093d9d0
commit 15c39c1976
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
13 changed files with 138 additions and 102 deletions

View File

@ -91,35 +91,6 @@ class HomeController extends Controller
return view('common.home', $commonData);
}
/**
* Get a js representation of the current translations
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
* @throws \Exception
*/
public function getTranslations()
{
$locale = app()->getLocale();
$cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale;
if (cache()->has($cacheKey) && config('app.env') !== 'development') {
$resp = cache($cacheKey);
} else {
$translations = [
// Get only translations which might be used in JS
'common' => trans('common'),
'components' => trans('components'),
'entities' => trans('entities'),
'errors' => trans('errors')
];
$resp = 'window.translations = ' . json_encode($translations);
cache()->put($cacheKey, $resp, 120);
}
return response($resp, 200, [
'Content-Type' => 'application/javascript'
]);
}
/**
* Get custom head HTML, Used in ajax calls to show in editor.
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View

View File

@ -39,6 +39,14 @@ class AppServiceProvider extends ServiceProvider
return "<?php echo icon($expression); ?>";
});
Blade::directive('exposeTranslations', function($expression) {
return "<?php \$__env->startPush('translations'); ?>" .
"<?php foreach({$expression} as \$key): ?>" .
'<meta name="translation" key="<?php echo e($key); ?>" value="<?php echo e(trans($key)); ?>">' . "\n" .
"<?php endforeach; ?>" .
'<?php $__env->stopPush(); ?>';
});
// Allow longer string lengths after upgrade to utf8mb4
Schema::defaultStringLength(191);

View File

@ -16,7 +16,7 @@ window.$events = eventManager;
// Translation setup
// Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
import Translations from "./services/translations"
const translator = new Translations(window.translations);
const translator = new Translations();
window.trans = translator.get.bind(translator);
window.trans_choice = translator.getPlural.bind(translator);

View File

@ -10,7 +10,20 @@ class Translator {
* @param translations
*/
constructor(translations) {
this.store = translations;
this.store = new Map();
this.parseTranslations();
}
/**
* Parse translations out of the page and place into the store.
*/
parseTranslations() {
const translationMetaTags = document.querySelectorAll('meta[name="translation"]');
for (let tag of translationMetaTags) {
const key = tag.getAttribute('key');
const value = tag.getAttribute('value');
this.store.set(key, value);
}
}
/**
@ -20,7 +33,7 @@ class Translator {
* @returns {*}
*/
get(key, replacements) {
let text = this.getTransText(key);
const text = this.getTransText(key);
return this.performReplacements(text, replacements);
}
@ -33,26 +46,26 @@ class Translator {
* @returns {*}
*/
getPlural(key, count, replacements) {
let text = this.getTransText(key);
let splitText = text.split('|');
const text = this.getTransText(key);
const splitText = text.split('|');
const exactCountRegex = /^{([0-9]+)}/;
const rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
let result = null;
let exactCountRegex = /^{([0-9]+)}/;
let rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
for (let i = 0, len = splitText.length; i < len; i++) {
let t = splitText[i];
for (const i = 0, len = splitText.length; i < len; i++) {
const t = splitText[i];
// Parse exact matches
let exactMatches = t.match(exactCountRegex);
const exactMatches = t.match(exactCountRegex);
if (exactMatches !== null && Number(exactMatches[1]) === count) {
result = t.replace(exactCountRegex, '').trim();
break;
}
// Parse range matches
let rangeMatches = t.match(rangeRegex);
const rangeMatches = t.match(rangeRegex);
if (rangeMatches !== null) {
let rangeStart = Number(rangeMatches[1]);
const rangeStart = Number(rangeMatches[1]);
if (rangeStart <= count && (rangeMatches[2] === '*' || Number(rangeMatches[2]) >= count)) {
result = t.replace(rangeRegex, '').trim();
break;
@ -74,14 +87,10 @@ class Translator {
* @returns {String|Object}
*/
getTransText(key) {
let splitKey = key.split('.');
let value = splitKey.reduce((a, b) => {
return a !== undefined ? a[b] : a;
}, this.store);
const value = this.store.get(key);
if (value === undefined) {
console.log(`Translation with key "${key}" does not exist`);
value = key;
console.warn(`Translation with key "${key}" does not exist`);
}
return value;
@ -95,10 +104,10 @@ class Translator {
*/
performReplacements(string, replacements) {
if (!replacements) return string;
let replaceMatches = string.match(/:([\S]+)/g);
const replaceMatches = string.match(/:([\S]+)/g);
if (replaceMatches === null) return string;
replaceMatches.forEach(match => {
let key = match.substring(1);
const key = match.substring(1);
if (typeof replacements[key] === 'undefined') return;
string = string.replace(match, replacements[key]);
});

View File

@ -13,14 +13,17 @@
<link rel="stylesheet" href="{{ versioned_asset('dist/styles.css') }}">
<link rel="stylesheet" media="print" href="{{ versioned_asset('dist/print-styles.css') }}">
<!-- Scripts -->
<script src="{{ baseUrl('/translations') }}"></script>
@yield('head')
<!-- Custom Styles & Head Content -->
@include('partials.custom-styles')
@include('partials.custom-head')
@stack('head')
<!-- Translations for JS -->
@stack('translations')
</head>
<body class="@yield('body-class')">

View File

@ -1,4 +1,12 @@
<div page-comments page-id="{{ $page->id }}" class="comments-list">
@exposeTranslations([
'entities.comment_updated_success',
'entities.comment_deleted_success',
'entities.comment_created_success',
'entities.comment_count',
])
<div comment-count-bar class="grid half left-focus v-center no-row-gap">
<h5 comments-title>{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h5>
@if (count($page->comments) === 0)

View File

@ -1,4 +1,13 @@
<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to ?? 0 }}">
@exposeTranslations([
'components.image_delete_success',
'components.image_upload_success',
'errors.server_upload_limit',
'components.image_upload_remove',
'components.file_upload_timeout',
])
<div overlay v-cloak @click="hide">
<div class="popup-body" @click.stop="">

View File

@ -10,6 +10,8 @@
<div class="flex-fill flex">
<form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
{{ csrf_field() }}
@if(!isset($isDraft))
<input type="hidden" name="_method" value="PUT">
@endif

View File

@ -18,6 +18,17 @@
@if(userCan('attachment-create-all'))
<div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id ?? 0 }}">
@exposeTranslations([
'entities.attachments_file_uploaded',
'entities.attachments_file_updated',
'entities.attachments_link_attached',
'entities.attachments_updated_success',
'errors.server_upload_limit',
'components.image_upload_remove',
'components.file_upload_timeout',
])
<h4>{{ trans('entities.attachments') }}</h4>
<div class="px-l files">

View File

@ -1,4 +1,3 @@
<div class="page-editor flex-fill flex" id="page-editor"
drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
drawio-enabled="{{ config('services.drawio') ? 'true' : 'false' }}"
@ -8,7 +7,14 @@
page-new-draft="{{ $model->draft ?? 0 }}"
page-update-draft="{{ $model->isDraft ?? 0 }}">
{{ csrf_field() }}
@exposeTranslations([
'entities.pages_editing_draft',
'entities.pages_editing_page',
'errors.page_draft_autosave_fail',
'entities.pages_editing_page',
'entities.pages_draft_discarded',
'entities.pages_edit_set_changelog',
])
{{--Header Bar--}}
<div class="primary-background-light toolbar page-edit-toolbar">
@ -65,57 +71,12 @@
{{--WYSIWYG Editor--}}
@if(setting('app-editor') === 'wysiwyg')
<div wysiwyg-editor class="flex-fill flex">
<textarea id="html-editor" name="html" rows="5" v-pre
@if($errors->has('html')) class="text-neg" @endif>@if(isset($model) || old('html')){{htmlspecialchars( old('html') ? old('html') : $model->html)}}@endif</textarea>
</div>
@if($errors->has('html'))
<div class="text-neg text-small">{{ $errors->first('html') }}</div>
@endif
@include('pages.wysiwyg-editor', ['model' => $model])
@endif
{{--Markdown Editor--}}
@if(setting('app-editor') === 'markdown')
<div v-pre id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
<div class="markdown-editor-wrap active">
<div class="editor-toolbar">
<span class="float left editor-toolbar-label">{{ trans('entities.pages_md_editor') }}</span>
<div class="float right buttons">
@if(config('services.drawio'))
<button class="text-button" type="button" data-action="insertDrawing">@icon('drawing'){{ trans('entities.pages_md_insert_drawing') }}</button>
&nbsp;|&nbsp
@endif
<button class="text-button" type="button" data-action="insertImage">@icon('image'){{ trans('entities.pages_md_insert_image') }}</button>
&nbsp;|&nbsp;
<button class="text-button" type="button" data-action="insertLink">@icon('link'){{ trans('entities.pages_md_insert_link') }}</button>
</div>
</div>
<div markdown-input class="flex flex-fill">
<textarea id="markdown-editor-input" name="markdown" rows="5"
@if($errors->has('markdown')) class="text-neg" @endif>@if(isset($model) || old('markdown')){{htmlspecialchars( old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown))}}@endif</textarea>
</div>
</div>
<div class="markdown-editor-wrap">
<div class="editor-toolbar">
<div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
</div>
<div class="markdown-display page-content">
</div>
</div>
<input type="hidden" name="html"/>
</div>
@if($errors->has('markdown'))
<div class="text-neg text-small">{{ $errors->first('markdown') }}</div>
@endif
@include('pages.markdown-editor', ['model' => $model])
@endif
</div>

View File

@ -0,0 +1,42 @@
<div v-pre id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
@exposeTranslations([
'errors.image_upload_error',
])
<div class="markdown-editor-wrap active">
<div class="editor-toolbar">
<span class="float left editor-toolbar-label">{{ trans('entities.pages_md_editor') }}</span>
<div class="float right buttons">
@if(config('services.drawio'))
<button class="text-button" type="button" data-action="insertDrawing">@icon('drawing'){{ trans('entities.pages_md_insert_drawing') }}</button>
&nbsp;|&nbsp
@endif
<button class="text-button" type="button" data-action="insertImage">@icon('image'){{ trans('entities.pages_md_insert_image') }}</button>
&nbsp;|&nbsp;
<button class="text-button" type="button" data-action="insertLink">@icon('link'){{ trans('entities.pages_md_insert_link') }}</button>
</div>
</div>
<div markdown-input class="flex flex-fill">
<textarea id="markdown-editor-input" name="markdown" rows="5"
@if($errors->has('markdown')) class="text-neg" @endif>@if(isset($model) || old('markdown')){{htmlspecialchars( old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown))}}@endif</textarea>
</div>
</div>
<div class="markdown-editor-wrap">
<div class="editor-toolbar">
<div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
</div>
<div class="markdown-display page-content">
</div>
</div>
<input type="hidden" name="html"/>
</div>
@if($errors->has('markdown'))
<div class="text-neg text-small">{{ $errors->first('markdown') }}</div>
@endif

View File

@ -0,0 +1,13 @@
<div wysiwyg-editor class="flex-fill flex">
@exposeTranslations([
'errors.image_upload_error',
])
<textarea id="html-editor" name="html" rows="5" v-pre
@if($errors->has('html')) class="text-neg" @endif>@if(isset($model) || old('html')){{htmlspecialchars( old('html') ? old('html') : $model->html)}}@endif</textarea>
</div>
@if($errors->has('html'))
<div class="text-neg text-small">{{ $errors->first('html') }}</div>
@endif

View File

@ -1,6 +1,5 @@
<?php
Route::get('/translations', 'HomeController@getTranslations');
Route::get('/robots.txt', 'HomeController@getRobots');
// Authenticated routes...