iOS/Tests/App/LocalizedStrings.test.swift
Michal Šrůtek 08781c19f0
Remove default options parameter (#2621)
<!-- Thank you for submitting a Pull Request and helping to improve Home
Assistant. Please complete the following sections to help the processing
and review of your changes. Please do not delete anything from this
template. -->

## Summary
<!-- Provide a brief summary of the changes you have made and most
importantly what they aim to achieve -->
This PR removes the `options` parameters which use the default value.

## Screenshots
<!-- If this is a user-facing change not in the frontend, please include
screenshots in light and dark mode. -->

<img width="1014" alt="Screenshot 2024-03-03 at 21 17 55"
src="https://github.com/home-assistant/iOS/assets/35694712/85324caf-c36d-4366-a9f8-9c349192a361">
<img width="1049" alt="Screenshot 2024-03-03 at 21 17 17"
src="https://github.com/home-assistant/iOS/assets/35694712/cebddbe6-8ea4-415e-b12a-0e35f86bf5a3">
<img width="1022" alt="Screenshot 2024-03-03 at 21 16 53"
src="https://github.com/home-assistant/iOS/assets/35694712/ccccb902-9791-42f6-8868-5900093c40ae">
<img width="991" alt="Screenshot 2024-03-03 at 21 16 28"
src="https://github.com/home-assistant/iOS/assets/35694712/f2f33685-a55c-47ed-a555-8cc0fc1561ce">
<img width="1329" alt="Screenshot 2024-03-03 at 21 16 00"
src="https://github.com/home-assistant/iOS/assets/35694712/2dc156da-29d6-44e4-a064-4fbdb79139bd">
<img width="1130" alt="Screenshot 2024-03-03 at 21 14 53"
src="https://github.com/home-assistant/iOS/assets/35694712/628dd2ff-5bba-4ee7-912b-cdbdf73f18b2">
2024-03-04 09:28:53 +01:00

141 lines
4.8 KiB
Swift

import Foundation
@testable import HomeAssistant
@testable import Shared
import XCTest
class LocalizedStrings: XCTestCase {
func testLanguages() throws {
let expressions: [NSRegularExpression] = try [
NSRegularExpression(pattern: "%{1,2}[+0123456789$.luq]*?[sduiefgcCp@]"),
NSRegularExpression(pattern: "\\$\\{[^}]+\\}"),
]
for bundle in [
Bundle(for: AppDelegate.self),
Bundle(for: AppEnvironment.self),
] {
for languageSet in try Self.languageSets(for: bundle) {
try validate(languageSet: languageSet, expressions: expressions)
}
}
}
struct LanguageWithStrings {
let file: URL
let strings: [String: String]
init(url: URL) throws {
self.file = url
self.strings = try XCTUnwrap(NSDictionary(contentsOf: url) as? [String: String])
}
}
struct LanguageSet {
let name: String
let english: LanguageWithStrings
let other: [String: LanguageWithStrings]
}
private static func languageSets(for bundle: Bundle) throws -> [LanguageSet] {
var languages = [String]()
var stringsFiles = [String]()
for case let url as URL in try XCTUnwrap(FileManager.default.enumerator(
at: XCTUnwrap(bundle.resourceURL),
includingPropertiesForKeys: [.isDirectoryKey],
options: [.skipsSubdirectoryDescendants]
)) {
let values = try url.resourceValues(forKeys: [.isDirectoryKey])
let isDirectory = try XCTUnwrap(values.isDirectory)
guard isDirectory, url.pathExtension == "lproj" else {
continue
}
let language = url.deletingPathExtension().lastPathComponent
if language == "en" {
for case let subURL as URL in try XCTUnwrap(FileManager.default.enumerator(
at: url,
includingPropertiesForKeys: []
)) where subURL.pathExtension == "strings" {
stringsFiles.append(subURL.deletingPathExtension().lastPathComponent)
}
} else if language != "Base" {
languages.append(language)
}
}
return try stringsFiles.map { strings in
func value(for language: String) throws -> LanguageWithStrings {
try .init(url: XCTUnwrap(bundle.url(
forResource: strings,
withExtension: "strings",
subdirectory: nil,
localization: language
)))
}
return try LanguageSet(
name: strings,
english: value(for: "en"),
other: Dictionary(uniqueKeysWithValues: languages.map { language in
try (key: language, value: value(for: language))
})
)
}
}
struct MatchSet: Equatable, CustomStringConvertible {
let countedSet: NSCountedSet
var description: String {
guard countedSet.count > 0 else {
return "<no matches>"
}
return countedSet.map { value in
let count = countedSet.count(for: value)
if count == 1 {
return String(describing: value)
} else {
return String(format: "%@ (%d)", String(describing: value), count)
}
}.joined(separator: ", ")
}
}
private func matchSet(for expressions: [NSRegularExpression], in string: String) -> MatchSet {
let matches = expressions.flatMap { expression in
expression.matches(
in: string,
range: NSRange(location: 0, length: string.utf16.count)
)
}
return MatchSet(countedSet: NSCountedSet(array: matches.map { result in
(string as NSString).substring(with: result.range)
}))
}
private func validate(languageSet: LanguageSet, expressions: [NSRegularExpression]) throws {
XCTAssertGreaterThan(languageSet.english.strings.count, 0)
for (key, englishValue) in languageSet.english.strings {
let englishSet = matchSet(for: expressions, in: englishValue)
for (language, languageStrings) in languageSet.other {
guard let languageValue = languageStrings.strings[key] else {
// it is okay for a language to be missing the string if it's new
continue
}
XCTAssertEqual(
matchSet(for: expressions, in: languageValue),
englishSet,
"for language '\(language)' in table '\(languageSet.name)' with key '\(key)'"
)
}
}
}
}