From dfbd8929752638a92964d9c32d4982546bb9f64f Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Fri, 19 May 2023 18:50:38 +0200 Subject: [PATCH] Implemented the "localise(for: in: value: table:)" function for the String+Localisation extension of the Core library. --- .../Core/Extensions/String+Localisation.swift | 45 ++++ .../Extensions/String+LocalisationTests.swift | 196 ++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 Sources/Core/Extensions/String+Localisation.swift create mode 100644 Tests/Core/Cases/Extensions/String+LocalisationTests.swift diff --git a/Sources/Core/Extensions/String+Localisation.swift b/Sources/Core/Extensions/String+Localisation.swift new file mode 100644 index 0000000..bb6472d --- /dev/null +++ b/Sources/Core/Extensions/String+Localisation.swift @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftLibs open source project +// +// Copyright (c) 2023 Röck+Cöde VoF. and the SwiftLibs project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftLibs project authors +// +//===----------------------------------------------------------------------===// + +import Foundation + +public extension String { + + // MARK: Functions + + /// Localise a string based on a given language code or identifier in an specific bundle. + /// - Parameters: + /// - languageCode: A string that represent a language code or identifier. + /// - bundle: A bundle in which to retrieve a localisation bundle. + /// - value: A default value to return if key is nil or if a localized string for key can’t be found in the table. + /// - table: The receiver’s string table to search. In case of nil or an empty string, the method attempts to use the table in `Localizable.strings`. + /// - Returns: A localized version of the string in case it is found. Otherwise, it returns the original string or a default string, if provided. + func localise( + for languageCode: String, + in bundle: Bundle, + value: String? = nil, + table: String? = nil + ) -> String { + do { + return try bundle + .localisation(for: languageCode) + .localizedString( + forKey: self, + value: value, + table: table + ) + } catch { + return value ?? self + } + } + +} diff --git a/Tests/Core/Cases/Extensions/String+LocalisationTests.swift b/Tests/Core/Cases/Extensions/String+LocalisationTests.swift new file mode 100644 index 0000000..dd6e809 --- /dev/null +++ b/Tests/Core/Cases/Extensions/String+LocalisationTests.swift @@ -0,0 +1,196 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftLibs open source project +// +// Copyright (c) 2023 Röck+Cöde VoF. and the SwiftLibs project authors +// Licensed under the EUPL 1.2 or later. +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftLibs project authors +// +//===----------------------------------------------------------------------===// + +import Core +import Foundation +import XCTest + +final class String_LocalisationTests: XCTestCase { + + // MARK: Properties + + private var languageCode: String! + private var stringToLocalise: String! + private var localisedString: String! + + private var defaultValue: String? + + // MARK: Tests + + func test_localise_definedKey_inDefinedLocalisationBundle() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.someLocalisableString + + // WHEN + localisedString = stringToLocalise.localise(for: languageCode, in: .module) + + // THEN + XCTAssertEqual(localisedString, .Result.Localisation.someLocalisableString) + } + + func test_localise_definedKey_inDefinedLocalisationBundle_withDefaultValue() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.otherLocalisableString + defaultValue = "Some default value goes here..." + + // WHEN + localisedString = stringToLocalise.localise( + for: languageCode, + in: .module, + value: defaultValue + ) + + // THEN + XCTAssertEqual(localisedString, .Result.Localisation.otherLocalisableString) + } + + func test_localise_definedKey_inDefinedLocalisationBundle_withDefinedTable() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.someLocalisableString + + // WHEN + localisedString = stringToLocalise.localise( + for: languageCode, + in: .module, + table: "Some table name goes in here..." + ) + + // THEN + XCTAssertEqual(localisedString, stringToLocalise) + } + + func test_localise_definedKey_inDefinedLocalisationBundle_withDefauledValue_andDefinedTable() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.otherLocalisableString + defaultValue = "Some default value goes in here..." + + // WHEN + localisedString = stringToLocalise.localise( + for: languageCode, + in: .module, + value: defaultValue, + table: "Some table name goes in here..." + ) + + // THEN + XCTAssertEqual(localisedString, defaultValue) + } + + func test_localise_definedKey_inNotDefinedLocalisationBundle() { + // GIVEN + languageCode = "nl" + stringToLocalise = .Seed.Localisation.someLocalisableString + + // WHEN + localisedString = stringToLocalise.localise(for: languageCode, in: .module) + + // THEN + XCTAssertEqual(localisedString, stringToLocalise) + } + + func test_localise_definedKey_inNotDefinedLocalisationBundle_withDefaultValue() { + // GIVEN + languageCode = "nl" + stringToLocalise = .Seed.Localisation.otherLocalisableString + defaultValue = "Some default value goes in here..." + + // WHEN + localisedString = stringToLocalise.localise( + for: languageCode, + in: .module, + value: defaultValue + ) + + // THEN + XCTAssertEqual(localisedString, defaultValue) + } + + func test_localise_notDefinedKey_inDefinedLocalisationBundle() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.notLocalisableString + + // WHEN + localisedString = stringToLocalise.localise(for: languageCode, in: .module) + + // THEN + XCTAssertEqual(localisedString, stringToLocalise) + } + + func test_localise_notDefinedKey_inDefinedLocalisationBundle_withDefaultValue() { + languageCode = "en" + stringToLocalise = .Seed.Localisation.notLocalisableString + defaultValue = "Some default value goes here..." + + // WHEN + localisedString = stringToLocalise.localise( + for: languageCode, + in: .module, + value: defaultValue + ) + + // THEN + XCTAssertEqual(localisedString, defaultValue) + } + + func test_localise_inDifferentBundle() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.someLocalisableString + + // WHEN + localisedString = stringToLocalise.localise(for: languageCode, in: .main) + + // THEN + XCTAssertEqual(localisedString, stringToLocalise) + } + + func test_localise_inDifferentBundle_withDefaultValue() { + // GIVEN + languageCode = "en" + stringToLocalise = .Seed.Localisation.otherLocalisableString + defaultValue = "Some default value goes here..." + + // WHEN + localisedString = stringToLocalise.localise( + for: languageCode, + in: .main, + value: defaultValue + ) + + // THEN + XCTAssertEqual(localisedString, defaultValue) + } + +} + // MARK: - String+Seed + +private extension String.Seed { + enum Localisation { + static let someLocalisableString = "test.core.bundle.some-localisable-string" + static let otherLocalisableString = "test.core.bundle.other-localisable-string" + static let notLocalisableString = "test.core.bundle.non-localisable-string" + } +} + +// MARK: - String+Result + +private extension String.Result { + enum Localisation { + static let someLocalisableString = "Some localisable string to use for testing purposes." + static let otherLocalisableString = "Other localisable string to use for testing purposes." + } +}