diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index ba93bfe65..d2c75f956 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -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 diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index fa10d4874..e45a0be92 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -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); diff --git a/resources/assets/js/index.js b/resources/assets/js/index.js index c23615a88..e0c7b34e5 100644 --- a/resources/assets/js/index.js +++ b/resources/assets/js/index.js @@ -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); diff --git a/resources/assets/js/services/translations.js b/resources/assets/js/services/translations.js index 06b44a580..645286c08 100644 --- a/resources/assets/js/services/translations.js +++ b/resources/assets/js/services/translations.js @@ -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]); }); diff --git a/resources/views/base.blade.php b/resources/views/base.blade.php index 367a2cd8b..f5459a717 100644 --- a/resources/views/base.blade.php +++ b/resources/views/base.blade.php @@ -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')"> diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index cfc89340d..99b21b9b2 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -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) diff --git a/resources/views/components/image-manager.blade.php b/resources/views/components/image-manager.blade.php index 7c9084ad1..6781bca5f 100644 --- a/resources/views/components/image-manager.blade.php +++ b/resources/views/components/image-manager.blade.php @@ -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=""> diff --git a/resources/views/pages/edit.blade.php b/resources/views/pages/edit.blade.php index c12bd6b4d..15f5d5d96 100644 --- a/resources/views/pages/edit.blade.php +++ b/resources/views/pages/edit.blade.php @@ -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 diff --git a/resources/views/pages/form-toolbox.blade.php b/resources/views/pages/form-toolbox.blade.php index e515c0b2d..d69be20c1 100644 --- a/resources/views/pages/form-toolbox.blade.php +++ b/resources/views/pages/form-toolbox.blade.php @@ -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"> diff --git a/resources/views/pages/form.blade.php b/resources/views/pages/form.blade.php index e9af4a4dd..380718dd7 100644 --- a/resources/views/pages/form.blade.php +++ b/resources/views/pages/form.blade.php @@ -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> - |  - @endif - <button class="text-button" type="button" data-action="insertImage">@icon('image'){{ trans('entities.pages_md_insert_image') }}</button> - | - <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> diff --git a/resources/views/pages/markdown-editor.blade.php b/resources/views/pages/markdown-editor.blade.php new file mode 100644 index 000000000..87bde33ac --- /dev/null +++ b/resources/views/pages/markdown-editor.blade.php @@ -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> + |  + @endif + <button class="text-button" type="button" data-action="insertImage">@icon('image'){{ trans('entities.pages_md_insert_image') }}</button> + | + <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 \ No newline at end of file diff --git a/resources/views/pages/wysiwyg-editor.blade.php b/resources/views/pages/wysiwyg-editor.blade.php new file mode 100644 index 000000000..f9a0f03cf --- /dev/null +++ b/resources/views/pages/wysiwyg-editor.blade.php @@ -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 \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 25d7ab692..94dd576fe 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,5 @@ <?php -Route::get('/translations', 'HomeController@getTranslations'); Route::get('/robots.txt', 'HomeController@getRobots'); // Authenticated routes...