From ea3a79a903f2ded5abffa7c71654dcfe86e5820a Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Mon, 18 Dec 2023 20:47:45 -0500 Subject: [PATCH 01/19] set XCStrings model --- .../Sources/StringCatalogEnum/model.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift index 584e087..6261aed 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift @@ -6,4 +6,21 @@ struct XCStrings: Decodable { let version: String let strings: [String: String] } -*/ \ No newline at end of file +*/ +struct XCStrings: Decodable { + let strings: [String: StringInfo] +} + +struct StringInfo: Decodable { + let extractionState: String? + let localizations: [String: Localization] +} + +struct Localization: Decodable { + let stringUnit: StringUnit +} + +struct StringUnit: Decodable { + let state: String + let value: String +} From eb4bb9f99d52a5e7f6158977224431d20ad4c967 Mon Sep 17 00:00:00 2001 From: uniboy <90630084+Uni-boy@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:03:46 +0000 Subject: [PATCH 02/19] Fix comments in model.swift --- .../Sources/StringCatalogEnum/model.swift | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift index 6261aed..b79e626 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift @@ -1,26 +1,80 @@ // TODO: make XCStrings Decodable by listing all possible models // Example: let obj = try JSONDecoder().decode(XCStrings.self, from: data) -/* -struct XCStrings: Decodable { - let sourceLanguage: String - let version: String - let strings: [String: String] -} -*/ + +/// Represents the root structure of the xcstrings JSON. +/// This struct is designed to handle various JSON formats, including those +/// generated from SwiftUI and manually added translations. +/// +/// Examples of supported JSON formats: +/// +/// 1. Generated from SwiftUI (no translation added): +/// ``` +/// "Home": {} +/// ``` +/// +/// 2. Generated from SwiftUI, with an English translation: +/// ``` +/// "Login": { +/// "localizations": { +/// "en": { +/// "stringUnit": { +/// "state": "translated", +/// "value": "Login" +/// } +/// } +/// } +/// } +/// ``` +/// +/// 3. Manually added, English only: +/// ``` +/// "welcomeBack": { +/// "extractionState": "manual", +/// "localizations": { +/// "en": { +/// "stringUnit": { +/// "state": "translated", +/// "value": "Welcome back" +/// } +/// } +/// } +/// } +/// ``` struct XCStrings: Decodable { + /// Dictionary mapping a string key to its `StringInfo`. + /// Each key represents a string identifier (e.g., "Login", "welcomeBack"). let strings: [String: StringInfo] } +/// Contains information about a specific string key, including its localizations. +/// This struct can handle both automatically generated and manually added data. struct StringInfo: Decodable { + /// The state of the extraction, if applicable. This field is optional + /// and typically present in manually added translations. + /// Example: "manual" let extractionState: String? + + /// Dictionary mapping locale codes (e.g., "en") to their respective `Localization`. + /// Handles multiple localizations for the same string. let localizations: [String: Localization] } +/// Represents the localization details of a string. +/// Contains information about the translated string and its state. struct Localization: Decodable { + /// Contains the actual localized string and its state. + /// Example: state - "translated", value - "Login" let stringUnit: StringUnit } +/// Details of the localized string, including its state and value. +/// Used for translating and tracking the state of a string. struct StringUnit: Decodable { + /// The state of the string, indicating its translation status. + /// Example: "translated" let state: String + + /// The localized string value. + /// Example: "Login", "Welcome back" let value: String } From f47ceb86b8155cfc2d8d618c8a773adbe41bee15 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Sat, 30 Dec 2023 04:00:22 -0500 Subject: [PATCH 03/19] Model Finalized --- .../contents.xcworkspacedata | 7 ++ .../Sources/StringCatalogEnum/model.swift | 67 ++++++++++++++++--- .../StringCatalogEnumTests.swift | 25 ++++++- 3 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift index 6261aed..94c9a0b 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift @@ -1,22 +1,73 @@ -// TODO: make XCStrings Decodable by listing all possible models -// Example: let obj = try JSONDecoder().decode(XCStrings.self, from: data) -/* +/// Represents the root structure of the xcstrings JSON. +/// This struct is designed to handle various JSON formats, including those +/// generated from SwiftUI and manually added translations. +/// +/// Examples of supported JSON formats: +/// +/// 1. Generated from SwiftUI (no translation added): +/// ``` +/// "Home": {} +/// ``` +/// +/// 2. Generated from SwiftUI, with an English translation: +/// ``` +/// "Login": { +/// "localizations": { +/// "en": { +/// "stringUnit": { +/// "state": "translated", +/// "value": "Login" +/// } +/// } +/// } +/// } +/// ``` +/// +/// 3. Manually added, English only: +/// ``` +/// "welcomeBack": { +/// "extractionState": "manual", +/// "localizations": { +/// "en": { +/// "stringUnit": { +/// "state": "translated", +/// "value": "Welcome back" +/// } +/// } +/// } +/// } +/// ``` + struct XCStrings: Decodable { let sourceLanguage: String let version: String - let strings: [String: String] -} -*/ -struct XCStrings: Decodable { let strings: [String: StringInfo] } struct StringInfo: Decodable { let extractionState: String? - let localizations: [String: Localization] + let localizations: [String: Localization]? } struct Localization: Decodable { + let variations: Variations? +} + +struct Variations: Decodable { + let plural: PluralVariations? + let device: DeviceVariations? +} + +struct PluralVariations: Decodable { + let one: StringUnitWrapper? + let other: StringUnitWrapper? +} + +struct DeviceVariations: Decodable { + let variations: [String: StringUnitWrapper]? +} + +struct StringUnitWrapper: Decodable { let stringUnit: StringUnit } diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index 7176021..d1a23cb 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -5,10 +5,29 @@ import Quick final class StringCatalogEnumSpec: QuickSpec { override class func spec() { context("StringCatalogEnum") { - describe("Example") { - it("should be replaced") { - expect(true).toNot(equal(false)) + describe("XCStrings Decoding") { + it("should decode simple key correctly") { + let jsonString = """ + { + "strings": { + "Home": {} + } + } + """ + let jsonData = Data(jsonString.utf8) + let decoder = JSONDecoder() + + do { + let xcStrings = try decoder.decode(XCStrings.self, from: jsonData) + expect(xcStrings.strings["Home"]).toNot(beNil()) + expect(xcStrings.strings["Home"]?.extractionState).to(beNil()) + expect(xcStrings.strings["Home"]?.localizations.isEmpty).to(beTrue()) + } catch { + fail("Decoding failed: \(error)") + } } + + // Add other test cases here in similar fashion } } } From 428022b0d5e6d336c05d818db0e292b2d5288b2a Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Sat, 30 Dec 2023 04:41:28 -0500 Subject: [PATCH 04/19] XCStrings Model finalized --- .../Sources/StringCatalogEnum/model.swift | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift index b3755a0..e166211 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift @@ -1,40 +1,38 @@ -// TODO: make XCStrings Decodable by listing all possible models -// Example: let obj = try JSONDecoder().decode(XCStrings.self, from: data) -/* + struct XCStrings: Decodable { let sourceLanguage: String let version: String - let strings: [String: String] -} -*/ -struct XCStrings: Decodable { let strings: [String: StringInfo] } -/// Contains information about a specific string key, including its localizations. -/// This struct can handle both automatically generated and manually added data. struct StringInfo: Decodable { - /// The state of the extraction, if applicable. This field is optional - /// and typically present in manually added translations. - /// Example: "manual" let extractionState: String? - let localizations: [String: Localization] + let localizations: [String: Localization]? } -/// Represents the localization details of a string. -/// Contains information about the translated string and its state. struct Localization: Decodable { + let variations: Variations? +} + +struct Variations: Decodable { + let plural: PluralVariations? + let device: DeviceVariations? +} + +struct PluralVariations: Decodable { + let one: StringUnitWrapper? + let other: StringUnitWrapper? +} + +struct DeviceVariations: Decodable { + let variations: [String: StringUnitWrapper]? +} + +struct StringUnitWrapper: Decodable { let stringUnit: StringUnit } -/// Details of the localized string, including its state and value. -/// Used for translating and tracking the state of a string. struct StringUnit: Decodable { - /// The state of the string, indicating its translation status. - /// Example: "translated" let state: String - - /// The localized string value. - /// Example: "Login", "Welcome back" let value: String -} +} \ No newline at end of file From 7667b946052d07c0872c079a0cfaed09d7aa4d64 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Sat, 30 Dec 2023 04:41:55 -0500 Subject: [PATCH 05/19] issue number added --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d7e12f2..c3a44e5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,4 +2,4 @@ ## Related issue(s) -- #ISSUE_NUMBER \ No newline at end of file +- #1 \ No newline at end of file From 426a6f3122575213cc26a9ff7ee0d8219ad8c48a Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Sat, 30 Dec 2023 20:26:39 -0500 Subject: [PATCH 06/19] modify model file position --- StringCatalogEnum/Package.resolved | 4 +- StringCatalogEnum/Package.swift | 2 +- .../{model.swift => Models.swift} | 7 ---- .../Sources/StringCatalogEnum/XCStrings.swift | 5 +++ .../StringCatalogEnumTests.swift | 41 ++++++++----------- 5 files changed, 26 insertions(+), 33 deletions(-) rename StringCatalogEnum/Sources/StringCatalogEnum/{model.swift => Models.swift} (83%) create mode 100644 StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift diff --git a/StringCatalogEnum/Package.resolved b/StringCatalogEnum/Package.resolved index 50808b1..3f32e58 100644 --- a/StringCatalogEnum/Package.resolved +++ b/StringCatalogEnum/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "d616f15123bfb36db1b1075153f73cf40605b39d", - "version": "13.0.0" + "revision": "371f7d278ba95a5511c248aaeec84f5dbe770491", + "version": "12.0.1" } }, { diff --git a/StringCatalogEnum/Package.swift b/StringCatalogEnum/Package.swift index 88cb89b..1a8e98a 100644 --- a/StringCatalogEnum/Package.swift +++ b/StringCatalogEnum/Package.swift @@ -40,7 +40,7 @@ let package = Package( ), .package( url: "https://github.com/Quick/Nimble.git", - .upToNextMinor(from: "13.0.0") + .upToNextMinor(from: "12.0.0") ), ], targets: [ diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/Models.swift similarity index 83% rename from StringCatalogEnum/Sources/StringCatalogEnum/model.swift rename to StringCatalogEnum/Sources/StringCatalogEnum/Models.swift index e166211..e6391d6 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/Models.swift @@ -1,10 +1,3 @@ - -struct XCStrings: Decodable { - let sourceLanguage: String - let version: String - let strings: [String: StringInfo] -} - struct StringInfo: Decodable { let extractionState: String? let localizations: [String: Localization]? diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift b/StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift new file mode 100644 index 0000000..68ac1c0 --- /dev/null +++ b/StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift @@ -0,0 +1,5 @@ +public struct XCStrings: Decodable { + let sourceLanguage: String + let version: String + let strings: [String: StringInfo] +} \ No newline at end of file diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index d1a23cb..83d8c55 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -4,30 +4,25 @@ import Quick final class StringCatalogEnumSpec: QuickSpec { override class func spec() { - context("StringCatalogEnum") { - describe("XCStrings Decoding") { - it("should decode simple key correctly") { - let jsonString = """ - { - "strings": { - "Home": {} - } - } - """ - let jsonData = Data(jsonString.utf8) - let decoder = JSONDecoder() - - do { - let xcStrings = try decoder.decode(XCStrings.self, from: jsonData) - expect(xcStrings.strings["Home"]).toNot(beNil()) - expect(xcStrings.strings["Home"]?.extractionState).to(beNil()) - expect(xcStrings.strings["Home"]?.localizations.isEmpty).to(beTrue()) - } catch { - fail("Decoding failed: \(error)") - } + describe("a decodable model") { + it("can decode the json data") { + let json = """ + { + "sourceLanguage" : "en", + "strings" : { + "Home" : { + }, + }, + "version" : "1.0" } - - // Add other test cases here in similar fashion + """ + guard let jsonData = json.data(using: .utf8) else { + fatalError("Invalid JSON string") + } + let decoder = JSONDecoder() + expect{ + try decoder.decode(XCStrings.self, from: jsonData) + }.toNot(throwError()) } } } From b97197cfb1088980b3ac9ff11f969a5e45250276 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Sun, 31 Dec 2023 00:10:38 -0500 Subject: [PATCH 07/19] Model test added --- StringCatalogEnum/Package.swift | 8 +- .../Localization.swift} | 2 +- .../Models.swift | 14 +-- .../StringCatalogEnumTests.swift | 109 +++++++++++++++++- 4 files changed, 119 insertions(+), 14 deletions(-) rename StringCatalogEnum/Sources/{StringCatalogEnum/XCStrings.swift => StringCatalogEnumLibrary/Localization.swift} (69%) rename StringCatalogEnum/Sources/{StringCatalogEnum => StringCatalogEnumLibrary}/Models.swift (57%) diff --git a/StringCatalogEnum/Package.swift b/StringCatalogEnum/Package.swift index 1a8e98a..39196b9 100644 --- a/StringCatalogEnum/Package.swift +++ b/StringCatalogEnum/Package.swift @@ -24,6 +24,7 @@ let package = Package( .macOS(.v10_12), ], products: [ + .library(name: "StringCatalogEnumLibrary", targets: ["StringCatalogEnumLibrary"]), .executable(name: "xcstrings-enum-generate", targets: ["StringCatalogEnum"]) ], dependencies: [ @@ -49,6 +50,7 @@ let package = Package( .target( name: "StringCatalogEnum", dependencies: [ + "StringCatalogEnumLibrary", .product( name: "ArgumentParser", package: "swift-argument-parser" @@ -61,9 +63,13 @@ let package = Package( */ ] ), + .target( + name: "StringCatalogEnumLibrary", + dependencies: [] + ), .testTarget( name: "StringCatalogEnumTests", - dependencies: ["StringCatalogEnum", "Quick", "Nimble"] + dependencies: ["StringCatalogEnumLibrary", "StringCatalogEnum", "Quick", "Nimble"] ), ] ) \ No newline at end of file diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift similarity index 69% rename from StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift rename to StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift index 68ac1c0..e8d6b39 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/XCStrings.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift @@ -1,4 +1,4 @@ -public struct XCStrings: Decodable { +public struct Localizations: Decodable { let sourceLanguage: String let version: String let strings: [String: StringInfo] diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/Models.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift similarity index 57% rename from StringCatalogEnum/Sources/StringCatalogEnum/Models.swift rename to StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift index e6391d6..5d43862 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/Models.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift @@ -1,31 +1,31 @@ -struct StringInfo: Decodable { +public struct StringInfo: Decodable { let extractionState: String? let localizations: [String: Localization]? } -struct Localization: Decodable { +public struct Localization: Decodable { let variations: Variations? } -struct Variations: Decodable { +public struct Variations: Decodable { let plural: PluralVariations? let device: DeviceVariations? } -struct PluralVariations: Decodable { +public struct PluralVariations: Decodable { let one: StringUnitWrapper? let other: StringUnitWrapper? } -struct DeviceVariations: Decodable { +public struct DeviceVariations: Decodable { let variations: [String: StringUnitWrapper]? } -struct StringUnitWrapper: Decodable { +public struct StringUnitWrapper: Decodable { let stringUnit: StringUnit } -struct StringUnit: Decodable { +public struct StringUnit: Decodable { let state: String let value: String } \ No newline at end of file diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index 83d8c55..aa70913 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -1,19 +1,20 @@ import Foundation import Nimble import Quick +import StringCatalogEnumLibrary final class StringCatalogEnumSpec: QuickSpec { override class func spec() { describe("a decodable model") { - it("can decode the json data") { + it("can decode the json data with key only") { let json = """ { "sourceLanguage" : "en", "strings" : { - "Home" : { + "Home" : { + }, }, - }, - "version" : "1.0" + "version" : "1.0" } """ guard let jsonData = json.data(using: .utf8) else { @@ -21,7 +22,105 @@ final class StringCatalogEnumSpec: QuickSpec { } let decoder = JSONDecoder() expect{ - try decoder.decode(XCStrings.self, from: jsonData) + try decoder.decode(Localizations.self, from: jsonData) + }.toNot(throwError()) + } + + it("can decode the json data with English translation added") { + let json = """ + { + "sourceLanguage" : "en", + "strings" : { + "Login" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Login" + } + } + } + }, + }, + "version" : "1.0" + } + """ + guard let jsonData = json.data(using: .utf8) else { + fatalError("Invalid JSON string") + } + let decoder = JSONDecoder() + expect{ + try decoder.decode(Localizations.self, from: jsonData) + }.toNot(throwError()) + } + + it("can decode the json data with English translation manually added") { + let json = """ + { + "sourceLanguage" : "en", + "strings" : { + "welcomeBack" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Welcome back" + } + } + } + }, + }, + "version" : "1.0" + } + """ + guard let jsonData = json.data(using: .utf8) else { + fatalError("Invalid JSON string") + } + let decoder = JSONDecoder() + expect{ + try decoder.decode(Localizations.self, from: jsonData) + }.toNot(throwError()) + } + + it("can decode the json data with English translation and pluralism manually added") { + let json = """ + { + "sourceLanguage" : "en", + "strings" : { + "%11d Books" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "variations" : { + "plural" : { + "one" : { + "stringUnit" : { + "state" : "translated", + "value" : "Book" + } + }, + "other" : { + "stringUnit" : { + "state" : "translated", + "value" : "%11d Books" + } + } + } + } + } + } + }, + }, + "version" : "1.0" + } + """ + guard let jsonData = json.data(using: .utf8) else { + fatalError("Invalid JSON string") + } + let decoder = JSONDecoder() + expect{ + try decoder.decode(Localizations.self, from: jsonData) }.toNot(throwError()) } } From f76beda63fb35a902cc5b7aae2a3d5cac23045ce Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Sun, 31 Dec 2023 00:12:54 -0500 Subject: [PATCH 08/19] modify models permission --- .../Sources/StringCatalogEnumLibrary/Models.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift index 5d43862..e6391d6 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift @@ -1,31 +1,31 @@ -public struct StringInfo: Decodable { +struct StringInfo: Decodable { let extractionState: String? let localizations: [String: Localization]? } -public struct Localization: Decodable { +struct Localization: Decodable { let variations: Variations? } -public struct Variations: Decodable { +struct Variations: Decodable { let plural: PluralVariations? let device: DeviceVariations? } -public struct PluralVariations: Decodable { +struct PluralVariations: Decodable { let one: StringUnitWrapper? let other: StringUnitWrapper? } -public struct DeviceVariations: Decodable { +struct DeviceVariations: Decodable { let variations: [String: StringUnitWrapper]? } -public struct StringUnitWrapper: Decodable { +struct StringUnitWrapper: Decodable { let stringUnit: StringUnit } -public struct StringUnit: Decodable { +struct StringUnit: Decodable { let state: String let value: String } \ No newline at end of file From 100669cda73b51c269a8ee0c2f75e1aa8f1e6d66 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Mon, 1 Jan 2024 11:39:20 -0500 Subject: [PATCH 09/19] Test and Package.swift conflicts resolved --- StringCatalogEnum/Package.resolved | 4 +- StringCatalogEnum/Package.swift | 4 - .../StringCatalogEnumTests.swift | 77 +++++++++---------- 3 files changed, 39 insertions(+), 46 deletions(-) diff --git a/StringCatalogEnum/Package.resolved b/StringCatalogEnum/Package.resolved index 074c01c..198829f 100644 --- a/StringCatalogEnum/Package.resolved +++ b/StringCatalogEnum/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/Quick/Nimble.git", "state": { "branch": null, - "revision": "371f7d278ba95a5511c248aaeec84f5dbe770491", - "version": "12.0.1" + "revision": "d616f15123bfb36db1b1075153f73cf40605b39d", + "version": "13.0.0" } }, { diff --git a/StringCatalogEnum/Package.swift b/StringCatalogEnum/Package.swift index 3e5a4f8..6235dca 100644 --- a/StringCatalogEnum/Package.swift +++ b/StringCatalogEnum/Package.swift @@ -69,10 +69,6 @@ let package = Package( // ] ), - .target( - name: "StringCatalogEnumLibrary", - dependencies: [] - ), .testTarget( name: "StringCatalogEnumTests", dependencies: ["StringCatalogEnumLibrary", "StringCatalogEnum", "Quick", "Nimble"] diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index 5cf7eb9..2dc6c3e 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -1,9 +1,45 @@ import Nimble import Quick import StringCatalogEnumLibrary +import Foundation -final class StringCatalogEnumSpec: QuickSpec { +/// Tests the convertToVariableName() function in StringKeyModel +final class StringKeyModelSpec: QuickSpec { override class func spec() { + let stringEnumHelper = StringEnumHelper() + + context("StringKeyModel") { + describe("Convert To Variable Name Function") { + it("Basic cases should convert properly") { + // Basic test cases + expect(stringEnumHelper.convertToVariableName(key: "SomeKey")).to(equal("someKey")) + expect(stringEnumHelper.convertToVariableName(key: "123Key")).to(equal("key")) + + // Test cases with special characters + expect(stringEnumHelper.convertToVariableName(key: "Special!@#$%^&*()Key")).to(equal("specialKey")) + + // Test cases with mixed case + expect(stringEnumHelper.convertToVariableName(key: "MixedCaseKey")).to(equal("mixedCaseKey")) + + // Empty Key Case + expect(stringEnumHelper.convertToVariableName(key: "")).to(beNil()) + } + it("White Space cases should convert properly") { + // Test cases with spaces + expect(stringEnumHelper.convertToVariableName(key: "Key with Spaces")).to(equal("keyWithSpaces")) + + // Test cases with whitespaces trailing and leaading + expect(stringEnumHelper.convertToVariableName(key: " whitespace ")).to(equal("whitespace")) + } + it("Auto CamelCase for character after white space") { + expect(stringEnumHelper.convertToVariableName(key: "an example")).to(equal("anExample")) + } + it("Should lowercase all capitalized keys") { + expect(stringEnumHelper.convertToVariableName(key: "EXAMPLE")).to(equal("example")) + } + } + } + describe("a decodable model") { it("can decode the json data with key only") { let json = """ @@ -125,42 +161,3 @@ final class StringCatalogEnumSpec: QuickSpec { } } } - -/// Tests the convertToVariableName() function in StringKeyModel -final class StringKeyModelSpec: QuickSpec { - override class func spec() { - let stringEnumHelper = StringEnumHelper() - - context("StringKeyModel") { - describe("Convert To Variable Name Function") { - it("Basic cases should convert properly") { - // Basic test cases - expect(stringEnumHelper.convertToVariableName(key: "SomeKey")).to(equal("someKey")) - expect(stringEnumHelper.convertToVariableName(key: "123Key")).to(equal("key")) - - // Test cases with special characters - expect(stringEnumHelper.convertToVariableName(key: "Special!@#$%^&*()Key")).to(equal("specialKey")) - - // Test cases with mixed case - expect(stringEnumHelper.convertToVariableName(key: "MixedCaseKey")).to(equal("mixedCaseKey")) - - // Empty Key Case - expect(stringEnumHelper.convertToVariableName(key: "")).to(beNil()) - } - it("White Space cases should convert properly") { - // Test cases with spaces - expect(stringEnumHelper.convertToVariableName(key: "Key with Spaces")).to(equal("keyWithSpaces")) - - // Test cases with whitespaces trailing and leaading - expect(stringEnumHelper.convertToVariableName(key: " whitespace ")).to(equal("whitespace")) - } - it("Auto CamelCase for character after white space") { - expect(stringEnumHelper.convertToVariableName(key: "an example")).to(equal("anExample")) - } - it("Should lowercase all capitalized keys") { - expect(stringEnumHelper.convertToVariableName(key: "EXAMPLE")).to(equal("example")) - } - } - } - } -} From 619bc03770c1734a040dc97afcf4920dce58f8ff Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Tue, 2 Jan 2024 19:25:57 -0500 Subject: [PATCH 10/19] model applied to main.swift & value extracted and added as inline document --- .../Sources/StringCatalogEnum/main.swift | 16 +++++--- .../Sources/StringCatalogEnum/model.swift | 2 +- .../StringCatalogEnumLibrary/Models.swift | 31 +++++++------- .../StringEnumHelper.swift | 18 ++++++-- .../StringCatalogEnumTests.swift | 41 ------------------- 5 files changed, 41 insertions(+), 67 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift index b6a2ea1..c3cbb2d 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift @@ -36,13 +36,17 @@ struct StringCatalogEnum: ParsableCommand { let data = try Data(contentsOf: url) print(data) - guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { - throw Error.unexpectedJSON(message: "cannot parse first level object") - } + // guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { + // throw Error.unexpectedJSON(message: "cannot parse first level object") + // } + + // guard let strings = json["strings"] as? [String: Any] else { + // throw Error.unexpectedJSON(message: "cannot parse `strings`") + // } + + let decoder = JSONDecoder() + let strings = try decoder.decode(Localizations.self, from: data) - guard let strings = json["strings"] as? [String: Any] else { - throw Error.unexpectedJSON(message: "cannot parse `strings`") - } var output = """ // This file is generated by XcodeStringEnum. Please do *NOT* update it manually. diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift index 5f8a01d..7a069b1 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift @@ -4,6 +4,6 @@ struct XCStrings: Decodable { let sourceLanguage: String let version: String - let strings: [Strsing: String] + let strings: [String: String] } */ diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift index e6391d6..270de38 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift @@ -4,26 +4,27 @@ struct StringInfo: Decodable { } struct Localization: Decodable { - let variations: Variations? + let stringUnit: StringUnit? + // let variations: Variations? } -struct Variations: Decodable { - let plural: PluralVariations? - let device: DeviceVariations? -} +// struct Variations: Decodable { +// let plural: PluralVariations? +// let device: DeviceVariations? +// } -struct PluralVariations: Decodable { - let one: StringUnitWrapper? - let other: StringUnitWrapper? -} +// struct PluralVariations: Decodable { +// let one: StringUnitWrapper? +// let other: StringUnitWrapper? +// } -struct DeviceVariations: Decodable { - let variations: [String: StringUnitWrapper]? -} +// struct DeviceVariations: Decodable { +// let variations: [String: StringUnitWrapper]? +// } -struct StringUnitWrapper: Decodable { - let stringUnit: StringUnit -} +// struct StringUnitWrapper: Decodable { +// let stringUnit: StringUnit +// } struct StringUnit: Decodable { let state: String diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift index 91a5c8d..c1ef6e6 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift @@ -10,11 +10,12 @@ public struct StringEnumHelper { /// - stringData: A dictionary containing string data. /// - keyNameMatches: A boolean flag indicating whether the enum cases should match the keys exactly. /// - keywordEnum: An array of raw values from the Keyword enum in StringCatalogEnum struct - public func createEnumKeys(with stringData: [String: Any], keyNameMatches: Bool, keywordEnum: [String]) -> String { + public func createEnumKeys(with stringData: Localizations, keyNameMatches: Bool, keywordEnum: [String]) -> String { var partialOutput = "" var cases = [String]() var knownCases = [String]() - for (key, _) in stringData { + + for (key, data) in stringData.strings { guard let name = convertToVariableName(key: key) else { print("SKIPPING: \(key)") continue @@ -37,6 +38,14 @@ public struct StringEnumHelper { knownCases.append(name) // TODO: extract `localizations.en.stringUnit.value` and add in comments as inline documents + // Extract the English localization value + let englishValue: String + if let localization = data.localizations?["en"], let stringUnit = localization.stringUnit { + englishValue = stringUnit.value.replacingOccurrences(of: "\n", with: " ") + } else { + englishValue = "No value" + } + let comment = " /// \(englishValue)\n" let caseString: String = if keywordEnum.contains(name) { keyNameMatches @@ -48,7 +57,8 @@ public struct StringEnumHelper { : " case \(name) = \"\(key.replacingOccurrences(of: "\n", with: ""))\"\n" } - cases.append(caseString) + + cases.append(comment + caseString) } cases.sort() cases.forEach { string in @@ -58,7 +68,7 @@ public struct StringEnumHelper { return partialOutput } - /// Convert a Strint Catalog key to a Swift variable name. + /// Convert a String Catalog key to a Swift variable name. public func convertToVariableName(key: String) -> String? { var result = key // Check if the entire string is uppercase diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index 2dc6c3e..0fec364 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -117,47 +117,6 @@ final class StringKeyModelSpec: QuickSpec { try decoder.decode(Localizations.self, from: jsonData) }.toNot(throwError()) } - - it("can decode the json data with English translation and pluralism manually added") { - let json = """ - { - "sourceLanguage" : "en", - "strings" : { - "%11d Books" : { - "extractionState" : "manual", - "localizations" : { - "en" : { - "variations" : { - "plural" : { - "one" : { - "stringUnit" : { - "state" : "translated", - "value" : "Book" - } - }, - "other" : { - "stringUnit" : { - "state" : "translated", - "value" : "%11d Books" - } - } - } - } - } - } - }, - }, - "version" : "1.0" - } - """ - guard let jsonData = json.data(using: .utf8) else { - fatalError("Invalid JSON string") - } - let decoder = JSONDecoder() - expect{ - try decoder.decode(Localizations.self, from: jsonData) - }.toNot(throwError()) - } } } } From 1b35b21223f43e41ee5b1b7312e43c0d0de082cb Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Tue, 2 Jan 2024 19:50:26 -0500 Subject: [PATCH 11/19] Add test to ensure model correctly decodes JSON data --- .../Localization.swift | 2 +- .../StringCatalogEnumLibrary/Models.swift | 4 ++-- .../StringCatalogEnumTests.swift | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift index e8d6b39..3adbcef 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift @@ -1,4 +1,4 @@ -public struct Localizations: Decodable { +struct Localizations: Decodable { let sourceLanguage: String let version: String let strings: [String: StringInfo] diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift index 270de38..d51059e 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift @@ -1,6 +1,6 @@ struct StringInfo: Decodable { - let extractionState: String? - let localizations: [String: Localization]? + public let extractionState: String? + public let localizations: [String: Localization]? } struct Localization: Decodable { diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index 0fec364..26d1edc 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -116,6 +116,24 @@ final class StringKeyModelSpec: QuickSpec { expect{ try decoder.decode(Localizations.self, from: jsonData) }.toNot(throwError()) + + // To complete this test, we should change all the structs and their attributes to be public + // Verify the decoded data + if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { + // Verify sourceLanguage + expect(decodedData.sourceLanguage).to(equal("en")) + + // Verify version + expect(decodedData.version).to(equal("1.0")) + + // Verify the contents of 'strings' dictionary + expect(decodedData.strings["welcomeBack"]).toNot(beNil()) + expect(decodedData.strings["welcomeBack"]?.extractionState).to(equal("manual")) + expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.state).to(equal("translated")) + expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.value).to(equal("Welcome back")) + } else { + fail("JSON data could not be decoded") + } } } } From 2f33359900e0aba43568381441618aea62b3a7b6 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Tue, 2 Jan 2024 22:33:14 -0500 Subject: [PATCH 12/19] refactor hard coded part in StringEnumHelper --- .../Localization.swift | 2 +- .../StringEnumHelper.swift | 22 +++++++++++----- .../StringCatalogEnumTests.swift | 26 +++++++++---------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift index 3adbcef..e8d6b39 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift @@ -1,4 +1,4 @@ -struct Localizations: Decodable { +public struct Localizations: Decodable { let sourceLanguage: String let version: String let strings: [String: StringInfo] diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift index c1ef6e6..710a011 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift @@ -38,14 +38,22 @@ public struct StringEnumHelper { knownCases.append(name) // TODO: extract `localizations.en.stringUnit.value` and add in comments as inline documents - // Extract the English localization value - let englishValue: String - if let localization = data.localizations?["en"], let stringUnit = localization.stringUnit { - englishValue = stringUnit.value.replacingOccurrences(of: "\n", with: " ") - } else { - englishValue = "No value" + // Extract localization values and format them for comments + var localizationComments = [String]() + if let localizations = data.localizations { + for (languageCode, localization) in localizations { + if let stringUnit = localization.stringUnit { + let value = stringUnit.value.replacingOccurrences(of: "\n", with: " ") + localizationComments.append(" /// \(languageCode): \(value)") + } + } } - let comment = " /// \(englishValue)\n" + + if localizationComments.isEmpty { + localizationComments.append(" /// No localizations available") + } + + let comment = localizationComments.joined(separator: "\n") let caseString: String = if keywordEnum.contains(name) { keyNameMatches diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index 26d1edc..bd767b5 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -119,21 +119,21 @@ final class StringKeyModelSpec: QuickSpec { // To complete this test, we should change all the structs and their attributes to be public // Verify the decoded data - if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { - // Verify sourceLanguage - expect(decodedData.sourceLanguage).to(equal("en")) + // if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { + // // Verify sourceLanguage + // expect(decodedData.sourceLanguage).to(equal("en")) - // Verify version - expect(decodedData.version).to(equal("1.0")) + // // Verify version + // expect(decodedData.version).to(equal("1.0")) - // Verify the contents of 'strings' dictionary - expect(decodedData.strings["welcomeBack"]).toNot(beNil()) - expect(decodedData.strings["welcomeBack"]?.extractionState).to(equal("manual")) - expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.state).to(equal("translated")) - expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.value).to(equal("Welcome back")) - } else { - fail("JSON data could not be decoded") - } + // // Verify the contents of 'strings' dictionary + // expect(decodedData.strings["welcomeBack"]).toNot(beNil()) + // expect(decodedData.strings["welcomeBack"]?.extractionState).to(equal("manual")) + // expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.state).to(equal("translated")) + // expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.value).to(equal("Welcome back")) + // } else { + // fail("JSON data could not be decoded") + // } } } } From 0d70803bff7683dc093b83cbd012ba08a07be3bc Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Wed, 3 Jan 2024 17:23:10 -0500 Subject: [PATCH 13/19] modify file structure --- .../Sources/StringCatalogEnum/main.swift | 4 +- .../Localization.swift | 5 --- .../StringCatalogEnumLibrary/Models.swift | 45 +++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) delete mode 100644 StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift index c3cbb2d..06702e4 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift @@ -45,7 +45,9 @@ struct StringCatalogEnum: ParsableCommand { // } let decoder = JSONDecoder() - let strings = try decoder.decode(Localizations.self, from: data) + guard let strings = try decoder.decode(Localizations.self, from: data) else { + throw Error.unexpectedJSON(message: "cannot parse json") + } var output = """ diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift deleted file mode 100644 index e8d6b39..0000000 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Localization.swift +++ /dev/null @@ -1,5 +0,0 @@ -public struct Localizations: Decodable { - let sourceLanguage: String - let version: String - let strings: [String: StringInfo] -} \ No newline at end of file diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift index d51059e..2ee5ebf 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift @@ -1,3 +1,48 @@ +/// Represents the root structure of the xcstrings JSON. +/// This struct is designed to handle various JSON formats, including those +/// generated from SwiftUI and manually added translations. +/// +/// Examples of supported JSON formats: +/// +/// 1. Generated from SwiftUI (no translation added): +/// ``` +/// "Home": {} +/// ``` +/// +/// 2. Generated from SwiftUI, with an English translation: +/// ``` +/// "Login": { +/// "localizations": { +/// "en": { +/// "stringUnit": { +/// "state": "translated", +/// "value": "Login" +/// } +/// } +/// } +/// } +/// ``` +/// +/// 3. Manually added, English only: +/// ``` +/// "welcomeBack": { +/// "extractionState": "manual", +/// "localizations": { +/// "en": { +/// "stringUnit": { +/// "state": "translated", +/// "value": "Welcome back" +/// } +/// } +/// } +/// } +/// ``` +public struct Localizations: Decodable { + let sourceLanguage: String + let version: String + let strings: [String: StringInfo] +} + struct StringInfo: Decodable { public let extractionState: String? public let localizations: [String: Localization]? From 30b0a0454fd164a044f3df639224773395646df1 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Wed, 3 Jan 2024 20:10:27 -0500 Subject: [PATCH 14/19] cancel error handling --- StringCatalogEnum/Sources/StringCatalogEnum/main.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift index 06702e4..c3cbb2d 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift @@ -45,9 +45,7 @@ struct StringCatalogEnum: ParsableCommand { // } let decoder = JSONDecoder() - guard let strings = try decoder.decode(Localizations.self, from: data) else { - throw Error.unexpectedJSON(message: "cannot parse json") - } + let strings = try decoder.decode(Localizations.self, from: data) var output = """ From 4b64da953c357b1a020a55971c872a64afa6acfd Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Wed, 3 Jan 2024 20:42:47 -0500 Subject: [PATCH 15/19] Modified StringEnumLibrary to correctly generate inline document --- .../Sources/StringCatalogEnumLibrary/StringEnumHelper.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift index 710a011..8645092 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift @@ -66,7 +66,7 @@ public struct StringEnumHelper { } - cases.append(comment + caseString) + cases.append("\(comment)\n\(caseString)") } cases.sort() cases.forEach { string in From 5ea8aebb3056e5eadce522b621ec974fffc42519 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Thu, 4 Jan 2024 16:38:32 -0500 Subject: [PATCH 16/19] removed unrelated files and dead codes --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .../xcode/package.xcworkspace/contents.xcworkspacedata | 7 ------- StringCatalogEnum/Sources/StringCatalogEnum/main.swift | 8 -------- StringCatalogEnum/Sources/StringCatalogEnum/model.swift | 9 --------- 4 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata delete mode 100644 StringCatalogEnum/Sources/StringCatalogEnum/model.swift diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c3a44e5..d7e12f2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,4 +2,4 @@ ## Related issue(s) -- #1 \ No newline at end of file +- #ISSUE_NUMBER \ No newline at end of file diff --git a/StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/StringCatalogEnum/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift index c3cbb2d..aa56ac3 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnum/main.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnum/main.swift @@ -36,14 +36,6 @@ struct StringCatalogEnum: ParsableCommand { let data = try Data(contentsOf: url) print(data) - // guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { - // throw Error.unexpectedJSON(message: "cannot parse first level object") - // } - - // guard let strings = json["strings"] as? [String: Any] else { - // throw Error.unexpectedJSON(message: "cannot parse `strings`") - // } - let decoder = JSONDecoder() let strings = try decoder.decode(Localizations.self, from: data) diff --git a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift b/StringCatalogEnum/Sources/StringCatalogEnum/model.swift deleted file mode 100644 index 7a069b1..0000000 --- a/StringCatalogEnum/Sources/StringCatalogEnum/model.swift +++ /dev/null @@ -1,9 +0,0 @@ -// TODO: make XCStrings Decodable by listing all possible models -// Example: let obj = try JSONDecoder().decode(XCStrings.self, from: data) -/* - struct XCStrings: Decodable { - let sourceLanguage: String - let version: String - let strings: [String: String] - } - */ From 686a60519256c1a2c996cf9e1907e34ad08723e5 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Thu, 4 Jan 2024 23:40:33 -0500 Subject: [PATCH 17/19] minor modification & removed dead codes --- .../Sources/StringCatalogEnumLibrary/StringEnumHelper.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift index 8645092..9e720b5 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift @@ -37,14 +37,13 @@ public struct StringEnumHelper { } knownCases.append(name) - // TODO: extract `localizations.en.stringUnit.value` and add in comments as inline documents // Extract localization values and format them for comments var localizationComments = [String]() if let localizations = data.localizations { for (languageCode, localization) in localizations { if let stringUnit = localization.stringUnit { let value = stringUnit.value.replacingOccurrences(of: "\n", with: " ") - localizationComments.append(" /// \(languageCode): \(value)") + localizationComments.append(" /// '\(languageCode)': \"\(value)\"") } } } From 4ec68fe56e3e3f0c7bbfe75374744a4b8c80a251 Mon Sep 17 00:00:00 2001 From: Uni-boy Date: Fri, 5 Jan 2024 00:10:02 -0500 Subject: [PATCH 18/19] added some tests --- .../StringCatalogEnumLibrary/Models.swift | 18 +++--- .../StringCatalogEnumTests.swift | 61 ++++++++++++++----- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift index 2ee5ebf..9bd5895 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/Models.swift @@ -38,18 +38,18 @@ /// } /// ``` public struct Localizations: Decodable { - let sourceLanguage: String - let version: String - let strings: [String: StringInfo] + public let sourceLanguage: String + public let version: String + public let strings: [String: StringInfo] } -struct StringInfo: Decodable { +public struct StringInfo: Decodable { public let extractionState: String? public let localizations: [String: Localization]? } -struct Localization: Decodable { - let stringUnit: StringUnit? +public struct Localization: Decodable { + public let stringUnit: StringUnit? // let variations: Variations? } @@ -71,7 +71,7 @@ struct Localization: Decodable { // let stringUnit: StringUnit // } -struct StringUnit: Decodable { - let state: String - let value: String +public struct StringUnit: Decodable { + public let state: String + public let value: String } \ No newline at end of file diff --git a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift index bd767b5..77e27e7 100644 --- a/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift +++ b/StringCatalogEnum/Tests/StringCatalogEnumTests/StringCatalogEnumTests.swift @@ -59,6 +59,21 @@ final class StringKeyModelSpec: QuickSpec { expect{ try decoder.decode(Localizations.self, from: jsonData) }.toNot(throwError()) + + // Verify the decoded data + if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { + // Verify the sourceLanguage + expect(decodedData.sourceLanguage).to(equal("en")) + + // Verify the version + expect(decodedData.version).to(equal("1.0")) + + // Verify the contents of 'strings' dictionary + // Here we verify it with Not Be Nil because we design the model not to be optional + expect(decodedData.strings["Home"]).toNot(beNil()) + } else { + fail("Failed to decode Localizations") + } } it("can decode the json data with English translation added") { @@ -87,6 +102,22 @@ final class StringKeyModelSpec: QuickSpec { expect{ try decoder.decode(Localizations.self, from: jsonData) }.toNot(throwError()) + + // Verify the decoded data + if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { + // Verify the sourceLanguage + expect(decodedData.sourceLanguage).to(equal("en")) + + // Verify the version + expect(decodedData.version).to(equal("1.0")) + + // Verify the contents of 'strings' dictionary + expect(decodedData.strings["Login"]).toNot(beNil()) + expect(decodedData.strings["Login"]?.localizations?["en"]?.stringUnit?.state).to(equal("translated")) + expect(decodedData.strings["Login"]?.localizations?["en"]?.stringUnit?.value).to(equal("Login")) + } else { + fail("Failed to decode Localizations") + } } it("can decode the json data with English translation manually added") { @@ -119,21 +150,21 @@ final class StringKeyModelSpec: QuickSpec { // To complete this test, we should change all the structs and their attributes to be public // Verify the decoded data - // if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { - // // Verify sourceLanguage - // expect(decodedData.sourceLanguage).to(equal("en")) - - // // Verify version - // expect(decodedData.version).to(equal("1.0")) - - // // Verify the contents of 'strings' dictionary - // expect(decodedData.strings["welcomeBack"]).toNot(beNil()) - // expect(decodedData.strings["welcomeBack"]?.extractionState).to(equal("manual")) - // expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.state).to(equal("translated")) - // expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.value).to(equal("Welcome back")) - // } else { - // fail("JSON data could not be decoded") - // } + if let decodedData = try? decoder.decode(Localizations.self, from: jsonData) { + // Verify sourceLanguage + expect(decodedData.sourceLanguage).to(equal("en")) + + // Verify version + expect(decodedData.version).to(equal("1.0")) + + // Verify the contents of 'strings' dictionary + expect(decodedData.strings["welcomeBack"]).toNot(beNil()) + expect(decodedData.strings["welcomeBack"]?.extractionState).to(equal("manual")) + expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.state).to(equal("translated")) + expect(decodedData.strings["welcomeBack"]?.localizations?["en"]?.stringUnit?.value).to(equal("Welcome back")) + } else { + fail("JSON data could not be decoded") + } } } } From 159ef883f764e8dfc2ef47f4278c4e6ca3a49603 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 4 Jan 2024 21:11:23 -0800 Subject: [PATCH 19/19] Apply suggestions from code review --- .../Sources/StringCatalogEnumLibrary/StringEnumHelper.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift index 9e720b5..10dffd5 100644 --- a/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift +++ b/StringCatalogEnum/Sources/StringCatalogEnumLibrary/StringEnumHelper.swift @@ -64,7 +64,6 @@ public struct StringEnumHelper { : " case \(name) = \"\(key.replacingOccurrences(of: "\n", with: ""))\"\n" } - cases.append("\(comment)\n\(caseString)") } cases.sort()