Skip to content

UI language toggle and static string lookup (frontend-only)#2187

Open
zerofrip wants to merge 2 commits into
safing:developmentfrom
zerofrip:feat/ui-language-toggle
Open

UI language toggle and static string lookup (frontend-only)#2187
zerofrip wants to merge 2 commits into
safing:developmentfrom
zerofrip:feat/ui-language-toggle

Conversation

@zerofrip

@zerofrip zerofrip commented Jun 20, 2026

Copy link
Copy Markdown

Summary

Presentation-layer update: a small UI language toggle in Settings (EN/JA) drives existing static string lookup for a handful of nav tooltips and one settings heading. No backend, integration, build pipeline, or service-layer changes.

UI state consolidation

  • Settings owns the language toggle — setLanguage() is the only UI mutation entry point
  • Navigation is a passive subscriber — displays translated tooltips via t(), refreshes on a lightweight window event (OnPush)
  • Removed duplicate language toggle from the navigation sidebar

Scope (intentionally limited)

  • Static flat-key dictionaries: assets/data/i18n/en.json, ja.json
  • Pure t() lookup in static-translate.ts — no services, pipes, HTTP, or persistence
  • Translated: 5 nav tooltips + Settings page heading
  • NOT translated: SPN, Get Help, Version/Tools, menu items, security-lock status, core views

Test plan

  • ng build succeeds
  • Go/integration diff vs development: 0 lines
  • static-translate logic validated (6 tests via direct execution)
  • Manual: Settings EN/JA toggle updates heading + nav tooltips immediately
  • Manual: Page reload resets to EN (non-persistent, by design)

Note: Karma coverage reporter is not installed in the local environment; tests were validated via direct execution.

Reversibility

Fully frontend-only. Removable without backend or configuration impact.

Made with Cursor

Summary by CodeRabbit

  • New Features
    • English and Japanese language support is now available
    • Change the UI language directly from the Settings page
    • Navigation tooltips and settings titles automatically adapt to your selected language for a more localized experience

日本語要約

概要

プレゼンテーション層のみの小さな変更です。Settings ページの UI 言語トグル(EN/JA)が、既存の静的文字列 lookup(t())を通じて、少数の Navigation ツールチップと Settings 見出しの表示を切り替えます。バックエンド・integration・ビルドパイプライン・サービス層への変更はありません。

UI 状態の整理

  • Settings が言語トグルの唯一の UI 操作入口(setLanguage()
  • Navigation は受動的な表示側 — t() でツールチップを表示し、軽量 window イベントで OnPush 再描画
  • Navigation サイドバーにあった重複トグルを削除

スコープ(意図的に限定)

  • 静的フラットキー辞書: assets/data/i18n/en.json, ja.json
  • 純粋関数 t()static-translate.ts)— サービス・pipe・HTTP・永続化なし
  • 翻訳対象: Navigation ツールチップ 5 件 + Settings 見出し 1 件
  • 翻訳対象外: SPN、Get Help、Version/Tools、メニュー項目、security-lock ステータス、コア画面

テスト

  • ng build 成功
  • Go/integration diff: 0 行
  • static-translate ロジック検証(直接実行、6 テスト)
  • 手動: Settings の EN/JA 切替で見出し + nav ツールチップが即更新
  • 手動: リロードで EN にリセット(非永続、仕様どおり)

補足: ローカル環境に Karma coverage reporter が未インストールのため、テストは直接実行で検証済みです。

可逆性

フロントエンドのみの変更。バックエンドや設定への影響なく削除可能です。

zerofrip and others added 2 commits June 21, 2026 07:02
Frontend-only pilot using a pure t() function and flat-key JSON dictionaries.
No backend, build pipeline, or persistence changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
Consolidates display-language selection into Settings as the single UI
entry point. Navigation subscribes via a lightweight window event for
OnPush tooltip refresh. No backend or i18n architecture changes.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new static translation module (static-translate.ts) is introduced, loading EN and JA JSON dictionaries and exporting t, setUiLanguage, and getUiLanguage. SettingsComponent gains a language-switcher UI and dispatches a portmaster-ui-lang-change window event on change. NavigationComponent listens for that event to re-render translated tooltips and cleans up the listener on destroy.

Changes

Static i18n with EN/JA language switcher

Layer / File(s) Summary
Translation dictionaries and static-translate module
assets/data/i18n/en.json, assets/data/i18n/ja.json, desktop/angular/tsconfig.json, desktop/angular/src/app/i18n/static-translate.ts, desktop/angular/src/app/i18n/static-translate.spec.ts
Adds EN and JA JSON translation files, enables resolveJsonModule in tsconfig, implements the static-translate module with t/setUiLanguage/getUiLanguage (with English fallback and fallback ?? key behavior), and covers it with a Jest spec for all fallback cases.
Settings language switcher UI and component wiring
desktop/angular/src/app/pages/settings/settings.ts, desktop/angular/src/app/pages/settings/settings.html
SettingsComponent gains a uiLanguage getter, a setLanguage method that calls setUiLanguage, dispatches portmaster-ui-lang-change on window, and calls cdr.markForCheck(). The template adds EN/JA toggle buttons and replaces the static "Global Settings" heading with a t() call.
NavigationComponent translation binding and lifecycle cleanup
desktop/angular/src/app/layout/navigation/navigation.ts, desktop/angular/src/app/layout/navigation/navigation.html
NavigationComponent implements OnDestroy, exposes readonly t, registers a portmaster-ui-lang-change window listener in ngOnInit that calls markForCheck(), and removes it in ngOnDestroy. Five tooltip attribute bindings in the template are switched from static strings to t() calls.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'UI language toggle and static string lookup (frontend-only)' accurately summarizes the primary changes: adding language toggle functionality and translation lookups for the UI.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
desktop/angular/src/app/pages/settings/settings.ts (1)

