From 70ebbcdd2b3e87af3b5b11d51a8331ebaeff5fde Mon Sep 17 00:00:00 2001 From: Kat Butler Date: Fri, 15 Nov 2019 15:01:21 -0500 Subject: [PATCH] Implemented t filter --- Sources/Liquid/Context.swift | 4 ++- Sources/Liquid/Filter+Standard.swift | 45 ++++++++++++++++++++++++++++ Sources/Liquid/FilterContext.swift | 6 ++-- Sources/Liquid/Template.swift | 10 +++++-- Tests/LiquidTests/FilterTests.swift | 15 ++++++++++ 5 files changed, 74 insertions(+), 6 deletions(-) diff --git a/Sources/Liquid/Context.swift b/Sources/Liquid/Context.swift index f13634d..6d0e718 100644 --- a/Sources/Liquid/Context.swift +++ b/Sources/Liquid/Context.swift @@ -47,17 +47,19 @@ public final class Context { private var registers: [String: Any] = [:] public let filters: [String: FilterFunc] + public let translations: [String: String]? public let tags: [String: TagBuilder] public let environment: Environment public let encoder: Encoder public let fileSystem: FileSystem - init(fileSystem: FileSystem, values: [String: Value] = [:], environment: Environment = Environment(), tags: [String: TagBuilder], filters: [String: FilterFunc] = [:], encoder: Encoder) { + init(fileSystem: FileSystem, values: [String: Value] = [:], environment: Environment = Environment(), tags: [String: TagBuilder], filters: [String: FilterFunc] = [:], translations: [String: String]? = nil, encoder: Encoder) { self.fileSystem = fileSystem self.scopes = [Scope(values: values)] self.environment = environment self.tags = tags self.filters = filters + self.translations = translations self.encoder = encoder } diff --git a/Sources/Liquid/Filter+Standard.swift b/Sources/Liquid/Filter+Standard.swift index 365963a..f1bb8b9 100644 --- a/Sources/Liquid/Filter+Standard.swift +++ b/Sources/Liquid/Filter+Standard.swift @@ -54,6 +54,7 @@ enum Filters { template.registerFilter(name: "sort", filter: sortFilter) template.registerFilter(name: "sort_natural", filter: sortNaturalFilter) template.registerFilter(name: "date", filter: dateFilter) + template.registerFilter(name: "t", filter: tFilter) } static func appendFilter(value: Value, args: [Value], kwargs: [String: Value], context: FilterContext) throws -> Value { @@ -512,6 +513,25 @@ enum Filters { } return Value(formatter.string(from: date)) } + + static func tFilter(value: Value, args: [Value], kwargs: [String: Value], context: FilterContext) throws -> Value { + guard let translations = context.translations else { + throw RuntimeError.reason("No translations dictionary available") + } + + guard let val = translations[value.toString()] else { + throw RuntimeError.reason("Invalid translation key") + } + + if kwargs.isEmpty && args.isEmpty { + return Value(val) + } + if !kwargs.isEmpty { + return Value(val.replaceWithValues(kwargs)) + } + + throw RuntimeError.unimplemented + } private static func convertValueToDate(_ value: Value, context: FilterContext) -> Date? { if value.isInteger { @@ -530,3 +550,28 @@ enum Filters { return context.encoder.dateEncodingStrategry.date(from: string) } } + +private extension String { + func replaceWithValues(_ values: [String: Value]) -> String { + let scanner = Scanner(string: self) + scanner.charactersToBeSkipped = CharacterSet.newlines + + var result: String = "" + var temp: NSString? + + while !scanner.isAtEnd { + scanner.scanUpTo("%{", into: &temp) + scanner.scanCharacters(from: CharacterSet.init(charactersIn: "%{"), into: nil) + + result = "\(result)\(temp ?? "")" + + scanner.scanUpTo("}", into: &temp) + scanner.scanCharacters(from: CharacterSet.init(charactersIn: "}"), into: nil) + + let resultValue = values[String(temp ?? "")]?.toString() ?? "" + result = "\(result)\(resultValue)" + } + + return result + } +} diff --git a/Sources/Liquid/FilterContext.swift b/Sources/Liquid/FilterContext.swift index 93c6c79..6500160 100644 --- a/Sources/Liquid/FilterContext.swift +++ b/Sources/Liquid/FilterContext.swift @@ -9,12 +9,14 @@ import Foundation public struct FilterContext { public let encoder: Encoder + public let translations: [String: String]? init(context: Context) { - self.init(encoder: context.encoder) + self.init(encoder: context.encoder, translations: context.translations) } - init(encoder: Encoder) { + init(encoder: Encoder, translations: [String: String]? = nil) { self.encoder = encoder + self.translations = translations } } diff --git a/Sources/Liquid/Template.swift b/Sources/Liquid/Template.swift index 9a43c31..ce878ef 100644 --- a/Sources/Liquid/Template.swift +++ b/Sources/Liquid/Template.swift @@ -22,7 +22,9 @@ public final class Template { private let fileSystem: FileSystem - public convenience init(sourceURL: URL, encoder: Encoder = Encoder(), environment: Environment = Environment()) throws { + private let translations: [String: String]? + + public convenience init(sourceURL: URL, encoder: Encoder = Encoder(), environment: Environment = Environment(), translations: [String: String]? = nil) throws { let source = try String(contentsOf: sourceURL) let fileSystem = LocalFileSystem(baseURL: sourceURL.deletingLastPathComponent()) self.init(source: source, fileSystem: fileSystem, encoder: encoder, environment: environment) @@ -43,13 +45,15 @@ public final class Template { self.environment = context.environment self.fileSystem = context.fileSystem self.filters = context.filters + self.translations = context.translations self.tags = context.tags } - private init(source: String, fileSystem: FileSystem, encoder: Encoder, environment: Environment) { + private init(source: String, fileSystem: FileSystem, encoder: Encoder, environment: Environment, translations: [String: String]? = nil) { self.source = source self.fileSystem = fileSystem self.environment = environment + self.translations = translations self.encoder = encoder tags["assign"] = Assign.init @@ -88,7 +92,7 @@ public final class Template { } public func render(values: [String: Value] = [:]) throws -> String { - let context = Context(fileSystem: fileSystem, values: values, environment: environment, tags: tags, filters: filters, encoder: encoder) + let context = Context(fileSystem: fileSystem, values: values, environment: environment, tags: tags, filters: filters, translations: translations, encoder: encoder) return try render(context: context) } diff --git a/Tests/LiquidTests/FilterTests.swift b/Tests/LiquidTests/FilterTests.swift index 66c5446..4a5bb7a 100644 --- a/Tests/LiquidTests/FilterTests.swift +++ b/Tests/LiquidTests/FilterTests.swift @@ -271,6 +271,21 @@ final class FilterTests: XCTestCase { XCTAssertEqual(try Filters.dateFilter(value: Value("1152098955"), args: [Value("%m/%d/%Y")], kwargs: [:], context: FilterContext(encoder: encoder)), Value("07/05/2006")) } + func testTranslate() { + var encoder = Encoder() + encoder.locale = Locale(identifier: "en_US") + + let sampleTranslationDict: [String: String] = [ + "x.files.main.characters": "%{argA} and %{argB}", + "x.files.title": "The X-Files", + ] + + let filterContext = FilterContext(encoder: encoder, translations: sampleTranslationDict) + + XCTAssertEqual(try Filters.tFilter(value: Value("x.files.title"), args: [], kwargs: [:], context: filterContext), Value("The X-Files")) + XCTAssertEqual(try Filters.tFilter(value: Value("x.files.main.characters"), args: [], kwargs: ["argA": Value("Fox Mulder"), "argB": Value("Dana Scully")], context: filterContext), Value("Fox Mulder and Dana Scully")) + } + func testFilterArgs() { let echoFilter: FilterFunc = { (value, args, kwargs, encoder) -> Value in let strArgs = args.map { "\($0)" }.sorted()