import json import tempfile import unittest from pathlib import Path from ui import app as ui_app class UiLanguageTests(unittest.TestCase): def test_default_language_is_chinese_when_config_has_no_language(self): self.assertEqual(ui_app.get_configured_language({}), "zh_CN") def test_configured_language_accepts_english(self): self.assertEqual(ui_app.get_configured_language({"language": "en_US"}), "en_US") def test_configured_language_rejects_unsupported_locale(self): with self.assertRaises(ValueError): ui_app.get_configured_language({"language": "ja_JP"}) def test_language_choice_maps_display_labels_to_locale_codes(self): self.assertEqual(ui_app.resolve_language_choice("中文"), "zh_CN") self.assertEqual(ui_app.resolve_language_choice("English"), "en_US") self.assertEqual(ui_app.resolve_language_choice("en_US"), "en_US") def test_save_language_setting_persists_locale_code(self): with tempfile.TemporaryDirectory() as tmp_dir: config_path = Path(tmp_dir) / "config.json" config_path.write_text(json.dumps({"device": "cpu"}), encoding="utf-8") status = ui_app.save_language_setting("English", config_path=config_path) saved = json.loads(config_path.read_text(encoding="utf-8")) self.assertEqual(saved["language"], "en_US") self.assertIn("English", status) self.assertIn("restart", status.lower()) def test_language_packs_include_selector_keys(self): for lang in ("zh_CN", "en_US"): data = ui_app.load_i18n(lang) self.assertIn("settings", data) self.assertIn("language", data["settings"]) self.assertIn("save_language", data["settings"]) self.assertIn("language_saved_restart", data["settings"]) def test_language_packs_include_primary_ui_keys(self): required_ui_keys = { "cover_usage", "series_filter", "keyword_search", "keyword_placeholder", "character_choice_info", "download_character_info", "refresh_models", "download_selected_character", "download_series_all", "download_all_characters", "download_status", "model_name", "model_path", "index_path", "no_models", "positive_pitch_info", "normal_volume_info", "reverb_info", "all_series", "unknown", "enabled", "disabled", "language_korean", "language_japanese", "language_chinese", "language_english", "character_label_meta_separator", "character_label_template", } required_settings_keys = { "runtime_settings", "compute_device", "save_settings", "settings_saved_restart", "status", "cpu_slow", "about_body", "model_sources", } required_message_keys = { "download_network_error", "please_select_character_to_download", "character_download_complete", "bulk_download_complete", "please_upload_song", "please_select_character", "character_model_missing", "cover_complete_status", "cover_process_failed", "vc_pipeline_mode_status", "all_files_dir_status", } required_character_detail_keys = { "downloaded_empty", "available_empty", "version_label", "continuity", "repo", "local_weight", "internal_key", "detail_code_line", "detail_text_line", } required_route_status_keys = { "mature_auto_preferred_suffix", "mature_current_preferred", "official_route_title", "strict_route_ready_title", "route_current_model", "strict_route_flow", "auto_route_missing_title", } required_device_info_keys = { "pytorch_version", "available_backends", "gpu_line", "backend_version", "no_gpu_cpu", } for lang in ("zh_CN", "en_US"): data = ui_app.load_i18n(lang) self.assertTrue(required_ui_keys.issubset(data.get("ui", {}))) self.assertTrue(required_settings_keys.issubset(data.get("settings", {}))) self.assertTrue(required_message_keys.issubset(data.get("messages", {}))) self.assertTrue(required_character_detail_keys.issubset(data.get("character_details", {}))) self.assertTrue(required_route_status_keys.issubset(data.get("route_status", {}))) self.assertTrue(required_device_info_keys.issubset(data.get("device_info", {}))) def test_character_metadata_values_are_not_localized(self): original_i18n = ui_app.i18n try: ui_app.i18n = ui_app.load_i18n("en_US") char_info = { "name": "rin", "display": "Rin Hoshizora", "source": "Love Live!", "continuity": "μ's", "version_label": "500 epochs·40k", "distribution": "HuggingFace", "repo": "trioskosmos/rvc_models", "source_page_url": "https://huggingface.co/trioskosmos/rvc_models", "download_url": "https://huggingface.co/trioskosmos/rvc_models/resolve/main/rin.pth", "model_path": "assets/weights/characters/rin/rin.pth", "index_path": "assets/weights/characters/rin/rin.index", } label = ui_app.format_character_label(char_info) details = ui_app.format_character_details(char_info, downloaded=True) finally: ui_app.i18n = original_i18n self.assertIn("[Japanese]", label) self.assertIn("trioskosmos/rvc_models", label) self.assertIn("500 epochs·40k", label) self.assertIn("- Source repository: `trioskosmos/rvc_models`", details) self.assertIn("- Version tag: `500 epochs·40k`", details) self.assertIn("assets/weights/characters/rin/rin.pth", details) self.assertNotIn("版本标识", details) self.assertNotIn("来源仓库", details) def test_ui_exposes_language_selector_and_save_handler(self): source = Path("ui/app.py").read_text(encoding="utf-8") self.assertIn('label=t("language", "settings")', source) self.assertIn("choices=list(LANGUAGE_LABEL_TO_CODE.keys())", source) self.assertIn("fn=save_language_setting", source) self.assertIn('t("cover_usage", "ui")', source) self.assertIn('t("runtime_settings", "settings")', source) self.assertIn("allow_custom_value=True", source) if __name__ == "__main__": unittest.main()