Skip to content

Commit

Permalink
Simplify TranslationsLoader and add LoadedTranslation struct with cor…
Browse files Browse the repository at this point in the history
…responding type
  • Loading branch information
tomkowz committed Jul 30, 2015
1 parent 5932213 commit 78e15bb
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 79 deletions.
6 changes: 6 additions & 0 deletions Swifternalization.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

/* Begin PBXBuildFile section */
6D140F441B56D03D00359143 /* RandomNumbers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D140F431B56D03D00359143 /* RandomNumbers.swift */; };
6D4C4EAF1B6AA48F00B7839A /* LoadedTranslation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4C4EAE1B6AA48F00B7839A /* LoadedTranslation.swift */; };
6D4C4EB01B6AA54800B7839A /* LoadedTranslation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4C4EAE1B6AA48F00B7839A /* LoadedTranslation.swift */; };
6D50044E1B3EF91600A54B36 /* Swifternalization.h in Headers */ = {isa = PBXBuildFile; fileRef = 6D50044D1B3EF91600A54B36 /* Swifternalization.h */; settings = {ATTRIBUTES = (Public, ); }; };
6D5004541B3EF91600A54B36 /* Swifternalization.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D5004481B3EF91600A54B36 /* Swifternalization.framework */; };
6D50045B1B3EF91600A54B36 /* SwifternalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D50045A1B3EF91600A54B36 /* SwifternalizationTests.swift */; };
Expand Down Expand Up @@ -134,6 +136,7 @@

/* Begin PBXFileReference section */
6D140F431B56D03D00359143 /* RandomNumbers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomNumbers.swift; sourceTree = "<group>"; };
6D4C4EAE1B6AA48F00B7839A /* LoadedTranslation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadedTranslation.swift; sourceTree = "<group>"; };
6D5004481B3EF91600A54B36 /* Swifternalization.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Swifternalization.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6D50044C1B3EF91600A54B36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6D50044D1B3EF91600A54B36 /* Swifternalization.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Swifternalization.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -473,6 +476,7 @@
6DB3CC671B5EBDA600A1220F /* SharedExpressionLoader.swift */,
6DB3CC691B5EBDA600A1220F /* JSONFileLoader.swift */,
6DB3CC731B5EBDA600A1220F /* TranslationsLoader.swift */,
6D4C4EAE1B6AA48F00B7839A /* LoadedTranslation.swift */,
);
name = Loaders;
sourceTree = "<group>";
Expand Down Expand Up @@ -654,6 +658,7 @@
6DB3CC761B5EBDA600A1220F /* ExpressionRepresentationType.swift in Sources */,
6DB3CC7C1B5EBDA600A1220F /* Processable.swift in Sources */,
6DB3CC751B5EBDA600A1220F /* CountryCode.swift in Sources */,
6D4C4EAF1B6AA48F00B7839A /* LoadedTranslation.swift in Sources */,
6D5BA6001B6526F0000D7E49 /* SharedExpression.swift in Sources */,
6D62829F1B3F1FA000E65FCD /* InequalityExpressionParser.swift in Sources */,
6D6282A51B3F24A800E65FCD /* ExpressionParser.swift in Sources */,
Expand Down Expand Up @@ -708,6 +713,7 @@
6D5BA5F21B6517FE000D7E49 /* TranslationType.swift in Sources */,
6DBB6C6A1B40412D002F39A3 /* InternalPattern.swift in Sources */,
6DD3B9411B5ED38B00C79EAC /* JSONFileLoaderTests.swift in Sources */,
6D4C4EB01B6AA54800B7839A /* LoadedTranslation.swift in Sources */,
6DB3CC911B5EC29E00A1220F /* ExpressionType.swift in Sources */,
6D6282941B3F052B00E65FCD /* TranslatablePairTests.swift in Sources */,
6DBB6C941B40769F002F39A3 /* SharedPolishExpression.swift in Sources */,
Expand Down
76 changes: 76 additions & 0 deletions Swifternalization/LoadedTranslation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// LoadedTranslation.swift
// Swifternalization
//
// Created by Tomasz Szulc on 30/07/15.
// Copyright (c) 2015 Tomasz Szulc. All rights reserved.
//

import Foundation

/**
Specifies few types of available translation type.
*/
enum LoadedTranslationType {
/**
Simple key-value pair.

"welcome": "welcome"
*/
case Simple

/**
Pair where value is a dictionary with expressions.

"cars": {
"one": "1 car",
"ie:x=2": "2 cars",
"more": "%d cars"
}
*/
case WithExpressions

/**
Pair where value is a dictionary with length variations.

"forgot-password": {
"@100": "Forgot Password? Help.",
"@200": "Forgot Password? Get password Help.",
"@300": "Forgotten Your Password? Get password Help."
}
*/
case WithLengthVariations

/**
Pair where value is dictionary that contains dictionary with expression and
length variations.

"car-sentence": {
"one": {
"@100": "one car",
"@200": "just one car",
"@300": "you've got just one car"
},

"more": {
"@100": "%d cars",
"@300": "you've got %d cars"
}
}
*/
case WithExpressionsAndLengthVariations

/// Not supported type.
case NotSupported
}

