Skip to content

Commit 7351b8a

Browse files
authored
Merge pull request #7 from igorkulman/code-cleanup
Code cleanup
2 parents 41f2b7a + 9ead47a commit 7351b8a

13 files changed

Lines changed: 326 additions & 102 deletions

.swiftlint.yml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
included:
2+
- LocalizationEditor
3+
4+
disabled_rules:
5+
- line_length
6+
- force_try
7+
- force_cast
8+
9+
analyzer_rules:
10+
- unused_import
11+
- unused_private_declaration
12+
13+
number_separator:
14+
minimum_length: 7
15+
16+
opt_in_rules:
17+
- anyobject_protocol
18+
- closure_end_indentation
19+
- closure_spacing
20+
- collection_alignment
21+
- contains_over_first_not_nil
22+
- empty_string
23+
- empty_xctest_method
24+
- explicit_init
25+
- fallthrough
26+
- fatal_error_message
27+
- first_where
28+
- identical_operands
29+
- joined_default_parameter
30+
- let_var_whitespace
31+
- literal_expression_end_indentation
32+
- lower_acl_than_parent
33+
- nimble_operator
34+
- number_separator
35+
- object_literal
36+
- operator_usage_whitespace
37+
- overridden_super_call
38+
- pattern_matching_keywords
39+
- private_action
40+
- private_outlet
41+
- prohibited_interface_builder
42+
- prohibited_super_call
43+
- quick_discouraged_call
44+
- quick_discouraged_focused_test
45+
- quick_discouraged_pending_test
46+
- redundant_nil_coalescing
47+
- redundant_type_annotation
48+
- single_test_class
49+
- sorted_first_last
50+
- sorted_imports
51+
- static_operator
52+
- unneeded_parentheses_in_closure_argument
53+
- vertical_parameter_alignment_on_call
54+
- yoda_condition
55+
56+
function_parameter_count:
57+
warning: 8
58+
error: 10
59+
60+
large_tuple:
61+
warning: 3
62+
custom_rules:
63+
double_space: # from https://github.com/IBM-Swift/Package-Builder
64+
include: "*.swift"
65+
name: "Double space"
66+
regex: '([a-z,A-Z] \s+)'
67+
message: "Double space between keywords"
68+
match_kinds: keyword
69+
severity: warning
70+
comments_space: # from https://github.com/brandenr/swiftlintconfig
71+
name: "Space After Comment"
72+
regex: '(^ *//\w+)'
73+
message: "There should be a space after //"
74+
severity: warning
75+
empty_line_after_guard: # from https://github.com/brandenr/swiftlintconfig
76+
name: "Empty Line After Guard"
77+
regex: '(^ *guard[ a-zA-Z0-9=?.\(\),><!]*\{[ a-zA-Z0-9=?.\(\),><!]*\}\n *(?!(?:return|guard))\S+)'
78+
message: "There should be an empty line after a guard"
79+
severity: warning
80+
unnecessary_type: # from https://github.com/brandenr/swiftlintconfig
81+
name: "Unnecessary Type"
82+
regex: '[ a-zA-Z0-9]*(?:let|var) [ a-zA-Z0-9]*: ([a-zA-Z0-9]*)[ ]*= \1'
83+
message: "Type Definition Not Needed"
84+
severity: warning

LocalizationEditor.xcodeproj/project.pbxproj

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@
189189
isa = PBXNativeTarget;
190190
buildConfigurationList = F3D9C95220BEC7460081830A /* Build configuration list for PBXNativeTarget "LocalizationEditor" */;
191191
buildPhases = (
192+
F3BC11A421C40FAC001F396F /* SwiftLint */,
192193
F3D9C93E20BEC7460081830A /* Sources */,
193194
F3D9C93F20BEC7460081830A /* Frameworks */,
194195
F3D9C94020BEC7460081830A /* Resources */,
@@ -253,6 +254,27 @@
253254
};
254255
/* End PBXResourcesBuildPhase section */
255256

