mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-09 19:29:41 +00:00
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.
55 lines
2.4 KiB
Swift
55 lines
2.4 KiB
Swift
import Foundation
|
|
|
|
/// A `(wire value, display label)` pair for the create-form option pickers.
|
|
public struct NewSessionOption: Equatable, Hashable, Sendable {
|
|
public let value: String
|
|
public let label: String
|
|
|
|
public init(value: String, label: String) {
|
|
self.value = value
|
|
self.label = label
|
|
}
|
|
}
|
|
|
|
/// Static option catalogs for the new-session form. The claude rows are
|
|
/// **derived** from `ClaudeModels` / `ClaudeEfforts` (issue #39: one source
|
|
/// of truth shared with `ModelCatalog`'s composer builders) rather than
|
|
/// duplicating the preset/label tables. Only the codex reasoning list is its
|
|
/// own data — a port of the web `CODEX_REASONING_EFFORT_OPTIONS`
|
|
/// (`web/src/components/NewSession/types.ts`; the web drops `max` for codex
|
|
/// — `EffortField.tsx`). Android keeps a feature-local copy
|
|
/// (`NewSessionCatalogs.kt`); here the catalogs live in one package so the
|
|
/// pure form logic and the app UI share one source.
|
|
public enum NewSessionCatalogs {
|
|
/// `'auto'` sentinel row uses the web's "Default" label; preset rows come
|
|
/// from `ClaudeModels` in picker order.
|
|
public static let claudeModels: [NewSessionOption] =
|
|
[NewSessionOption(value: "auto", label: "Default")]
|
|
+ ClaudeModels.presets.map {
|
|
NewSessionOption(value: $0, label: ClaudeModels.label(for: $0) ?? $0)
|
|
}
|
|
|
|
/// `'auto'` sentinel row, then the `ClaudeEfforts` levels in ascending
|
|
/// order.
|
|
public static let claudeEfforts: [NewSessionOption] =
|
|
[NewSessionOption(value: "auto", label: "Auto")]
|
|
+ ClaudeEfforts.levels.map {
|
|
NewSessionOption(value: $0, label: ClaudeEfforts.label(for: $0) ?? effortLabel($0))
|
|
}
|
|
|
|
/// Static codex fallback when the model row advertises no efforts
|
|
/// (the web drops `max` for codex — `EffortField.tsx`).
|
|
public static let codexReasoningEfforts: [NewSessionOption] = [
|
|
NewSessionOption(value: "default", label: "Default"),
|
|
NewSessionOption(value: "low", label: "Low"),
|
|
NewSessionOption(value: "medium", label: "Medium"),
|
|
NewSessionOption(value: "high", label: "High"),
|
|
NewSessionOption(value: "xhigh", label: "XHigh"),
|
|
]
|
|
|
|
/// Capitalized label for a server-advertised effort id (`high` → `High`).
|
|
public static func effortLabel(_ value: String) -> String {
|
|
ModelCatalog.capitalizedFirst(value)
|
|
}
|
|
}
|