/**
Struct that represents loaded translation.
*/
struct LoadedTranslation {
/// A type of translation.
let type: LoadedTranslationType

/// A content of translation just loaded from a file.
let content: Dictionary<String, AnyObject>
}
108 changes: 29 additions & 79 deletions Swifternalization/TranslationsLoader.swift
Original file line number Diff line number Diff line change
@@ -1,103 +1,53 @@
import Foundation

/**
Represents translations loader.
Class is looking for json file named as country code.
It parse such file.
/**
Class that loads translations from a file.
*/
final class TranslationsLoader: JSONFileLoader {

typealias DictWithStrings = Dictionary<String, String>
typealias DictWithDicts = Dictionary<String, DictWithStrings>

enum ElementType {
case NotSupported
case TranslationWithLengthVariations
case TranslationWithLoadedExpressions
case TranslationWithLengthVariationsAndLoadedExpressions
}

/**
Method loads a file and parses it.
Loads content from file with name equal to passed country code in a bundle.

:params: countryCode A country code.

:return: translations parsed from the file.
:params: bundle A bundle when file is placed.
:return: `LoadedTranslation` objects from specified file.
*/
class func loadTranslations(countryCode: CountryCode, bundle: NSBundle = NSBundle.mainBundle()) -> [TranslationType] {
var loadedTranslations = [TranslationType]()
let json = self.load(countryCode, bundle: bundle)
if json == nil { return [TranslationType]() }

for (translationKey, value) in json! {
if let translationValue = value as? String {
loadedTranslations.append(TranslationSimple(key: translationKey, value: translationValue))
} else {
let dictionary = value as! JSONDictionary
switch detectElementType(dictionary) {
case .TranslationWithLengthVariations:
let variations = parseLengthVariations(dictionary as! DictWithStrings)
loadedTranslations.append(TranslationLengthVariation(key: translationKey, variations: variations))

case .TranslationWithLoadedExpressions:
let expressions = parseExpressions(dictionary as! DictWithStrings)
loadedTranslations.append(ProcessableTranslationExpression(key: translationKey, loadedExpressions: expressions))

case .TranslationWithLengthVariationsAndLoadedExpressions:
var expressions = [ProcessableLengthVariationExpression]()
for (expressionIdentifier, lengthVariationsDict) in dictionary as! DictWithDicts {
let variations = parseLengthVariations(lengthVariationsDict)
expressions.append(ProcessableLengthVariationExpression(identifier: expressionIdentifier, variations: variations))
class func loadTranslations(countryCode: CountryCode, bundle: NSBundle = NSBundle.mainBundle()) -> [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, content: [key: value]))
} else {
let dictionary = value as! JSONDictionary
let type = detectElementType(dictionary)
if type != .NotSupported {
loadedTranslations.append(LoadedTranslation(type: type, content: dictionary))
}
loadedTranslations.append(ProcessableTranslationExpressionLengthVariationExpression(key: translationKey, expressions: expressions))

case .NotSupported:
// Do nothing
continue
}
}
}

return loadedTranslations
}

private class func parseLengthVariations(dict: DictWithStrings) -> [LengthVariation] {
var variations = [LengthVariation]()
for (key, translationValue) in dict {
let numberValue = parseNumberFromLengthVariation(key)
variations.append(LengthVariation(length: numberValue, value: translationValue))
}
return variations
}

private class func parseExpressions(dict: DictWithStrings) -> [ProcessableExpressionSimple] {
var expressions = [ProcessableExpressionSimple]()
for (expressionKey, translationValue) in dict {
expressions.append(ProcessableExpressionSimple(identifier: expressionKey, value: translationValue))
}
return expressions
}
/**
Analyzes passed dictionary and checks its content to match it to some translation type.

private class func detectElementType(element: JSONDictionary) -> ElementType {

if element is DictWithStrings {
if let key = element.keys.first {
let toIndex = advance(key.startIndex, 1)
let firstCharacter = key.substringToIndex(toIndex)
if firstCharacter == "@" {
return .TranslationWithLengthVariations
} else {
return .TranslationWithLoadedExpressions
}
}
:params: element A dictionary that will be analyzed.
:returns: translation type of a dictionary.
*/
private class func detectElementType(element: JSONDictionary) -> LoadedTranslationType {
typealias DictWithStrings = Dictionary<String, String>
typealias DictWithDicts = Dictionary<String, DictWithStrings>

if element is DictWithStrings, let key = element.keys.first {
let toIndex = advance(key.startIndex, 1)
return key.substringToIndex(toIndex) == "@" ? .WithLengthVariations : .WithExpressions
} else if element is DictWithDicts {
return .TranslationWithLengthVariationsAndLoadedExpressions
return .WithExpressionsAndLengthVariations
}

return .NotSupported
}

private class func parseNumberFromLengthVariation(string: String) -> Int {
return (Regex.matchInString(string, pattern: "@(\\d+)", capturingGroupIdx: 1)! as NSString).integerValue
}
}

0 comments on commit 78e15bb

Please sign in to comment.