257+
/* Begin PBXShellScriptBuildPhase section */
258+
F3BC11A421C40FAC001F396F /* SwiftLint */ = {
259+
isa = PBXShellScriptBuildPhase;
260+
buildActionMask = 2147483647;
261+
files = (
262+
);
263+
inputFileListPaths = (
264+
);
265+
inputPaths = (
266+
);
267+
name = SwiftLint;
268+
outputFileListPaths = (
269+
);
270+
outputPaths = (
271+
);
272+
runOnlyForDeploymentPostprocessing = 0;
273+
shellPath = /bin/sh;
274+
shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
275+
};
276+
/* End PBXShellScriptBuildPhase section */
277+
256278
/* Begin PBXSourcesBuildPhase section */
257279
F3D9C93E20BEC7460081830A /* Sources */ = {
258280
isa = PBXSourcesBuildPhase;

LocalizationEditor/AppDelegate.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
1717
setupLogging()
1818
}
1919

20-
func applicationWillTerminate(_: Notification) {
21-
}
20+
func applicationWillTerminate(_: Notification) {}
2221

2322
private func setupLogging() {
2423
var configs = [LogConfiguration]()

LocalizationEditor/Models/Localization.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
import Foundation
1010

11+
/**
12+
Complete localization for a single language. Represents a single strings file for a single language
13+
*/
1114
class Localization {
1215
let language: String
1316
let translations: [LocalizationString]
@@ -20,6 +23,8 @@ class Localization {
2023
}
2124
}
2225

26+
// MARK: Description
27+
2328
extension Localization: CustomStringConvertible {
2429
var description: String {
2530
return language.uppercased()

LocalizationEditor/Models/LocalizationGroup.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@
88

99
import Foundation
1010

11+
/**
12+
Group of localizations, like Localizabe.strings, InfoPlist.strings, etc.
13+
*/
1114
class LocalizationGroup {
1215
let name: String
1316
let path: String
14-
let localizations : [Localization]
15-
17+
let localizations: [Localization]
18+
1619
init(name: String, localizations: [Localization], path: String) {
1720
self.name = name
1821
self.localizations = localizations
1922
self.path = path
2023
}
2124
}
2225

26+
// MARK: Description
27+
2328
extension LocalizationGroup: CustomStringConvertible {
2429
var description: String {
2530
return name

LocalizationEditor/Models/LocalizationString.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
import Foundation
1010

11+
/**
12+
Class representing single localization string in form of key: "value"; as found in strings files
13+
*/
1114
class LocalizationString {
1215
let key: String
1316
private(set) var value: String
@@ -17,11 +20,18 @@ class LocalizationString {
1720
self.value = value
1821
}
1922

23+
/**
24+
Updates the localization string with a new value
25+
26+
Parameter value: new value
27+
*/
2028
func update(value: String) {
2129
self.value = value
2230
}
2331
}
2432

33+
// MARK: Description
34+
2535
extension LocalizationString: CustomStringConvertible {
2636
var description: String {
2737
return "\(key) = \(value)"

LocalizationEditor/Providers/LocalizationProvider.swift

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,54 @@ import CleanroomLogger
1010
import Files
1111
import Foundation
1212

13+
/**
14+
Service for working with the strings files
15+
*/
1316
class LocalizationProvider {
17+
/**
18+
List of folder that should be ignored when searching for localization files
19+
*/
1420
private let ignoredDirectories = ["Pods", "Carthage", "build", ".framework"]
15-
21+
22+
// MARK: Actions
23+
24+
/**
25+
Updates given localization values in given localization file. Basially regenerates the whole localization files changing the given value
26+
27+
- Parameter localization: localization to update
28+
- Parameter string: localization string
29+
- Parameter value: new value for the localization string
30+
*/
31+
func updateLocalization(localization: Localization, string: LocalizationString, with value: String) {
32+
guard string.value != value else {
33+
Log.debug?.message("Same value provided for \(string)")
34+
return
35+
}
36+
37+
Log.debug?.message("Updating \(string) with \(value) in \(localization)")
38+
39+
string.update(value: value)
40+
41+
let data = localization.translations.map { string in
42+
"\"\(string.key)\" = \"\(string.value.replacingOccurrences(of: "\"", with: "\\\""))\";"
43+
}.reduce("") { prev, next in
44+
"\(prev)\n\(next)"
45+
}
46+
47+
do {
48+
try data.write(toFile: localization.path, atomically: false, encoding: .utf8)
49+
Log.debug?.message("Localization file for \(localization) updated")
50+
} catch {
51+
Log.error?.message("Writing localization file for \(localization) failed with \(error)")
52+
}
53+
}
54+
55+
/**
56+
Finds and constructs localiations for given directory path
57+
58+
- Parameter url: diretcory URL to start the search
59+
- Returns: list of localization groups
60+
*/
1661
func getLocalizations(url: URL) -> [LocalizationGroup] {
1762
Log.debug?.message("Searching \(url) for Localizable.strings")
1863

@@ -21,61 +66,45 @@ class LocalizationProvider {
2166
}
2267

2368
let localizationFiles = Dictionary(grouping: folder.makeFileSequence(recursive: true).filter { file in
24-
return file.name.hasSuffix(".strings") && ignoredDirectories.map({file.path.contains("\($0)/")}).filter({$0}).count == 0
25-
}, by: {$0.path.components(separatedBy:"/").filter({!$0.hasSuffix(".lproj")}).joined(separator:"/")})
26-
27-
Log.debug?.message("Found \(localizationFiles) localization files")
28-
29-
return localizationFiles.map({ (path, files) in
69+
file.name.hasSuffix(".strings") && ignoredDirectories.map({ file.path.contains("\($0)/") }).filter({ $0 }).count == 0
70+
}, by: { $0.path.components(separatedBy: "/").filter({ !$0.hasSuffix(".lproj") }).joined(separator: "/") })
71+
72+
Log.debug?.message("Found \(localizationFiles.count) localization files")
73+
74+
return localizationFiles.map({ path, files in
3075
let name = URL(fileURLWithPath: path).lastPathComponent
3176
return LocalizationGroup(name: name, localizations: files.map({ file in
3277
let parts = file.path.split(separator: "/")
3378
let lang = String(parts[parts.count - 2]).replacingOccurrences(of: ".lproj", with: "")
3479
return Localization(language: lang, translations: getLocalizationStrings(path: file.path), path: file.path)
3580
}), path: path)
36-
}).sorted(by: {$0.name < $1.name})
81+
}).sorted(by: { $0.name < $1.name })
3782
}
3883

84+
// MARK: Internal implementation
85+
86+
/**
87+
Reads given strings file and constructs an array of localization strings from it
88+
89+
- Parameter path: strings file path
90+
- Returns: array of localization strings
91+
*/
3992
private func getLocalizationStrings(path: String) -> [LocalizationString] {
4093
guard let dict = NSDictionary(contentsOfFile: path) as? [String: String] else {
4194
Log.error?.message("Could not parse \(path) as dictionary")
4295
return []
4396
}
4497

45-
var strings: [LocalizationString] = []
98+
var localizationStrings: [LocalizationString] = []
4699
for (key, value) in dict {
47-
let s = LocalizationString(key: key, value: value)
48-
strings.append(s)
100+
let localizationString = LocalizationString(key: key, value: value)
101+
localizationStrings.append(localizationString)
49102
}
50103

51-
Log.debug?.message("Found \(strings.count) keys for in \(path)")
104+
Log.debug?.message("Found \(localizationStrings.count) keys for in \(path)")
52105

53-
return strings.sorted(by: { (lhs, rhs) -> Bool in
106+
return localizationStrings.sorted(by: { lhs, rhs -> Bool in
54107
lhs.key < rhs.key
55108
})
56109
}
57-
58-
func updateLocalization(localization: Localization, string: LocalizationString, with value: String) {
59-
guard string.value != value else {
60-
Log.debug?.message("Same value provided for \(string)")
61-
return
62-
}
63-
64-
Log.debug?.message("Updating \(string) with \(value) in \(localization)")
65-
66-
string.update(value: value)
67-
68-
let data = localization.translations.map { string in
69-
"\"\(string.key)\" = \"\(string.value.replacingOccurrences(of: "\"", with: "\\\""))\";"
70-
}.reduce("") { prev, next in
71-
"\(prev)\n\(next)"
72-
}
73-
74-
do {
75-
try data.write(toFile: localization.path, atomically: false, encoding: .utf8)
76-
Log.debug?.message("Localization file for \(localization) updated")
77-
} catch {
78-
Log.error?.message("Writing localization file for \(localization) failed with \(error)")
79-
}
80-
}
81110
}

0 commit comments

Comments
 (0)