feat(apple): generate type-safe L10n accessors and migrate all key literals

Replaces every stringly-typed localization key in the Apple app with
compile-time-checked accessors generated from localization/strings.json.
A mistyped key is now a build error instead of a silent fallback to the
raw key at runtime. The runtime path is unchanged: plain keys are
String.LocalizationValue constants resolved with String(localized:) and
Apple's String Catalog still does the lookup; keys with arguments become
typed, named functions applying args through String(format:).

Generator: new renderSwiftAccessors emits apple/VniDrop/Generated/L10n.swift,
wired into generate. Renamed generic arg1/arg2 tokens on four keys to
semantic names (receiver, transferName, deviceId) and updated their context
notes; positional output is unchanged so .xcstrings (bar the 4 comments)
and the Android XML regenerate identical.

Migration: every key-carrying value flipped to String.LocalizationValue
end to end, resolved only at the leaf. Zero key literals and zero
LocalizedStringKey remain in app or test code. macOS build passes; iOS
test run pending.
This commit is contained in:
2026-07-23 17:12:43 +02:00
parent 5939489432
commit ef42875ddb
28 changed files with 4465 additions and 395 deletions

View File

@@ -40,7 +40,7 @@ final class ProgressDerivationTests: XCTestCase {
event(phase: "import", kind: "started", json: "{}"),
]
let progress = progressForTransfer(events: events, transferId: 1)
XCTAssertEqual(progress?.labelKey, "progress_preparing")
XCTAssertEqual(progress?.labelKey, L10n.Progress.preparing)
XCTAssertEqual(progress?.progress, 0.3)
}
@@ -58,14 +58,14 @@ final class ProgressDerivationTests: XCTestCase {
totalSizeHint: 100
)
XCTAssertEqual(progress?.kind, "completed")
XCTAssertEqual(progress?.labelKey, "progress_completed")
XCTAssertEqual(progress?.labelKey, L10n.Progress.completed)
XCTAssertEqual(progress?.progress, 1)
}
func testStatusLabelKeys() {
XCTAssertEqual(statusLabelKey(.sharing), "status_available")
XCTAssertEqual(statusLabelKey(.receiving), "status_receiving")
XCTAssertEqual(statusLabelKey(.done), "status_completed")
XCTAssertEqual(statusLabelKey(.sharing), L10n.Status.available)
XCTAssertEqual(statusLabelKey(.receiving), L10n.Status.receiving)
XCTAssertEqual(statusLabelKey(.done), L10n.Status.completed)
}
private func event(phase: String, kind: String, json: String) -> CoreEventModel {