xcstrings #10

Merged
q.bandera merged 11 commits from xcstrings into master 2024-04-19 16:59:04 +02:00
5 changed files with 564 additions and 95 deletions
Showing only changes of commit 0d651b810f - Show all commits

View File

@ -1,6 +1,6 @@
// //
// StringsFileGenerator.swift // StringsFileGenerator.swift
// //
// //
// Created by Thibaut Schmitt on 04/01/2022. // Created by Thibaut Schmitt on 04/01/2022.
// //
@ -9,28 +9,49 @@ import Foundation
import ToolCore import ToolCore
class StringsFileGenerator { class StringsFileGenerator {
// MARK: - Strings Files // MARK: - Strings Files
static func writeStringsFiles(sections: [Section], static func writeStringsFiles(sections: [Section],
langs: [String], langs: [String],
defaultLang: String, defaultLang: String,
tags: [String], tags: [String],
outputPath: String, outputPath: String,
inputFilenameWithoutExt: String) { inputFilenameWithoutExt: String,
var stringsFilesContent = [String: String]() isXcString: Bool = false) {
for lang in langs {
stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang, if !isXcString {
defaultLang: defaultLang, var stringsFilesContent = [String: String]()
tags: tags, for lang in langs {
sections: sections) stringsFilesContent[lang] = Self.generateStringsFileContent(lang: lang,
} defaultLang: defaultLang,
tags: tags,
// Write strings file content sections: sections)
langs.forEach { lang in }
guard let fileContent = stringsFilesContent[lang] else { return }
// Write strings file content
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings" langs.forEach { lang in
guard let fileContent = stringsFilesContent[lang] else { return }
let stringsFilePath = "\(outputPath)/\(lang).lproj/\(inputFilenameWithoutExt).strings"
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
do {
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
} catch let error {
let error = StringiumError.writeFile(error.localizedDescription, stringsFilePath)
print(error.description)
Stringium.exit(withError: error)
}
}
} else {
let fileContent: String = Self.generateXcStringsFileContent(
langs: langs,
defaultLang: defaultLang,
tags: tags,
sections: sections
)
let stringsFilePath = "\(outputPath)/Localizable.xcstrings"
let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath) let stringsFilePathURL = URL(fileURLWithPath: stringsFilePath)
do { do {
try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8) try fileContent.write(to: stringsFilePathURL, atomically: false, encoding: .utf8)
@ -41,7 +62,7 @@ class StringsFileGenerator {
} }
} }
} }
static func generateStringsFileContent(lang: String, static func generateStringsFileContent(lang: String,
defaultLang: String, defaultLang: String,
tags inputTags: [String], tags inputTags: [String],
@ -53,13 +74,13 @@ class StringsFileGenerator {
* Language: \(lang) * Language: \(lang)
*/\n */\n
""" """
sections.forEach { section in sections.forEach { section in
// Check that at least one string will be generated // Check that at least one string will be generated
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else { guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
return // Go to next section return // Go to next section
} }
stringsFileContent += "\n/********** \(section.name) **********/\n\n" stringsFileContent += "\n/********** \(section.name) **********/\n\n"
section.definitions.forEach { definition in section.definitions.forEach { definition in
var skipDefinition = false // Set to true if not matching tag var skipDefinition = false // Set to true if not matching tag
@ -69,16 +90,16 @@ class StringsFileGenerator {
skipDefinition = true skipDefinition = true
return nil return nil
} }
// If tags contains `noTranslationTag` => get default lang // If tags contains `noTranslationTag` => get default lang
if definition.tags.contains(Stringium.noTranslationTag) { if definition.tags.contains(Stringium.noTranslationTag) {
return definition.translations[defaultLang] return definition.translations[defaultLang]
} }
// Else: get specific lang // Else: get specific lang
return definition.translations[lang] return definition.translations[lang]
}() }()
if let translation = translationOpt { if let translation = translationOpt {
stringsFileContent += "\"\(definition.name)\" = \"\(translation)\";\n\n" stringsFileContent += "\"\(definition.name)\" = \"\(translation)\";\n\n"
} else if skipDefinition == false { } else if skipDefinition == false {
@ -88,12 +109,101 @@ class StringsFileGenerator {
} }
} }
} }
return stringsFileContent return stringsFileContent
} }
// MARK: - XcStrings Generation
static func generateXcStringsFileContent(langs: [String],
defaultLang: String,
tags inputTags: [String],
sections: [Section]) -> String {
let rootObject = generateRootObject(langs: langs, defaultLang: defaultLang, tags: inputTags, sections: sections)
let file = generateXcStringsFileContentFromRootObject(rootObject: rootObject)
return file
}
static func generateXcStringsFileContentFromRootObject(rootObject: Root) -> String {
do {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted]
let json = try encoder.encode(rootObject)
if let jsonString = String(data: json, encoding: .utf8) {
return jsonString
}
} catch {
debugPrint("Failed to encode: \(error)")
}
return ""
}
static func generateRootObject(langs: [String],
defaultLang: String,
tags inputTags: [String],
sections: [Section]) -> Root {
var xcStringDefinitionTab: [XCStringDefinition] = []
sections.forEach { section in
// Check that at least one string will be generated
guard section.hasOneOrMoreMatchingTags(tags: inputTags) else {
return // Go to next section
}
section.definitions.forEach { definition in
var skipDefinition = false
var localizationTab: [XCStringLocalization] = []
if definition.hasOneOrMoreMatchingTags(inputTags: inputTags) == false {
skipDefinition = true
}
if !skipDefinition {
for (lang, value) in definition.translations {
let localization = XCStringLocalization(
lang: lang,
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(state: "translated", value: value)
)
)
localizationTab.append(localization)
}
let xcStringDefinition = XCStringDefinition(
title: definition.name,
content: XCStringDefinitionContent(
extractionState: "manual",
localizations: XCStringLocalizationContainer(
localizations: localizationTab
)
)
)
xcStringDefinitionTab.append(xcStringDefinition)
}
}
}
let xcStringContainer = XCStringDefinitionContainer(strings: xcStringDefinitionTab)
return Root(
sourceLanguage: defaultLang,
strings: xcStringContainer,
version: "1.0"
)
}
// MARK: - Extension file // MARK: - Extension file
static func writeExtensionFiles(sections: [Section], static func writeExtensionFiles(sections: [Section],
defaultLang lang: String, defaultLang lang: String,
tags: [String], tags: [String],
@ -110,7 +220,7 @@ class StringsFileGenerator {
inputFilename: inputFilename, inputFilename: inputFilename,
extensionName: extensionName, extensionName: extensionName,
extensionSuffix: extensionSuffix) extensionSuffix: extensionSuffix)
// Write content // Write content
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath) let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
do { do {
@ -121,9 +231,9 @@ class StringsFileGenerator {
Stringium.exit(withError: error) Stringium.exit(withError: error)
} }
} }
// MARK: - Extension content // MARK: - Extension content
static func getExtensionContent(sections: [Section], static func getExtensionContent(sections: [Section],
defaultLang lang: String, defaultLang lang: String,
tags: [String], tags: [String],
@ -139,31 +249,31 @@ class StringsFileGenerator {
] ]
.joined(separator: "\n") .joined(separator: "\n")
} }
// MARK: - Extension part // MARK: - Extension part
private static func getHeader(stringsFilename: String, extensionClassname: String) -> String { private static func getHeader(stringsFilename: String, extensionClassname: String) -> String {
""" """
// Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion) // Generated by ResgenSwift.Strings.\(Stringium.toolName) \(ResgenSwiftVersion)
import UIKit import UIKit
fileprivate let kStringsFileName = "\(stringsFilename)" fileprivate let kStringsFileName = "\(stringsFilename)"
extension \(extensionClassname) { extension \(extensionClassname) {
""" """
} }
private static func getEnumKey(sections: [Section], tags: [String], extensionClassname: String, extensionSuffix: String) -> String { private static func getEnumKey(sections: [Section], tags: [String], extensionClassname: String, extensionSuffix: String) -> String {
var enumDefinition = "\n enum Key\(extensionSuffix.uppercasedFirst()): String {\n" var enumDefinition = "\n enum Key\(extensionSuffix.uppercasedFirst()): String {\n"
// Enum // Enum
sections.forEach { section in sections.forEach { section in
// Check that at least one string will be generated // Check that at least one string will be generated
guard section.hasOneOrMoreMatchingTags(tags: tags) else { guard section.hasOneOrMoreMatchingTags(tags: tags) else {
return // Go to next section return // Go to next section
} }
section.definitions.forEach { definition in section.definitions.forEach { definition in
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
return // Go to next definition return // Go to next definition
@ -172,7 +282,7 @@ class StringsFileGenerator {
enumDefinition += " case \(definition.name) = \"\(definition.name)\"\n" enumDefinition += " case \(definition.name) = \"\(definition.name)\"\n"
} }
} }
// KeyPath accessors // KeyPath accessors
enumDefinition += "\n" enumDefinition += "\n"
enumDefinition += " var keyPath: KeyPath<\(extensionClassname), String> {\n" enumDefinition += " var keyPath: KeyPath<\(extensionClassname), String> {\n"
@ -182,7 +292,7 @@ class StringsFileGenerator {
guard section.hasOneOrMoreMatchingTags(tags: tags) else { guard section.hasOneOrMoreMatchingTags(tags: tags) else {
return // Go to next section return // Go to next section
} }
section.definitions.forEach { definition in section.definitions.forEach { definition in
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
return // Go to next definition return // Go to next definition
@ -194,23 +304,23 @@ class StringsFileGenerator {
enumDefinition += " }\n" // Switch enumDefinition += " }\n" // Switch
enumDefinition += " }\n" // var keyPath enumDefinition += " }\n" // var keyPath
enumDefinition += " }" // Enum enumDefinition += " }" // Enum
return enumDefinition return enumDefinition
} }
private static func getProperties(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool) -> String { private static func getProperties(sections: [Section], defaultLang lang: String, tags: [String], staticVar: Bool) -> String {
sections.compactMap { section in sections.compactMap { section in
// Check that at least one string will be generated // Check that at least one string will be generated
guard section.hasOneOrMoreMatchingTags(tags: tags) else { guard section.hasOneOrMoreMatchingTags(tags: tags) else {
return nil // Go to next section return nil // Go to next section
} }
var res = "\n // MARK: - \(section.name)\n" var res = "\n // MARK: - \(section.name)\n"
res += section.definitions.compactMap { definition in res += section.definitions.compactMap { definition in
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else { guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
return nil // Go to next definition return nil // Go to next definition
} }
if staticVar { if staticVar {
return "\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))" return "\n\(definition.getNSLocalizedStringStaticProperty(forLang: lang))"
} }
@ -221,11 +331,11 @@ class StringsFileGenerator {
} }
.joined(separator: "\n") .joined(separator: "\n")
} }
private static func getFooter() -> String { private static func getFooter() -> String {
""" """
} }
""" """
} }
} }

View File

@ -0,0 +1,93 @@
//
// XcString.swift
//
//
// Created by Quentin Bandera on 12/04/2024.
//
import SwiftUI
struct DynamicKey: CodingKey {
var intValue: Int?
init?(intValue: Int) {
self.intValue = intValue
self.stringValue = "\(intValue)"
}
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
}
struct Root: Codable, Equatable {
let sourceLanguage: String
let strings: XCStringDefinitionContainer
let version: String
}
struct XCStringDefinitionContainer: Codable, Equatable {
let strings: [XCStringDefinition]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: DynamicKey.self)
for str in strings {
if let codingKey = DynamicKey(stringValue: str.title) {
try container.encode(str.content, forKey: codingKey)
}
}
}
}
struct XCStringDefinition: Codable, Equatable {
let title: String // json key -> custom encoding methods
let content: XCStringDefinitionContent
}
struct XCStringDefinitionContent: Codable, Equatable {
let extractionState: String
var localizations: XCStringLocalizationContainer
}
struct XCStringLocalizationContainer: Codable, Equatable {
let localizations: [XCStringLocalization]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: DynamicKey.self)
for loca in localizations {
if let codingKey = DynamicKey(stringValue: loca.lang) {
try container.encode(loca.content, forKey: codingKey)
}
}
}
}
struct XCStringLocalization: Codable, Equatable {
let lang: String // json key -> custom encoding method
let content: XCStringLocalizationLangContent
}
struct XCStringLocalizationLangContent: Codable, Equatable {
let stringUnit: DefaultStringUnit
}
//enum VarationOrStringUnit: Encodable {
// case variations([Varation])
// case stringUnit: (DefaultStringUnit)
//
// func encode(to encoder: any Encoder) throws {
// if let varations {
//
// } else if let {
//
// }
// }
//}
struct DefaultStringUnit: Codable, Equatable {
let state: String
let value: String
}

View File

@ -48,8 +48,9 @@ struct Stringium: ParsableCommand {
defaultLang: options.defaultLang, defaultLang: options.defaultLang,
tags: options.tags, tags: options.tags,
outputPath: options.stringsFileOutputPath, outputPath: options.stringsFileOutputPath,
inputFilenameWithoutExt: options.inputFilenameWithoutExt) inputFilenameWithoutExt: options.inputFilenameWithoutExt,
isXcString: options.isXcstring)
// Generate extension // Generate extension
StringsFileGenerator.writeExtensionFiles(sections: sections, StringsFileGenerator.writeExtensionFiles(sections: sections,
defaultLang: options.defaultLang, defaultLang: options.defaultLang,

View File

@ -11,7 +11,10 @@ import ArgumentParser
struct StringiumOptions: ParsableArguments { struct StringiumOptions: ParsableArguments {
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation") @Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
var forceGeneration = false var forceGeneration = false
@Flag(name: [.customShort("x"), .customLong("xcstrings")], help: "Generate xcstrings catalog")
var isXcstring = false
@Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) @Argument(help: "Input files where strings ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
var inputFile: String var inputFile: String

View File

@ -1,6 +1,6 @@
// //
// StringsFileGeneratorTests.swift // StringsFileGeneratorTests.swift
// //
// //
// Created by Thibaut Schmitt on 06/09/2022. // Created by Thibaut Schmitt on 06/09/2022.
// //
@ -12,7 +12,7 @@ import ToolCore
@testable import ResgenSwift @testable import ResgenSwift
final class StringsFileGeneratorTests: XCTestCase { final class StringsFileGeneratorTests: XCTestCase {
private func getDefinition(name: String, translations: [String: String], tags: [String], comment: String? = nil) -> Definition { private func getDefinition(name: String, translations: [String: String], tags: [String], comment: String? = nil) -> Definition {
let definition = Definition(name: name) let definition = Definition(name: name)
definition.tags = tags definition.tags = tags
@ -36,7 +36,7 @@ final class StringsFileGeneratorTests: XCTestCase {
"en": "Section One - Definition Two"], "en": "Section One - Definition Two"],
tags: ["ios","iosonly"]) tags: ["ios","iosonly"])
] ]
let sectionTwo = Section(name: "section_two") let sectionTwo = Section(name: "section_two")
sectionTwo.definitions = [ sectionTwo.definitions = [
getDefinition(name: "s2_def_one", getDefinition(name: "s2_def_one",
@ -47,7 +47,7 @@ final class StringsFileGeneratorTests: XCTestCase {
translations: ["fr": "Section Deux - Definition Deux"], translations: ["fr": "Section Deux - Definition Deux"],
tags: ["notranslation"]) tags: ["notranslation"])
] ]
// When // When
let stringsFileContentFr = StringsFileGenerator.generateStringsFileContent(lang: "fr", let stringsFileContentFr = StringsFileGenerator.generateStringsFileContent(lang: "fr",
defaultLang: "fr", defaultLang: "fr",
@ -57,7 +57,7 @@ final class StringsFileGeneratorTests: XCTestCase {
defaultLang: "fr", defaultLang: "fr",
tags: ["ios", "iosonly", "notranslation"], tags: ["ios", "iosonly", "notranslation"],
sections: [sectionOne, sectionTwo]) sections: [sectionOne, sectionTwo])
// Expect // Expect
let expectFr = """ let expectFr = """
/** /**
@ -65,40 +65,40 @@ final class StringsFileGeneratorTests: XCTestCase {
* Generated by ResgenSwift \(ResgenSwiftVersion) * Generated by ResgenSwift \(ResgenSwiftVersion)
* Language: fr * Language: fr
*/ */
/********** section_one **********/ /********** section_one **********/
"s1_def_one" = "Section Un - Definition Un"; "s1_def_one" = "Section Un - Definition Un";
"s1_def_two" = "Section Un - Definition Deux"; "s1_def_two" = "Section Un - Definition Deux";
/********** section_two **********/ /********** section_two **********/
"s2_def_one" = "Section Deux - Definition Un"; "s2_def_one" = "Section Deux - Definition Un";
"s2_def_two" = "Section Deux - Definition Deux"; "s2_def_two" = "Section Deux - Definition Deux";
""" """
let expectEn = """ let expectEn = """
/** /**
* Apple Strings File * Apple Strings File
* Generated by ResgenSwift \(ResgenSwiftVersion) * Generated by ResgenSwift \(ResgenSwiftVersion)
* Language: en * Language: en
*/ */
/********** section_one **********/ /********** section_one **********/
"s1_def_one" = "Section One - Definition One"; "s1_def_one" = "Section One - Definition One";
"s1_def_two" = "Section One - Definition Two"; "s1_def_two" = "Section One - Definition Two";
/********** section_two **********/ /********** section_two **********/
"s2_def_one" = "Section Two - Definition One"; "s2_def_one" = "Section Two - Definition One";
"s2_def_two" = "Section Deux - Definition Deux"; "s2_def_two" = "Section Deux - Definition Deux";
""" """
XCTAssertEqual(stringsFileContentFr.adaptForXCTest(), expectFr.adaptForXCTest()) XCTAssertEqual(stringsFileContentFr.adaptForXCTest(), expectFr.adaptForXCTest())
XCTAssertEqual(stringsFileContentEn.adaptForXCTest(), expectEn.adaptForXCTest()) XCTAssertEqual(stringsFileContentEn.adaptForXCTest(), expectEn.adaptForXCTest())
} }
@ -187,6 +187,268 @@ final class StringsFileGeneratorTests: XCTestCase {
XCTAssertEqual(stringsFileContentEn.adaptForXCTest(), expectEn.adaptForXCTest()) XCTAssertEqual(stringsFileContentEn.adaptForXCTest(), expectEn.adaptForXCTest())
} }
// MARK: - XcString File Content
func testGenerateXcStringsRootObject() {
// Given
let sectionOne = Section(name: "section_one")
sectionOne.definitions = [
getDefinition(name: "s1_def_one",
translations: ["fr": "Section Un - Definition Un",
"en": "Section One - Definition One"],
tags: ["ios","iosonly"]),
getDefinition(name: "s1_def_two",
translations: ["fr": "Section Un - Definition Deux",
"en": "Section One - Definition Two"],
tags: ["ios","iosonly"])
]
let sectionTwo = Section(name: "section_two")
sectionTwo.definitions = [
getDefinition(name: "s2_def_one",
translations: ["fr": "Section Deux - Definition Un",
"en": "Section Two - Definition One"],
tags: ["ios","iosonly"]),
getDefinition(name: "s2_def_two",
translations: ["fr": "Section Deux - Definition Deux"],
tags: ["notranslation"])
]
// When
let rootObject = StringsFileGenerator.generateRootObject(
langs: ["fr", "en"],
defaultLang: "en",
tags: ["ios", "iosonly", "notranslation"],
sections: [sectionOne, sectionTwo]
)
// [[section_one]]
// [s1_def_one]
// fr = Section Un - Definition Un
// en = Section One - Definition One
// tags = ios,iosonly
// comments =
// [s1_def_two]
// fr = Section Un - Definition Deux
// en = Section One - Definition Two
// tags = ios,iosonly
// comments =
//
// [[section_two]
// [s2_def_one]
// fr = Section Deux - Definition Un
// en = Section Two - Definition One
// tags = ios,iosonly
// comments =
// [s2_def_two]
// fr = Section Deux - Definition deux
// en = Section Two - Definition Two
// tags = ios,iosonly
// comments =
// Expect
let expect =
Root(
sourceLanguage: "en",
strings: XCStringDefinitionContainer(
strings: [
XCStringDefinition(
title: "s1_def_one",
content: XCStringDefinitionContent(
extractionState: "manual",
localizations: XCStringLocalizationContainer(
localizations: [
XCStringLocalization(
lang: "en",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section One - Definition One"
)
)
),
XCStringLocalization(
lang: "fr",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section Un - Definition Un"
)
)
)
]
)
)
),
XCStringDefinition(
title: "s1_def_two",
content: XCStringDefinitionContent(
extractionState: "manual",
localizations: XCStringLocalizationContainer(
localizations: [
XCStringLocalization(
lang: "en",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section One - Definition Two"
)
)
),
XCStringLocalization(
lang: "fr",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section Un - Definition Deux"
)
)
)
]
)
)
),
XCStringDefinition(
title: "s2_def_one",
content: XCStringDefinitionContent(
extractionState: "manual",
localizations: XCStringLocalizationContainer(
localizations: [
XCStringLocalization(
lang: "en",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section Two - Definition One"
)
)
),
XCStringLocalization(
lang: "fr",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section Two - Definition Un"
)
)
)
]
)
)
),
XCStringDefinition(
title: "s2_def_two",
content: XCStringDefinitionContent(
extractionState: "manual",
localizations: XCStringLocalizationContainer(
localizations: [
XCStringLocalization(
lang: "en",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section Two - Definition Two"
)
)
),
XCStringLocalization(
lang: "fr",
content: XCStringLocalizationLangContent(
stringUnit: DefaultStringUnit(
state: "translated",
value: "Section Deux - Definition Deux"
)
)
)
]
)
)
)
]
),
version: "1.0"
)
// """
// {
// "sourceLanguage" : "en",
// "strings" : {
// "s1_def_one" : {
// "extractionState" : "manual",
// "localizations" : {
// "en" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section One - Definition One"
// }
// },
// "fr" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section Un - Definition Un"
// }
// }
// }
// },
// "s1_def_two" : {
// "extractionState" : "manual",
// "localizations" : {
// "en" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section One - Definition Two"
// }
// },
// "fr" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section Un - Definition Deux"
// }
// }
// }
// },
// "s2_def_one" : {
// "extractionState" : "manual",
// "localizations" : {
// "en" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section Two - Definition One"
// }
// },
// "fr" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section Deux - Definition Une"
// }
// }
// }
// },
// "s2_def_two" : {
// "extractionState" : "manual",
// "localizations" : {
// "en" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section Two - Definition Two"
// }
// },
// "fr" : {
// "stringUnit" : {
// "state" : "translated",
// "value" : "Section Deux - Definition Deux"
// }
// }
// }
// }
// },
// "version" : "1.0"
// }
// """
XCTAssertEqual(rootObject, expect)
}
// MARK: - Extension Content // MARK: - Extension Content
func testGeneratedExtensionContent() { func testGeneratedExtensionContent() {
// Given // Given
@ -201,7 +463,7 @@ final class StringsFileGeneratorTests: XCTestCase {
"en": "Section One - Definition Two"], "en": "Section One - Definition Two"],
tags: ["ios","iosonly"]) tags: ["ios","iosonly"])
] ]
let sectionTwo = Section(name: "section_two") let sectionTwo = Section(name: "section_two")
sectionTwo.definitions = [ sectionTwo.definitions = [
getDefinition(name: "s2_def_one", getDefinition(name: "s2_def_one",
@ -212,7 +474,7 @@ final class StringsFileGeneratorTests: XCTestCase {
translations: ["fr": "Section Deux - Definition Deux"], translations: ["fr": "Section Deux - Definition Deux"],
tags: ["notranslation"]) tags: ["notranslation"])
] ]
// When // When
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo], let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
defaultLang: "fr", defaultLang: "fr",
@ -221,23 +483,23 @@ final class StringsFileGeneratorTests: XCTestCase {
inputFilename: "myInputFilename", inputFilename: "myInputFilename",
extensionName: "GenStrings", extensionName: "GenStrings",
extensionSuffix: "strings") extensionSuffix: "strings")
// Expect // Expect
let expect = """ let expect = """
// Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion) // Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion)
import UIKit import UIKit
fileprivate let kStringsFileName = "myInputFilename" fileprivate let kStringsFileName = "myInputFilename"
extension GenStrings { extension GenStrings {
enum KeyStrings: String { enum KeyStrings: String {
case s1_def_one = "s1_def_one" case s1_def_one = "s1_def_one"
case s1_def_two = "s1_def_two" case s1_def_two = "s1_def_two"
case s2_def_one = "s2_def_one" case s2_def_one = "s2_def_one"
case s2_def_two = "s2_def_two" case s2_def_two = "s2_def_two"
var keyPath: KeyPath<GenStrings, String> { var keyPath: KeyPath<GenStrings, String> {
switch self { switch self {
case .s1_def_one: return \\GenStrings.s1_def_one case .s1_def_one: return \\GenStrings.s1_def_one
@ -247,9 +509,9 @@ final class StringsFileGeneratorTests: XCTestCase {
} }
} }
} }
// MARK: - section_one // MARK: - section_one
/// Translation in fr : /// Translation in fr :
/// Section Un - Definition Un /// Section Un - Definition Un
/// ///
@ -258,7 +520,7 @@ final class StringsFileGeneratorTests: XCTestCase {
var s1_def_one: String { var s1_def_one: String {
NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "") NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Section Un - Definition Deux /// Section Un - Definition Deux
/// ///
@ -267,9 +529,9 @@ final class StringsFileGeneratorTests: XCTestCase {
var s1_def_two: String { var s1_def_two: String {
NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "") NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "")
} }
// MARK: - section_two // MARK: - section_two
/// Translation in fr : /// Translation in fr :
/// Section Deux - Definition Un /// Section Deux - Definition Un
/// ///
@ -278,7 +540,7 @@ final class StringsFileGeneratorTests: XCTestCase {
var s2_def_one: String { var s2_def_one: String {
NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "") NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Section Deux - Definition Deux /// Section Deux - Definition Deux
/// ///
@ -289,7 +551,7 @@ final class StringsFileGeneratorTests: XCTestCase {
} }
} }
""" """
if extensionContent != expect { if extensionContent != expect {
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect)) print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
} }
@ -422,7 +684,7 @@ final class StringsFileGeneratorTests: XCTestCase {
"en": "Section One - Definition Two"], "en": "Section One - Definition Two"],
tags: ["ios","iosonly"]) tags: ["ios","iosonly"])
] ]
let sectionTwo = Section(name: "section_two") let sectionTwo = Section(name: "section_two")
sectionTwo.definitions = [ sectionTwo.definitions = [
getDefinition(name: "s2_def_one", getDefinition(name: "s2_def_one",
@ -433,7 +695,7 @@ final class StringsFileGeneratorTests: XCTestCase {
translations: ["fr": "Section Deux - Definition Deux"], translations: ["fr": "Section Deux - Definition Deux"],
tags: ["notranslation"]) tags: ["notranslation"])
] ]
// When // When
let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo], let extensionContent = StringsFileGenerator.getExtensionContent(sections: [sectionOne, sectionTwo],
defaultLang: "fr", defaultLang: "fr",
@ -442,23 +704,23 @@ final class StringsFileGeneratorTests: XCTestCase {
inputFilename: "myInputFilename", inputFilename: "myInputFilename",
extensionName: "GenStrings", extensionName: "GenStrings",
extensionSuffix: "strings") extensionSuffix: "strings")
// Expect // Expect
let expect = """ let expect = """
// Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion) // Generated by ResgenSwift.Strings.Stringium \(ResgenSwiftVersion)
import UIKit import UIKit
fileprivate let kStringsFileName = "myInputFilename" fileprivate let kStringsFileName = "myInputFilename"
extension GenStrings { extension GenStrings {
enum KeyStrings: String { enum KeyStrings: String {
case s1_def_one = "s1_def_one" case s1_def_one = "s1_def_one"
case s1_def_two = "s1_def_two" case s1_def_two = "s1_def_two"
case s2_def_one = "s2_def_one" case s2_def_one = "s2_def_one"
case s2_def_two = "s2_def_two" case s2_def_two = "s2_def_two"
var keyPath: KeyPath<GenStrings, String> { var keyPath: KeyPath<GenStrings, String> {
switch self { switch self {
case .s1_def_one: return \\GenStrings.s1_def_one case .s1_def_one: return \\GenStrings.s1_def_one
@ -468,9 +730,9 @@ final class StringsFileGeneratorTests: XCTestCase {
} }
} }
} }
// MARK: - section_one // MARK: - section_one
/// Translation in fr : /// Translation in fr :
/// Section Un - Definition Un /// Section Un - Definition Un
/// ///
@ -479,7 +741,7 @@ final class StringsFileGeneratorTests: XCTestCase {
static var s1_def_one: String { static var s1_def_one: String {
NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "") NSLocalizedString("s1_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Un", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Section Un - Definition Deux /// Section Un - Definition Deux
/// ///
@ -488,9 +750,9 @@ final class StringsFileGeneratorTests: XCTestCase {
static var s1_def_two: String { static var s1_def_two: String {
NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "") NSLocalizedString("s1_def_two", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Un - Definition Deux", comment: "")
} }
// MARK: - section_two // MARK: - section_two
/// Translation in fr : /// Translation in fr :
/// Section Deux - Definition Un /// Section Deux - Definition Un
/// ///
@ -499,7 +761,7 @@ final class StringsFileGeneratorTests: XCTestCase {
static var s2_def_one: String { static var s2_def_one: String {
NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "") NSLocalizedString("s2_def_one", tableName: kStringsFileName, bundle: Bundle.main, value: "Section Deux - Definition Un", comment: "")
} }
/// Translation in fr : /// Translation in fr :
/// Section Deux - Definition Deux /// Section Deux - Definition Deux
/// ///
@ -510,7 +772,7 @@ final class StringsFileGeneratorTests: XCTestCase {
} }
} }
""" """
if extensionContent != expect { if extensionContent != expect {
print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect)) print(prettyFirstDifferenceBetweenStrings(s1: extensionContent, s2: expect))
} }