diff --git a/Example/LocalizeExample/Sources/AppDelegate.swift b/Example/LocalizeExample/Sources/AppDelegate.swift index afb844d..9accffe 100644 --- a/Example/LocalizeExample/Sources/AppDelegate.swift +++ b/Example/LocalizeExample/Sources/AppDelegate.swift @@ -17,4 +17,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } } - diff --git a/Example/LocalizeExample/Sources/ViewController.swift b/Example/LocalizeExample/Sources/ViewController.swift index 4d88dbd..7a34936 100644 --- a/Example/LocalizeExample/Sources/ViewController.swift +++ b/Example/LocalizeExample/Sources/ViewController.swift @@ -9,14 +9,12 @@ import UIKit class ViewController: UIViewController { - override func viewDidLoad() { super.viewDidLoad() - + // Here simulates their usage in code - let _ = NSLocalizedString("UntranslatedKey", comment: "") - let _ = NSLocalizedString("IgnoredUntranslatedKey", comment: "") - let _ = NSLocalizedString("MissingKey", comment: "") + _ = NSLocalizedString("UntranslatedKey", comment: "") + _ = NSLocalizedString("IgnoredUntranslatedKey", comment: "") + _ = NSLocalizedString("MissingKey", comment: "") } } - diff --git a/Localize.swift b/Localize.swift index 5e7ead4..46e420a 100755 --- a/Localize.swift +++ b/Localize.swift @@ -2,14 +2,12 @@ import Foundation - // WHAT // 1. Find Missing keys in other Localisation files // 2. Find potentially untranslated keys // 3. Find Duplicate keys // 4. Find Unused keys and generate script to delete them all at once - // MARK: Start Of Configurable Section /* @@ -44,7 +42,7 @@ let patterns = [ For instance, Keys that you concatenate will not be detected by the parsing so you want to add them here in order not to create false positives :) */ -let ignoredFromUnusedKeys = [String]() +let ignoredFromUnusedKeys: [String] = [] /* example let ignoredFromUnusedKeys = [ "NotificationNoOne", @@ -86,13 +84,13 @@ if enabled == false { // Detect list of supported languages automatically func listSupportedLanguages() -> [String] { - var sl = [String]() + var sl: [String] = [] let path = FileManager.default.currentDirectoryPath + relativeLocalizableFolders if !FileManager.default.fileExists(atPath: path) { print("Invalid configuration: \(path) does not exist.") exit(1) } - let enumerator: FileManager.DirectoryEnumerator? = FileManager.default.enumerator(atPath: path) + let enumerator = FileManager.default.enumerator(atPath: path) let extensionName = "lproj" print("Found these languages:") while let element = enumerator?.nextObject() as? String { @@ -105,23 +103,22 @@ func listSupportedLanguages() -> [String] { return sl } - let supportedLanguages = listSupportedLanguages() -var ignoredFromSameTranslation = [String:[String]]() +var ignoredFromSameTranslation: [String: [String]] = [:] let path = FileManager.default.currentDirectoryPath + relativeLocalizableFolders var numberOfWarnings = 0 var numberOfErrors = 0 struct LocalizationFiles { var name = "" - var keyValue = [String:String]() - var linesNumbers = [String:Int]() - + var keyValue: [String: String] = [:] + var linesNumbers: [String: Int] = [:] + init(name: String) { self.name = name process() } - + mutating func process() { if sanitizeFiles { removeCommentsFromFile() @@ -130,29 +127,28 @@ struct LocalizationFiles { } let location = singleLanguage ? "\(path)/Localizable.strings" : "\(path)/\(name).lproj/Localizable.strings" if let string = try? String(contentsOfFile: location, encoding: .utf8) { - let lines = string.components(separatedBy: CharacterSet.newlines) - keyValue = [String:String]() + let lines = string.components(separatedBy: .newlines) + keyValue = [:] let pattern = "\"(.*)\" = \"(.+)\";" let regex = try? NSRegularExpression(pattern: pattern, options: []) - var ignoredTranslation = [String]() - + var ignoredTranslation: [String] = [] + for (lineNumber, line) in lines.enumerated() { - let range = NSRange(location:0, length:(line as NSString).length) - - + let range = NSRange(location: 0, length: (line as NSString).length) + // Ignored pattern let ignoredPattern = "\"(.*)\" = \"(.+)\"; *\\/\\/ *ignore-same-translation-warning" let ignoredRegex = try? NSRegularExpression(pattern: ignoredPattern, options: []) - if let ignoredMatch = ignoredRegex?.firstMatch(in:line, + if let ignoredMatch = ignoredRegex?.firstMatch(in: line, options: [], range: range) { - let key = (line as NSString).substring(with: ignoredMatch.range(at:1)) + let key = (line as NSString).substring(with: ignoredMatch.range(at: 1)) ignoredTranslation.append(key) } if let firstMatch = regex?.firstMatch(in: line, options: [], range: range) { - let key = (line as NSString).substring(with: firstMatch.range(at:1)) - let value = (line as NSString).substring(with: firstMatch.range(at:2)) - if let _ = keyValue[key] { + let key = (line as NSString).substring(with: firstMatch.range(at: 1)) + let value = (line as NSString).substring(with: firstMatch.range(at: 2)) + if let _ = keyValue[key] { let str = "\(path)/\(name).lproj" + "/Localizable.strings:\(linesNumbers[key]!): " + "error: [Redundance] \"\(key)\" " @@ -161,7 +157,7 @@ struct LocalizationFiles { numberOfErrors += 1 } else { keyValue[key] = value - linesNumbers[key] = lineNumber+1 + linesNumbers[key] = lineNumber + 1 } } } @@ -169,54 +165,54 @@ struct LocalizationFiles { ignoredFromSameTranslation[name] = ignoredTranslation } } - + func rebuildFileString(from lines: [String]) -> String { return lines.reduce("") { (r: String, s: String) -> String in - return (r == "") ? (r + s) : (r + "\n" + s) + (r == "") ? (r + s) : (r + "\n" + s) } } - + func removeEmptyLinesFromFile() { let location = "\(path)/\(name).lproj/Localizable.strings" if let string = try? String(contentsOfFile: location, encoding: .utf8) { - var lines = string.components(separatedBy: CharacterSet.newlines) - lines = lines.filter { $0.trimmingCharacters(in: CharacterSet.whitespaces) != "" } + var lines = string.components(separatedBy: .newlines) + lines = lines.filter { $0.trimmingCharacters(in: .whitespaces) != "" } let s = rebuildFileString(from: lines) - try? s.write(toFile:location, atomically:false, encoding:String.Encoding.utf8) + try? s.write(toFile: location, atomically: false, encoding: .utf8) } } - + func removeCommentsFromFile() { let location = "\(path)/\(name).lproj/Localizable.strings" if let string = try? String(contentsOfFile: location, encoding: .utf8) { - var lines = string.components(separatedBy: CharacterSet.newlines) + var lines = string.components(separatedBy: .newlines) lines = lines.filter { !$0.hasPrefix("//") } let s = rebuildFileString(from: lines) - try? s.write(toFile:location, atomically:false, encoding:String.Encoding.utf8) + try? s.write(toFile: location, atomically: false, encoding: .utf8) } } - + func sortLinesAlphabetically() { let location = "\(path)/\(name).lproj/Localizable.strings" if let string = try? String(contentsOfFile: location, encoding: .utf8) { - let lines = string.components(separatedBy: CharacterSet.newlines) - + let lines = string.components(separatedBy: .newlines) + var s = "" - for (i,l) in sortAlphabetically(lines).enumerated() { + for (i, l) in sortAlphabetically(lines).enumerated() { s += l - if (i != lines.count - 1) { + if i != lines.count - 1 { s += "\n" } } - try? s.write(toFile:location, atomically:false, encoding:String.Encoding.utf8) + try? s.write(toFile: location, atomically: false, encoding: .utf8) } } - - func removeEmptyLinesFromLines(_ lines:[String]) -> [String] { - return lines.filter { $0.trimmingCharacters(in: CharacterSet.whitespaces) != "" } + + func removeEmptyLinesFromLines(_ lines: [String]) -> [String] { + return lines.filter { $0.trimmingCharacters(in: .whitespaces) != "" } } - - func sortAlphabetically(_ lines:[String]) -> [String] { + + func sortAlphabetically(_ lines: [String]) -> [String] { return lines.sorted() } } @@ -229,24 +225,25 @@ let localizationFiles = supportedLanguages .map { LocalizationFiles(name: $0) } // MARK: - Detect Unused Keys + let sourcesPath = FileManager.default.currentDirectoryPath + relativeSourceFolder let fileManager = FileManager.default -let enumerator = fileManager.enumerator(atPath:sourcesPath) -var localizedStrings = [String]() +let enumerator = fileManager.enumerator(atPath: sourcesPath) +var localizedStrings: [String] = [] while let swiftFileLocation = enumerator?.nextObject() as? String { - // checks the extension // TODO OBJC? - if swiftFileLocation.hasSuffix(".swift") || swiftFileLocation.hasSuffix(".m") || swiftFileLocation.hasSuffix(".mm") { + // checks the extension + if swiftFileLocation.hasSuffix(".swift") || swiftFileLocation.hasSuffix(".m") || swiftFileLocation.hasSuffix(".mm") { let location = "\(sourcesPath)/\(swiftFileLocation)" if let string = try? String(contentsOfFile: location, encoding: .utf8) { for p in patterns { let regex = try? NSRegularExpression(pattern: p, options: []) - let range = NSRange(location:0, length:(string as NSString).length) //Obj c wa + let range = NSRange(location: 0, length: (string as NSString).length) // Obj c wa regex?.enumerateMatches(in: string, options: [], range: range, - using: { (result, _, _) in + using: { result, _, _ in if let r = result { - let value = (string as NSString).substring(with:r.range(at:r.numberOfRanges-1)) + let value = (string as NSString).substring(with: r.range(at: r.numberOfRanges - 1)) localizedStrings.append(value) } }) @@ -272,7 +269,7 @@ for v in unused { replaceCommand += "|" } replaceCommand += v - if counter == unused.count-1 { + if counter == unused.count - 1 { replaceCommand += ")\" = \".*\";" } counter += 1 @@ -280,8 +277,8 @@ for v in unused { print(replaceCommand) - // MARK: - Compare each translation file against master (en) + for file in localizationFiles { for k in masterLocalizationfile.keyValue.keys { if let v = file.keyValue[k] { @@ -310,4 +307,3 @@ print("Number of errors : \(numberOfErrors)") if numberOfErrors > 0 { exit(1) } -