From 0af35607feb99ff463d9083b0600f186cfbbecf4 Mon Sep 17 00:00:00 2001 From: Maciej Trybilo Date: Fri, 1 May 2026 13:17:28 -0400 Subject: [PATCH] Make StringEntry.localizations optional and improve decoding error reporting. --- Sources/LocheckLogic/Types/StringCatalog.swift | 8 ++++---- .../Validators/parseAndValidateStringCatalog.swift | 6 +++--- .../StringCatalogCommandTests.swift | 2 ++ Tests/LocheckLogicTests/StringCatalogTests.swift | 10 +++++----- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Sources/LocheckLogic/Types/StringCatalog.swift b/Sources/LocheckLogic/Types/StringCatalog.swift index 603137f..1c3e8b5 100644 --- a/Sources/LocheckLogic/Types/StringCatalog.swift +++ b/Sources/LocheckLogic/Types/StringCatalog.swift @@ -21,7 +21,7 @@ struct StringCatalog: Codable { */ struct StringEntry: Codable { let extractionState: String? - let localizations: [String: Localization] + let localizations: [String: Localization]? let comment: String? } @@ -74,7 +74,7 @@ extension StringCatalog { self = try decoder.decode(StringCatalog.self, from: data) } catch { problemReporter.report( - InvalidFile(details: "Failed to parse string catalog: \(error.localizedDescription)"), + InvalidFile(details: "Failed to parse string catalog: \(error)"), path: path, lineNumber: 0) return nil @@ -93,13 +93,13 @@ extension StringEntry { ) -> [LocalizedStringPair] { var pairs: [LocalizedStringPair] = [] - guard let sourceLocalization = localizations[sourceLanguage] else { + guard let sourceLocalization = localizations?[sourceLanguage] else { return pairs } let sourceString = sourceLocalization.stringUnit?.value ?? key - for (language, localization) in localizations { + for (language, localization) in localizations ?? [:] { if language == sourceLanguage { continue // Skip source language for validation } diff --git a/Sources/LocheckLogic/Validators/parseAndValidateStringCatalog.swift b/Sources/LocheckLogic/Validators/parseAndValidateStringCatalog.swift index 5d8be4a..a30ada2 100644 --- a/Sources/LocheckLogic/Validators/parseAndValidateStringCatalog.swift +++ b/Sources/LocheckLogic/Validators/parseAndValidateStringCatalog.swift @@ -139,7 +139,7 @@ private func validateStringCatalogStructure( // Check that source language exists in all entries for (key, entry) in stringCatalog.strings { - if entry.localizations[sourceLanguage] == nil { + if entry.localizations?[sourceLanguage] == nil { problemReporter.report( KeyMissingFromTranslation( key: key, @@ -149,8 +149,8 @@ private func validateStringCatalogStructure( } // Check for incomplete translations - let sourceLocalization = entry.localizations[sourceLanguage] - for (language, localization) in entry.localizations { + let sourceLocalization = entry.localizations?[sourceLanguage] + for (language, localization) in entry.localizations ?? [:] { if language == sourceLanguage { continue } diff --git a/Tests/LocheckCommandTests/StringCatalogCommandTests.swift b/Tests/LocheckCommandTests/StringCatalogCommandTests.swift index 3f6eb31..904b866 100644 --- a/Tests/LocheckCommandTests/StringCatalogCommandTests.swift +++ b/Tests/LocheckCommandTests/StringCatalogCommandTests.swift @@ -44,6 +44,8 @@ final class StringCatalogCommandTests: XCTestCase { } } } + }, + "empty_key" : { } }, "version" : "1.0" diff --git a/Tests/LocheckLogicTests/StringCatalogTests.swift b/Tests/LocheckLogicTests/StringCatalogTests.swift index aad6608..dc0ea96 100644 --- a/Tests/LocheckLogicTests/StringCatalogTests.swift +++ b/Tests/LocheckLogicTests/StringCatalogTests.swift @@ -44,9 +44,9 @@ final class StringCatalogTests: XCTestCase { XCTAssertEqual(catalog.strings.count, 1) let helloWorldEntry = catalog.strings["hello_world"]! - XCTAssertEqual(helloWorldEntry.localizations.count, 2) - XCTAssertEqual(helloWorldEntry.localizations["en"]?.stringUnit?.value, "Hello, World!") - XCTAssertEqual(helloWorldEntry.localizations["de"]?.stringUnit?.value, "Hallo, Welt!") + XCTAssertEqual(helloWorldEntry.localizations?.count, 2) + XCTAssertEqual(helloWorldEntry.localizations?["en"]?.stringUnit?.value, "Hello, World!") + XCTAssertEqual(helloWorldEntry.localizations?["de"]?.stringUnit?.value, "Hallo, Welt!") } func testStringCatalogWithPluralForms() { @@ -104,8 +104,8 @@ final class StringCatalogTests: XCTestCase { let catalog = try! JSONDecoder().decode(StringCatalog.self, from: data) let itemCountEntry = catalog.strings["item_count"]! - let enPlurals = itemCountEntry.localizations["en"]?.variations?.plural - let dePlurals = itemCountEntry.localizations["de"]?.variations?.plural + let enPlurals = itemCountEntry.localizations?["en"]?.variations?.plural + let dePlurals = itemCountEntry.localizations?["de"]?.variations?.plural XCTAssertNotNil(enPlurals) XCTAssertNotNil(dePlurals)