Skip to content

feat(translation): add master 'Enable translation' preference toggle#365

Open
katanumahotori wants to merge 4 commits intoOpen-Less:betafrom
katanumahotori:feat/translate-enabled-toggle
Open

feat(translation): add master 'Enable translation' preference toggle#365
katanumahotori wants to merge 4 commits intoOpen-Less:betafrom
katanumahotori:feat/translate-enabled-toggle

Conversation

@katanumahotori
Copy link
Copy Markdown
Contributor

@katanumahotori katanumahotori commented May 8, 2026

User description

Why

Today translation is implicitly disabled by leaving translation_target_language empty. That's effective for the polish pipeline but it doesn't disable the translation hotkey nor the overlay, so a misfire on (e.g.) LeftOption still flashes the 'translating' capsule. Users who don't use translation at all want a hard kill switch.

What

  • New UserPreferences.translate_enabled: bool (defaults to true for migration parity).
  • set_translate_enabled IPC command, persisted via the existing preferences pipeline.
  • Capsule's translation overlay is gated on translation && !quiet && translateEnabled, so even if the translation hotkey misfires the user sees no overlay.
  • Settings -> Translation section: new toggle row with description.
  • i18n strings added in 5 languages.

Note (scope leakage)

types.rs / commands.rs / lib.rs / lib/types.ts / lib/ipc.ts in this PR also include scaffolding for follow-up PRs (custom polish styles and per-app overrides): the CustomMode and AppModeOverride structs, the PolishMode::Custom variant, and related IPC commands. The UI that consumes those fields is not present in this branch, so they are dormant code on this branch alone.

They are bundled here to avoid 3 follow-up schema-migration conflicts on the same files. If this PR is merged independently of the trailing PRs, those PRs will rebase the type changes (which would then become no-ops). If reviewers prefer the schema split out to a prerequisite PR, happy to do that.

i18n keys for follow-up PRs are also included here for the same reason as #364.


PR Type

Enhancement


Description

  • Add master translation enable toggle (translateEnabled pref, set_translate_enabled command, Capsule gating)

  • Introduce custom polish styles (CustomMode) with full CRUD commands and custom system prompt override

  • Implement per‑app auto style switching via AppModeOverride rules and pick_mode_for_app logic

  • Provide quiet mode foundation (quietMode.ts) for suppressing transient capsule labels, consumed by Capsule

  • Extend i18n strings in 5 languages for the new settings, plus dormant UI font/quiet keys for parallel PRs


Diagram Walkthrough

flowchart LR
  SET["Settings UI"] -- "persists translateEnabled" --> PREF["UserPreferences"]
  PREF -- "gates overlay" --> CAP["Capsule.tsx"]
  SET -- "CRUD custom modes" --> CM["customModes"]
  CM -- "custom prompt override" --> POLISH["polish.rs / coordinator"]
  SET -- "set app overrides" --> AO["appModeOverrides"]
  AO -- "auto-selects mode at dictation" --> COORD["coordinator.rs"]
  QUIET["quietMode.ts (localStorage)"] -- "gates labels" --> CAP
  I18N["i18n locales"] -- "new labels" --> SET
Loading

File Walkthrough

Relevant files
Enhancement
15 files
commands.rs
Add translation toggle, custom mode CRUD, and app override commands
+166/-3 
coordinator.rs
Wire custom prompt, app overrides, and translation gating into polish
pipeline
+53/-17 
lib.rs
Register new Tauri commands for translation, custom modes, and
overrides
+7/-0     
polish.rs
Support optional prompt override in system prompt composition
+72/-9   
types.rs
Define CustomMode, AppModeOverride, PolishMode::Custom, and pick logic
+226/-8 
Capsule.tsx
Gate translation overlay on translateEnabled and quiet mode; add quiet
label suppression
+60/-24 
en.ts
Add English labels for custom styles, app overrides, translation
toggle, and dormant UI keys
+52/-0   
ja.ts
Add Japanese labels for new settings, polish mode names, and dormant
UI keys
+54/-2   
ko.ts
Add Korean labels for new settings, style names, and dormant UI keys
+52/-0   
zh-CN.ts
Add Simplified Chinese labels for custom modes, app overrides,
translation toggle, and dormant UI keys
+52/-0   
zh-TW.ts
Add Traditional Chinese labels for new settings and dormant UI keys
+52/-0   
ipc.ts
Add frontend IPC wrappers for translation toggle, custom modes, and
app overrides
+37/-0   
quietMode.ts
New helper for quiet mode state persisted in localStorage
+20/-0   
types.ts
Mirror CustomMode, AppModeOverride, and widen PolishMode type for
custom variants
+39/-2   
Settings.tsx
Add translation enable toggle row in Settings UI                 
+13/-1   
Tests
1 files
stylePrefs.test.ts
Update mock preferences with new fields to fix test compilation
+3/-0     

Today translation is implicitly disabled by leaving
translation_target_language empty. That's effective for the polish
pipeline but it doesn't disable the translation hotkey nor the overlay,
so a misfire on (e.g.) LeftOption still flashes the "translating"
capsule.

This PR adds an explicit master:

- New UserPreferences.translate_enabled: bool (defaults to true for
  migration parity).
- set_translate_enabled IPC command, persisted via existing
  preferences pipeline.
- Capsule's translation overlay is gated on
  translation && !quiet && translateEnabled, so even if the
  translation hotkey misfires the user sees no overlay.
- Settings -> Translation section: new toggle row with description.

i18n strings added in 5 languages.

