+semver:minor Replace retired BingTranslator with alternative on-the-fly localization - #167
+semver:minor Replace retired BingTranslator with alternative on-the-fly localization#167tombogle wants to merge 3 commits into
Conversation
…MicrosoftTranslator The Bing/Microsoft Translator v1 SOAP API used by the internal BingTranslator class (LanguageChoosingDialog's fail-safe, on-the-fly translation of its title/message/OK button) was retired years ago, so this had been silently falling back to English the whole time. Replaces it with: - MyMemoryTranslator (internal, new default): a free, keyless REST API with no provisioning required, replacing the also-dead GoogleTranslator.cs in place. - MicrosoftTranslator (new public class): the real Azure AI Translator v3 REST API, opt-in via a subscription key (MicrosoftTranslator.SubscriptionKey or the L10NSHARP_TRANSLATOR_KEY/L10NSHARP_TRANSLATOR_REGION env vars), for host apps that want more robust translation. TranslatorBase is now public (required for MicrosoftTranslator to subclass it publicly). LanguageChoosingDialog picks MicrosoftTranslator when configured, else MyMemoryTranslator. Also removes BingTranslator.cs, the generated WCF service reference, and the System.ServiceModel/System.Security.Cryptography.Xml dependencies they required, since nothing REST-based needs WCF. Adds a "Show Language Chooser" button to the SampleApp (randomly demoing "de", "it", or "fr") so the fix is visible without faking a missing-locale scenario. "ar" is deliberately excluded: LanguageChoosingDialog doesn't set RightToLeft, so Arabic would demo a layout bug rather than the translator. Fixes #163. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
andrew-polk
left a comment
There was a problem hiding this comment.
@andrew-polk partially reviewed 19 files and all commit messages, and made 4 comments.
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on imnasnainaec and tombogle).
src/L10NSharp.Windows.Forms/Translators/MicrosoftTranslator.cs line 97 at r1 (raw file):
var requestBody = Encoding.UTF8.GetString(ms.ToArray()); using var client = new HttpClient();
devin says
src/L10NSharp.Windows.Forms/Translators/MicrosoftTranslator.cs:97
Connections cannot be reused
Each translation creates a new HttpClient. Repeated dialogs open separate connections for every string and add avoidable network overhead.
src/L10NSharp.Windows.Forms/UIComponents/LanguageChoosingDialog.cs line 29 at r1 (raw file):
else translator = new MyMemoryTranslator("en", targetCultureId); _model.TranslateStrings(translator);
devin says
src/L10NSharp.Windows.Forms/UIComponents/LanguageChoosingDialog.cs:R29
Translation freezes the language dialog
When translation starts, TranslateStrings runs synchronous HTTP requests on the UI thread. A slow endpoint can freeze the dialog for nearly 300 seconds.
src/L10NSharp.Windows.Forms/Translators/MyMemoryTranslator.cs line 67 at r1 (raw file):
var translation = ser.ReadObject(ms) as JSONResponse; if (translation == null || translation.quotaFinished)
devin says
src/L10NSharp.Windows.Forms/Translators/MyMemoryTranslator.cs:67
Provider errors can reach users
MyMemory returns some failures as HTTP 200 responses. Check responseStatus before displaying translatedText, or unsupported languages can show provider error messages.
src/L10NSharp.Windows.Forms/Translators/MyMemoryTranslator.cs line 90 at r1 (raw file):
public int responseStatus; /// ------------------------------------------------------------------------------------ public bool quotaFinished;
This looks like a partial implementation which got left behind.
…r error leakage Responds to 4 review comments on the BingTranslator replacement: - MicrosoftTranslator/MyMemoryTranslator now share a single static HttpClient (with a 10s timeout) instead of creating a new one per call. MicrosoftTranslator moves its per-call headers (subscription key/region) onto the HttpRequestMessage instead of the shared client's DefaultRequestHeaders, since those are read fresh from mutable static properties on every call and mustn't race across calls. - LanguageChoosingDialog.Application_Idle now runs TranslateStrings on a background thread (Task.Run) and marshals the UI update back via BeginInvoke once done, so a slow/unresponsive translation endpoint can no longer block the dialog's UI thread at all (previously it ran synchronously on Application.Idle with no bound). Guards against the dialog being closed before translation completes. - MyMemoryTranslator now checks responseStatus (!= 200) in addition to the existing quotaFinished check before trusting responseData.translatedText. MyMemory always returns HTTP 200, signaling errors (invalid language pair, quota exhaustion, etc.) only in the body; without this, a provider error/warning string could flow into the dialog as if it were a translation. quotaFinished is now bool? since MyMemory sends JSON null (not false) for it on error responses, which a non-nullable bool can't deserialize. Verified live against real MyMemory success/error responses. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Addressed all 4 review comments in 97916d4:
All changes verified: full solution build + test suite pass, plus a live round-trip against the real MyMemory API for both a valid translation and an intentionally-invalid language pair (confirming the new guard filters the provider's raw error text rather than leaking it). |
imnasnainaec
left a comment
There was a problem hiding this comment.
There are stale Bing comments in files not touched by this pr.
@imnasnainaec reviewed 11 files and all commit messages, and made 2 comments.
Reviewable status: 16 of 19 files reviewed, 5 unresolved discussions (waiting on andrew-polk and tombogle).
src/L10NSharp.Windows.Forms/UIComponents/LanguageChoosingDialog.cs line 20 at r2 (raw file):
Recommended code from Devin:
private readonly LanguageChoosingDialogViewModel _model;
private bool _translationNeeded;
public LanguageChoosingDialog(L10NCultureInfo requestedCulture, Icon icon)
{
InitializeComponent();
Icon = icon;
// The callback just records that translation is needed; we wait to hook
// Application.Idle until the handle exists (see OnHandleCreated) so the
// background BeginInvoke can never run against a not-yet-created handle.
_model = new LanguageChoosingDialogViewModel(_messageLabel.Text, _OKButton.Text, Text,
requestedCulture, () => { _translationNeeded = true; });
_messageLabel.Text = _model.Message;
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (_translationNeeded)
{
_translationNeeded = false;
Application.Idle += Application_Idle;
}
}
to address
Application.Idleis subscribed in the constructor (src/L10NSharp.Windows.Forms/UIComponents/LanguageChoosingDialog.cs:17), before the dialog's window handle exists. If idle fires (app-wide event) and background translation finishes before the handle is created,BeginInvoke(src/L10NSharp.Windows.Forms/UIComponents/LanguageChoosingDialog.cs:41) throwsInvalidOperationException, which isn't caught (onlyObjectDisposedExceptionis, atsrc/L10NSharp.Windows.Forms/UIComponents/LanguageChoosingDialog.cs:51) — so the dialog stays English. Moving the subscription toOnHandleCreatedguarantees the handle exists beforeBeginInvokeruns, eliminating the race.
andrew-polk
left a comment
There was a problem hiding this comment.
@andrew-polk partially reviewed 3 files and all commit messages, made 1 comment, and resolved 4 discussions.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on tombogle).
src/L10NSharp.Windows.Forms/Translators/MyMemoryTranslator.cs line 90 at r1 (raw file):
Previously, andrew-polk wrote…
This looks like a partial implementation which got left behind.
Sorry; I completely missed that this was coming from deserialization.
…ists
- Reword remaining "Bing"/"native language" comments in
LanguageChoosingDialogViewModel.cs and its tests to reflect the current
generic on-the-fly translator (MyMemoryTranslator), now that BingTranslator
is gone. Verified live that MyMemoryTranslator doesn't exhibit either of
the old Bing-specific quirks the test comment described (choking on a
literal "{0}", or substituting "English" for the target language name).
- LanguageChoosingDialog: defer subscribing to Application.Idle until
OnHandleCreated instead of doing it in the constructor. Per @imnasnainaec's
review, Application.Idle is a process-wide event; if it fired (and the
background translation from a previous commit's fix completed) before this
dialog's window handle existed, BeginInvoke would throw
InvalidOperationException, which nothing catches -- silently leaving the
dialog in English. The constructor now just records that translation is
needed via a flag; OnHandleCreated hooks Idle once the handle is guaranteed
to exist.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Pushed 40577b5, addressing @imnasnainaec's finding (via Devin) on
Also batched in two small doc-only cleanups while I was in the area (per @tombogle):
Full build + test suite pass. |
|
Previously, imnasnainaec (D. Ror.) wrote…
Done |
The Bing/Microsoft Translator v1 SOAP API used by the internal BingTranslator class (LanguageChoosingDialog's fail-safe, on-the-fly translation of its title/message/OK button) was retired years ago, so this had been silently falling back to English the whole time.
Replaces it with:
TranslatorBase is now public (required for MicrosoftTranslator to subclass it publicly). LanguageChoosingDialog picks MicrosoftTranslator when configured, else MyMemoryTranslator.
Also removes BingTranslator.cs, the generated WCF service reference, and the System.ServiceModel/System.Security.Cryptography.Xml dependencies they required, since nothing REST-based needs WCF.
Adds a "Show Language Chooser" button to the SampleApp (randomly demoing "de", "it", or "fr") so the fix is visible without faking a missing-locale scenario. "ar" is deliberately excluded: LanguageChoosingDialog doesn't set RightToLeft, so Arabic would demo a layout bug rather than the translator.
Fixes #163.
This change is