diff --git a/assets/css/multilingual.css b/assets/css/multilingual.css index 71259b4..72f15f8 100644 --- a/assets/css/multilingual.css +++ b/assets/css/multilingual.css @@ -1,4 +1,6 @@ +.ml-modal label{color:#333 !important;text-transform:none !important} .field-multilingual{position:relative} +.field-multilingual .lang-code-display{margin-bottom:2rem;text-transform:uppercase} .field-multilingual .form-control:focus{z-index:20} .field-multilingual .ml-btn, .field-multilingual .ml-copy-btn{z-index:12;position:absolute;top:1px;bottom:1px;right:1px;width:auto;width:44px;height:36px;color:#7b7b7b;background-color:#F8F6F3;box-shadow:none;font-size:11px;letter-spacing:1px;text-transform:uppercase;margin:0 !important;padding:0 12px;border-top-left-radius:0;border-bottom-left-radius:0;transition:color 420ms ease} @@ -51,6 +53,7 @@ .field-multilingual.field-multilingual-richeditor .ml-btn{border-radius:0;border-bottom-left-radius:0.375rem} .field-multilingual.field-multilingual-markdowneditor .ml-dropdown-menu, .field-multilingual.field-multilingual-richeditor .ml-dropdown-menu{top:28px;right:1px} +.field-multilingual .loading-indicator{z-index:99} .field-multilingual.field-multilingual-repeater .ml-dropdown-menu, .field-multilingual.field-multilingual-nestedform .ml-dropdown-menu, .field-multilingual.field-multilingual-repeater .ml-copy-dropdown>.dropdown-menu, diff --git a/assets/js/multilingual.js b/assets/js/multilingual.js index c3ab027..f515ebb 100644 --- a/assets/js/multilingual.js +++ b/assets/js/multilingual.js @@ -28,6 +28,8 @@ this.$copyDropdown = $('ul.ml-copy-dropdown-menu', this.$el) this.$dropdown = $('ul.ml-dropdown-menu', this.$el) this.$placeholder = $(this.options.placeholderField) + this.$modal = $('.ml-modal', this.$el) + /* * Init locale @@ -36,13 +38,26 @@ this.$activeField = this.getLocaleElement(this.activeLocale) this.$activeButton.text(this.activeLocale) + // MODAL + this.$modal.on('click', '[data-selected-locale]', function(_event) { + var copyFromLocale = $(this).attr('data-selected-locale') + self.copyLocale(copyFromLocale) + }); + this.$copyDropdown.on('click', '[data-copy-locale]', function(_event) { var currentLocale = self.activeLocale var copyFromLocale = $(this).data('copy-locale') if (!copyFromLocale || currentLocale === copyFromLocale) return; - self.copyLocale(copyFromLocale) + const spanCurrentLocale = $('[data-display-active-locale]', self.$modal) + const spanCopyFromLocale = $('[data-display-locale]', self.$modal) + spanCurrentLocale.text(currentLocale) + spanCopyFromLocale.text(copyFromLocale) + + const copyLocaleBtn = $('[data-selected-locale]', self.$modal) + copyLocaleBtn.attr('data-selected-locale', copyFromLocale) + self.$modal.modal("show") }); this.$dropdown.on('click', '[data-switch-locale]', this.$activeButton, function(event){ @@ -99,6 +114,11 @@ return value ? value.val() : null } + MultiLingual.prototype.getProvider = function() { + const $select = $('select[name^="translation_provider_"]', this.$modal); + return $select.val() ?? ''; + } + MultiLingual.prototype.setLocaleValue = function(value, locale) { if (locale) { this.getLocaleElement(locale).val(value) @@ -108,15 +128,52 @@ } } - MultiLingual.prototype.copyLocale = function(copyFromLocale) { - if (!confirm(this.$el.data("copy-confirm"))) { + MultiLingual.prototype.autoTranslate = function(copyFromLocale, provider) { + var self = this + if (provider === '') { return } + var currentLocale = this.activeLocale + var copyFromValue = this.getLocaleValue(copyFromLocale) + + if (!copyFromValue || copyFromLocale === currentLocale) { + return + } + + this.$el + .addClass('loading-indicator-container size-form-field') + .loadIndicator() + + this.$el.request(this.options.autoTranslateHandler, { + data: { + _copy_from_locale: copyFromLocale, + _copy_from_value: copyFromValue, + _current_locale: currentLocale, + _provider: provider, + }, + success: function(data) { + self.$el.trigger('autoTranslateSuccess.oc.multilingual', [data]) + this.success(data) + }, + complete: function() { + self.$el.loadIndicator('hide') + } + }) + } + + MultiLingual.prototype.copyLocale = function(copyFromLocale) { + var currentLocale = this.activeLocale + const provider = this.getProvider() var copyFromLocaleValue = this.getLocaleValue(copyFromLocale) this.$activeField.val(copyFromLocaleValue) this.$placeholder.val(copyFromLocaleValue) - this.$el.trigger('copyLocale.oc.multilingual', [copyFromLocale, copyFromLocaleValue]) + this.$el.trigger('copyLocale.oc.multilingual', [{ + copyFromLocale: copyFromLocale, + copyFromValue: copyFromLocaleValue, + currentLocale: currentLocale, + provider: provider, + }]) } MultiLingual.prototype.setLocale = function(locale) { diff --git a/assets/less/multilingual.less b/assets/less/multilingual.less index b0bba95..b8df695 100644 --- a/assets/less/multilingual.less +++ b/assets/less/multilingual.less @@ -4,9 +4,18 @@ @copy-btn-right-offset: 41px; @copy-btn-top-offset: 28px; +.ml-modal label { + color: #333 !important; + text-transform: none !important; +} + .field-multilingual { position: relative; + .lang-code-display { + margin-bottom: 2rem; + text-transform: uppercase; + } .form-control:focus { z-index: 20; } @@ -37,8 +46,9 @@ right: 0; } } - .dropdown.ml-dropdown.open > .dropdown-menu, - .dropdown.ml-copy-dropdown.open > .dropdown-menu { + + .dropdown.ml-dropdown.open>.dropdown-menu, + .dropdown.ml-copy-dropdown.open>.dropdown-menu { top: @copy-btn-top-offset; } @@ -46,12 +56,14 @@ // Repeater-specific override &.field-multilingual-repeater, &.field-multilingual-nestedform { + .ml-btn, .ml-copy-btn { top: -36px; } - .dropdown.ml-dropdown.open > .dropdown-menu, - .dropdown.ml-copy-dropdown.open > .dropdown-menu { + + .dropdown.ml-dropdown.open>.dropdown-menu, + .dropdown.ml-copy-dropdown.open>.dropdown-menu { top: -10px; } } @@ -60,15 +72,17 @@ // media finder specific stuff &.field-multilingual-mediafinder { - .dropdown.ml-dropdown.open > .dropdown-menu, - .dropdown.ml-copy-dropdown.open > .dropdown-menu { + .dropdown.ml-dropdown.open>.dropdown-menu, + .dropdown.ml-copy-dropdown.open>.dropdown-menu { top: -10px; } - .dropdown.ml-dropdown:not(.open),// to stop it from overlapping with the upload button + .dropdown.ml-dropdown:not(.open), + // to stop it from overlapping with the upload button .dropdown.ml-copy-dropdown:not(.open) { height: 3px; } + .ml-btn, .ml-copy-btn { top: -36px; @@ -87,12 +101,13 @@ } .ml-dropdown-menu, - .ml-copy-dropdown > .dropdown-menu { + .ml-copy-dropdown>.dropdown-menu { top: @copy-btn-top-offset; } } &.field-multilingual-markdowneditor { + .mode-tab .editor-write, .mode-split .editor-preview { padding-right: 2rem; @@ -105,18 +120,22 @@ &.field-multilingual-markdowneditor, &.field-multilingual-richeditor { + .ml-btn, .ml-copy-btn { height: 38px; } - .dropdown.ml-dropdown.open > .dropdown-menu, - .dropdown.ml-copy-dropdown.open > .dropdown-menu { + + .dropdown.ml-dropdown.open>.dropdown-menu, + .dropdown.ml-copy-dropdown.open>.dropdown-menu { top: 37px; } + .dropdown.ml-dropdown:not(.open), .dropdown.ml-copy-dropdown:not(.open) { height: 3px; } + .ml-btn { border-radius: 0; border-bottom-left-radius: .375rem; @@ -128,11 +147,16 @@ } } + .loading-indicator { + z-index: 99; + } + &.field-multilingual-repeater, &.field-multilingual-nestedform { + .ml-dropdown-menu, - .ml-copy-dropdown > .dropdown-menu { - top: calc(@copy-btn-top-offset - 20px ); + .ml-copy-dropdown>.dropdown-menu { + top: calc(@copy-btn-top-offset - 20px); right: 0; } } @@ -141,6 +165,7 @@ // Fancy layout overrides .fancy-layout { .form-tabless-fields .field-multilingual { + .ml-btn, .ml-copy-btn { background-color: rgba(248, 246, 243, 0.75); diff --git a/composer.json b/composer.json index 10e3c7f..9b32062 100644 --- a/composer.json +++ b/composer.json @@ -30,12 +30,18 @@ "require": { "php": ">=7.2", "winter/wn-backend-module": ">=1.2.8", - "composer/installers": "~1.0" + "composer/installers": "~1.0", + "guzzlehttp/guzzle": "^7.10" }, "replace": { "rainlab/translate-plugin": "~1.6" }, "extra": { "installer-name": "translate" + }, + "config": { + "allow-plugins": { + "composer/installers": true + } } } diff --git a/config/config.php b/config/config.php index 98e9241..364ee26 100644 --- a/config/config.php +++ b/config/config.php @@ -63,4 +63,83 @@ 'redirectStatus' => env('TRANSLATE_REDIRECT_STATUS', 302), + /* + |-------------------------------------------------------------------------- + | Auto Translation Whitelist + |-------------------------------------------------------------------------- + | + | Specifies which form inputs should be automatically translated when using + | auto-translation. + | + | Example scenario: + | fields: + | does_not_work: <- this is the key you put into the whitelist + | label: Does not work + | trigger: + | action: hide + | field: is_delayed + | condition: checked + | + | Important Notes: + | - Only applies to formwidgets that have multiple inputs (e.g. NestedForm), + | otherwise it's ignored. + | + | Example: + | 'autoTranslateWhiteList' => ['name', 'content'] + | + */ + + 'autoTranslateWhiteList' => [], + + /* + |-------------------------------------------------------------------------- + | Default Auto Translation Provider + |-------------------------------------------------------------------------- + | + | Sets the default provider used when performing auto translation. + | This must match one of the providers defined in the "providers" section, + | otherwise a standard copy will be performed (empty string = no translation). + | + | Default is empty string as users will need to setup access to a provider. + | Empty string means "None" - just copy content without translation. + | + | Example: + | TRANSLATE_PROVIDER=google + | + */ + + 'defaultProvider' => env('TRANSLATE_PROVIDER', ''), + + /* + |-------------------------------------------------------------------------- + | Auto Translation Providers + |-------------------------------------------------------------------------- + | + | Configure the translation API services available to your application. + | You may define multiple providers; each provider will appear in the + | dropdown list when choosing a translation service. + | + | To create new providers create a new class that implements TranslationProvider + | and add it to the ProviderFactory, see translate/providers. + | + | Each provider must include: + | - url : API endpoint + | - key : API key or authentication token + | + */ + + 'providers' => [ + + 'google' => [ + 'url' => env('GOOGLE_TRANSLATE_URL', 'https://translation.googleapis.com/language/translate/v2'), + 'key' => env('GOOGLE_TRANSLATE_KEY', ''), + ], + + // Example for adding an additional provider: + // + // 'deepl' => [ + // 'url' => env('DEEPL_API_URL', 'https://api.deepl.com/v2/translate'), + // 'key' => env('DEEPL_API_KEY', ''), + // ], + ], ]; diff --git a/formwidgets/MLBlocks.php b/formwidgets/MLBlocks.php index 989306b..1abb720 100644 --- a/formwidgets/MLBlocks.php +++ b/formwidgets/MLBlocks.php @@ -18,6 +18,7 @@ class MLBlocks extends Blocks { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} @@ -107,8 +108,18 @@ public function onAddItem() public function onCopyItemLocale() { $copyFromLocale = post('_blocks_copy_locale'); + $currentLocale = post('_blocks_current_locale'); + $provider = post('_provider'); $copyFromValues = $this->getLocaleSaveDataAsArray($copyFromLocale); + if ($provider !== '' && !empty($copyFromValues)) { + $copyFromValues = $this->autoTranslateArray( + $copyFromValues, + $currentLocale, + $copyFromLocale, + $provider + ); + } $this->reprocessLocaleItems($copyFromValues); foreach ($this->formWidgets as $key => $widget) { diff --git a/formwidgets/MLMarkdownEditor.php b/formwidgets/MLMarkdownEditor.php index 518e0f2..f8a291a 100644 --- a/formwidgets/MLMarkdownEditor.php +++ b/formwidgets/MLMarkdownEditor.php @@ -15,6 +15,7 @@ class MLMarkdownEditor extends MarkdownEditor { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} diff --git a/formwidgets/MLMediaFinder.php b/formwidgets/MLMediaFinder.php index 4a19784..e9fe839 100755 --- a/formwidgets/MLMediaFinder.php +++ b/formwidgets/MLMediaFinder.php @@ -62,6 +62,7 @@ public function prepareVars() parent::prepareVars(); $this->prepareLocaleVars(); // make root path of media files accessible + $this->vars['autoTranslatable'] = false; $this->vars['mediaPath'] = $this->mediaPath = MediaLibrary::url('/'); } diff --git a/formwidgets/MLNestedForm.php b/formwidgets/MLNestedForm.php index e656b75..484950e 100644 --- a/formwidgets/MLNestedForm.php +++ b/formwidgets/MLNestedForm.php @@ -15,6 +15,7 @@ class MLNestedForm extends NestedForm { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} @@ -97,8 +98,18 @@ protected function getParentAssetPath() public function onCopyItemLocale() { $copyFromLocale = post('_repeater_copy_locale'); + $currentLocale = post('_repeater_current_locale'); + $provider = post('_provider'); $copyFromValues = $this->getLocaleSaveDataAsArray($copyFromLocale); + if ($provider !== '' && !empty($copyFromValues)) { + $copyFromValues = $this->autoTranslateArray( + $copyFromValues, + $currentLocale, + $copyFromLocale, + $provider + ); + } $this->reprocessLocaleItems($copyFromValues); diff --git a/formwidgets/MLRepeater.php b/formwidgets/MLRepeater.php index 18b6ecd..72c25b9 100644 --- a/formwidgets/MLRepeater.php +++ b/formwidgets/MLRepeater.php @@ -18,6 +18,7 @@ class MLRepeater extends Repeater { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} @@ -107,8 +108,18 @@ public function onAddItem() public function onCopyItemLocale() { $copyFromLocale = post('_repeater_copy_locale'); + $currentLocale = post('_repeater_current_locale'); + $provider = post('_provider'); $copyFromValues = $this->getLocaleSaveDataAsArray($copyFromLocale); + if ($provider !== '' && !empty($copyFromValues)) { + $copyFromValues = $this->autoTranslateArray( + $copyFromValues, + $currentLocale, + $copyFromLocale, + $provider + ); + } $this->reprocessLocaleItems($copyFromValues); foreach ($this->formWidgets as $key => $widget) { diff --git a/formwidgets/MLRichEditor.php b/formwidgets/MLRichEditor.php index 1d0613e..05c6102 100644 --- a/formwidgets/MLRichEditor.php +++ b/formwidgets/MLRichEditor.php @@ -15,6 +15,7 @@ class MLRichEditor extends RichEditor { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} diff --git a/formwidgets/MLText.php b/formwidgets/MLText.php index 872ed52..d2a0071 100644 --- a/formwidgets/MLText.php +++ b/formwidgets/MLText.php @@ -3,6 +3,7 @@ namespace Winter\Translate\FormWidgets; use Backend\Classes\FormWidgetBase; +use Exception; /** * ML Text @@ -14,6 +15,7 @@ class MLText extends FormWidgetBase { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} @@ -57,5 +59,6 @@ public function getSaveValue($value) protected function loadAssets() { $this->loadLocaleAssets(); + $this->addJs('js/mltext.js'); } } diff --git a/formwidgets/MLTextarea.php b/formwidgets/MLTextarea.php index 48884a1..b405d1d 100644 --- a/formwidgets/MLTextarea.php +++ b/formwidgets/MLTextarea.php @@ -14,6 +14,7 @@ class MLTextarea extends FormWidgetBase { use \Winter\Translate\Traits\MLControl; + use \Winter\Translate\Traits\MLAutoTranslate; /** * {@inheritDoc} diff --git a/formwidgets/mlblocks/assets/js/mlblocks.js b/formwidgets/mlblocks/assets/js/mlblocks.js index 200034d..7007cf0 100644 --- a/formwidgets/mlblocks/assets/js/mlblocks.js +++ b/formwidgets/mlblocks/assets/js/mlblocks.js @@ -90,9 +90,8 @@ this.$el.css('margin-top','36px') } } - MLBlocks.prototype.onCopyLocale = function(e, locale, localeValue) { - var self = this, - copyFromLocale = this.locale + MLBlocks.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + var self = this this.$el .addClass('loading-indicator-container size-form-field') @@ -101,10 +100,14 @@ this.$el.request(this.options.copyHandler, { data: { _blocks_copy_locale: copyFromLocale, + _blocks_current_locale: currentLocale, + _provider: provider, }, success: function(data) { - self.$el.loadIndicator('hide') this.success(data) + }, + complete: function() { + self.$el.loadIndicator('hide') } }) } diff --git a/formwidgets/mlmarkdowneditor/assets/js/mlmarkdowneditor.js b/formwidgets/mlmarkdowneditor/assets/js/mlmarkdowneditor.js index 99b3739..34883f6 100644 --- a/formwidgets/mlmarkdowneditor/assets/js/mlmarkdowneditor.js +++ b/formwidgets/mlmarkdowneditor/assets/js/mlmarkdowneditor.js @@ -49,6 +49,7 @@ this.$el.on('setLocale.oc.multilingual', this.proxy(this.onSetLocale)) this.$el.on('copyLocale.oc.multilingual', this.proxy(this.onCopyLocale)) + this.$el.on('autoTranslateSuccess.oc.multilingual', this.proxy(this.onAutoTranslateSuccess)) this.$textarea.on('changeContent.oc.markdowneditor', this.proxy(this.onChangeContent)) this.codeEditor.on('blur', this.proxy(this.toggleIsFocused)) @@ -64,6 +65,7 @@ MLMarkdownEditor.prototype.dispose = function() { this.$el.off('setLocale.oc.multilingual', this.proxy(this.onSetLocale)) this.$el.off('copyLocale.oc.multilingual', this.proxy(this.onCopyLocale)) + this.$el.off('autoTranslateSuccess.oc.multilingual', this.proxy(this.onAutoTranslateSuccess)) this.$textarea.off('changeContent.oc.markdowneditor', this.proxy(this.onChangeContent)) this.$el.off('dispose-control', this.proxy(this.dispose)) this.codeEditor.off('blur', this.proxy(this.toggleIsFocused)) @@ -86,10 +88,20 @@ } } - MLMarkdownEditor.prototype.onCopyLocale = function(e, locale, localeValue) { - if (typeof localeValue === 'string' && this.$markdownEditor.data('oc.markdownEditor')) { - this.$markdownEditor.markdownEditor('setContent', localeValue); + MLMarkdownEditor.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + if (typeof copyFromValue === 'string' && this.$markdownEditor.data('oc.markdownEditor')) { + this.$markdownEditor.markdownEditor('setContent', copyFromValue); + } + this.$el.multiLingual('autoTranslate', copyFromLocale, provider) + } + + MLMarkdownEditor.prototype.onAutoTranslateSuccess = function(e, data) { + const translatedValue = data.translatedValue[0] + if (typeof translatedValue != 'string' || !this.$markdownEditor.data('oc.markdownEditor')) { + return } + this.$el.multiLingual('setLocaleValue', translatedValue, data.translatedLocale) + this.$markdownEditor.markdownEditor('setContent', translatedValue); } MLMarkdownEditor.prototype.onChangeContent = function(ev, markdowneditor, value) { diff --git a/formwidgets/mlmarkdowneditor/partials/_mlmarkdowneditor.htm b/formwidgets/mlmarkdowneditor/partials/_mlmarkdowneditor.htm index c597ecc..b3721bf 100644 --- a/formwidgets/mlmarkdowneditor/partials/_mlmarkdowneditor.htm +++ b/formwidgets/mlmarkdowneditor/partials/_mlmarkdowneditor.htm @@ -6,6 +6,7 @@ data-copy-confirm="= e(trans('winter.translate::lang.locale.copy_confirm')); ?>" data-default-locale="= $defaultLocale->code ?>" data-placeholder-field="#= $this->getId('textarea') ?>" + data-auto-translate-handler="= $this->getEventHandler('onAutoTranslate') ?>" class="field-multilingual field-multilingual-markdowneditor dropdown = $stretch?'layout-relative stretch':'' ?>" > diff --git a/formwidgets/mlmediafinder/assets/js/mlmediafinder.js b/formwidgets/mlmediafinder/assets/js/mlmediafinder.js index f6b433a..8d7a6b1 100755 --- a/formwidgets/mlmediafinder/assets/js/mlmediafinder.js +++ b/formwidgets/mlmediafinder/assets/js/mlmediafinder.js @@ -96,8 +96,8 @@ this.setPath(localeValue) } - MLMediaFinder.prototype.onCopyLocale = function(e, locale, localeValue) { - this.setPath(localeValue) + MLMediaFinder.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + this.setPath(copyFromValue) } MLMediaFinder.prototype.setPath = function(localeValue) { diff --git a/formwidgets/mlnestedform/assets/js/mlnestedform.js b/formwidgets/mlnestedform/assets/js/mlnestedform.js index 3490f1a..64ba1e8 100644 --- a/formwidgets/mlnestedform/assets/js/mlnestedform.js +++ b/formwidgets/mlnestedform/assets/js/mlnestedform.js @@ -77,9 +77,8 @@ BaseProto.dispose.call(this) } - MLNestedForm.prototype.onCopyLocale = function(e, locale, localeValue) { - var self = this, - copyFromLocale = this.locale + MLNestedForm.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + var self = this this.$el .addClass('loading-indicator-container size-form-field') @@ -88,10 +87,14 @@ this.$el.request(this.options.copyHandler, { data: { _repeater_copy_locale: copyFromLocale, + _repeater_current_locale: currentLocale, + _provider: provider, }, success: function(data) { - self.$el.loadIndicator('hide') this.success(data) + }, + complete: function() { + self.$el.loadIndicator('hide') } }) } diff --git a/formwidgets/mlrepeater/assets/js/mlrepeater.js b/formwidgets/mlrepeater/assets/js/mlrepeater.js index 0c3076c..7c70768 100644 --- a/formwidgets/mlrepeater/assets/js/mlrepeater.js +++ b/formwidgets/mlrepeater/assets/js/mlrepeater.js @@ -80,9 +80,8 @@ this.$el.toggleClass('is-empty', isEmpty) } - MLRepeater.prototype.onCopyLocale = function(e, locale, localeValue) { - var self = this, - copyFromLocale = this.locale + MLRepeater.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + var self = this this.$el .addClass('loading-indicator-container size-form-field') @@ -91,10 +90,14 @@ this.$el.request(this.options.copyHandler, { data: { _repeater_copy_locale: copyFromLocale, + _repeater_current_locale: currentLocale, + _provider: provider, }, success: function(data) { - self.$el.loadIndicator('hide') this.success(data) + }, + complete: function() { + self.$el.loadIndicator('hide') } }) } diff --git a/formwidgets/mlricheditor/assets/js/mlricheditor.js b/formwidgets/mlricheditor/assets/js/mlricheditor.js index 2c3965c..6e5d699 100644 --- a/formwidgets/mlricheditor/assets/js/mlricheditor.js +++ b/formwidgets/mlricheditor/assets/js/mlricheditor.js @@ -48,6 +48,7 @@ this.$el.on('setLocale.oc.multilingual', this.proxy(this.onSetLocale)) this.$el.on('copyLocale.oc.multilingual', this.proxy(this.onCopyLocale)) + this.$el.on('autoTranslateSuccess.oc.multilingual', this.proxy(this.onAutoTranslateSuccess)) this.$textarea.on('syncContent.oc.richeditor', this.proxy(this.onSyncContent)) this.editor.events.on('focus', this.proxy(this.toggleIsFocused)); @@ -67,6 +68,7 @@ MLRichEditor.prototype.dispose = function() { this.$el.off('setLocale.oc.multilingual', this.proxy(this.onSetLocale)) this.$el.off('copyLocale.oc.multilingual', this.proxy(this.onCopyLocale)) + this.$el.off('autoTranslateSuccess.oc.multilingual', this.proxy(this.onAutoTranslateSuccess)) this.$textarea.off('syncContent.oc.richeditor', this.proxy(this.onSyncContent)) $(window).off('resize', this.proxy(this.updateLayout)) $(window).off('oc.updateUi', this.proxy(this.updateLayout)) @@ -91,10 +93,20 @@ this.$richeditor.richEditor('setContent', localeValue); } } - MLRichEditor.prototype.onCopyLocale = function(e, locale, localeValue) { - if (typeof localeValue === 'string' && this.$richeditor.data('oc.richEditor')) { - this.$richeditor.richEditor('setContent', localeValue); + MLRichEditor.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + if (typeof copyFromValue === 'string' && this.$richeditor.data('oc.richEditor')) { + this.$richeditor.richEditor('setContent', copyFromValue); + } + this.$el.multiLingual('autoTranslate', copyFromLocale, provider) + } + + MLRichEditor.prototype.onAutoTranslateSuccess = function(e, data) { + const translatedValue = data.translatedValue[0] + if (typeof translatedValue != 'string' || !this.$richeditor.data('oc.richEditor')) { + return } + this.$el.multiLingual('setLocaleValue', translatedValue, data.translatedLocale) + this.$richeditor.richEditor('setContent', translatedValue); } MLRichEditor.prototype.onSyncContent = function(ev, richeditor, value) { diff --git a/formwidgets/mlricheditor/partials/_mlricheditor.htm b/formwidgets/mlricheditor/partials/_mlricheditor.htm index 7e6c019..acc1af0 100644 --- a/formwidgets/mlricheditor/partials/_mlricheditor.htm +++ b/formwidgets/mlricheditor/partials/_mlricheditor.htm @@ -6,6 +6,7 @@ data-copy-confirm="= e(trans('winter.translate::lang.locale.copy_confirm')); ?>" data-default-locale="= $defaultLocale->code ?>" data-placeholder-field="#= $this->getId('textarea') ?>" + data-auto-translate-handler="= $this->getEventHandler('onAutoTranslate') ?>" class="field-multilingual field-multilingual-textarea field-multilingual-richeditor dropdown = $stretch?'layout-relative stretch':'' ?>" > diff --git a/formwidgets/mltext/assets/js/mltext.js b/formwidgets/mltext/assets/js/mltext.js new file mode 100644 index 0000000..f87ca22 --- /dev/null +++ b/formwidgets/mltext/assets/js/mltext.js @@ -0,0 +1,89 @@ +/* + * MLText plugin + * + * Data attributes: + * - data-control="mltext" - enables the plugin on an element + * + * JavaScript API: + * $('a#someElement').mlText({ option: 'value' }) + * + */ + ++function($) { + + var Base = $.wn.foundation.base, + BaseProto = Base.prototype + + "use strict"; + + var MLText = function(element, options) { + this.options = options + this.$el = $(element) + $.wn.foundation.controlUtils.markDisposable(element) + Base.call(this) + + this.init() + } + + MLText.prototype = Object.create(BaseProto) + MLText.prototype.constructor = MLText + + MLText.DEFAULTS = { + autoTranslateHandler: null, + // switchHan + // defaultLocale: 'en' + } + + MLText.prototype.init = function() { + this.$el.multiLingual() + + // NOTE: this.$el.on('setLocale.oc.multilingual', this.proxy(this.onSetLocale)) + this.$el.on('copyLocale.oc.multilingual', this.proxy(this.onCopyLocale)) + this.$el.on('autoTranslateSuccess.oc.multilingual', this.proxy(this.onAutoTranslateSuccess)) + this.$el.one('dispose-control', this.proxy(this.dispose)) + } + + MLText.prototype.dispose = function() { + this.$el.off('copyLocale.oc.multilingual', this.proxy(this.onCopyLocale)) + this.$el.off('autoTranslateSuccess.oc.multilingual', this.proxy(this.onAutoTranslateSuccess)) + this.$el.off('dispose-control', this.proxy(this.dispose)) + + this.$el.removeData('oc.mlText') + this.$el = null + this.options = null + } + + MLText.prototype.onCopyLocale = function(e, {copyFromLocale, copyFromValue, currentLocale, provider}) { + this.$el.multiLingual('autoTranslate', copyFromLocale, provider) + } + + MLText.prototype.onAutoTranslateSuccess = function(e, data) { + const translatedValue = data.translatedValue[0] + if (data.translatedValue && data.translatedLocale) { + const $visibleInput = $('input.form-control', this.$el) + $visibleInput.val(translatedValue).trigger('input') + this.$el.multiLingual('setLocaleValue', translatedValue, data.translatedLocale) + } + } + + $.fn.mlText = function(option) { + return this.each(function() { + var $this = $(this) + var data = $this.data('oc.mlText') + var options = $.extend({}, MLText.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('oc.mlText', (data = new MLText(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.mlText.noConflict = function() { + $.fn.mlText = old + return this + } + + $(document).render(function() { + $('[data-control="mltext"]').mlText() + }) + +}(window.jQuery); diff --git a/formwidgets/mltext/partials/_mltext.htm b/formwidgets/mltext/partials/_mltext.htm index e846d9a..758bba3 100644 --- a/formwidgets/mltext/partials/_mltext.htm +++ b/formwidgets/mltext/partials/_mltext.htm @@ -4,10 +4,11 @@