From b9d3ba5c534e19c06af0140749ad821f7849d052 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 22 Aug 2026 09:12:31 -0400 Subject: [PATCH 1/4] do not open the provider selection popup if only a single provider exists --- assets/js/multilingual.js | 5 +++++ traits/mlcontrol/partials/_locale_selector.htm | 1 + 2 files changed, 6 insertions(+) diff --git a/assets/js/multilingual.js b/assets/js/multilingual.js index 4607b95..b4dab80 100644 --- a/assets/js/multilingual.js +++ b/assets/js/multilingual.js @@ -49,12 +49,17 @@ if (!copyFromLocale || currentLocale === copyFromLocale) return; // No usable translation provider configured: keep the plain one-click copy. + var defaultProvider = $(this).data('default-provider') var copyOpenHandler = $(this).data('copy-open-handler') if (!copyOpenHandler) { self.copyLocale(copyFromLocale, '') return } + if (defaultProvider) { + self.copyLocale(copyFromLocale, defaultProvider) + return + } self.$el.on('complete.oc.popup', function (e, $source, $popup) { const $button = $popup.find(`[data-widget-id="${self.$el.attr('id')}"]`) $button.on('click', function(event) { diff --git a/traits/mlcontrol/partials/_locale_selector.htm b/traits/mlcontrol/partials/_locale_selector.htm index 1eae53b..9d71e4a 100644 --- a/traits/mlcontrol/partials/_locale_selector.htm +++ b/traits/mlcontrol/partials/_locale_selector.htm @@ -27,6 +27,7 @@ type="button" class="ml-locale-copy icon-copy" data-copy-locale="" + data-default-provider="" data-copy-open-handler="getEventHandler('onShowTranslationMethodSelector')) ?>" title=" $name])) ?>"> From e53f5aa8060c0886813ec393f14a1b800d891605 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 22 Aug 2026 12:57:32 -0400 Subject: [PATCH 2/4] need shift click to autotranslate without popup --- assets/js/multilingual.js | 2 +- lang/en/lang.php | 1 + models/Setting.php | 15 +++++++++++++++ models/setting/fields.yaml | 8 ++++++++ traits/MLControl.php | 15 +++++++++++---- 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/assets/js/multilingual.js b/assets/js/multilingual.js index b4dab80..1d7c65d 100644 --- a/assets/js/multilingual.js +++ b/assets/js/multilingual.js @@ -56,7 +56,7 @@ return } - if (defaultProvider) { + if (defaultProvider && event.shiftKey) { self.copyLocale(copyFromLocale, defaultProvider) return } diff --git a/lang/en/lang.php b/lang/en/lang.php index 827da3e..0e65bd9 100755 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -12,6 +12,7 @@ 'settings' => [ 'title' => 'Translation Providers', 'description' => 'Configure Google & DeepL machine translation.', + 'default_provider' => 'Default translation provider', 'tab_google' => 'Google Translate', 'tab_deepl' => 'DeepL', diff --git a/models/Setting.php b/models/Setting.php index 0f9620f..8f1f1e7 100644 --- a/models/Setting.php +++ b/models/Setting.php @@ -1,5 +1,6 @@ set('winter.translate::providers.deepl.key', $key); $config->set('winter.translate::providers.deepl.url', $settings->getDeeplUrl()); } + $config->set('winter.translate::defaultProvider', $settings->defaultProvider); + } + + public function filterFields($fields) + { + $providers = []; + if (!empty($fields->google_api_key->value)) { + $providers['google'] = 'Google'; + } + if (!empty($fields->deepl_api_key->value)) { + $providers['deepl'] = 'Deepl'; + } + + $fields->defaultProvider->options = $providers; } } diff --git a/models/setting/fields.yaml b/models/setting/fields.yaml index a01c2ca..da9b996 100644 --- a/models/setting/fields.yaml +++ b/models/setting/fields.yaml @@ -2,6 +2,14 @@ # Translation provider settings # =================================== +fields: + defaultProvider: + label: winter.translate::lang.settings.default_provider + type: dropdown + emptyOption: -- select -- + dependsOn: [google_api_key, deepl_api_key] + span: left + tabs: fields: diff --git a/traits/MLControl.php b/traits/MLControl.php index faeefa6..228e698 100644 --- a/traits/MLControl.php +++ b/traits/MLControl.php @@ -129,14 +129,21 @@ public function prepareLocaleVars() $this->vars['defaultLocale'] = $this->defaultLocale; $this->vars['locales'] = Locale::listAvailable(); $this->vars['providers'] = $usableProviders; + $this->vars['defaultProvider'] = $this->getDefaultProvider($usableProviders); + $this->vars['field'] = $this->makeRenderFormField(); + } + + public function getDefaultProvider($usableProviders): ?string + { // Pre-select a provider only when exactly one is usable — a lone configured // provider is an unambiguous default that saves a click. With several, stay // on "None" so the user consciously picks a service rather than silently // defaulting to a paid one. - $this->vars['defaultProvider'] = count($usableProviders) === 1 - ? (string) array_key_first($usableProviders) - : ''; - $this->vars['field'] = $this->makeRenderFormField(); + if (count($usableProviders) === 1) { + return (string) array_key_first($usableProviders); + } else { + return Config::get('winter.translate::defaultProvider'); + } } /** From 516192e8c1de566a85f2b3710243a23e4565a3ec Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 22 Aug 2026 13:05:41 -0400 Subject: [PATCH 3/4] add a note about the SHIFT modifier to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 234c35d..8bd71b1 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,8 @@ Every multilingual backend field has a "copy from another locale" action. When a **This is opt-in and invisible by default:** with no provider configured, the copy action stays a plain one-click copy — no provider popup, no extra UI. As soon as a provider key is set, a small "translation method" picker appears so the user can choose *None*, *Google* or *DeepL* when copying. +Note: if the SHIFT key is pressed while clicking the copy icon, the "translation method" picker will be skipped if there is a default provider setup or if there is a single provider configured. + ### Configuring a provider The easiest way is the backend settings screen: **Settings → Translation Providers**. It has a guided tab for each provider with step-by-step instructions (and direct links to each provider's console) beside a masked field for the API key: From 23afdf61d2d747cb4ac95f941951f9d34a086db3 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sun, 23 Aug 2026 00:01:48 -0400 Subject: [PATCH 4/4] allow direct copy without translation withautoTranslate ctrl-click --- README.md | 3 +++ assets/js/multilingual.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/README.md b/README.md index 8bd71b1..c56509e 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,9 @@ Every multilingual backend field has a "copy from another locale" action. When a Note: if the SHIFT key is pressed while clicking the copy icon, the "translation method" picker will be skipped if there is a default provider setup or if there is a single provider configured. +Note: if the CTRL key is pressed while clicking the copy icon, the locale value is copied without translation + (same as selecting None as the translation provider). + ### Configuring a provider The easiest way is the backend settings screen: **Settings → Translation Providers**. It has a guided tab for each provider with step-by-step instructions (and direct links to each provider's console) beside a masked field for the API key: diff --git a/assets/js/multilingual.js b/assets/js/multilingual.js index 1d7c65d..ab07c24 100644 --- a/assets/js/multilingual.js +++ b/assets/js/multilingual.js @@ -60,6 +60,10 @@ self.copyLocale(copyFromLocale, defaultProvider) return } + if (event.ctrlKey) { + self.copyLocale(copyFromLocale, "") + return + } self.$el.on('complete.oc.popup', function (e, $source, $popup) { const $button = $popup.find(`[data-widget-id="${self.$el.attr('id')}"]`) $button.on('click', function(event) {