-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathi18n.py
More file actions
155 lines (130 loc) · 6.69 KB
/
Copy pathi18n.py
File metadata and controls
155 lines (130 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# i18n.py
import os
import json
from typing import Dict
from PyQt5 import QtCore
from constants import BASE_DIR, KEY_UI_LANGUAGE
from utils import get_settings
LANG_DIR = os.path.join(BASE_DIR, "lang")
class TranslationManager:
"""
Простой менеджер переводов:
- грузит lang/ru.json и lang/en.json (если есть);
- хранит текущий язык в QSettings (ключ ui/language);
- даёт метод translate(key) и глобальную функцию _().
"""
def __init__(self) -> None:
# Базовые встроенные fallback-переводы для runtime/UI-критичных строк.
# JSON-файлы по-прежнему имеют приоритет и мёрджатся поверх этих значений.
self._translations: Dict[str, Dict[str, str]] = {
"ru": {
"app_title": "XTimer",
"settings_title": "Настройки XTimer",
"dlg_sound_read_error": "Не удалось прочитать выбранный файл.",
"dlg_sound_too_large": "Файл слишком большой ({size_mb:.1f} MB). Максимум: {max_mb} MB.",
"dlg_sound_too_long": "Файл слишком длинный ({duration_s:.1f} s). Максимум: {max_s} s.",
"update_status_available": "Найдена версия {tag}",
"update_status_uptodate": "У вас уже последняя версия.",
"update_status_failed": "Ошибка проверки обновления: {stage}",
"tray_update_available_title": "XTimer",
"tray_update_available_message": "Новая версия {tag} доступна. Нажмите уведомление, чтобы открыть релиз.",
"tray_update_uptodate_title": "XTimer",
"tray_update_uptodate_message": "Вы используете последнюю версию.",
"app_already_running": "XTimer уже запущен.",
"startup_failed": "XTimer не удалось запустить. Подробности смотрите в логе.",
},
"en": {
"app_title": "XTimer",
"settings_title": "XTimer Settings",
"dlg_sound_read_error": "Cannot read selected file.",
"dlg_sound_too_large": "File is too large ({size_mb:.1f} MB). Max allowed: {max_mb} MB.",
"dlg_sound_too_long": "File is too long ({duration_s:.1f} s). Max allowed: {max_s} s.",
"update_status_available": "Version {tag} is available",
"update_status_uptodate": "You already have the latest version.",
"update_status_failed": "Update check failed: {stage}",
"tray_update_available_title": "XTimer",
"tray_update_available_message": "A new version {tag} is available. Click the notification to open the release page.",
"tray_update_uptodate_title": "XTimer",
"tray_update_uptodate_message": "You are using the latest version.",
"app_already_running": "XTimer is already running.",
"startup_failed": "XTimer failed to start. See the log for details.",
},
}
self._default_lang = "en" # первый запуск
self._fallback_lang = "en" # страховка переводов
# Подгружаем JSON-файлы, если существуют
self._load_json("ru")
self._load_json("en")
# Определяем текущий язык из настроек
settings = get_settings()
lang_code = settings.value(KEY_UI_LANGUAGE, self._default_lang, type=str)
if lang_code not in self._translations:
lang_code = self._default_lang
self._current_lang = lang_code
def _load_json(self, code: str) -> None:
"""
Загружает lang/<code>.json и мёрджит в словарь переводов.
Если файла нет или JSON битый — тихо игнорируем.
"""
path = os.path.join(LANG_DIR, f"{code}.json")
if not os.path.isfile(path):
return
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict):
# все ключи/значения приводим к строкам
self._translations[code].update(
{str(k): str(v) for k, v in data.items()}
)
except Exception:
# никаких падений из-за переводов
pass
def set_language(self, code: str) -> None:
"""
Смена языка: 'ru' или 'en'. Сохраняем в QSettings.
"""
if code not in self._translations:
code = self._default_lang
self._current_lang = code
settings = get_settings()
settings.setValue(KEY_UI_LANGUAGE, code)
def get_language(self) -> str:
return self._current_lang
def format(self, key: str, **kwargs) -> str:
"""
Возвращает перевод и безопасно форматирует его через str.format(**kwargs).
Если форматирование не удалось, возвращает сырой template без падения.
"""
template = self.translate(key)
try:
return template.format(**kwargs)
except Exception:
return template
def translate(self, key: str) -> str:
"""
Вернуть перевод по ключу:
1) текущий язык
2) fallback (en)
3) сам key, если ничего не найдено
"""
lang = self._current_lang
if key in self._translations.get(lang, {}):
return self._translations[lang][key]
if key in self._translations.get(self._fallback_lang, {}):
return self._translations[self._fallback_lang][key]
return key
# Глобальный экземпляр менеджера
_manager = TranslationManager()
def set_language(code: str) -> None:
_manager.set_language(code)
def get_language() -> str:
return _manager.get_language()
def tr(key: str, **kwargs) -> str:
return _manager.format(key, **kwargs)
def _(key: str) -> str:
"""
Глобальная функция-переводчик:
_("settings_title") -> "Настройки XTimer" / "XTimer Settings"
"""
return _manager.translate(key)