diff --git a/src/ui/Features/Main/MainViewModel.cs b/src/ui/Features/Main/MainViewModel.cs index ab74921eb64..36014daa39c 100644 --- a/src/ui/Features/Main/MainViewModel.cs +++ b/src/ui/Features/Main/MainViewModel.cs @@ -12682,12 +12682,15 @@ private async Task AutoTranslateSelectedLines() return; } - var captureOriginal = noOriginal && !result.TranslateInPlace; - if (captureOriginal && !result.Rows.Any(r => !string.IsNullOrEmpty(r.TranslatedText))) + // OK can be enabled after a partial/cancelled translation run. If no row actually + // contains translated text, the operation is a no-op in both modes: do not capture an + // original and do not reset language/spell-check state for unchanged subtitle content. + if (!result.Rows.Any(r => !string.IsNullOrEmpty(r.TranslatedText))) { - return; // nothing came back - do not switch to translator mode for an unchanged subtitle + return; } + var captureOriginal = noOriginal && !result.TranslateInPlace; var wasOldTranslationChanged = captureOriginal && _changeSubtitleHash != GetFastHash(); if (captureOriginal) diff --git a/tests/UI/Features/Main/AutoTranslateSelectedLinesTests.cs b/tests/UI/Features/Main/AutoTranslateSelectedLinesTests.cs index 097252d64d6..1cae2702e8e 100644 --- a/tests/UI/Features/Main/AutoTranslateSelectedLinesTests.cs +++ b/tests/UI/Features/Main/AutoTranslateSelectedLinesTests.cs @@ -85,6 +85,39 @@ public async Task NoOriginal_TranslateInPlaceChecked_LeavesNoOriginal() } } + [AvaloniaFact] + public async Task NoOriginal_TranslateInPlaceWithNoResult_IsNoOp() + { + var (window, vm) = CreateMainViewModel(); + try + { + AddLine(vm, "One", 0, 1000); + AddLine(vm, "Two", 1000, 2000); + var rows = vm.Subtitles.ToList(); + vm.SubtitleGrid.SelectedItems?.Clear(); + vm.SubtitleGrid.SelectedItems?.Add(rows[0]); + await SettleAsync(window); + + // OnSubtitleLanguageChanged clears this cache. An empty in-place result must leave it + // untouched because no subtitle text changed. + var detectedField = typeof(MainViewModel).GetField("_detectedLanguageCode", BindingFlags.Instance | BindingFlags.NonPublic)!; + detectedField.SetValue(vm, "zz"); + + FakeTranslateWindowService.Install(vm, translateInPlace: true, produceTranslations: false); + await vm.AutoTranslateSelectedLinesCommand.ExecuteAsync(null); + await SettleAsync(window); + + Assert.Equal(new[] { "One", "Two" }, vm.Subtitles.Select(p => p.Text)); + Assert.All(vm.Subtitles, p => Assert.True(string.IsNullOrEmpty(p.OriginalText))); + Assert.False(vm.ShowColumnOriginalText); + Assert.Equal("zz", detectedField.GetValue(vm)); + } + finally + { + CloseWindow(window, vm); + } + } + private static List GridCellTexts(MainViewModel vm) { return vm.SubtitleGrid.GetVisualDescendants().OfType() @@ -97,12 +130,14 @@ private static List GridCellTexts(MainViewModel vm) public class FakeTranslateWindowService : DispatchProxy { private static bool _translateInPlace; + private static bool _produceTranslations = true; public static bool LastInPlaceOffered { get; private set; } - public static void Install(MainViewModel vm, bool translateInPlace) + public static void Install(MainViewModel vm, bool translateInPlace, bool produceTranslations = true) { _translateInPlace = translateInPlace; + _produceTranslations = produceTranslations; var field = typeof(MainViewModel).GetField("_windowService", BindingFlags.Instance | BindingFlags.NonPublic)!; field.SetValue(vm, Create()); } @@ -121,9 +156,12 @@ public static void Install(MainViewModel vm, bool translateInPlace) translateVm.OnLoaded(); LastInPlaceOffered = translateVm.TranslateInPlaceIsVisible; translateVm.TranslateInPlace = _translateInPlace; - foreach (var row in translateVm.Rows) + if (_produceTranslations) { - row.TranslatedText = "T:" + row.Text; + foreach (var row in translateVm.Rows) + { + row.TranslatedText = "T:" + row.Text; + } } translateVm.OkPressed = true;