mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
feat(i18n): close silently un-localizable sites from the audit
Burns down the follow-ups tracked in scarf/docs/I18N.md so that future
translation passes (Phase 2+) don't see English leak through ternary UI
copy, enum rawValue displays, or fixed-format strings.
- Ternary status copy: Text(cond ? "A" : "B") → cond ? Text("A") : Text("B")
(each branch routes through LocalizedStringKey). Covers Health, Chat
(voice/TTS/recording/ACP status), Profiles, MCPServer test result,
SignalSetup, QuickCommands header.
- Enum .rawValue displays: LogFile, LogComponent, DashboardTab, Skills.Tab,
InsightsPeriod, ToolKind, AuthType each expose a
displayName: LocalizedStringResource. LogEntry.LogLevel stays verbatim
(technical jargon — DEBUG/INFO/ERROR/… are industry-standard).
- displayName passthroughs: HermesToolPlatform, ServerRegistry.Entry,
MCPServerPreset wrapped with Text(verbatim:) at call sites (brand names
and user data, not UI chrome). MCPTransport.displayName promoted to
LocalizedStringResource.
- Composite format strings: ModelPickerSheet "ctx" suffix, InsightsView
"tokens" suffix and MCPServerTestResultView "%.1fs · %d tools" rewritten
as Text("\(arg) suffix") LocalizedStringKey. Percent display uses
.formatted(.percent) after /100.
- Day-of-week chart now sources from Calendar.current.shortWeekdaySymbols,
re-indexed for the existing Mon=0 data model.
- ConnectionStatusPill's label + tooltip return Text (not String) so the
.help(Text) / direct-render paths localize correctly.
- Catalog re-synced: 545 → 575 keys (+30 from new ternary branches and
enum displayName values).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+9
-47
@@ -47,56 +47,18 @@ For the three supported non-English locales we use Xcode's built-in AI translati
|
||||
|
||||
Strings that are **user data** (session titles, memory file contents, log lines, shell commands shown in UI, file paths) should pass through without localization — this happens naturally when the value is a `String` variable, since those overloads skip the catalog.
|
||||
|
||||
## Outstanding audit follow-ups
|
||||
## Audit status
|
||||
|
||||
The [initial i18n PR](#) enabled the catalog infrastructure and migrated clear locale-bug formatter sites. The following patterns remain in the codebase and should be refactored **before** translations ship in Phase 2 — otherwise their English copy will leak through regardless of locale:
|
||||
Phase 1b (the `multi-language` PR) closed every tracked site from the original audit:
|
||||
|
||||
### Category A — UI copy held in a `String` variable (needs `LocalizedStringResource`)
|
||||
- **Category A high-priority (ternary UI copy)** — converted to `Text`-ternary form so each branch routes through `LocalizedStringKey`.
|
||||
- **Category A medium-priority (enum `.rawValue` displays)** — each enum now exposes `displayName: LocalizedStringResource` and call sites use it. `LogEntry.LogLevel` (technical jargon) stays verbatim.
|
||||
- **Category A lower-priority (displayName passthroughs)** — wrapped with `Text(verbatim:)` for proper nouns / user data (`HermesToolPlatform`, `ServerRegistry.Entry`, `MCPServerPreset`). `MCPTransport.displayName` promoted to `LocalizedStringResource`.
|
||||
- **Category B (composite format strings)** — migrated to `Text("\(arg) suffix")` with `LocalizedStringKey` or to `.percent` / `.currency` FormatStyle.
|
||||
- **Category C (hard-coded day names)** — replaced with `Calendar.current.shortWeekdaySymbols`, re-indexed to match the existing Mon=0 data model.
|
||||
- **Category D (`.help(stringVar)` sites)** — `ConnectionStatusPill` now returns `Text` from its `labelText` / `tooltipText` properties.
|
||||
|
||||
High-priority (ternary UI copy, visible status chrome):
|
||||
|
||||
- `Features/Health/Views/HealthView.swift:135` — `"Hermes Running"` / `"Hermes Stopped"` ternary
|
||||
- `Features/Chat/Views/ChatView.swift:125` — `"Active"` fallback
|
||||
- `Features/Chat/Views/ChatView.swift:241` — `"Voice On" / "Voice Off"`
|
||||
- `Features/Chat/Views/ChatView.swift:256` — `"TTS On" / "TTS Off"`
|
||||
- `Features/Chat/Views/ChatView.swift:271` — `"Recording..." / "Push to Talk"`
|
||||
- `Features/MCPServers/Views/MCPServerTestResultView.swift:13` — `"Test passed" / "Test failed"`
|
||||
- `Features/Profiles/Views/ProfilesView.swift:145` — `"Active profile" / "Inactive"`
|
||||
- `Features/Platforms/Views/PlatformSetup/SignalSetupView.swift:54` — `"signal-cli is available on PATH"` / not-found message
|
||||
- `Features/QuickCommands/Views/QuickCommandsView.swift:148` — `"Add Quick Command"` / `"Edit /\(name)"` ternary
|
||||
- `Features/MCPServers/Views/MCPServersView.swift:131` — `.help("\(count) tools")` vs `"Test failed"` ternary
|
||||
- `Features/MCPServers/Views/MCPServerDetailView.swift:157, 185` — masked-value placeholder
|
||||
|
||||
Medium-priority (enum `.rawValue` → display):
|
||||
|
||||
- `Features/Logs/Views/LogsView.swift:30,38,48,69` — file / component / level display
|
||||
- `Features/Projects/Views/ProjectsView.swift:153` — tab display
|
||||
- `Features/Skills/Views/SkillsView.swift:37` — tab display
|
||||
- `Features/Insights/Views/InsightsView.swift:40, 201` — period picker, day-of-week names (see also Category C)
|
||||
- `Features/CredentialPools/Views/CredentialPoolsView.swift:265` — type display
|
||||
- `Features/Activity/Views/ActivityView.swift:117` — kind display
|
||||
|
||||
Lower-priority (displayName passthroughs from services that could be wrapped once at the source):
|
||||
|
||||
- `Features/Platforms/Views/PlatformsView.swift:43, 91`, `Features/Tools/Views/ToolsView.swift:46` — `platform.displayName`
|
||||
- `Features/Gateway/Views/GatewayView.swift:105` — `platform.name.capitalized`
|
||||
- `Features/MCPServers/Views/*` — `transport.displayName`, preset names / descriptions
|
||||
- `Features/Servers/Views/ServerSwitcherToolbar.swift:40`, `ManageServersView.swift:96` — `server.displayName`
|
||||
|
||||
### Category B — Composite format strings with translatable suffixes
|
||||
|
||||
- `Features/Settings/Views/Components/ModelPickerSheet.swift:105` — `ctx + " ctx"` — literal " ctx" needs keying
|
||||
- `Features/Insights/Views/InsightsView.swift:93` — `formatTokens(...) + " tokens"` — literal " tokens" needs keying
|
||||
- `Features/MCPServers/Views/MCPServerTestResultView.swift:15` — `"%.1fs · %d tools"` — needs splitting into a `String(localized: "\(elapsed)s · \(count) tools")`
|
||||
- `Features/Insights/Views/InsightsView.swift:167` — `"%.1f%%"` — needs `.formatted(.percent)` after verifying the input range (0…1 vs 0…100)
|
||||
|
||||
### Category C — Hard-coded day / month name arrays
|
||||
|
||||
- `Features/Insights/Views/InsightsView.swift:196` — `["Mon", "Tue", …]` literal — use `Calendar.current.shortWeekdaySymbols` (locale-aware).
|
||||
|
||||
### Category D — `.help(stringVar)` sites
|
||||
|
||||
- `Features/Servers/Views/ConnectionStatusPill.swift:42` — `.help(tooltip)`; refactor `tooltip` to return `LocalizedStringKey` / `LocalizedStringResource`.
|
||||
If you spot a new silently-un-localizable site during translation review, prefer the patterns in the table above over one-off workarounds.
|
||||
|
||||
### Non-blocking (intentional verbatim)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user