Note: types.rs / commands.rs / lib.rs / lib/types.ts / lib/ipc.ts in
this PR also include scaffolding for follow-up custom polish styles
and per-app overrides PRs (CustomMode, AppModeOverride structs,
PolishMode::Custom variant, related IPC commands). The UI consuming
those fields is not present in this branch, so they are dormant on
this branch alone. They are bundled here to avoid 3 follow-up
schema-migration conflicts on the same files. If merged independently
of this PR, the trailing PRs will rebase those types.
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

lightnovel0 added 2 commits May 8, 2026 19:35
…Mode::Custom

Without these the PolishMode::Custom variant added to types.rs in this
PR makes the existing match arms in polish.rs / coordinator.rs
non-exhaustive and the crate fails to compile. Same scope-leakage
caveat from the previous commit applies: these will be the canonical
implementations once appergb#366 / appergb#367 land.
Capsule.tsx's quiet-mode gating depends on src/lib/quietMode.ts which
is otherwise added in Open-Less#364. Including the file here keeps this PR
buildable in isolation. The file is identical to Open-Less#364's; if both PRs
land, the second merge is a no-op.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

PR Reviewer Guide 🔍

(Review updated until commit 7ac18a2)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis 🔶

translate-enabled-toggle - Partially compliant

Compliant requirements:

  • Settings toggle row and localized labels/help text.
  • Capsule overlay gating when translation is disabled.

Non-compliant requirements:

  • Dedicated translate_enabled preference field.
  • set_translate_enabled IPC command and separate persistence path.
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Stale Toggle State

The translation-enabled flag is only loaded once on mount, so changing the setting while the app is running will not update the capsule until the component remounts. If a translation session is already active, the overlay can keep showing even after the user turns translation off.

const [translationEnabled, setTranslationEnabled] = useState<boolean>(true);
// Windows 端 host 在翻译模式从 84 长到 118;macOS / Linux 上 capsuleLayout 已固定 42 忽略此参数。
const hostMetrics = getCapsuleHostMetrics(os, translation && translationEnabled);
// Quiet mode: Capsule()'s own translation-mode overlay also needs to be
// suppressed when the user has turned the quiet toggle on. Pill reads
// this independently for its own labels.
const quiet = readQuietCompletion();
const showTranslationOverlay = translation && !quiet && translationEnabled;

useEffect(() => {
  if (!isTauri) return;
  let cancelled = false;
  (async () => {
    try {
      const prefs = await getSettings();
      if (!cancelled)
        setTranslationEnabled(
          typeof prefs.translationTargetLanguage === 'string' &&
            prefs.translationTargetLanguage.trim().length > 0
        );
    } catch {
      /* 取得失敗時はデフォルト true のまま */
    }
  })();
  return () => {
    cancelled = true;
  };
}, []);
Dangling Overrides

Deleting a custom mode does not clean up app_mode_overrides that still point at that mode. When one of those rules matches later, the app resolves to a missing custom id and ends up building a broken/empty system prompt instead of falling back to a builtin style.

let mut prefs = coord.prefs().get();
let before = prefs.custom_modes.len();
prefs.custom_modes.retain(|m| m.id != id);
if prefs.custom_modes.len() == before {
    return Err(format!("custom mode id '{id}' not found"));
}
// default_mode が消したカスタムmodeを参照していたら Light にフォールバック
if matches!(&prefs.default_mode, PolishMode::Custom(cur_id) if *cur_id == id) {
    prefs.default_mode = PolishMode::Light;
}
// enabled_modes から該当 Custom を除去
prefs.enabled_modes.retain(|m| !matches!(m, PolishMode::Custom(cur_id) if *cur_id == id));
persist_settings(&*coord, prefs.clone())?;
let _ = app.emit("prefs:changed", &prefs);

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Persistent review updated to latest commit 1b9f0f1

@appergb
Copy link
Copy Markdown
Collaborator

appergb commented May 8, 2026

不符合要求:

将 @fontsource/noto-sans-jp 和 @fontsource/inter 打包到本地,而不是依赖 CDN。
添加 src/lib/fontFamily.ts ,其中包含预设字体堆栈、localStorage 持久化和可选的系统字体发现。
公开一个运行时 CSS 变量,以便在不重新构建 CSS 的情况下更改活动 UI 字体。
添加一个设置界面行,用于选择字体,包括预设字体、一次性字体加载按钮、自定义字体输入和错误处理。
更新默认字体层叠,使日语文本优先使用 Noto Sans JP 字体,而不是 Microsoft YaHei 等 CJK 备用字体。
添加一个静音模式切换开关,该开关可以抑制瞬态胶囊标签,同时保持错误可见。

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Persistent review updated to latest commit 1b9f0f1

@katanumahotori
Copy link
Copy Markdown
Contributor Author

Re: the previous comment — looks like content from PR #364 was pasted here. Treating this PR on its own:

Original design added a new translate_enabled field that overlapped with the existing translation_target_language === "" semantics. That was redundant — this revision drops the new field entirely and reuses the existing one as the master switch:

  • Settings: the toggle now writes '' to translation_target_language when off, and restores the user's last non-empty target (cached in localStorage) when on.
  • coordinator.rs::translation_hotkey listener bails out at registration time if translation_target_language is empty, so a misfire on (e.g.) LeftOption no longer flashes the translation capsule overlay.
  • Capsule overlay gating uses translation && !quiet && translation_target_language !== ''.
  • Backward compat: UserPreferencesWire still accepts a legacy translateEnabled field via serde so old preferences.json files deserialize without error; the value is then ignored.

No new pref field, no breaking changes for existing users — just a tighter integration of the existing one.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Persistent review updated to latest commit 7ac18a2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants