Skip to content

Commit

Permalink
Add tests for LoadedTranslationsProcessor
Browse files Browse the repository at this point in the history
  • Loading branch information
tomkowz committed Jul 31, 2015
1 parent 08f2952 commit cd2aca9
Show file tree
Hide file tree
Showing 13 changed files with 356 additions and 111 deletions.
50 changes: 23 additions & 27 deletions Swifternalization.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,12 @@
6D62834B1B3F622A00E65FCD /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6D6283491B3F622A00E65FCD /* Localizable.strings */; };
6D62834E1B3F628000E65FCD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6D62834D1B3F628000E65FCD /* Main.storyboard */; };
6D6283501B3F62B100E65FCD /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6D62834F1B3F62B100E65FCD /* LaunchScreen.xib */; };
6D62F9FC1B6B808400596A7C /* ExpressionJSONs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D62F9FB1B6B808400596A7C /* ExpressionJSONs.swift */; };
6D62F9FE1B6B824300596A7C /* TranslationJSONs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D62F9FD1B6B824300596A7C /* TranslationJSONs.swift */; };
6D6464821B40106C00C46C6D /* LocalizableFilesLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6464811B40106C00C46C6D /* LocalizableFilesLoader.swift */; };
6D6464841B40146100C46C6D /* KeyValueType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6464831B40146100C46C6D /* KeyValueType.swift */; };
6D6464851B40146600C46C6D /* KeyValueType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6464831B40146100C46C6D /* KeyValueType.swift */; };
6D75E6D41B6B6CFE00B370DC /* LoadedTranslationsProcessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D75E6D31B6B6CFE00B370DC /* LoadedTranslationsProcessorTests.swift */; };
6DB3CC751B5EBDA600A1220F /* CountryCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DB3CC651B5EBDA600A1220F /* CountryCode.swift */; };
6DB3CC761B5EBDA600A1220F /* ExpressionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DB3CC661B5EBDA600A1220F /* ExpressionType.swift */; };
6DB3CC771B5EBDA600A1220F /* SharedExpressionLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DB3CC671B5EBDA600A1220F /* SharedExpressionLoader.swift */; };
Expand Down Expand Up @@ -176,8 +179,11 @@
6D62834C1B3F623200E65FCD /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
6D62834D1B3F628000E65FCD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
6D62834F1B3F62B100E65FCD /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = "<group>"; };
6D62F9FB1B6B808400596A7C /* ExpressionJSONs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpressionJSONs.swift; sourceTree = "<group>"; };
6D62F9FD1B6B824300596A7C /* TranslationJSONs.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TranslationJSONs.swift; sourceTree = "<group>"; };
6D6464811B40106C00C46C6D /* LocalizableFilesLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalizableFilesLoader.swift; sourceTree = "<group>"; };
6D6464831B40146100C46C6D /* KeyValueType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyValueType.swift; sourceTree = "<group>"; };
6D75E6D31B6B6CFE00B370DC /* LoadedTranslationsProcessorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadedTranslationsProcessorTests.swift; sourceTree = "<group>"; };
6DB3CC651B5EBDA600A1220F /* CountryCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CountryCode.swift; sourceTree = "<group>"; };
6DB3CC661B5EBDA600A1220F /* ExpressionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExpressionType.swift; sourceTree = "<group>"; };
6DB3CC671B5EBDA600A1220F /* SharedExpressionLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharedExpressionLoader.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -233,8 +239,10 @@
6D140F451B56D04300359143 /* Helpers */ = {
isa = PBXGroup;
children = (
6D140F431B56D03D00359143 /* RandomNumbers.swift */,
6D62F9FB1B6B808400596A7C /* ExpressionJSONs.swift */,
6DD3B94A1B5ED65A00C79EAC /* NSBundle+TestExtension.swift */,
6D140F431B56D03D00359143 /* RandomNumbers.swift */,
6D62F9FD1B6B824300596A7C /* TranslationJSONs.swift */,
);
name = Helpers;
sourceTree = "<group>";
Expand Down Expand Up @@ -288,7 +296,6 @@
6DB3CC8E1B5EC27600A1220F /* Enums */,
6DB3CC8D1B5EC1FF00A1220F /* Matchers and Parsers */,
6DB3CC8A1B5EC19400A1220F /* Protocols */,
6DB3CC8B1B5EC1A400A1220F /* Typealiases */,
6D6282911B3F04C800E65FCD /* Expression.swift */,
6D6464811B40106C00C46C6D /* LocalizableFilesLoader.swift */,
6D6282991B3F17CA00E65FCD /* Regex.swift */,
Expand All @@ -313,9 +320,8 @@
6D5004571B3EF91600A54B36 /* SwifternalizationTests */ = {
isa = PBXGroup;
children = (
6D75E6D21B6B6CBC00B370DC /* Next */,
6D140F451B56D04300359143 /* Helpers */,
6DD3B93F1B5ED37A00C79EAC /* Loaders */,
6D5BA6021B6537BE000D7E49 /* Processors */,
6D6282971B3F13C300E65FCD /* ExpressionTests.swift */,
6D6282BE1B3F42CA00E65FCD /* InequalityExpressionMatcherTests.swift */,
6D6282A81B3F25DC00E65FCD /* InequalityExpressionParserTests.swift */,
Expand Down Expand Up @@ -345,14 +351,6 @@
name = "Supporting Files";
sourceTree = "<group>";
};
6D5BA6021B6537BE000D7E49 /* Processors */ = {
isa = PBXGroup;
children = (
6D5BA6031B6537D5000D7E49 /* SharedExpressionsProcessorTests.swift */,
);
name = Processors;
sourceTree = "<group>";
};
6D6283261B3F613D00E65FCD /* DemoOrdering */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -384,19 +382,24 @@
name = Resources;
sourceTree = "<group>";
};
6DB3CC8A1B5EC19400A1220F /* Protocols */ = {
6D75E6D21B6B6CBC00B370DC /* Next */ = {
isa = PBXGroup;
children = (
6D6464831B40146100C46C6D /* KeyValueType.swift */,
6DD3B9401B5ED38B00C79EAC /* JSONFileLoaderTests.swift */,
6D75E6D31B6B6CFE00B370DC /* LoadedTranslationsProcessorTests.swift */,
6DD3B9431B5ED55500C79EAC /* SharedExpressionsLoaderTests.swift */,
6D5BA6031B6537D5000D7E49 /* SharedExpressionsProcessorTests.swift */,
6D5BA5EF1B651796000D7E49 /* TranslationsLoaderTests.swift */,
);
name = Protocols;
name = Next;
sourceTree = "<group>";
};
6DB3CC8B1B5EC1A400A1220F /* Typealiases */ = {
6DB3CC8A1B5EC19400A1220F /* Protocols */ = {
isa = PBXGroup;
children = (
6D6464831B40146100C46C6D /* KeyValueType.swift */,
);
name = Typealiases;
name = Protocols;
sourceTree = "<group>";
};
6DB3CC8D1B5EC1FF00A1220F /* Matchers and Parsers */ = {
Expand Down Expand Up @@ -444,16 +447,6 @@
name = "Localizable Files";
sourceTree = "<group>";
};
6DD3B93F1B5ED37A00C79EAC /* Loaders */ = {
isa = PBXGroup;
children = (
6DD3B9401B5ED38B00C79EAC /* JSONFileLoaderTests.swift */,
6DD3B9431B5ED55500C79EAC /* SharedExpressionsLoaderTests.swift */,
6D5BA5EF1B651796000D7E49 /* TranslationsLoaderTests.swift */,
);
name = Loaders;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -653,6 +646,7 @@
6DD3B9451B5ED58A00C79EAC /* SharedExpressionLoader.swift in Sources */,
6D6282981B3F13C300E65FCD /* ExpressionTests.swift in Sources */,
6DBB6C661B40369A002F39A3 /* SharedExpressionsConfigurator.swift in Sources */,
6D75E6D41B6B6CFE00B370DC /* LoadedTranslationsProcessorTests.swift in Sources */,
6D50045B1B3EF91600A54B36 /* SwifternalizationTests.swift in Sources */,
6D6282A61B3F25B900E65FCD /* InequalityExpressionParser.swift in Sources */,
6D6282B81B3F3E2200E65FCD /* InequalitySign.swift in Sources */,
Expand All @@ -668,6 +662,7 @@
6D5BA6011B65271A000D7E49 /* SharedExpression.swift in Sources */,
6D4C4EC51B6AD01300B7839A /* LoadedTranslationsProcessor.swift in Sources */,
6DBB6C6A1B40412D002F39A3 /* InternalPattern.swift in Sources */,
6D62F9FC1B6B808400596A7C /* ExpressionJSONs.swift in Sources */,
6D4C4EB91B6AB6EF00B7839A /* LoadedTranslationType.swift in Sources */,
6DD3B9411B5ED38B00C79EAC /* JSONFileLoaderTests.swift in Sources */,
6D4C4EB01B6AA54800B7839A /* LoadedTranslation.swift in Sources */,
Expand Down Expand Up @@ -697,6 +692,7 @@
6D5BA5F11B6517A6000D7E49 /* TranslationsLoader.swift in Sources */,
6D6282BB1B3F41DB00E65FCD /* InequalityExtendedExpressionMatcher.swift in Sources */,
6DBB6C511B401B7C002F39A3 /* LocalizableFilesLoader.swift in Sources */,
6D62F9FE1B6B824300596A7C /* TranslationJSONs.swift in Sources */,
6DBB6C6C1B40431D002F39A3 /* LocalizableFilesLoaderTests.swift in Sources */,
6DBB6C961B4076E8002F39A3 /* SharedBaseExpressionTests.swift in Sources */,
6D62829B1B3F17FE00E65FCD /* Regex.swift in Sources */,
Expand Down
36 changes: 31 additions & 5 deletions Swifternalization/JSONFileLoader.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
import Foundation

/**
Represents json content.
*/
typealias JSONDictionary = Dictionary<String, AnyObject>

/**
Simple JSON loader.
*/
class JSONFileLoader {
typealias JSONDictionary = Dictionary<String, AnyObject>
final class JSONFileLoader {

/**
Loads translations dict for specified language.

:param: countryCode A country code.
:param: bundle A bundle when file is located.
:returns: Returns json of file or nil if cannot load a file.
*/
class func loadTranslations(countryCode: CountryCode, bundle: NSBundle = NSBundle.mainBundle()) -> JSONDictionary? {
return self.load(countryCode, bundle: bundle)
}

/**
Loads expressions dict for specified language.

:param: countryCode A country code.
:param: bundle A bundle when file is located.
:returns: dictionary with expressions or nil.
*/
class func loadExpressions(countryCode: CountryCode, bundle: NSBundle = NSBundle.mainBundle()) -> Dictionary<String, String>? {
return self.load("expressions", bundle: bundle)?[countryCode] as? Dictionary<String, String>
}

/**
Loads content of a file with specified name, type and bundle.
Expand All @@ -14,7 +40,7 @@ class JSONFileLoader {
:param: bundle A bundle when file is located.
:returns: JSON or nil.
*/
final class func load(fileName: String, bundle: NSBundle = NSBundle.mainBundle()) -> JSONDictionary? {
private class func load(fileName: String, bundle: NSBundle = NSBundle.mainBundle()) -> JSONDictionary? {
if let fileURL = bundle.URLForResource(fileName, withExtension: "json") {
return load(fileURL)
}
Expand All @@ -33,9 +59,9 @@ class JSONFileLoader {
var error: NSError?
if let dictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? JSONDictionary {
return dictionary
} else {
print("Cannot parse JSON. It might be broken.")
}
print("Cannot parse JSON. It might be broken.")
return nil
}
print("Cannot load content of file.")
return nil
Expand Down
4 changes: 3 additions & 1 deletion Swifternalization/LengthVariationType.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Foundation

/// Represents length variation.
/**
Represents length variation.
*/
protocol LengthVariationType {
/// Length of a screen.
var length: Int {get}
Expand Down
26 changes: 15 additions & 11 deletions Swifternalization/LoadedTranslationsProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class LoadedTranslationsProcessor {
expressions as well as built-in ones. Translations are processed in shared
expressions processor.
*/
class func processTrnslations(baseTranslations: [LoadedTranslation], preferedLanguageTranslations: [LoadedTranslation], sharedExpressions: [SharedExpression]) -> [TranslationType] {
class func processTranslations(baseTranslations: [LoadedTranslation], preferedLanguageTranslations: [LoadedTranslation], sharedExpressions: [SharedExpression]) -> [TranslationType] {

// Find those base translations that are not contained in prefered
// language translations.
Expand Down Expand Up @@ -74,19 +74,23 @@ class LoadedTranslationsProcessor {

case .WithExpressionsAndLengthVariations:
// The most advanced translation type. It contains expressions
// that contain length variations. THe job done here is similar
// to the one in .WithExpressions and .WithLengthVariations
// cases. key is filtered in shared expressions to get one of
// shared expressions and then method builds array of variations.
// that contain length variations or just simple expressions.
// THe job done here is similar to the one in .WithExpressions
// and .WithLengthVariations cases. key is filtered in shared
// expressions to get one of shared expressions and then method
// builds array of variations.
var expressions = [ExpressionType]()
for (key, value) in $0.content as! Dictionary<String, Dictionary<String, String>> {
for (key, value) in $0.content {
let pattern = sharedExpressions.filter({$0.identifier == key}).first?.pattern ?? key

var lengthVariations = [LengthVariation]()
for (lvKey, lvValue) in value as Dictionary<String, String> {
lengthVariations.append(LengthVariation(length: self.parseNumberFromLengthVariation(lvKey), value: lvValue))
if value is Dictionary<String, String> {
var lengthVariations = [LengthVariation]()
for (lvKey, lvValue) in value as! Dictionary<String, String> {
lengthVariations.append(LengthVariation(length: self.parseNumberFromLengthVariation(lvKey), value: lvValue))
}
expressions.append(LengthVariationExpression(pattern: pattern, variations: lengthVariations))
} else if value is String {
expressions.append(SimpleExpression(pattern:pattern, localizedValue: value as! String))
}
expressions.append(LengthVariationExpression(pattern: pattern, variations: lengthVariations))
}
return TranslationWithExpressions(key: $0.key, expressions: expressions)
}
Expand Down
13 changes: 4 additions & 9 deletions Swifternalization/SharedExpressionLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,18 @@ import Foundation
/**
Used to load content from `expressions.json` file for specified language.
*/
final class SharedExpressionsLoader: JSONFileLoader {
final class SharedExpressionsLoader {

/**
Loads expressions for specified language.

:param: countryCode A country code
:returns: array of loaded expressions.
*/
class func loadExpressions(countryCode: CountryCode, bundle: NSBundle) -> [SharedExpression] {
class func loadExpressions(json: Dictionary<String, String>) -> [SharedExpression] {
var expressions = [SharedExpression]()
if let json = self.load("expressions", bundle: bundle),
let expressionsDict = json[countryCode] as? Dictionary<String, String> {
for (identifier, pattern) in expressionsDict {
expressions.append(SharedExpression(identifier: identifier, pattern: pattern))
}
} else {
println("expressions.json file structure is incorrect.")
for (identifier, pattern) in json {
expressions.append(SharedExpression(identifier: identifier, pattern: pattern))
}
return expressions
}
Expand Down
48 changes: 29 additions & 19 deletions Swifternalization/TranslationsLoader.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
import Foundation

/**
Class that loads translations from a file.
Class that gets dictionary of translations and turn it into `LoadedTranslation`s.
*/
final class TranslationsLoader: JSONFileLoader {
final class TranslationsLoader {

/**
Loads content from file with name equal to passed country code in a bundle.
Converts dictionary into array of `LoadedTranslation`s.

:params: countryCode A country code.
:params: bundle A bundle when file is placed.
:returns: `LoadedTranslation` objects from specified file.
:params: dictionary A dictionary with content of file which contains
translations.
:returns: Array of `LoadedTranslation` objects from specified file.
*/
class func loadTranslations(countryCode: CountryCode, bundle: NSBundle = NSBundle.mainBundle()) -> [LoadedTranslation] {
class func loadTranslations(json: Dictionary<String, AnyObject>) -> [LoadedTranslation] {
var loadedTranslations = [LoadedTranslation]()
if let json = self.load(countryCode, bundle: bundle) {
for (key, value) in json {
if value is String {
loadedTranslations.append(LoadedTranslation(type: .Simple, key: key, content: [key: value]))
for (key, value) in json {
if value is String {
loadedTranslations.append(LoadedTranslation(type: .Simple, key: key, content: [key: value]))
} else {
let dictionary = value as! JSONDictionary
if let type = detectElementType(dictionary) {
loadedTranslations.append(LoadedTranslation(type: type, key: key, content: dictionary))
} else {
let dictionary = value as! JSONDictionary
if let type = detectElementType(dictionary) {
loadedTranslations.append(LoadedTranslation(type: type, key: key, content: dictionary))
} else {
println("Translation type is not supported for: \(dictionary)")
}
println("Translation type is not supported for: \(dictionary)")
}
}
}
Expand All @@ -42,10 +40,22 @@ final class TranslationsLoader: JSONFileLoader {
typealias DictWithStrings = Dictionary<String, String>
typealias DictWithDicts = Dictionary<String, DictWithStrings>

if element is DictWithStrings, let key = element.keys.first {
var dicts = 0
var strings = 0

for (key, value) in element {
if value is String {
strings++
} else if value is Dictionary<String, AnyObject> {
dicts++
}
}

if strings > 0 && dicts == 0 {
let key = element.keys.first!
let toIndex = advance(key.startIndex, 1)
return key.substringToIndex(toIndex) == "@" ? .WithLengthVariations : .WithExpressions
} else if element is DictWithDicts {
} else if strings >= 0 && dicts > 0 {
return .WithExpressionsAndLengthVariations
}

Expand Down
28 changes: 28 additions & 0 deletions SwifternalizationTests/ExpressionJSONs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// ExpressionJSONs.swift
// Swifternalization
//
// Created by Tomasz Szulc on 31/07/15.
// Copyright (c) 2015 Tomasz Szulc. All rights reserved.
//

import Foundation

class ExpressionJSONs {

class func base() -> Dictionary<String, String> {
return [
"key1": "val1",
"key2": "val2",
"key3": "val3"
]
}

class func en() -> Dictionary<String, String> {
return [
"key3": "val3",
"key4": "val4",
"key5": "val5"
]
}
}
Loading

0 comments on commit cd2aca9

Please sign in to comment.