mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
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:
@@ -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 {
|
||||
|
||||
@@ -42,22 +42,22 @@ final class UserFacingErrorTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testToUiTextMapsKnownReasons() {
|
||||
XCTAssertEqual(InvitationError.message("The transfer was refused").toUiText(), .resource("error_permission"))
|
||||
XCTAssertEqual(InvitationError.message("invalid ticket").toUiText(), .resource("error_invalid_ticket"))
|
||||
XCTAssertEqual(InvitationError.message("Select at least one file to share").toUiText(), .resource("error_share_empty"))
|
||||
XCTAssertEqual(InvitationError.message("Camera access is required").toUiText(), .resource("error_camera"))
|
||||
XCTAssertEqual(InvitationError.message("The transfer was refused").toUiText(), .resource(L10n.Error.permission))
|
||||
XCTAssertEqual(InvitationError.message("invalid ticket").toUiText(), .resource(L10n.Error.invalidTicket))
|
||||
XCTAssertEqual(InvitationError.message("Select at least one file to share").toUiText(), .resource(L10n.Error.shareEmpty))
|
||||
XCTAssertEqual(InvitationError.message("Camera access is required").toUiText(), .resource(L10n.Error.camera))
|
||||
}
|
||||
|
||||
func testToUiTextFallsBackToGeneric() {
|
||||
XCTAssertEqual(InvitationError.message("something entirely unexpected").toUiText(), .resource("error_generic"))
|
||||
XCTAssertEqual(InvitationError.message("something entirely unexpected").toUiText(), .resource(L10n.Error.generic))
|
||||
}
|
||||
|
||||
func testToUiTextMapsTypedTransferFailures() {
|
||||
XCTAssertEqual(VnidropError.FilesystemPermission(reason: "read-only folder").toUiText(), .resource("error_filesystem"))
|
||||
XCTAssertEqual(VnidropError.DestinationExists(reason: "target exists").toUiText(), .resource("error_destination_exists"))
|
||||
XCTAssertEqual(VnidropError.StorageFull(reason: "disk full").toUiText(), .resource("error_storage_full"))
|
||||
XCTAssertEqual(VnidropError.Network(reason: "offline").toUiText(), .resource("error_network"))
|
||||
XCTAssertEqual(VnidropError.InvalidInput(reason: "bad path").toUiText(), .resource("error_invalid_input"))
|
||||
XCTAssertEqual(VnidropError.FilesystemPermission(reason: "read-only folder").toUiText(), .resource(L10n.Error.filesystem))
|
||||
XCTAssertEqual(VnidropError.DestinationExists(reason: "target exists").toUiText(), .resource(L10n.Error.destinationExists))
|
||||
XCTAssertEqual(VnidropError.StorageFull(reason: "disk full").toUiText(), .resource(L10n.Error.storageFull))
|
||||
XCTAssertEqual(VnidropError.Network(reason: "offline").toUiText(), .resource(L10n.Error.network))
|
||||
XCTAssertEqual(VnidropError.InvalidInput(reason: "bad path").toUiText(), .resource(L10n.Error.invalidInput))
|
||||
XCTAssertFalse(VnidropError.FilesystemPermission(reason: "read-only").canRetryWithoutChangingInput)
|
||||
XCTAssertFalse(VnidropError.DestinationExists(reason: "target exists").canRetryWithoutChangingInput)
|
||||
XCTAssertTrue(VnidropError.Network(reason: "offline").canRetryWithoutChangingInput)
|
||||
|
||||
Reference in New Issue
Block a user