Files
hapi/ios/Hapi/Models/LocalizedNoticeMapper.swift
T
weishu 6066f20c20 feat(ios): zh-CN localization + catalog dedupe (A-M5)
Part 1 — Simplified Chinese localization of the iOS app layer:
- ios/Hapi/Resources/Localizable.xcstrings: hand-authored String Catalog
  (395 keys, zh-Hans; en implicit as source). Terminology mined from
  web/src/lib/locales/zh-CN.ts (会话/新建会话/权限模式/允许/拒绝/工作树/
  机器/语音输入/用量/草稿夹 …).
- Mechanical edits only: merged multi-part string concatenations into
  single LocalizedStringKey literals, converted ternary/plain-String user
  copy to String(localized:), switched helper params (detailRow,
  DashboardCard, optionPicker, notice) to LocalizedStringKey.
- LocalizedNoticeMapper (app layer): display-point translation of the 27
  known HapiKit-emitted strings (ChatInteractor notices, dictation errors,
  window-sync warnings, files fallbacks, worktree-name validation) with
  verbatim passthrough for server-originated text; package stays
  language-free.
- Deliberately untranslated (web parity / non-copy): event rows
  (EventPresentation — web renders presentation.ts verbatim), catalog
  option labels (Default/Auto/Sonnet/permission modes — web shows them
  verbatim), tool names, code-like titles (grep(pattern:), MCP:, Skill,
  Task), unit suffixes (B/KB/MB, m/h/d/w), decorative separators.
- Language row wired: AppLanguage gains .system (follow system, new
  default); explicit picks write the AppleLanguages override, Follow
  system removes it; footer notes a relaunch applies it (no supported
  in-place SwiftUI locale swap).
- project.pbxproj: zh-Hans added to knownRegions.

Part 2 — #39 catalog dedupe (HapiProtocol):
- NewSessionCatalogs.claudeModels/claudeEfforts now derive from
  ClaudeModels/ClaudeEfforts (single source shared with ModelCatalog);
  codexReasoningEfforts stays own data (web CODEX_REASONING_EFFORT_OPTIONS
  minus max); effortLabel delegates to ModelCatalog.capitalizedFirst.
- New CatalogTests lock test for the derived option lists.

Gate: bash ios/scripts/linux-test.sh — 459 tests green.
2026-08-18 16:04:03 +08:00

56 lines
2.2 KiB
Swift

import Foundation
/// Display-point localization for user-facing strings emitted by HapiKit
/// (the package layer stays language-free, A-M5): a known package-produced
/// notice maps to its `Localizable.xcstrings` entry, everything else
/// (server-originated errors, dynamic content) passes through verbatim.
///
/// Covered sources: `ChatInteractor` notices, `DictationController` errors,
/// `MessageWindowController` tail-sync warnings, and the files models'
/// fallback errors. Low-value/debug package strings are deliberately not
/// mapped.
enum LocalizedNoticeMapper {
static func map(_ message: String) -> String {
guard knownMessages.contains(message) else { return message }
return String(localized: String.LocalizationValue(message))
}
/// Exact strings the package can emit — each is a key in
/// `Localizable.xcstrings` (hand-authored; runtime-built keys are not
/// extracted by the compiler).
private static let knownMessages: Set<String> = [
// ChatInteractor
"Attachments are still uploading — wait, or retry/remove the failed ones",
"Failed to abort",
"Session is inactive and could not be resumed",
"Failed to cancel queued message",
"Message cancelled — kept your current draft",
"Already delivered to the agent",
"Failed to steer message",
"Request was already handled",
"Request failed",
"Failed to load models",
"Failed to update session",
"Draft parked to scratchlist",
"Scratchlist is full (200 entries)",
"Couldn't park the draft — check the hub connection",
// DictationController
"Could not start recording",
"Could not reach the hub",
"Audio recording failed",
"No audio was recorded",
"Transcription failed",
// NewSessionLogic
"Name needs at least one letter or digit",
// MessageWindowController (degraded-sync banner)
"Failed to synchronize messages",
"Failed to load older messages",
// FilesModel / FileViewerModel fallbacks
"Git status unavailable",
"Failed to list directory",
"Failed to search files",
"Failed to read file",
"Failed to load diff",
]
}