Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If needed, pluralize to `Tasks`, `PRs` or `Authors` and list multiple entries se

## [Unreleased]
### Added
- None.
- Adds support for Intent Definition files.
### Changed
- None.
### Deprecated
Expand Down
4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ let package = Package(
.package(name: "MungoHealer", url: "https://github.com/Flinesoft/MungoHealer.git", from: "0.3.4"),
.package(name: "Rainbow", url: "https://github.com/onevcat/Rainbow.git", from: "3.1.5"),
.package(name: "SwiftCLI", url: "https://github.com/jakeheis/SwiftCLI.git", from: "6.0.3"),
.package(name: "Toml", url: "https://github.com/jdfergason/swift-toml.git", .branch("master")),
.package(name: "SwiftSyntax", url: "https://github.com/apple/swift-syntax.git", from: "0.50500.0"),
.package(name: "SwiftyXML", url: "https://github.com/chenyunguiMilook/SwiftyXML.git", from: "3.1.0"),
.package(name: "Toml", url: "https://github.com/jdfergason/swift-toml.git", .branch("master")),
],
targets: [
.executableTarget(
Expand All @@ -32,6 +33,7 @@ let package = Package(
"Rainbow",
"SwiftCLI",
"SwiftSyntax",
"SwiftyXML",
"Toml",
]
),
Expand Down
6 changes: 6 additions & 0 deletions Sources/BartyCrouchKit/FileHandling/StringsFilesSearch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ public final class StringsFilesSearch: FilesSearchable {
return self.findAllFilePaths(inDirectoryPath: baseDirectoryPath, matching: ibFileRegex)
}

public func findAllIntentDefinitionFiles(within baseDirectoryPath: String, withLocale locale: String = "Base") -> [String] {
// swiftlint:disable:next force_try
let intentsFileRegex = try! NSRegularExpression(pattern: "^(.*\\/)?\(locale).lproj.*\\.intentdefinition\\z", options: .caseInsensitive)
return self.findAllFilePaths(inDirectoryPath: baseDirectoryPath, matching: intentsFileRegex)
}

public func findAllStringsFiles(within baseDirectoryPath: String, withLocale locale: String) -> [String] {
// swiftlint:disable:next force_try
let stringsFileRegex = try! NSRegularExpression(pattern: "^(.*\\/)?\(locale).lproj.*\\.strings\\z", options: .caseInsensitive)
Expand Down
112 changes: 111 additions & 1 deletion Sources/BartyCrouchKit/OldCommandLine/CommandLineActor.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// swiftlint:disable function_parameter_count type_body_length cyclomatic_complexity
// swiftlint:disable function_parameter_count type_body_length cyclomatic_complexity file_length

import Foundation
import SwiftyXML

// NOTE:
// This file was not refactored as port of the work/big-refactoring branch for version 4.0 to prevent unexpected behavior changes.
Expand Down Expand Up @@ -76,6 +77,29 @@ public class CommandLineActor {
}
}

func actOnIntentDefinitions(paths: [String], override: Bool, verbose: Bool, defaultToBase: Bool, unstripped: Bool, ignoreEmptyStrings: Bool) {
let inputFilePaths = paths.flatMap { StringsFilesSearch.shared.findAllIntentDefinitionFiles(within: $0, withLocale: "Base") }.withoutDuplicates()

guard !inputFilePaths.isEmpty else { print("No input files found.", level: .warning); return }

for inputFilePath in inputFilePaths {
guard FileManager.default.fileExists(atPath: inputFilePath) else {
print("No file exists at input path '\(inputFilePath)'", level: .error); return
}

let outputStringsFilePaths = StringsFilesSearch.shared.findAllLocalesForStringsFile(sourceFilePath: inputFilePath).filter { $0 != inputFilePath }
self.incrementalIntentDefinitionUpdate(
inputFilePath,
outputStringsFilePaths,
override: override,
verbose: verbose,
defaultToBase: defaultToBase,
unstripped: unstripped,
ignoreEmptyStrings: ignoreEmptyStrings
)
}
}

func actOnTranslate(paths: [String], override: Bool, verbose: Bool, secret: Secret, locale: String) {
let inputFilePaths = paths.flatMap { StringsFilesSearch.shared.findAllStringsFiles(within: $0, withLocale: locale) }.withoutDuplicates()

Expand Down Expand Up @@ -320,6 +344,92 @@ public class CommandLineActor {
print("Successfully updated strings file(s) of Storyboard or XIB file.", level: .success, file: inputFilePath)
}

// swiftlint:disable:next function_body_length
private func incrementalIntentDefinitionUpdate(
_ inputFilePath: String,
_ outputStringsFilePaths: [String],
override: Bool,
verbose: Bool,
defaultToBase: Bool,
unstripped: Bool,
ignoreEmptyStrings: Bool
) {
let extractedStringsFilePath = inputFilePath + ".tmpstrings"

// Extract translations
var xmlString = ""
do {
xmlString = try String(contentsOfFile: inputFilePath)
} catch {
print("Could not extract string for file at path '\(inputFilePath)'.", level: .error)
return
}

let xml = XML(string: xmlString)
var translationDict = [String: String]()

// Traverse the xml structure and search for translatable values
// To check if a value is translatable, it is sufficient to check if the parent dictionary
// contains a key with the same name and the suffix "ID".
func traverse(xml: XML) {
for child in xml.xmlChildren {
traverse(xml: child)
}

guard xml.xmlName == "dict" else { return }

var xmlDict = [String: String]()
var currentKey = ""
for child in xml.xmlChildren {
if child.xmlName == "key" {
currentKey = child.stringValue
} else if child.xmlName == "string" {
xmlDict[currentKey] = child.stringValue
}
}

for (key, value) in xmlDict {
guard key.hasSuffix("ID") else { continue }
guard let baseTranslation = xmlDict[String(key.prefix(key.count - 2))] else { continue }

// Convert linebreaks
translationDict[value] = baseTranslation.replacingOccurrences(of: "/\\\n", with: "\\n")
}
}

traverse(xml: xml)

let translationString = translationDict.map { key, value in "\"\(key)\" = \"\(value)\";" }
.joined(separator: "\n\n")

try? translationString.write(toFile: extractedStringsFilePath, atomically: true, encoding: .utf8)

for outputStringsFilePath in outputStringsFilePaths {
guard let stringsFileUpdater = StringsFileUpdater(path: outputStringsFilePath) else { continue }

stringsFileUpdater.incrementallyUpdateKeys(
withStringsFileAtPath: extractedStringsFilePath,
addNewValuesAsEmpty: !defaultToBase,
override: override,
keepWhitespaceSurroundings: unstripped,
ignoreEmptyStrings: ignoreEmptyStrings
)

if verbose {
print("Incrementally updated keys of file '\(outputStringsFilePath)'.", level: .info)
}
}

do {
try FileManager.default.removeItem(atPath: extractedStringsFilePath)
} catch {
print("Temporary strings file couldn't be deleted at path '\(extractedStringsFilePath)'", level: .error)
return
}

print("Successfully updated strings file(s) of Intent definition file.", level: .success, file: inputFilePath)
}

private func translate(secret: Secret, _ inputFilePath: String, _ outputStringsFilePaths: [String], override: Bool, verbose: Bool) {
var overallTranslatedValuesCount = 0
var filesWithTranslatedValuesCount = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ extension InterfacesTaskHandler: TaskHandler {
unstripped: options.unstripped,
ignoreEmptyStrings: options.ignoreEmptyStrings
)

CommandLineActor().actOnIntentDefinitions(
paths: options.paths,
override: false,
verbose: GlobalOptions.verbose.value,
defaultToBase: options.defaultToBase,
unstripped: options.unstripped,
ignoreEmptyStrings: options.ignoreEmptyStrings
)
}
}
}
Expand Down