From aee69887cdb6ecd4a3b34a21176268daaa089a82 Mon Sep 17 00:00:00 2001 From: Ryan ZHAO <> Date: Fri, 16 Aug 2024 15:26:00 +1000 Subject: [PATCH] Lint strings script --- Scripts/LintLocalizableStrings.swift | 116 +++++++++++++-------------- Session.xcodeproj/project.pbxproj | 6 +- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/Scripts/LintLocalizableStrings.swift b/Scripts/LintLocalizableStrings.swift index afd7894ff..b8712e146 100755 --- a/Scripts/LintLocalizableStrings.swift +++ b/Scripts/LintLocalizableStrings.swift @@ -185,65 +185,57 @@ enum ScriptAction: String { break case .lintStrings: + guard !projectState.localizationFile.strings.isEmpty else { + return print("------------ Nothing to lint ------------") + } + + var allKeys: [String] = [] + var duplicates: [String] = [] + projectState.localizationFile.strings.forEach { key, value in + if allKeys.contains(key) { + duplicates.append(key) + } else { + allKeys.append(key) + } + + // Add warning for probably faulty translation + if let localizations: JSON = (value as? JSON)?["localizations"] as? JSON { + if let original: String = ((localizations["en"] as? JSON)?["stringUnit"] as? JSON)?["value"] as? String { + localizations.forEach { locale, translation in + if let phrase: String = ((translation as? JSON)?["stringUnit"] as? JSON)?["value"] as? String { + let numberOfVarablesOrignal = Regex.matches("\\{.*\\}", content: original).count + let numberOfVarablesPhrase = Regex.matches("\\{.*\\}", content: phrase).count + if numberOfVarablesPhrase != numberOfVarablesOrignal { + Output.warning("\(key) in \(locale) may be faulty") + } + } + } + } + } + } + + // Add warnings for any duplicate keys + duplicates.forEach { Output.duplicate(key: $0) } + + // Process the source code + print("------------ Processing \(projectState.sourceFiles.count) Source File(s) ------------") + + projectState.sourceFiles.forEach { file in + // Add logs for unlocalised strings + file.unlocalizedPhrases.forEach { phrase in + Output.warning(phrase, "Found unlocalized string '\(phrase.key)'") + } + + // Add errors for missing localised strings + let missingKeys: Set = Set(file.keyPhrase.keys).subtracting(Set(allKeys)) + missingKeys.forEach { key in + switch file.keyPhrase[key] { + case .some(let phrase): Output.error(phrase, "Localized phrase '\(key)' missing from strings files") + case .none: Output.error(file, "Localized phrase '\(key)' missing from strings files") + } + } + } break -// guard !projectState.localizationFiles.isEmpty else { -// return print("------------ Nothing to lint ------------") -// } -// -// // Add warnings for any duplicate keys -// projectState.localizationFiles.forEach { file in -// // Show errors for any duplicates -// file.duplicates.forEach { phrase, original in Output.duplicate(phrase, original: original) } -// -// // Show warnings for any phrases missing from the file -// let allKeys: Set = Set(file.keyPhrase.keys) -// let missingKeysFromOtherFiles: [String: [String]] = projectState.localizationFiles.reduce(into: [:]) { result, otherFile in -// guard otherFile.path != file.path else { return } -// -// let missingKeys: Set = Set(otherFile.keyPhrase.keys) -// .subtracting(allKeys) -// -// missingKeys.forEach { missingKey in -// result[missingKey] = ((result[missingKey] ?? []) + [otherFile.name]) -// } -// } -// -// missingKeysFromOtherFiles.forEach { missingKey, namesOfFilesItWasFound in -// Output.warning(file, "Phrase '\(missingKey)' is missing (found in: \(namesOfFilesItWasFound.joined(separator: ", ")))") -// } -// -// var maybeFaulty: [String] = [] -// file.keyPhrase.forEach { key, phrase in -// guard let original = projectState.primaryLocalizationFile.keyPhrase[key] else { return } -// let numberOfVarablesOrignal = Regex.matches("\\{.*\\}", content: original.value).count -// let numberOfVarablesPhrase = Regex.matches("\\{.*\\}", content: phrase.value).count -// if numberOfVarablesPhrase != numberOfVarablesOrignal { -// maybeFaulty.append(key) -// } -// } -// maybeFaulty.forEach { key in Output.warning(file, "\(key) may be faulty.") } -// } -// -// // Process the source code -// print("------------ Processing \(projectState.sourceFiles.count) Source File(s) ------------") -// let allKeys: Set = Set(projectState.primaryLocalizationFile.keyPhrase.keys) -// -// projectState.sourceFiles.forEach { file in -// // Add logs for unlocalised strings -// file.unlocalizedPhrases.forEach { phrase in -// Output.warning(phrase, "Found unlocalized string '\(phrase.key)'") -// } -// -// // Add errors for missing localised strings -// let missingKeys: Set = Set(file.keyPhrase.keys).subtracting(allKeys) -// missingKeys.forEach { key in -// switch file.keyPhrase[key] { -// case .some(let phrase): Output.error(phrase, "Localized phrase '\(key)' missing from strings files") -// case .none: Output.error(file, "Localized phrase '\(key)' missing from strings files") -// } -// } -// } -// break case .updatePermissionStrings: break // print("------------ Updating permission strings ------------") @@ -325,6 +317,10 @@ enum Output { print("\(location.location): error: \(error)") } + static func warning(_ warning: String) { + print("warning: \(warning)") + } + static func warning(_ location: Locatable, _ warning: String) { print("\(location.location): warning: \(warning)") } @@ -339,6 +335,10 @@ enum Output { // currently include the reference to the original entry // print("\(original.location): note: previously found here") } + + static func duplicate(key: String) { + print("Error: duplicate key '\(key)'") + } } // MARK: - ProjectState diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 929ca985d..36fcd3bd8 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -5763,7 +5763,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# \"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" validate\n"; + shellScript = "\"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" validate\n"; showEnvVarsInLog = 0; }; FDC498C12AC1775400EDD897 /* Ensure Localizable.strings included */ = { @@ -5783,7 +5783,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# \"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" validate\n"; + shellScript = "\"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" validate\n"; showEnvVarsInLog = 0; }; FDD82C422A2085B900425F05 /* Add Commit Hash To Build Info Plist */ = { @@ -5824,7 +5824,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# \"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" lint update\n"; + shellScript = "\"${SRCROOT}/Scripts/LintLocalizableStrings.swift\" lint\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */