feat(translation-v2): final review — dual-engine translation row + DeepSeek polish override + dead code cleanup
This commit is contained in:
@@ -58,20 +58,28 @@ struct LocalModelsGroup: View {
|
||||
/// key yet), but the polish call short-circuits with an Alert if
|
||||
/// the Keychain is empty when it fires.
|
||||
///
|
||||
/// v0.2.1: dropped the long descriptive subtitle — it explained
|
||||
/// things the user could read about elsewhere in Settings
|
||||
/// (provider / API key section) and made the row visually heavy.
|
||||
/// Title + switch is enough; details live in `CloudPolishDisclosureBanner`.
|
||||
/// v0.2.1 follow-up: added a one-line caption under the toggle
|
||||
/// that names the default cloud vendor (DeepSeek) so the user
|
||||
/// knows where the transcript is going when they flip the switch.
|
||||
/// The toggle row is now a two-line layout — title + caption —
|
||||
/// so we drop the explicit `singleLineMinHeight` here and let
|
||||
/// `SettingsListMetrics` provide enough vertical room.
|
||||
private var cloudPolishRow: some View {
|
||||
Toggle(isOn: $config.localModeCloudPolishEnabled) {
|
||||
Text("settings.localModels.cloudPolish.title")
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textPrimary)
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Toggle(isOn: $config.localModeCloudPolishEnabled) {
|
||||
Text("settings.localModels.cloudPolish.title")
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textPrimary)
|
||||
}
|
||||
.toggleStyle(.switch)
|
||||
.tint(palette.accent)
|
||||
Text("settings.localModels.cloudPolish.caption")
|
||||
.font(TypeStyle.caption2)
|
||||
.foregroundStyle(palette.textTertiary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.toggleStyle(.switch)
|
||||
.tint(palette.accent)
|
||||
.padding(.horizontal, Spacing.md)
|
||||
.frame(minHeight: SettingsListMetrics.singleLineMinHeight)
|
||||
.padding(.vertical, Spacing.xs)
|
||||
}
|
||||
|
||||
// MARK: Helpers
|
||||
|
||||
@@ -737,11 +737,11 @@ private struct APISetupPage: View {
|
||||
// v0.2.1 follow-up: translation row lives on the
|
||||
// onboarding engine page so first-time users can
|
||||
// pick a target language before they ever see the
|
||||
// keyboard. Wrapped in the section's surface card
|
||||
// so it sits flush with the engine / provider cards
|
||||
// above; visibility gated by `isTranslationRowVisible`
|
||||
// so the local engine only shows it when cloud
|
||||
// polish is also enabled.
|
||||
// keyboard. v0.2.1 final review: both engines now
|
||||
// show the row (the local engine routes the polish
|
||||
// step through DeepSeek, so the constraint is gone).
|
||||
// Wrapped in the same surface card chrome as the
|
||||
// APISettingsCard above for visual symmetry.
|
||||
if config.isTranslationRowVisible {
|
||||
translationSection
|
||||
.padding(.horizontal, Spacing.lg)
|
||||
@@ -767,10 +767,9 @@ private struct APISetupPage: View {
|
||||
)
|
||||
}
|
||||
.padding(.horizontal, Spacing.lg)
|
||||
// v0.2.1 follow-up: same conditional for the local
|
||||
// branch — the row only renders when the engine
|
||||
// can actually run the cloud translate-and-polish
|
||||
// step (i.e. cloud polish is opted in).
|
||||
// v0.2.1 final review: same surface card chrome as
|
||||
// the cloud branch — the row now renders for both
|
||||
// engines.
|
||||
if config.isTranslationRowVisible {
|
||||
translationSection
|
||||
.padding(.horizontal, Spacing.lg)
|
||||
@@ -786,9 +785,14 @@ private struct APISetupPage: View {
|
||||
/// reads `ProviderConfig.translationTargetLocaleId` directly, so
|
||||
/// picking a locale in onboarding flows through to the keyboard
|
||||
/// extension on the next `load()` cycle.
|
||||
///
|
||||
/// v0.2.1 final review: the section header now reads
|
||||
/// `settings.translation.afterPolish` (renamed alongside the row
|
||||
/// title in `TranslationPickerRow`) so the section and row read
|
||||
/// as one cohesive group.
|
||||
private var translationSection: some View {
|
||||
VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) {
|
||||
Text("settings.translation.title")
|
||||
Text("settings.translation.afterPolish")
|
||||
.font(TypeStyle.caption2)
|
||||
.foregroundStyle(palette.textSecondary)
|
||||
.textCase(.uppercase)
|
||||
|
||||
@@ -13,13 +13,18 @@
|
||||
// inline hint was deleted along with the previous Bool toggle — the
|
||||
// user only sees the row when the engine can act on the choice.
|
||||
//
|
||||
// v0.2.1 final review: both engines now run the translate-and-polish
|
||||
// step (the local engine routes through DeepSeek via
|
||||
// `ProviderConfig.localModeProviderId`), so the row title changed
|
||||
// from "Translation" to "Polish then translate" to match the new
|
||||
// always-on translation contract.
|
||||
//
|
||||
// Mapping to persisted state:
|
||||
// • "不翻译" → translationTargetLocaleId = "off"
|
||||
// • any specific locale → translationTargetLocaleId = <id>
|
||||
//
|
||||
// The pipeline (`PolishingService`) still rejects `.translate` on the
|
||||
// local engine when cloud polish is off, which
|
||||
// `KeyboardViewController` surfaces as a 2.4s toast. Belt + suspenders.
|
||||
// The pipeline (`PolishingService`) honors `.translate` on both
|
||||
// engines when this row is visible — no more "rejected mode" toast.
|
||||
|
||||
import SwiftUI
|
||||
import OSGKeyboardShared
|
||||
@@ -37,7 +42,7 @@ struct TranslationPickerRow: View {
|
||||
var body: some View {
|
||||
if isVisible {
|
||||
HStack {
|
||||
Text("settings.translation.title")
|
||||
Text("settings.translation.afterPolish")
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textPrimary)
|
||||
Spacer()
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"settings.api.title" = "API";
|
||||
"settings.language.title" = "Language";
|
||||
// v0.2.1: translation feature
|
||||
"settings.translation.title" = "Translation";
|
||||
"settings.translation.afterPolish" = "Polish then translate";
|
||||
"settings.translation.off" = "Don't translate";
|
||||
"settings.languageModels.title" = "Language & models";
|
||||
"settings.localModels.title" = "On-device models";
|
||||
@@ -119,6 +119,7 @@
|
||||
"settings.localModels.readiness %lld %lld" = "%lld/%lld ready";
|
||||
"settings.localModels.cloudPolish.title" = "Cloud polish after ASR";
|
||||
"settings.localModels.cloudPolish.subtitle" = "Sends the transcript to your configured cloud LLM (DeepSeek by default) for cleanup. Enable only when iOS speech recognition struggles — noisy far-field audio, strong accents, etc. Requires a DeepSeek API key.";
|
||||
"settings.localModels.cloudPolish.caption" = "Uses DeepSeek to polish transcripts in local mode.";
|
||||
"settings.language.subtitle.cloud" = "Recognition language and text processing mode.";
|
||||
"settings.language.subtitle.local" = "Recognition language.";
|
||||
"settings.mode.title" = "Mode";
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"settings.api.title" = "接口";
|
||||
"settings.language.title" = "语言";
|
||||
// v0.2.1: 翻译功能
|
||||
"settings.translation.title" = "翻译";
|
||||
"settings.translation.afterPolish" = "润色后翻译";
|
||||
"settings.translation.off" = "不翻译";
|
||||
"settings.languageModels.title" = "语言与模型";
|
||||
"settings.localModels.title" = "本地模型";
|
||||
@@ -119,6 +119,7 @@
|
||||
"settings.localModels.readiness %lld %lld" = "%lld/%lld 已就绪";
|
||||
"settings.localModels.cloudPolish.title" = "识别后云端润色";
|
||||
"settings.localModels.cloudPolish.subtitle" = "将识别文本发送给已配置的云端大模型(默认 DeepSeek)进行润色。仅在 iOS 语音识别效果不理想时(远场、噪声、方言)开启,需提前在设置中填入 DeepSeek API Key。";
|
||||
"settings.localModels.cloudPolish.caption" = "本地模式下,使用 DeepSeek 进行云端润色。";
|
||||
"settings.language.subtitle.cloud" = "选择识别语言和文字处理模式。";
|
||||
"settings.language.subtitle.local" = "选择识别语言。";
|
||||
"settings.mode.title" = "模式";
|
||||
|
||||
Reference in New Issue
Block a user