""" i18n GUI 검증: 언어 전환 시 UI 라벨이 실제로 바뀌는지. 동일 SettingsView를 한국어로 만들고 확인 → 영어로 만들고 확인. (런타임 언어 전환은 위젯 재생성을 요구하므로 새 인스턴스로 검증) """ import os import sys import tempfile os.environ['QT_QPA_PLATFORM'] = 'offscreen' sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from PyQt5.QtWidgets import QApplication, QPushButton, QGroupBox app = QApplication.instance() or QApplication(sys.argv) from core.database import Database from core.i18n import set_language db_path = os.path.join(tempfile.gettempdir(), 'clockout_i18n.db') if os.path.exists(db_path): os.remove(db_path) db = Database(db_path) PASS, FAIL = [], [] def case(label, fn): try: fn() PASS.append(label) print(f"[PASS] {label}") except Exception as e: FAIL.append((label, f"{type(e).__name__}: {e}")) print(f"[FAIL] {label}: {type(e).__name__}: {e}") def find_button_text(widget, text_substring): for btn in widget.findChildren(QPushButton): if text_substring in btn.text(): return btn return None def find_groupbox_title(widget, title_substring): for gb in widget.findChildren(QGroupBox): if title_substring in gb.title(): return gb return None def test_settings_korean_labels(): """한국어 모드에서 SettingsView 라벨 검증.""" set_language('ko') from ui.settings_view import SettingsView dlg = SettingsView(db=db) # 윈도우 제목 assert '설정' in dlg.windowTitle(), f"title: {dlg.windowTitle()}" # 그룹박스 assert find_groupbox_title(dlg, '근무 시간') is not None assert find_groupbox_title(dlg, '연장근무') is not None assert find_groupbox_title(dlg, '데이터') is not None # 저장 버튼 save_btn = find_button_text(dlg, '저장') assert save_btn is not None dlg.deleteLater() def test_settings_english_labels(): """영어 모드에서 SettingsView 라벨 검증.""" set_language('en') from ui.settings_view import SettingsView dlg = SettingsView(db=db) assert 'Settings' in dlg.windowTitle() assert find_groupbox_title(dlg, 'Work Time') is not None assert find_groupbox_title(dlg, 'Overtime') is not None assert find_groupbox_title(dlg, 'Data') is not None save_btn = find_button_text(dlg, 'Save') assert save_btn is not None dlg.deleteLater() set_language('ko') # 원복 def test_main_window_language(): """MainWindow 라벨 — 한국어 / 영어""" set_language('ko') from ui.main_window import MainWindow w = MainWindow() assert '퇴근시간' in w.windowTitle() # 메뉴 버튼: 통계/캘린더/일일보고/도움말/설정/퇴근하기 assert find_button_text(w, '통계') is not None assert find_button_text(w, '캘린더') is not None assert find_button_text(w, '도움말') is not None assert find_button_text(w, '점심') is not None w.deleteLater() def test_mini_widget_language(): set_language('ko') from ui.mini_widget import MiniWidget w = MiniWidget() assert '남은 시간' in w.title_label.text() w.update_remaining('+00:30:00') assert '추가' in w.title_label.text() w.deleteLater() set_language('en') w2 = MiniWidget() assert 'Remaining' in w2.title_label.text() w2.update_remaining('+00:30:00') assert 'Overtime' in w2.title_label.text() w2.deleteLater() set_language('ko') def test_notifier_language(): """알림 메시지가 언어에 맞춰 나오는지""" set_language('ko') from core.notifier import Notifier from datetime import datetime, timedelta n = Notifier(db=db) fired = [] n.notification_signal.connect(lambda t, m: fired.append((t, m))) n.check_clock_out_soon(datetime.now() + timedelta(minutes=20), datetime.now()) assert len(fired) == 1 assert '퇴근' in fired[0][0] set_language('en') n2 = Notifier(db=db) fired2 = [] n2.notification_signal.connect(lambda t, m: fired2.append((t, m))) n2.check_clock_out_soon(datetime.now() + timedelta(minutes=20), datetime.now()) assert len(fired2) == 1 assert 'Clock-out' in fired2[0][0] set_language('ko') case("I18N-1. SettingsView 한국어 라벨", test_settings_korean_labels) case("I18N-2. SettingsView 영어 라벨", test_settings_english_labels) case("I18N-3. MainWindow 한국어 (메뉴/버튼)", test_main_window_language) case("I18N-4. MiniWidget ko/en 전환 후 라벨", test_mini_widget_language) case("I18N-5. Notifier 알림 메시지 ko/en", test_notifier_language) print() print("=" * 60) print(f"PASS: {len(PASS)} FAIL: {len(FAIL)}") if FAIL: print("\nFAILED:") for label, err in FAIL: print(f" - {label}: {err}") if os.path.exists(db_path): try: os.remove(db_path) except: pass sys.exit(1 if FAIL else 0)