Skip to content

Commit 4b9eb4a

Browse files
committed
Use a regex to parse .strings files.
1 parent 18a19f1 commit 4b9eb4a

File tree

1 file changed

+41
-15
lines changed

1 file changed

+41
-15
lines changed

R.swift/ResourceTypes/LocalizableStrings.swift

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct LocalizableStrings : WhiteListedExtensionsResourceType {
4040
let dictionary: [String : (params: [StringParam], commentValue: String)]
4141
switch url.pathExtension {
4242
case "strings"?:
43-
dictionary = try parseStrings(nsDictionary, source: locale.withFilename("\(filename).strings"))
43+
dictionary = try parseStrings(String(contentsOfURL: url), source: locale.withFilename("\(filename).strings"))
4444
case "stringsdict"?:
4545
dictionary = try parseStringsdict(nsDictionary, source: locale.withFilename("\(filename).stringsdict"))
4646
default:
@@ -53,37 +53,63 @@ struct LocalizableStrings : WhiteListedExtensionsResourceType {
5353
}
5454
}
5555

56-
private func parseStrings(nsDictionary: NSDictionary, source: String) throws -> [String : (params: [StringParam], commentValue: String)] {
56+
private func parseStrings(stringsFile: String, source: String) throws -> [String : (params: [StringParam], commentValue: String)] {
5757
var dictionary: [String : (params: [StringParam], commentValue: String)] = [:]
5858

59-
for (key, obj) in nsDictionary {
60-
if let
61-
key = key as? String,
62-
val = obj as? String
63-
{
59+
for entry in StringsEntry.parse(stringsFile) {
6460
var params: [StringParam] = []
6561

66-
for part in FormatPart.formatParts(formatString: val) {
62+
for part in FormatPart.formatParts(formatString: entry.val) {
6763
switch part {
6864
case .Reference:
69-
throw ResourceParsingError.ParsingFailed("Non-specifier reference in \(source): \(key) = \(val)")
65+
throw ResourceParsingError.ParsingFailed("Non-specifier reference in \(source): \(entry.key) = \(entry.val)")
7066

7167
case .Spec(let formatSpecifier):
7268
params.append(StringParam(name: nil, spec: formatSpecifier))
7369
}
7470
}
7571

76-
77-
dictionary[key] = (params, val)
78-
}
79-
else {
80-
throw ResourceParsingError.ParsingFailed("Non-string value in \(source): \(key) = \(obj)")
81-
}
72+
dictionary[entry.key] = (params, entry.val)
8273
}
8374

8475
return dictionary
8576
}
8677

78+
private struct StringsEntry {
79+
let comment: String?
80+
let key: String
81+
let val: String
82+
83+
static let regex: NSRegularExpression = {
84+
let capturedTrimmedComment = "/[*] \\s* (.*?) \\s* [*]/"
85+
let whitespaceOrComment = "(?: \\s | /[*] .*? [*]/)"
86+
let slash = "\\\\"
87+
let quotedString = "\" .*? (?<! \(slash))\""
88+
let unquotedString = "[^\\s\(slash)\"=]+"
89+
let string = "(?: \(quotedString) | \(unquotedString) )"
90+
let pattern = "(?: \(capturedTrimmedComment) )? \\s* ( \(string) ) \(whitespaceOrComment)* = \(whitespaceOrComment)* ( \(string) ) \(whitespaceOrComment)* ;"
91+
return try! NSRegularExpression(pattern: pattern, options: [.AllowCommentsAndWhitespace, .DotMatchesLineSeparators])
92+
}()
93+
94+
init(source: String, match: NSTextCheckingResult) {
95+
guard match.numberOfRanges == 4 else { fatalError("must be used with StringsEntry.regex") }
96+
func extract(range: NSRange, unescape: Bool) -> String? {
97+
guard range.location != NSNotFound else { return nil }
98+
let raw = (source as NSString).substringWithRange(range)
99+
if !unescape { return raw }
100+
return try! NSPropertyListSerialization.propertyListWithData(raw.dataUsingEncoding(NSUTF8StringEncoding)!, options: [], format: nil) as! String
101+
}
102+
comment = extract(match.rangeAtIndex(1), unescape: false)
103+
key = extract(match.rangeAtIndex(2), unescape: true)!
104+
val = extract(match.rangeAtIndex(3), unescape: true)!
105+
}
106+
107+
static func parse(stringsFileContents: String) -> [StringsEntry] {
108+
return regex.matchesInString(stringsFileContents, options: [], range: NSRange(0..<stringsFileContents.utf16.count))
109+
.map { StringsEntry(source: stringsFileContents, match: $0) }
110+
}
111+
}
112+
87113
private func parseStringsdict(nsDictionary: NSDictionary, source: String) throws -> [String : (params: [StringParam], commentValue: String)] {
88114

89115
var dictionary: [String : (params: [StringParam], commentValue: String)] = [:]

0 commit comments

Comments
 (0)