Fix i18n localization#18508
Conversation
| import { formatNextRun } from "../presenter.ts"; | ||
| import type { GatewayHelloOk } from "../gateway"; | ||
| import type { UiSettings } from "../storage"; | ||
| import { formatAgo, formatDurationMs } from "../format"; |
There was a problem hiding this comment.
Build-breaking import of non-existent functions
formatAgo and formatDurationMs are not exported from ../format. The actual exports are formatRelativeTimestamp and formatDurationHuman (see ui/src/ui/format.ts lines 1–5). This will cause a TypeScript compilation error or a runtime import failure.
| import { formatAgo, formatDurationMs } from "../format"; | |
| import { formatRelativeTimestamp, formatDurationHuman } from "../format"; |
You'll also need to update the usages on lines 30 and 212:
formatDurationMs(...)→formatDurationHuman(...)formatAgo(...)→formatRelativeTimestamp(...)
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/views/overview.ts
Line: 4:4
Comment:
**Build-breaking import of non-existent functions**
`formatAgo` and `formatDurationMs` are not exported from `../format`. The actual exports are `formatRelativeTimestamp` and `formatDurationHuman` (see `ui/src/ui/format.ts` lines 1–5). This will cause a TypeScript compilation error or a runtime import failure.
```suggestion
import { formatRelativeTimestamp, formatDurationHuman } from "../format";
```
You'll also need to update the usages on lines 30 and 212:
- `formatDurationMs(...)` → `formatDurationHuman(...)`
- `formatAgo(...)` → `formatRelativeTimestamp(...)`
How can I resolve this? If you propose a fix, please make it concise.| export function renderOverview(props: OverviewProps) { | ||
| const snapshot = props.hello?.snapshot as | ||
| | { | ||
| uptimeMs?: number; | ||
| policy?: { tickIntervalMs?: number }; | ||
| authMode?: "none" | "token" | "password" | "trusted-proxy"; | ||
| } | ||
| | { uptimeMs?: number; policy?: { tickIntervalMs?: number } } | ||
| | undefined; |
There was a problem hiding this comment.
Removal of authMode / trusted-proxy logic is a behavioral regression
The base version of this file read snapshot?.authMode and used isTrustedProxy to conditionally hide the token/password fields and show "Authenticated via trusted proxy." in the hint text. This PR removed that logic entirely (the authMode field was dropped from the type assertion and the conditional rendering around token/password fields was flattened).
This means gateways using trusted-proxy auth mode will now unnecessarily show token and password fields, and the "Authenticated via trusted proxy." message is replaced with a generic "Click Connect to apply connection changes." hint.
If this was intentional, it should be noted in the PR description. If not, the trusted-proxy handling should be preserved.
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/ui/views/overview.ts
Line: 26:29
Comment:
**Removal of `authMode` / trusted-proxy logic is a behavioral regression**
The base version of this file read `snapshot?.authMode` and used `isTrustedProxy` to conditionally hide the token/password fields and show "Authenticated via trusted proxy." in the hint text. This PR removed that logic entirely (the `authMode` field was dropped from the type assertion and the conditional rendering around token/password fields was flattened).
This means gateways using `trusted-proxy` auth mode will now unnecessarily show token and password fields, and the "Authenticated via trusted proxy." message is replaced with a generic "Click Connect to apply connection changes." hint.
If this was intentional, it should be noted in the PR description. If not, the trusted-proxy handling should be preserved.
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| private loadLocale() { | ||
| const saved = localStorage.getItem("openclaw.i18n.locale") as Locale; | ||
| if (saved && ["en", "zh-CN", "zh-TW", "pt-BR"].includes(saved)) { | ||
| this.locale = saved; | ||
| } else { | ||
| const navLang = navigator.language; | ||
| if (navLang.startsWith("zh")) { | ||
| this.locale = navLang === "zh-TW" || navLang === "zh-HK" ? "zh-TW" : "zh-CN"; | ||
| } else if (navLang.startsWith("pt")) { | ||
| this.locale = "pt-BR"; | ||
| } else { | ||
| this.locale = "en"; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Dual locale storage creates potential sync issues
The I18nManager stores locale in localStorage["openclaw.i18n.locale"] (line 59), while UiSettings stores it separately under the "openclaw.control.settings.v1" JSON key. On app startup:
I18nManagersingleton is created at module load (line 114) and callsloadLocale(), reading from"openclaw.i18n.locale"or falling back tonavigator.language- Then
OpenClawAppconstructor reads fromUiSettings.localeand callsi18n.setLocale()
If only one store is populated (e.g., first-time users who had "openclaw.i18n.locale" set from browser auto-detection but no locale in settings), the UiSettings default of "en" will override the browser-detected locale.
Consider using a single source of truth for the stored locale rather than keeping two separate localStorage entries in sync.
Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/i18n/lib/translate.ts
Line: 14:29
Comment:
**Dual locale storage creates potential sync issues**
The `I18nManager` stores locale in `localStorage["openclaw.i18n.locale"]` (line 59), while `UiSettings` stores it separately under the `"openclaw.control.settings.v1"` JSON key. On app startup:
1. `I18nManager` singleton is created at module load (line 114) and calls `loadLocale()`, reading from `"openclaw.i18n.locale"` or falling back to `navigator.language`
2. Then `OpenClawApp` constructor reads from `UiSettings.locale` and calls `i18n.setLocale()`
If only one store is populated (e.g., first-time users who had `"openclaw.i18n.locale"` set from browser auto-detection but no `locale` in settings), the UiSettings default of `"en"` will override the browser-detected locale.
Consider using a single source of truth for the stored locale rather than keeping two separate localStorage entries in sync.
How can I resolve this? If you propose a fix, please make it concise. - Add translation keys for language selector label and language names
- Update all locale files (en, pt-BR, zh-CN, zh-TW) with:
- overview.access.language key for selector label
- languages.* keys for language display names
- Localize language selector in overview.ts to react to locale changes
- Add validation for stored locale in app.ts to prevent invalid values
from causing silent failures in setLocale
Fixes issues identified in code review:
- Unlocalized language selector inconsistency
- Settings locale type drift risk
92e657e to
d19bc16
Compare
- Add ko-KR.ts with full Korean translations - Add koKR language entry to all locale files (en, zh-CN, zh-TW, pt-BR) - Register ko-KR in SUPPORTED_LOCALES and setLocale dynamic import - Add browser language auto-detection for Korean (navigator.language startsWith 'ko') Relates to issue openclaw#3460 Related to pr openclaw#18508
Greptile Summary
This PR adds a full i18n system to the OpenClaw Control UI using a custom
I18nManagerwith lazy-loaded locale bundles (en, zh-CN, zh-TW, pt-BR), a Lit reactive controller for automatic re-renders, and replaces hardcoded English strings across the navigation, overview, and chat views with translation function calls.overview.tsimportsformatAgoandformatDurationMsfrom../format, but these functions don't exist — the actual exports areformatRelativeTimestampandformatDurationHuman. This will cause a compilation/import error.authMode/isTrustedProxylogic) was removed from the overview page, meaning gateways using trusted-proxy auth will now show unnecessary token/password fields.localStoragekeys ("openclaw.i18n.locale"byI18nManagerand"locale"inUiSettings), which could get out of sync in edge cases. Consider consolidating to a single source of truth.navGroupsCollapsedpreferences for existing users.Confidence Score: 1/5
overview.tsimportsformatAgo/formatDurationMswhich don't exist informat.ts, causing a guaranteed build failure; (2) the removal of trusted-proxy auth mode handling is an unmentioned behavioral change that breaks the expected UX for that gateway configuration. The i18n architecture itself is reasonable but the dual-storage pattern is fragile.ui/src/ui/views/overview.tsneeds immediate attention for the broken imports and the removed trusted-proxy logic.ui/src/i18n/lib/translate.tsshould be reviewed for the dual-storage concern.Last reviewed commit: 2c83cb4
(2/5) Greptile learns from your feedback when you react with thumbs up/down!