feat(translation-v2): final review — dual-engine translation row + DeepSeek polish override + dead code cleanup
This commit is contained in:
@@ -533,14 +533,28 @@ public final class KeyboardViewController: UIInputViewController {
|
||||
guard let self else { return }
|
||||
// v0.2.1: pick the polish mode once at task start so a
|
||||
// mid-flight toggle flip doesn't change the request we
|
||||
// already sent. `isTranslationEffective` honours the cloud-
|
||||
// only constraint so we never accidentally translate on the
|
||||
// local engine.
|
||||
// already sent. `isTranslationEffective` no longer gates on
|
||||
// `engineMode == "cloud"` — the row visibility predicate
|
||||
// already keeps the picker honest, and the local engine's
|
||||
// translate-and-polish path now routes through DeepSeek.
|
||||
let polishMode: PolishingService.PolishMode = self.state.isTranslationEffective
|
||||
? .translate(targetLocaleId: self.state.translationTargetLocaleId)
|
||||
: .polish
|
||||
// v0.2.1: local engine routes through DeepSeek for the
|
||||
// polish / translate step regardless of the user's chosen
|
||||
// cloud provider — DeepSeek is cheap and strong on
|
||||
// Chinese, which is the dominant input for the on-device
|
||||
// ASR transcript. Cloud engine honors the user's own
|
||||
// provider id by passing `nil`.
|
||||
let overrideProviderId: String? = self.state.engineMode == "local"
|
||||
? "deepseek"
|
||||
: nil
|
||||
do {
|
||||
let polished = try await self.polisher.polish(trimmed, mode: polishMode)
|
||||
let polished = try await self.polisher.polish(
|
||||
trimmed,
|
||||
mode: polishMode,
|
||||
providerIdOverride: overrideProviderId
|
||||
)
|
||||
self.textDocumentProxy.insertText(polished)
|
||||
self.state.lastTranscript = ""
|
||||
self.state.phase = .idle
|
||||
@@ -588,19 +602,6 @@ public final class KeyboardViewController: UIInputViewController {
|
||||
message: ExtL10n.string("keyboard.error.llm.noApiKey")
|
||||
)
|
||||
self.scheduleAutoClearError()
|
||||
} catch let polishError as PolishingService.PolishError where polishError == .translationNotAvailable {
|
||||
// v0.2.1: user toggled translation on while the local
|
||||
// engine is active. Fall back to a plain polish — and if
|
||||
// we're on local-without-cloud-polish, fall all the way
|
||||
// back to raw ASR. The keyboard surfaces a short hint
|
||||
// telling them to switch to the cloud engine.
|
||||
self.textDocumentProxy.insertText(trimmed)
|
||||
self.state.lastTranscript = ""
|
||||
self.state.phase = .error(
|
||||
.unknown(ExtL10n.string("keyboard.error.translation.needsCloud")),
|
||||
message: ExtL10n.string("keyboard.error.translation.needsCloud")
|
||||
)
|
||||
self.scheduleAutoClearError()
|
||||
} catch {
|
||||
// Network / timeout / decoding — fall back to the raw
|
||||
// transcript so the user still gets their text, with a
|
||||
|
||||
@@ -12,12 +12,15 @@
|
||||
// derived from the locale id so the chip / pipeline read the same
|
||||
// source of truth.
|
||||
//
|
||||
// v0.2.1 final review: dropped the "needs cloud" warning state —
|
||||
// both engines now run the translate-and-polish step (the local
|
||||
// engine routes through DeepSeek via
|
||||
// `ProviderConfig.localModeProviderId`). The chip is therefore just
|
||||
// off / on, with the same accent treatment either way.
|
||||
//
|
||||
// Visual states:
|
||||
// • off → dim outline, "翻译" label
|
||||
// • on + cloud → accent fill, "→ EN" / "→ 日本語" style label
|
||||
// • on + local engine→ warning fill + "翻译需云端" hint (effectively
|
||||
// inert; the pipeline rejects the mode and the controller surfaces
|
||||
// the error toast)
|
||||
// • on (any engine) → accent fill, "→ EN" / "→ 日本語" style label
|
||||
//
|
||||
// Stays in the same visual family as `CloudEngineChip` / `LocaleChip`
|
||||
// (Capsule + 26 pt min height + 5 pt vertical padding) so the top bar
|
||||
@@ -92,11 +95,10 @@ struct TranslationChip: View {
|
||||
}
|
||||
|
||||
private func chipLabel(target: TranslationLanguage, enabled: Bool, isLocal: Bool) -> String {
|
||||
// Local-engine + on shows the constraint hint instead of the
|
||||
// target label so the user knows why nothing's happening.
|
||||
if enabled, isLocal {
|
||||
return ExtL10n.string("keyboard.translation.needsCloudShort")
|
||||
}
|
||||
// v0.2.1 follow-up: with the local engine now routing the
|
||||
// polish / translate step through DeepSeek, the chip shows
|
||||
// the same "→EN"-style label on both engines. There's no
|
||||
// "needs cloud" hint path anymore.
|
||||
if !enabled {
|
||||
return ExtL10n.string("keyboard.translation.off")
|
||||
}
|
||||
@@ -124,19 +126,23 @@ struct TranslationChip: View {
|
||||
}
|
||||
|
||||
private func foreground(enabled: Bool, isLocal: Bool) -> Color {
|
||||
if enabled, isLocal { return palette.warning }
|
||||
// v0.2.1 follow-up: the chip no longer needs a "warning" path
|
||||
// for local + on — both engines share the accent treatment
|
||||
// now. `isLocal` is kept in the signature so callers don't
|
||||
// need to change; it's intentionally unused below.
|
||||
_ = isLocal
|
||||
if enabled { return palette.accent }
|
||||
return palette.textPrimary
|
||||
}
|
||||
|
||||
private func background(enabled: Bool, isLocal: Bool) -> Color {
|
||||
if enabled, isLocal { return palette.warning.opacity(0.15) }
|
||||
_ = isLocal
|
||||
if enabled { return palette.accent.opacity(0.15) }
|
||||
return palette.surfaceElevated
|
||||
}
|
||||
|
||||
private func stroke(enabled: Bool, isLocal: Bool) -> Color {
|
||||
if enabled, isLocal { return palette.warning.opacity(0.35) }
|
||||
_ = isLocal
|
||||
if enabled { return palette.accent.opacity(0.35) }
|
||||
return palette.divider
|
||||
}
|
||||
|
||||
@@ -181,10 +181,8 @@
|
||||
"keyboard.translation.off" = "Translate";
|
||||
"keyboard.translation.enable" = "Enable translation";
|
||||
"keyboard.translation.disable" = "Disable translation";
|
||||
"keyboard.translation.needsCloudShort" = "Need cloud";
|
||||
"keyboard.translation.a11y" = "Translation";
|
||||
"keyboard.translation.a11yHint" = "Toggle translation or change the target language.";
|
||||
"keyboard.error.translation.needsCloud" = "Translation needs the cloud engine — switch to cloud in Settings.";
|
||||
|
||||
/* Mode chip labels (used in both ext + preview stub) */
|
||||
"mode.off" = "Off";
|
||||
|
||||
@@ -181,10 +181,8 @@
|
||||
"keyboard.translation.off" = "翻译";
|
||||
"keyboard.translation.enable" = "开启翻译";
|
||||
"keyboard.translation.disable" = "关闭翻译";
|
||||
"keyboard.translation.needsCloudShort" = "需云端";
|
||||
"keyboard.translation.a11y" = "翻译";
|
||||
"keyboard.translation.a11yHint" = "切换翻译开关或修改目标语言。";
|
||||
"keyboard.error.translation.needsCloud" = "翻译需要云端引擎,请到设置切换为云端模式。";
|
||||
|
||||
/* Mode chip labels */
|
||||
"mode.off" = "关闭";
|
||||
|
||||
Reference in New Issue
Block a user