mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-09-19 05:00:48 +00:00
Add 26 code-referenced keys to the catalog + a test that closes the gap (#1654)
LocalizationCoverageTests validated the catalog but never the code: a String(localized:) whose key is missing from every catalog compiles fine and silently ships its English defaultValue to all 29 non-source locales. That blind spot let 26 keys go untranslated while CI stayed green: the entire notices/board composer (10), the private-media encryption warnings and delivery-failure reasons (9), both courier-header strings, two delivery states, two media failure reasons, and the [? people] channel row. - All 26 keys added with full 30-locale coverage. - New everyCodeReferencedKeyExistsInACatalog scans the source for literal String(localized:) keys and fails on any key absent from both catalogs. (The audit's original count of 17 was itself an undercount — its scan missed multi-line String( localized: calls; the test's regex does not.) Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
dba67c1466
commit
eca147191c
File diff suppressed because it is too large
Load Diff
@ -69,4 +69,45 @@ struct LocalizationCoverageTests {
|
|||||||
let missing = main.allLocales.subtracting(shareExt.allLocales).sorted()
|
let missing = main.allLocales.subtracting(shareExt.allLocales).sorted()
|
||||||
#expect(missing.isEmpty, "share extension is missing locales: \(missing.joined(separator: ", "))")
|
#expect(missing.isEmpty, "share extension is missing locales: \(missing.joined(separator: ", "))")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The catalog tests above validate the CATALOG; this one validates the
|
||||||
|
/// CODE. A `String(localized:)` whose key is absent from every catalog
|
||||||
|
/// compiles and runs fine — it just silently ships its English
|
||||||
|
/// `defaultValue` to all 29 non-source locales. That blind spot let the
|
||||||
|
/// entire notices/board composer (10 keys), two delivery states, and two
|
||||||
|
/// media-failure reasons go untranslated while the coverage tests stayed
|
||||||
|
/// green. Interpolated keys can't be checked statically and are skipped;
|
||||||
|
/// every literal key must resolve.
|
||||||
|
@Test func everyCodeReferencedKeyExistsInACatalog() throws {
|
||||||
|
let main = try Self.loadCatalog("bitchat/Localizable.xcstrings")
|
||||||
|
let shareExt = try Self.loadCatalog("bitchatShareExtension/Localization/Localizable.xcstrings")
|
||||||
|
let knownKeys = Set(main.coverage.keys).union(shareExt.coverage.keys)
|
||||||
|
|
||||||
|
let pattern = try NSRegularExpression(pattern: #"String\(\s*localized:\s*"([^"\\]+)""#)
|
||||||
|
var missing: [String] = []
|
||||||
|
|
||||||
|
for sourceRoot in ["bitchat", "bitchatShareExtension"] {
|
||||||
|
let rootURL = Self.repoRoot.appendingPathComponent(sourceRoot)
|
||||||
|
let enumerator = try #require(FileManager.default.enumerator(
|
||||||
|
at: rootURL,
|
||||||
|
includingPropertiesForKeys: nil
|
||||||
|
))
|
||||||
|
for case let fileURL as URL in enumerator where fileURL.pathExtension == "swift" {
|
||||||
|
let source = try String(contentsOf: fileURL, encoding: .utf8)
|
||||||
|
let range = NSRange(source.startIndex..., in: source)
|
||||||
|
pattern.enumerateMatches(in: source, range: range) { match, _, _ in
|
||||||
|
guard let match, let keyRange = Range(match.range(at: 1), in: source) else { return }
|
||||||
|
let key = String(source[keyRange])
|
||||||
|
if !knownKeys.contains(key) {
|
||||||
|
missing.append("\(key) (\(fileURL.lastPathComponent))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#expect(
|
||||||
|
missing.isEmpty,
|
||||||
|
"keys referenced in code but absent from every catalog — these ship English to all non-source locales: \(missing.sorted().joined(separator: ", "))"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user