Files
XStats/Packages/XStatsKit/Tests/LocalizationTests/LanguageSupportTests.swift
T

110 lines
6.9 KiB
Swift

// Copyright (C) 2026 ysicing
// SPDX-License-Identifier: AGPL-3.0-or-later
import Foundation
import Testing
@testable import Localization
struct LanguageSupportTests {
@Test func containsExactlyNineLanguagesAndAutomaticDetection() {
#expect(AppLanguage.allCases.count == 10)
#expect(Set(AppLanguage.allCases.filter { $0 != .system }.map(\.languageCode)) ==
Set(["zh-Hans", "zh-Hant", "ja", "ko", "en", "de", "es", "fr", "ar"]))
#expect(AppLanguage(rawValue: "chinese") == .chinese)
#expect(AppLanguage(rawValue: "english") == .english)
#expect(AppLanguage(rawValue: "system") == .system)
}
@Test func resolvesScriptsRegionsAndPreferenceOrder() {
for value in ["zh-Hant", "zh-TW", "zh-HK", "zh_MO", "zh-Hant-CN"] {
#expect(AppLanguage.resolve(preferredLanguages: [value]) == .traditionalChinese)
}
for value in ["zh-Hans", "zh-CN", "zh-SG", "zh-Hans-TW", "zh"] {
#expect(AppLanguage.resolve(preferredLanguages: [value]) == .chinese)
}
#expect(AppLanguage.resolve(preferredLanguages: ["ru-RU", "ja-JP", "en"]) == .japanese)
#expect(AppLanguage.resolve(preferredLanguages: ["es-MX", "zh-CN"]) == .spanish)
#expect(AppLanguage.resolve(preferredLanguages: ["ar-EG"]) == .arabic)
#expect(AppLanguage.resolve(preferredLanguages: ["ko-KR"]) == .korean)
#expect(AppLanguage.resolve(preferredLanguages: ["de-CH"]) == .german)
#expect(AppLanguage.resolve(preferredLanguages: ["fr-CA"]) == .french)
#expect(AppLanguage.resolve(preferredLanguages: ["ru"]) == .english)
#expect(AppLanguage.resolve(preferredLanguages: []) == .english)
}
@Test func searchesNativeChineseEnglishAndAccents() {
#expect(AppLanguage.japanese.matches(search: "日语"))
#expect(AppLanguage.japanese.matches(search: "日本"))
#expect(AppLanguage.german.matches(search: "german"))
#expect(AppLanguage.german.matches(search: "DEUTSCH"))
#expect(AppLanguage.french.matches(search: "francais"))
#expect(AppLanguage.spanish.matches(search: "espanol"))
#expect(AppLanguage.arabic.matches(search: "العربية"))
#expect(AppLanguage.traditionalChinese.matches(search: "繁体"))
#expect(AppLanguage.system.matches(search: "auto"))
#expect(AppLanguage.allCases.allSatisfy { $0.matches(search: " ") })
#expect(AppLanguage.allCases.contains { $0.matches(search: "zzzzzz") } == false)
}
@Test func setsProcessLocaleWithoutChangingOtherPreferences() {
let name = "LanguageSupportTests.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: name)!
defer { defaults.removePersistentDomain(forName: name) }
defaults.set("keep", forKey: "unrelated")
AppLanguage.traditionalChinese.applyToProcessLocale(defaults: defaults)
#expect(defaults.stringArray(forKey: "AppleLanguages") == ["zh-Hant"])
#expect(defaults.string(forKey: "AppleLocale")?.hasPrefix("zh_Hant_") == true)
AppLanguage.arabic.applyToProcessLocale(defaults: defaults)
#expect(defaults.stringArray(forKey: "AppleLanguages") == ["ar"])
AppLanguage.system.applyToProcessLocale(defaults: defaults)
// 移除应用覆盖后 UserDefaults 会继承全局值,应检查自身域而不是合并后的查询结果。
#expect(defaults.persistentDomain(forName: name)?["AppleLanguages"] == nil)
#expect(defaults.persistentDomain(forName: name)?["AppleLocale"] == nil)
#expect(defaults.string(forKey: "unrelated") == "keep")
#expect(AppLanguage.arabic.isRightToLeft)
#expect(AppLanguage.french.isRightToLeft == false)
#expect(L10n.locale(for: .french).language.languageCode?.identifier == "fr")
}
@Test func templatesPreserveLiteralBracesAndTranslateNestedValues() {
let table = Translations(tables: ["内存\tMemory\n打开 {}\tOpen {}\n{} / {}\t{2} then {1}"])
#expect(table.translate("打开 config{}.json") == "Open config{}.json")
#expect(table.translate("打开 内存") == "Open Memory")
#expect(table.translate("first / second") == "second then first")
let rtl = Translations(tables: ["地址:{}\tالعنوان: {}"], rightToLeft: true)
#expect(rtl.translate("地址:192.0.2.1") == "العنوان: \u{2068}192.0.2.1\u{2069}")
}
@Test func rejectsBrokenPlaceholdersAndEmptyTranslations() {
let table = Translations(tables: ["下载 {}\tDownload\n{} / {}\t{1} twice {1}\n设置\t\n内存\tMemory"])
#expect(table.sourceKeys == ["内存"])
#expect(table.translate("下载 file.zip") == nil)
}
@Test(arguments: AppLanguage.allCases.filter { $0 != .system && $0 != .chinese })
func everyLanguageHasCompleteCatalog(_ language: AppLanguage) throws {
let catalog = try #require(Translations.catalog(for: language))
#expect(catalog.sourceKeys == Translations.shared.sourceKeys)
for key in ["语言", "应用 UI 语言", "自动检测", "搜索语言", "没有匹配的语言",
"热压力:正常", "热压力:偏高", "热压力:严重", "热压力:临界", "热压力:未知",
"高效处理多线程任务", "由 macOS 报告的系统热状态,与 CPU 温度读数独立。",
"核心最高温度;余量以 100°C 为参考,不代表设备实际降频阈值。",
"亮度", "对比度", "音量", "显示器 {} 台", "重新检测显示器控制",
"显示器不支持此控制", "暂时无法读取,请检查 DDC/CI 与连接",
"显示链路正在恢复…", "修改未确认,请重新检测",
"合并显示", "仅图标", "面板项目", "启动中…", "正在启动风扇…",
"只显示 XStats 图标,点击查看已选项目的状态总览",
"所选项目显示在弹出面板中,原有图标风格会保留。", "打开进程监控",
"额度显示", "数字单位", "货币", "估算费用",
"30 天", "按小时统计", "按天统计", "暂无小时明细,请刷新用量。",
"套餐:Pro", "套餐有效期至:2026-10-20", "可用重置次数:3", "重置次数最早到期:2026-10-05",
"AI 用量与额度共用此间隔;新数据到达后更新菜单栏。",
"价格来源:models.dev;人民币按缓存汇率换算。",
"按模型基础 API 单价估算,不含阶梯加价,非订阅账单。",
"设置", "内存", "下载并应用", "中国大陆节假日与调休", "黄历", "每日", "每周", "累计",
"XStats 基于 OpenStats 开发,感谢原项目的开源贡献 · MIT License"] {
#expect(catalog.translate(key)?.isEmpty == false)
}
}
}