import Foundation import Testing @testable import Cable /// Guards the string tables themselves. A malformed `.strings` file still parses — a value /// spanning several lines silently swallows the entries behind it — and the app then renders /// raw keys like "tab.overview". These tests fail instead. struct LocalizationIntegrityTests { private static let locales = ["Base", "de", "es", "fr", "nl"] /// Keys rendered without a `defaultValue:`, so a missing entry is visible to users. private static let keysWithoutFallback = [ "tab.overview", "tab.components", "tab.batteries", "tab.chargers", ] private func table(for locale: String) throws -> [String: String] { let bundle = Bundle.main guard let url = bundle.url( forResource: "Localizable", withExtension: "strings", subdirectory: nil, localization: locale ) else { throw LocalizationTestError.tableMissing(locale) } let data = try Data(contentsOf: url) let parsed = try PropertyListSerialization.propertyList(from: data, format: nil) guard let table = parsed as? [String: String] else { throw LocalizationTestError.tableMalformed(locale) } return table } @Test func everyLocaleProvidesTheKeysThatHaveNoFallback() async throws { for locale in Self.locales { let table = try table(for: locale) for key in Self.keysWithoutFallback { #expect(table[key] != nil, "\(locale): missing \(key), the UI would show the raw key") #expect( table[key] != key, "\(locale): \(key) is not translated" ) } } } @Test func noValueSpansMultipleLines() async throws { for locale in Self.locales { let table = try table(for: locale) for (key, value) in table { #expect( !value.contains("\n"), "\(locale): \(key) contains a real newline; removing such an entry line by line corrupts the table" ) } } } @Test func retiredProKeysAreGone() async throws { for locale in Self.locales { let table = try table(for: locale) let leftovers = table.keys.filter { $0.hasPrefix("cable.pro.") } #expect(leftovers.isEmpty, "\(locale): dead PRO keys \(leftovers.sorted())") } } } private enum LocalizationTestError: Error { case tableMissing(String) case tableMalformed(String) }