diff --git a/Package.swift b/Package.swift index 22400ca..7343685 100644 --- a/Package.swift +++ b/Package.swift @@ -171,7 +171,7 @@ let package = Package( ), .testTarget( name: "TranslateKitTests", - dependencies: ["TranslateKit", "TranslateKitFinance"] + dependencies: ["TranslateKit", "TranslateKitBusiness", "TranslateKitFinance"] ), ] ) diff --git a/Sources/TranslateKitBusiness/Localizable.xcstrings b/Sources/TranslateKitBusiness/Localizable.xcstrings index 72d426b..4006d09 100644 --- a/Sources/TranslateKitBusiness/Localizable.xcstrings +++ b/Sources/TranslateKitBusiness/Localizable.xcstrings @@ -15312,6 +15312,257 @@ } } }, + "TK.Label.ordered" : { + "comment" : "Use as a status showing that something has been ordered", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم الطلب" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поръчано" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encarregat" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objednáno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bestilt" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bestellt" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Παραγγέλθηκε" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordered" + } + }, + "en-AU" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordered" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordered" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pedido" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pedido" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilattu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commandé" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commandé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוזמן" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "ऑर्डर किया गया" + } + }, + "hr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naručeno" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Megrendelve" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dipesan" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ordinato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "注文済み" + } + }, + "kk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тапсырыс берілді" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "주문됨" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dipesan" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bestilt" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Besteld" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zamówiono" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pedido feito" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encomendado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comandat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заказано" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Objednané" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beställd" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "สั่งซื้อแล้ว" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sipariş Verildi" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Замовлено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã đặt hàng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已下单" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "已下單" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "已下單" + } + } + } + }, "TK.Label.organization" : { "comment" : "Use for company or group organization details", "localizations" : { @@ -16818,6 +17069,257 @@ } } }, + "TK.Label.validated" : { + "comment" : "Use as a status showing that something has been checked and approved", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تم التحقق" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверено" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validat" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ověřeno" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valideret" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validiert" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επικυρώθηκε" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validated" + } + }, + "en-AU" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validated" + } + }, + "en-GB" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validated" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validado" + } + }, + "es-419" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validado" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tarkistettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validé" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validé" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אומת" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "सत्यापित" + } + }, + "hr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Provjereno" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ellenőrizve" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "Divalidasi" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Convalidato" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "検証済み" + } + }, + "kk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Тексерілді" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "검증됨" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disahkan" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validert" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gevalideerd" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zweryfikowano" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validado" + } + }, + "pt-PT" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validado" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Проверено" + } + }, + "sk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Overené" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Validerad" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "ตรวจสอบแล้ว" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Doğrulandı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перевірено" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã xác thực" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "已验证" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "已驗證" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "已驗證" + } + } + } + }, "TK.Label.vendor" : { "comment" : "Use for referring to a supplier or service provider", "localizations" : { diff --git a/Sources/TranslateKitBusiness/TK+Label.swift b/Sources/TranslateKitBusiness/TK+Label.swift index 5b2009d..f731e53 100644 --- a/Sources/TranslateKitBusiness/TK+Label.swift +++ b/Sources/TranslateKitBusiness/TK+Label.swift @@ -187,6 +187,16 @@ extension TK.Label { String(localized: "TK.Label.meeting", defaultValue: "Meeting", bundle: .module, comment: "Use for a scheduled business meeting or appointment") } + /// "Ordered" - Use as a status showing that something has been ordered + public static var ordered: String { + String( + localized: "TK.Label.ordered", + defaultValue: "Ordered", + bundle: .module, + comment: "Use as a status showing that something has been ordered" + ) + } + /// "Organization" - Use for company or group organization details public static var organization: String { String( @@ -247,6 +257,16 @@ extension TK.Label { ) } + /// "Validated" - Use as a status showing that something has been checked and approved + public static var validated: String { + String( + localized: "TK.Label.validated", + defaultValue: "Validated", + bundle: .module, + comment: "Use as a status showing that something has been checked and approved" + ) + } + /// "Vendor" - Use for referring to a supplier or service provider public static var vendor: String { String(localized: "TK.Label.vendor", defaultValue: "Vendor", bundle: .module, comment: "Use for referring to a supplier or service provider") diff --git a/Tests/TranslateKitTests/BusinessStatusLabelTests.swift b/Tests/TranslateKitTests/BusinessStatusLabelTests.swift new file mode 100644 index 0000000..173e400 --- /dev/null +++ b/Tests/TranslateKitTests/BusinessStatusLabelTests.swift @@ -0,0 +1,18 @@ +import Testing + +import TranslateKitBusiness + +@Suite("Business status labels") +struct BusinessStatusLabelTests { + @Test func statusLabelsUseTheirEnglishSource() { + #expect(TK.Label.ordered == "Ordered") + #expect(TK.Label.validated == "Validated") + } + + @Test(arguments: ["TK.Label.ordered", "TK.Label.validated"]) + func statusLabelIsTranslatedIntoEveryLanguage(key: String) throws { + let bundle = try CompiledCatalog.bundle(for: "TranslateKitBusiness") + #expect(try CompiledCatalog.languages(in: bundle).count >= 40) + #expect(try CompiledCatalog.languagesMissing(key, inTarget: "TranslateKitBusiness") == []) + } +} diff --git a/Tests/TranslateKitTests/CompiledCatalog.swift b/Tests/TranslateKitTests/CompiledCatalog.swift new file mode 100644 index 0000000..061502b --- /dev/null +++ b/Tests/TranslateKitTests/CompiledCatalog.swift @@ -0,0 +1,41 @@ +import Foundation + +/// Reads the compiled String Catalog of a target the way an app sees it at runtime: one `.lproj` per language. +enum CompiledCatalog { + /// Placeholder returned for a key that a language does not contain. + static let missing = "__missing__" + + /// Locates the resource bundle SwiftPM places next to the test bundle for the given target. + static func bundle(for target: String) throws -> Bundle { + let bundleName = "TranslateKit_\(target).bundle" + let searchDirectories = ([Bundle(for: Token.self)] + Bundle.allBundles + [Bundle.main]).map { $0.bundleURL.deletingLastPathComponent() } + + for directory in searchDirectories { + if let bundle = Bundle(url: directory.appendingPathComponent(bundleName)) { return bundle } + } + throw BundleNotFound(name: bundleName) + } + + /// Every language the compiled catalog ships, read from its `.lproj` folders. + static func languages(in bundle: Bundle) throws -> [String] { + try FileManager.default.contentsOfDirectory(atPath: bundle.resourcePath ?? bundle.bundlePath) + .filter { $0.hasSuffix(".lproj") && $0 != "Base.lproj" } + .map { String($0.dropLast(".lproj".count)) } + .sorted() + } + + /// The value of `key` in one language, or `missing` when that language lacks it. + static func value(of key: String, in bundle: Bundle, language: String) -> String { + guard let path = bundle.path(forResource: language, ofType: "lproj"), let languageBundle = Bundle(path: path) else { return missing } + return languageBundle.localizedString(forKey: key, value: missing, table: nil) + } + + /// Languages of the target's catalog in which `key` has no value. + static func languagesMissing(_ key: String, inTarget target: String) throws -> [String] { + let bundle = try bundle(for: target) + return try languages(in: bundle).filter { value(of: key, in: bundle, language: $0) == missing } + } + + private struct BundleNotFound: Error { let name: String } + private final class Token {} +}