""" 경량 i18n. `tr('key')` → 현재 언어의 번역 문자열. 키 미존재 시 ko 폴백, ko도 없으면 키 그대로 반환 (미번역도 동작). 점진 도입 가능. 새 언어 추가: 1. _DICT['en'] 옆에 'ja'/'zh' 등 추가 2. 사용자가 설정 → 언어 콤보에서 선택 """ from __future__ import annotations _current_lang = 'ko' _DICT = { 'ko': { # === 메뉴/버튼 === 'menu.stats': '통계', 'menu.calendar': '캘린더', 'menu.daily_report': '일일보고', 'menu.help': '도움말', 'menu.settings': '설정', 'btn.clock_out': '퇴근하기', 'btn.clock_out_cancel': '🔄 퇴근 취소', 'btn.lunch_add': '점심시간 추가', 'btn.lunch_applied': '점심시간 (적용됨)', 'btn.dinner_add': '저녁시간 추가', 'btn.dinner_applied': '저녁시간 (적용됨)', 'btn.break_out': '🚪 외출 시작', 'btn.break_in': '↩️ 복귀', 'btn.save': '💾 저장', 'btn.close': '닫기', 'btn.apply': '적용', 'btn.cancel': '취소', 'btn.add': '추가', 'btn.delete': '삭제', 'btn.edit': '편집', 'btn.confirm': '확인', 'btn.ok': '확인', # === 윈도우/다이얼로그 제목 === 'window.main_title': '퇴근시간 계산기', 'window.settings': '⚙️ 설정', 'window.help': '📖 사용 설명서', 'window.stats': '📊 근무 통계', 'window.calendar': '📅 캘린더', 'window.mini_widget': '퇴근시간', 'window.clock_in_dialog': '출근 시간', 'window.break_view': '외출 관리', 'window.overtime_view': '연장근무 관리', 'window.leave_view': '연차 관리', # === 라벨 === 'label.remaining': '남은 시간', 'label.overtime_progress': '추가 근무 중', 'label.expected_clock_out': '예상 퇴근', 'label.clock_in_time': '출근 시간', 'label.clock_out_time': '퇴근 시간', 'label.work_hours_label': '하루 기본 근무:', 'label.lunch_default': '점심시간 기본:', 'label.dinner_default': '저녁시간 기본:', 'label.work_pattern': '근무 패턴:', 'label.time_format': '시간 형식:', 'label.theme': '테마:', 'label.language': '언어 / Language:', 'label.unit_hour': '시간', 'label.unit_minute': '분', 'label.unit_day': '일', 'label.unit_count': '개', 'label.weekday_mon': '월', 'label.weekday_tue': '화', 'label.weekday_wed': '수', 'label.weekday_thu': '목', 'label.weekday_fri': '금', 'label.weekday_sat': '토', 'label.weekday_sun': '일', 'label.am': '오전', 'label.pm': '오후', # === 알림 === 'notif.clock_out_soon.title': '⏰ 퇴근 시간 임박', 'notif.clock_out_soon.body': '퇴근까지 {minutes}분 남았습니다.\n마무리 준비를 시작하세요!', 'notif.lunch_reminder.title': '🍱 점심시간 등록', 'notif.lunch_reminder.body': '점심시간을 등록하지 않으셨네요.\n점심시간을 추가하시겠어요?', 'notif.overtime_earning.title': '🔥 연장근무 적립 예정', 'notif.overtime_earning.body': '오늘 {time_str}의 연장근무가 적립될 예정입니다!', 'notif.overtime_threshold.title': '💰 연장근무 적립 알림', 'notif.overtime_threshold.body': '적립된 연장근무가 {hours:.1f}시간 입니다.\n사용을 고려해보세요!', 'notif.health.title': '⚠️ 건강 경고', 'notif.health.body': '{days}일 연속 연장근무 중입니다.\n건강을 챙기세요!', 'notif.weekly_52.title': '🚨 주 52시간 초과', 'notif.weekly_52.body': '이번 주 총 근무시간이 {hours:.1f}시간입니다.\n법정 근로시간을 초과했습니다!', # === 메시지박스 === 'msg.save_success.title': '저장 완료', 'msg.save_success.body': '설정이 저장되었습니다.', 'msg.input_error.title': '입력 오류', 'msg.work_min_too_small': '하루 기본 근무 시간은 최소 30분 이상이어야 합니다.', 'msg.no_clock_in.title': '외출 불가', 'msg.no_clock_in.body': '출근하지 않은 상태입니다.', 'msg.already_break.body': '이미 외출 중입니다.', 'msg.no_record.title': '기록 없음', 'msg.no_record.body': '오늘 출근 기록이 없습니다.', 'msg.confirm_delete.title': '삭제 확인', 'msg.no_data.title': '데이터 없음', # === 트레이 === 'tray.open': '프로그램 열기', 'tray.mini_widget': '📌 미니 위젯', 'tray.toggle_lunch': '🍱 점심시간 토글', 'tray.quit': '종료', 'tray.tooltip_remaining': '퇴근까지: {time}', 'tray.tooltip_overtime': '추가 근무 중: {time}', 'tray.background': '프로그램이 트레이에서 실행 중입니다.', # === 그룹 박스 === 'group.work_time': '근무 시간 설정', 'group.notification': '알림 및 표시 설정', 'group.overtime': '연장근무 설정', 'group.leave': '휴가 설정', 'group.holiday': '공휴일 설정', 'group.data': '데이터 관리', # === StatsView === 'stats.title': '근무 통계', 'stats.tab_weekly': '주간', 'stats.tab_monthly': '월간', 'stats.tab_pattern': '패턴 분석', 'stats.weekly_summary': '이번 주 요약', 'stats.monthly_summary': '이번 달 요약', 'stats.total_hours': '총 근무시간:', 'stats.work_days': '근무일수:', 'stats.avg_hours': '평균 근무시간:', 'stats.total_overtime': '총 연장근무:', 'stats.pattern_insights': '근무 패턴 인사이트', 'stats.analyzing': '데이터를 분석 중입니다...', 'stats.no_data': '아직 충분한 데이터가 없습니다.', # === CalendarView === 'cal.title': '월간 근무 캘린더', 'cal.year_label': '년', 'cal.month_label': '월', 'cal.prev': '◀ 이전', 'cal.next': '다음 ▶', 'cal.today': '오늘', 'cal.no_record': '기록 없음', 'cal.edit_record': '기록 편집', # === HelpView (각 탭의 큰 HTML은 별도 키) === 'help.tab_intro': '👋 시작하기', 'help.tab_work_hours': '🕘 근무시간', 'help.tab_overtime': '🏦 연장근무', 'help.tab_leave': '🌴 연차/휴가', 'help.tab_break': '🚪 외출/저녁', 'help.tab_faq': '❓ 자주 묻는 질문', }, 'en': { # === Menu/Buttons === 'menu.stats': 'Stats', 'menu.calendar': 'Calendar', 'menu.daily_report': 'Daily Report', 'menu.help': 'Help', 'menu.settings': 'Settings', 'btn.clock_out': 'Clock Out', 'btn.clock_out_cancel': '🔄 Cancel Clock-out', 'btn.lunch_add': 'Add Lunch', 'btn.lunch_applied': 'Lunch (Applied)', 'btn.dinner_add': 'Add Dinner', 'btn.dinner_applied': 'Dinner (Applied)', 'btn.break_out': '🚪 Start Break', 'btn.break_in': '↩️ Return', 'btn.save': '💾 Save', 'btn.close': 'Close', 'btn.apply': 'Apply', 'btn.cancel': 'Cancel', 'btn.add': 'Add', 'btn.delete': 'Delete', 'btn.edit': 'Edit', 'btn.confirm': 'Confirm', 'btn.ok': 'OK', # === Windows === 'window.main_title': 'Clock-out Time Calculator', 'window.settings': '⚙️ Settings', 'window.help': '📖 User Guide', 'window.stats': '📊 Statistics', 'window.calendar': '📅 Calendar', 'window.mini_widget': 'Clock-out', 'window.clock_in_dialog': 'Clock-in Time', 'window.break_view': 'Break Management', 'window.overtime_view': 'Overtime Management', 'window.leave_view': 'Leave Management', # === Labels === 'label.remaining': 'Remaining', 'label.overtime_progress': 'Overtime', 'label.expected_clock_out': 'Expected Clock-out', 'label.clock_in_time': 'Clock-in Time', 'label.clock_out_time': 'Clock-out Time', 'label.work_hours_label': 'Daily work:', 'label.lunch_default': 'Lunch break:', 'label.dinner_default': 'Dinner break:', 'label.work_pattern': 'Work pattern:', 'label.time_format': 'Time format:', 'label.theme': 'Theme:', 'label.language': 'Language / 언어:', 'label.unit_hour': 'h', 'label.unit_minute': 'min', 'label.unit_day': 'day(s)', 'label.unit_count': '', 'label.weekday_mon': 'Mon', 'label.weekday_tue': 'Tue', 'label.weekday_wed': 'Wed', 'label.weekday_thu': 'Thu', 'label.weekday_fri': 'Fri', 'label.weekday_sat': 'Sat', 'label.weekday_sun': 'Sun', 'label.am': 'AM', 'label.pm': 'PM', # === Notifications === 'notif.clock_out_soon.title': '⏰ Clock-out Soon', 'notif.clock_out_soon.body': "{minutes} minutes until clock-out.\nWrap things up!", 'notif.lunch_reminder.title': '🍱 Lunch Reminder', 'notif.lunch_reminder.body': "You haven't registered lunch yet.\nWant to add it?", 'notif.overtime_earning.title': '🔥 Overtime Will Accrue', 'notif.overtime_earning.body': "{time_str} of overtime will be banked today!", 'notif.overtime_threshold.title': '💰 Overtime Balance High', 'notif.overtime_threshold.body': "{hours:.1f} hours of overtime banked.\nConsider using some.", 'notif.health.title': '⚠️ Health Warning', 'notif.health.body': "{days} consecutive days of overtime.\nTake care of your health!", 'notif.weekly_52.title': '🚨 Weekly 52h Exceeded', 'notif.weekly_52.body': "This week's total work hours: {hours:.1f}\nLegal limit exceeded!", # === Message Boxes === 'msg.save_success.title': 'Saved', 'msg.save_success.body': 'Settings saved successfully.', 'msg.input_error.title': 'Input Error', 'msg.work_min_too_small': 'Daily work time must be at least 30 minutes.', 'msg.no_clock_in.title': 'Cannot Take Break', 'msg.no_clock_in.body': 'Not clocked in.', 'msg.already_break.body': 'Already on break.', 'msg.no_record.title': 'No Record', 'msg.no_record.body': 'No clock-in record for today.', 'msg.confirm_delete.title': 'Confirm Delete', 'msg.no_data.title': 'No Data', # === Tray === 'tray.open': 'Open Program', 'tray.mini_widget': '📌 Mini Widget', 'tray.toggle_lunch': '🍱 Toggle Lunch', 'tray.quit': 'Quit', 'tray.tooltip_remaining': 'Until clock-out: {time}', 'tray.tooltip_overtime': 'Overtime: {time}', 'tray.background': 'Program is running in the tray.', # === Groups === 'group.work_time': 'Work Time Settings', 'group.notification': 'Notifications & Display', 'group.overtime': 'Overtime Settings', 'group.leave': 'Leave Settings', 'group.holiday': 'Holidays', 'group.data': 'Data Management', # === StatsView === 'stats.title': 'Work Statistics', 'stats.tab_weekly': 'Weekly', 'stats.tab_monthly': 'Monthly', 'stats.tab_pattern': 'Patterns', 'stats.weekly_summary': 'This Week', 'stats.monthly_summary': 'This Month', 'stats.total_hours': 'Total hours:', 'stats.work_days': 'Work days:', 'stats.avg_hours': 'Avg hours/day:', 'stats.total_overtime': 'Total overtime:', 'stats.pattern_insights': 'Work Pattern Insights', 'stats.analyzing': 'Analyzing...', 'stats.no_data': 'Not enough data yet.', # === CalendarView === 'cal.title': 'Monthly Calendar', 'cal.year_label': 'Y', 'cal.month_label': 'M', 'cal.prev': '◀ Prev', 'cal.next': 'Next ▶', 'cal.today': 'Today', 'cal.no_record': 'No record', 'cal.edit_record': 'Edit record', # === HelpView === 'help.tab_intro': '👋 Getting Started', 'help.tab_work_hours': '🕘 Work Hours', 'help.tab_overtime': '🏦 Overtime', 'help.tab_leave': '🌴 Leave', 'help.tab_break': '🚪 Break/Dinner', 'help.tab_faq': '❓ FAQ', }, } # === HelpView 큰 HTML 콘텐츠 (별도 사전) === _HELP_HTML = { 'ko': { 'help.html.intro': """
퇴근시간 계산기는 출근 시간 자동 감지부터 연장근무 적립·사용까지 하루 근무를 정리해 주는 데스크톱 앱입니다.
예) 하루 7시간 30분 근무 + 점심 30분
7 시간 30 분,
점심시간 기본에 30 분설정에서 "자동 적용"을 체크하면 출근 후 4시간 경과 시 점심시간이 자동으로 켜집니다.
""", 'help.html.overtime': """정규 퇴근시간 이후 일한 시간은 30분 단위로 절삭되어 적립됩니다.
적립된 연장근무는 메인 화면 "30분 사용" / "1시간 사용" 버튼으로 쓸 수 있어요. 사용한 만큼 그날 퇴근시간이 앞당겨집니다.
주말 또는 등록된 공휴일에 일한 시간은 모든 시간이 연장근무로 적립됩니다.
""", 'help.html.leave': """잔액 = 연간 연차 − (프로그램 외 사용분 + 프로그램에서 기록된 사용분)
1일 연차의 시간 길이는 설정의 하루 기본 근무를 따릅니다. 예: 7시간 30분 근무자는 1일 연차가 7시간 30분(=450분)으로 환산됩니다.
""", 'help.html.break': """병원, 잠깐 외근 등으로 자리를 비울 때 외출 시작 → 복귀 버튼으로 시간을 추적하세요.
설정에서 "화면 잠금 시 자동 외출/복귀"를 켜면 PC 잠금 시 자동으로 외출이 시작되고, 풀리면 복귀로 처리됩니다.
야근하면서 저녁을 먹는다면 저녁시간 추가 버튼을 눌러주세요.
""", 'help.html.faq': """메인 화면 출근 시각 옆 편집(연필) 아이콘으로 수정할 수 있어요.
설정 → 근무 시간 → 근무 패턴에서 프리셋을 선택하거나 시·분을 직접 입력하세요.
실행 폴더의 database.db (SQLite). 자동 백업은
~/.clockout_backups/에 1일 1회 회전됩니다.
Clock-out Time Calculator is a desktop app that organizes your daily work — from auto-detecting clock-in time to banking and using overtime.
Example: 7h30m daily + 30-min lunch
Enable "Auto Apply" in settings to automatically turn on lunch after 4 hours from clock-in.
""", 'help.html.overtime': """Time worked past your scheduled clock-out is truncated to 30-min units.
Use buttons on the main screen to consume banked overtime. Each unit lets you clock out earlier on a chosen day.
All hours worked on weekends or registered holidays are banked entirely as overtime.
""", 'help.html.leave': """Balance = annual leave − (pre-program usage + recorded usage)
1 leave day equals your configured daily work time. Example: 7h30m worker → 1 day = 7h30m (450 min).
""", 'help.html.break': """For short absences (medical, errands), use Start Break / Return.
Enable "Auto break on screen lock" in settings — when the PC locks, a break starts automatically; on unlock, you return.
For overtime with dinner, press Add Dinner.
""", 'help.html.faq': """Click the pencil icon next to clock-in time on the main screen to edit.
Settings → Work Time → pick the preset or enter hours/minutes directly.
database.db in the program folder (SQLite). Daily auto-backups
rotate in ~/.clockout_backups/.
missing: {key}
" def available_languages() -> list: return list(_DICT.keys()) def language_label(code: str) -> str: return {'ko': '한국어', 'en': 'English'}.get(code, code)