54-54: ⚡ Quick win

Extract the language-change event name into a shared constant.

'portmaster-ui-lang-change' is part of a cross-component contract (Settings dispatch, Navigation subscribe). Keeping it as duplicated string literals is brittle; a shared exported constant would prevent silent breakage from typos.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/angular/src/app/pages/settings/settings.ts` at line 54, Extract the
hardcoded event name string 'portmaster-ui-lang-change' used in the
window.dispatchEvent call into a shared exported constant in a common constants
file or shared utilities module. Then import and use this constant in the
window.dispatchEvent call in the Settings component instead of the string
literal. Also update any other components (such as Navigation) that subscribe to
this same event to import and use the same shared constant instead of
duplicating the string literal, ensuring consistent event naming across all
components.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@desktop/angular/src/app/pages/settings/settings.html`:
- Around line 15-20: The EN and JA language toggle buttons lack accessibility
attributes for screen reader users to determine the active language state. Add
aria-pressed attribute bindings to both buttons using [attr.aria-pressed] that
dynamically bind to the condition matching the current uiLanguage value. For the
EN button, bind aria-pressed to uiLanguage === 'en', and for the JA button, bind
it to uiLanguage === 'ja' so screen readers can announce the selected state.

---

Nitpick comments:
In `@desktop/angular/src/app/pages/settings/settings.ts`:
- Line 54: Extract the hardcoded event name string 'portmaster-ui-lang-change'
used in the window.dispatchEvent call into a shared exported constant in a
common constants file or shared utilities module. Then import and use this
constant in the window.dispatchEvent call in the Settings component instead of
the string literal. Also update any other components (such as Navigation) that
subscribe to this same event to import and use the same shared constant instead
of duplicating the string literal, ensuring consistent event naming across all
components.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 142bc819-e6ea-4892-8fe6-69eb99978d35

📥 Commits

Reviewing files that changed from the base of the PR and between af0c601 and cc5f8d1.

📒 Files selected for processing (9)
  • assets/data/i18n/en.json
  • assets/data/i18n/ja.json
  • desktop/angular/src/app/i18n/static-translate.spec.ts
  • desktop/angular/src/app/i18n/static-translate.ts
  • desktop/angular/src/app/layout/navigation/navigation.html
  • desktop/angular/src/app/layout/navigation/navigation.ts
  • desktop/angular/src/app/pages/settings/settings.html
  • desktop/angular/src/app/pages/settings/settings.ts
  • desktop/angular/tsconfig.json

Comment on lines +15 to +20
<button type="button" class="px-2 py-1 rounded bg-gray-300 hover:bg-gray-200"
[class.font-semibold]="uiLanguage === 'en'"
(click)="setLanguage('en')">EN</button>
<button type="button" class="px-2 py-1 rounded bg-gray-300 hover:bg-gray-200"
[class.font-semibold]="uiLanguage === 'ja'"
(click)="setLanguage('ja')">JA</button>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Expose selected language state for assistive tech.

The EN/JA toggle currently signals selection only visually. Add aria-pressed bindings so screen-reader users can detect the active language.

Suggested change
-    <button type="button" class="px-2 py-1 rounded bg-gray-300 hover:bg-gray-200"
+    <button type="button" class="px-2 py-1 rounded bg-gray-300 hover:bg-gray-200"
+      [attr.aria-pressed]="uiLanguage === 'en'"
       [class.font-semibold]="uiLanguage === 'en'"
       (click)="setLanguage('en')">EN</button>
-    <button type="button" class="px-2 py-1 rounded bg-gray-300 hover:bg-gray-200"
+    <button type="button" class="px-2 py-1 rounded bg-gray-300 hover:bg-gray-200"
+      [attr.aria-pressed]="uiLanguage === 'ja'"
       [class.font-semibold]="uiLanguage === 'ja'"
       (click)="setLanguage('ja')">JA</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@desktop/angular/src/app/pages/settings/settings.html` around lines 15 - 20,
The EN and JA language toggle buttons lack accessibility attributes for screen
reader users to determine the active language state. Add aria-pressed attribute
bindings to both buttons using [attr.aria-pressed] that dynamically bind to the
condition matching the current uiLanguage value. For the EN button, bind
aria-pressed to uiLanguage === 'en', and for the JA button, bind it to
uiLanguage === 'ja' so screen readers can announce the selected state.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant