diff --git a/Sources/RswiftCore/Generators/AggregatedStructGenerator.swift b/Sources/RswiftCore/Generators/AggregatedStructGenerator.swift index e57c42fd..c65edc0f 100644 --- a/Sources/RswiftCore/Generators/AggregatedStructGenerator.swift +++ b/Sources/RswiftCore/Generators/AggregatedStructGenerator.swift @@ -30,6 +30,7 @@ class AggregatedStructGenerator: StructGenerator { return result } .reduce(StructGeneratorResultCollector()) { collector, result in collector.appending(result) } + .sorted let externalStruct = Struct( availables: [], @@ -83,5 +84,12 @@ private struct StructGeneratorResultCollector { internalStructs: internalStructs + [result.internalStruct].compactMap { $0 } ) } + + var sorted: StructGeneratorResultCollector { + return StructGeneratorResultCollector( + externalStructs: externalStructs.sorted(by: { $0.type.name.description < $1.type.name.description }), + internalStructs: internalStructs.sorted(by: { $0.type.name.description < $1.type.name.description }) + ) + } } diff --git a/Sources/RswiftCore/Generators/StringsStructGenerator.swift b/Sources/RswiftCore/Generators/StringsStructGenerator.swift index 5fdce0ee..cf239d3a 100644 --- a/Sources/RswiftCore/Generators/StringsStructGenerator.swift +++ b/Sources/RswiftCore/Generators/StringsStructGenerator.swift @@ -11,9 +11,11 @@ import Foundation struct StringsStructGenerator: ExternalOnlyStructGenerator { private let localizableStrings: [LocalizableStrings] + private let developmentLanguage: String - init(localizableStrings: [LocalizableStrings]) { + init(localizableStrings: [LocalizableStrings], developmentLanguage: String) { self.localizableStrings = localizableStrings + self.developmentLanguage = developmentLanguage } func generatedStruct(at externalAccessLevel: AccessLevel, prefix: SwiftIdentifier) -> Struct { @@ -71,13 +73,20 @@ struct StringsStructGenerator: ExternalOnlyStructGenerator { private func computeParams(filename: String, strings: [LocalizableStrings]) -> [StringValues] { var allParams: [String: [(Locale, String, [StringParam])]] = [:] - let baseKeys: Set? + let primaryLanguage: String + let primaryKeys: Set? let bases = strings.filter { $0.locale.isBase } - if bases.isEmpty { - baseKeys = nil - } - else { - baseKeys = Set(bases.flatMap { $0.dictionary.keys }) + let developments = strings.filter { $0.locale.language == developmentLanguage } + + if !bases.isEmpty { + primaryKeys = Set(bases.flatMap { $0.dictionary.keys }) + primaryLanguage = "Base" + } else if !developments.isEmpty { + primaryKeys = Set(developments.flatMap { $0.dictionary.keys }) + primaryLanguage = developmentLanguage + } else { + primaryKeys = nil + primaryLanguage = developmentLanguage } // Warnings about duplicates and empties @@ -103,7 +112,7 @@ struct StringsStructGenerator: ExternalOnlyStructGenerator { // Warnings about missing translations for (locale, lss) in strings.grouped(by: { $0.locale }) { let filenameLocale = locale.withFilename(filename) - let sourceKeys = baseKeys ?? Set(allParams.keys) + let sourceKeys = primaryKeys ?? Set(allParams.keys) let missing = sourceKeys.subtracting(lss.flatMap { $0.dictionary.keys }) @@ -117,10 +126,28 @@ struct StringsStructGenerator: ExternalOnlyStructGenerator { warn("Strings file \(filenameLocale) is missing translations for keys: \(paddedKeysString)") } - // Only include translation if it exists in Base + // Warnings about extra translations + for (locale, lss) in strings.grouped(by: { $0.locale }) { + let filenameLocale = locale.withFilename(filename) + let sourceKeys = primaryKeys ?? Set(allParams.keys) + + let usedKeys = Set(lss.flatMap { $0.dictionary.keys }) + let extra = usedKeys.subtracting(sourceKeys) + + if extra.isEmpty { + continue + } + + let paddedKeys = extra.sorted().map { "'\($0)'" } + let paddedKeysString = paddedKeys.joined(separator: ", ") + + warn("Strings file \(filenameLocale) has extra translations (not in \(primaryLanguage)) for keys: \(paddedKeysString)") + } + + // Only include translation if it exists in the primary language func includeTranslation(_ key: String) -> Bool { - if let baseKeys = baseKeys { - return baseKeys.contains(key) + if let primaryKeys = primaryKeys { + return primaryKeys.contains(key) } return true @@ -164,7 +191,7 @@ struct StringsStructGenerator: ExternalOnlyStructGenerator { if !areCorrectFormatSpecifiers { continue } let vals = keyParams.map { ($0.0, $0.1) } - let values = StringValues(key: key, params: params, tableName: filename, values: vals ) + let values = StringValues(key: key, params: params, tableName: filename, values: vals, developmentLanguage: developmentLanguage) results.append(values) } @@ -273,13 +300,14 @@ private struct StringValues { let params: [StringParam] let tableName: String let values: [(Locale, String)] + let developmentLanguage: String var localizedString: String { let escapedKey = key.escapedStringLiteral var valueArgument: String = "" - if let baseValue = baseValue { - valueArgument = ", value: \"\(baseValue.escapedStringLiteral)\"" + if let value = primaryLanguageValue { + valueArgument = ", value: \"\(value.escapedStringLiteral)\"" } if tableName == "Localizable" { @@ -290,30 +318,28 @@ private struct StringValues { } } - private var baseValue: String? { - return values.filter { $0.0.isBase }.map { $0.1 }.first + private var primaryLanguageValues: [(Locale, String)] { + return values.filter { $0.0.isBase } + values.filter { $0.0.language == developmentLanguage } + } + + private var primaryLanguageValue: String? { + return primaryLanguageValues.map { $0.1 }.first } var comments: [String] { var results: [String] = [] - let containsBase = values.contains { $0.0.isBase } let anyNone = values.contains { $0.0.isNone } + let vs = primaryLanguageValues + values - if let baseValue = baseValue { - let str = "Base translation: \(baseValue)".commentString - results.append(str) - } - else if !containsBase { - if let (locale, value) = values.first { - if let localeDescription = locale.localeDescription { - let str = "\(localeDescription) translation: \(value)".commentString - results.append(str) - } - else { - let str = "Value: \(value)".commentString - results.append(str) - } + if let (locale, value) = vs.first { + if let localeDescription = locale.localeDescription { + let str = "\(localeDescription) translation: \(value)".commentString + results.append(str) + } + else { + let str = "Value: \(value)".commentString + results.append(str) } } diff --git a/Sources/RswiftCore/ResourceTypes/Locale.swift b/Sources/RswiftCore/ResourceTypes/Locale.swift index c1bfd803..3ee384d0 100644 --- a/Sources/RswiftCore/ResourceTypes/Locale.swift +++ b/Sources/RswiftCore/ResourceTypes/Locale.swift @@ -11,24 +11,32 @@ import Foundation enum Locale { case none - case base + case base // Older projects use a "Base" locale case language(String) - var isBase: Bool { - if case .base = self { + var isNone: Bool { + if case .none = self { return true } return false } - var isNone: Bool { - if case .none = self { + var isBase: Bool { + if case .base = self { return true } return false } + + var language: String? { + if case .language(let language) = self { + return language + } + + return nil + } } extension Locale: Hashable { @@ -38,8 +46,7 @@ extension Locale: Hashable { if lang == "Base" { self = .base - } - else { + } else { self = .language(lang) } } diff --git a/Sources/RswiftCore/ResourceTypes/Xcodeproj.swift b/Sources/RswiftCore/ResourceTypes/Xcodeproj.swift index a1f91143..23d27dfb 100644 --- a/Sources/RswiftCore/ResourceTypes/Xcodeproj.swift +++ b/Sources/RswiftCore/ResourceTypes/Xcodeproj.swift @@ -15,6 +15,8 @@ struct Xcodeproj: WhiteListedExtensionsResourceType { private let projectFile: XCProjectFile + let developmentLanguage: String + init(url: URL) throws { try Xcodeproj.throwIfUnsupportedExtension(url.pathExtension) let projectFile: XCProjectFile @@ -35,6 +37,7 @@ struct Xcodeproj: WhiteListedExtensionsResourceType { } self.projectFile = projectFile + self.developmentLanguage = projectFile.project.developmentRegion } func resourcePathsForTarget(_ targetName: String) throws -> [Path] { diff --git a/Sources/RswiftCore/RswiftCore.swift b/Sources/RswiftCore/RswiftCore.swift index d9f93d73..26526f90 100644 --- a/Sources/RswiftCore/RswiftCore.swift +++ b/Sources/RswiftCore/RswiftCore.swift @@ -39,7 +39,7 @@ public struct RswiftCore { NibStructGenerator(nibs: resources.nibs), ReuseIdentifierStructGenerator(reusables: resources.reusables), ResourceFileStructGenerator(resourceFiles: resources.resourceFiles), - StringsStructGenerator(localizableStrings: resources.localizableStrings), + StringsStructGenerator(localizableStrings: resources.localizableStrings, developmentLanguage: xcodeproj.developmentLanguage), AccessibilityIdentifierStructGenerator(nibs: resources.nibs, storyboards: resources.storyboards), ]) writeIfChanged(contents: fileContents, toURL: callInformation.outputURL)