Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Sources/HTMLKit/Framework/Localization/Localization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ public class Localization {
}
}

/// Indicates whether the localization is properly configured
internal var isConfigured: Bool {

if self.tables != nil && self.locale != nil {
return true
}

return false
}

/// The translations tables
internal var tables: [Locale: [TranslationTable]]?

Expand Down
7 changes: 6 additions & 1 deletion Sources/HTMLKit/Framework/Rendering/Renderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,12 @@ public final class Renderer {
private func render(localized string: LocalizedString) throws -> String {

guard let localization = self.localization else {
// Bail early with the fallback since the localization isn't set up
// Bail early with the fallback since the localization is not in use
return string.key.literal
}

if !localization.isConfigured {
// Bail early, since the localization is not properly configured
return string.key.literal
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/HTMLKitVapor/Extensions/Vapor+HTMLKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ extension Request {
public var htmlkit: ViewRenderer {

if let acceptLanguage = self.acceptLanguage {
self.application.htmlkit.localization.set(locale: acceptLanguage)
self.application.htmlkit.environment.locale = HTMLKit.Locale(tag: acceptLanguage)
}

return .init(eventLoop: self.eventLoop, configuration: self.application.htmlkit.configuration, logger: self.logger)
Expand Down
148 changes: 144 additions & 4 deletions Tests/HTMLKitVaporTests/ProviderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,22 +155,162 @@ final class ProviderTests: XCTestCase {
)
}
}


/// Tests the setup of localization through Vapor
func testLocalizationIntegration() throws {

let currentFile = URL(fileURLWithPath: #file).deletingLastPathComponent()
guard let source = Bundle.module.url(forResource: "Localization", withExtension: nil) else {
return
}

let app = Application(.testing)

defer { app.shutdown() }

app.htmlkit.localization.set(source: source)
app.htmlkit.localization.set(locale: "fr")

app.get("test") { request async throws -> Vapor.View in

return try await request.htmlkit.render(TestPage.ChildView())
}

try app.test(.GET, "test") { response in
XCTAssertEqual(response.status, .ok)
XCTAssertEqual(response.body.string,
"""
<!DOCTYPE html>\
<html>\
<head>\
<title>TestPage</title>\
</head>\
<body>\
<p>Bonjour le monde</p>\
</body>\
</html>
"""
)
}
}

/// Tests the behavior when localization is not properly configured
///
/// Localization is considered improperly configured when one or both of the essential factors are missing.
/// In such case the renderer is expected to skip the localization and directly return the fallback string literal.
func testLocalizationFallback() throws {

let app = Application(.testing)

defer { app.shutdown() }

app.get("test") { request async throws -> Vapor.View in

return try await request.htmlkit.render(TestPage.ChildView())
}

try app.test(.GET, "test") { response in
XCTAssertEqual(response.status, .ok)
XCTAssertEqual(response.body.string,
"""
<!DOCTYPE html>\
<html>\
<head>\
<title>TestPage</title>\
</head>\
<body>\
<p>hello.world</p>\
</body>\
</html>
"""
)
}
}

/// Tests the error reporting to Vapor for issues that may occur during localization
///
/// The error is expected to be classified as an internal server error and includes a error message.
func testLocalizationErrorReporting() throws {

struct UnknownTableView: HTMLKit.View {

var body: HTMLKit.Content {
Paragraph("hello.world", tableName: "unknown.table")
}
}

struct UnknownTagView: HTMLKit.View {

var body: HTMLKit.Content {
Division {
Heading1("greeting.world")
.environment(key: \.locale)
}
.environment(key: \.locale, value: Locale(tag: "unknown.tag"))
}
}

let currentDirectory = currentFile.appendingPathComponent("Localization")
guard let source = Bundle.module.url(forResource: "Localization", withExtension: nil) else {
return
}

let app = Application(.testing)

defer { app.shutdown() }

app.htmlkit.localization.set(source: currentDirectory)
app.htmlkit.localization.set(source: source)
app.htmlkit.localization.set(locale: "fr")

app.get("unknowntable") { request async throws -> Vapor.View in

return try await request.htmlkit.render(UnknownTableView())
}

app.get("unknowntag") { request async throws -> Vapor.View in

return try await request.htmlkit.render(UnknownTagView())
}

try app.test(.GET, "unknowntable") { response in

XCTAssertEqual(response.status, .internalServerError)

let abort = try response.content.decode(AbortResponse.self)

XCTAssertEqual(abort.reason, "Unable to find translation table 'unknown.table' for the locale 'fr'.")
}

try app.test(.GET, "unknowntag") { response in

XCTAssertEqual(response.status, .internalServerError)

let abort = try response.content.decode(AbortResponse.self)

XCTAssertEqual(abort.reason, "Unable to find a translation table for the locale 'unknown.tag'.")
}
}

/// Tests the localization behavior based on the accept language of the client
///
/// The environment locale is expected to be changed according to the language given by the provider.
/// The renderer is expected to localize correctly the content based on the updated environment locale.
func testLocalizationByAcceptingHeaders() throws {

guard let source = Bundle.module.url(forResource: "Localization", withExtension: nil) else {
return
}

let app = Application(.testing)

defer { app.shutdown() }

app.htmlkit.localization.set(source: source)
app.htmlkit.localization.set(locale: "en-GB")

app.get("test") { request async throws -> Vapor.View in

// Overwrite the accept language header to simulate a different language
request.headers.replaceOrAdd(name: "accept-language", value: "fr")

return try await request.htmlkit.render(TestPage.ChildView())
}

Expand Down
6 changes: 6 additions & 0 deletions Tests/HTMLKitVaporTests/Utilities/AbortResponse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Vapor

struct AbortResponse: Vapor.Content {

var reason: String
}
Loading