i18n fixes

This commit is contained in:
Flowseal
2026-06-23 18:24:26 +03:00
parent 85b5e7f22a
commit 6b5fd72612
9 changed files with 38 additions and 19 deletions
+3 -1
View File
@@ -12,6 +12,8 @@ block_cipher = None
import customtkinter import customtkinter
ctk_path = os.path.dirname(customtkinter.__file__) ctk_path = os.path.dirname(customtkinter.__file__)
_i18n_path = os.path.join(os.path.dirname(SPEC), os.pardir, 'ui', 'i18n')
# Collect gi (PyGObject) submodules and data so pystray._appindicator works # Collect gi (PyGObject) submodules and data so pystray._appindicator works
gi_hiddenimports = collect_submodules('gi') gi_hiddenimports = collect_submodules('gi')
gi_datas = collect_data_files('gi') gi_datas = collect_data_files('gi')
@@ -26,7 +28,7 @@ a = Analysis(
[os.path.join(os.path.dirname(SPEC), os.pardir, 'linux.py')], [os.path.join(os.path.dirname(SPEC), os.pardir, 'linux.py')],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[(ctk_path, 'customtkinter/')] + gi_datas + typelib_datas, datas=[(ctk_path, 'customtkinter/'), (_i18n_path, 'ui/i18n')] + gi_datas + typelib_datas,
hiddenimports=[ hiddenimports=[
'pystray._appindicator', 'pystray._appindicator',
'PIL._tkinter_finder', 'PIL._tkinter_finder',
+3 -1
View File
@@ -5,11 +5,13 @@ import os
block_cipher = None block_cipher = None
_i18n_path = os.path.join(os.path.dirname(SPEC), os.pardir, 'ui', 'i18n')
a = Analysis( a = Analysis(
[os.path.join(os.path.dirname(SPEC), os.pardir, 'macos.py')], [os.path.join(os.path.dirname(SPEC), os.pardir, 'macos.py')],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[], datas=[(_i18n_path, 'ui/i18n')],
hiddenimports=[ hiddenimports=[
'rumps', 'rumps',
'objc', 'objc',
+3 -1
View File
@@ -9,11 +9,13 @@ block_cipher = None
import customtkinter import customtkinter
ctk_path = os.path.dirname(customtkinter.__file__) ctk_path = os.path.dirname(customtkinter.__file__)
_i18n_path = os.path.join(os.path.dirname(SPEC), os.pardir, 'ui', 'i18n')
a = Analysis( a = Analysis(
[os.path.join(os.path.dirname(SPEC), os.pardir, 'windows.py')], [os.path.join(os.path.dirname(SPEC), os.pardir, 'windows.py')],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[(ctk_path, 'customtkinter/')], datas=[(ctk_path, 'customtkinter/'), (_i18n_path, 'ui/i18n')],
hiddenimports=[ hiddenimports=[
'pystray._win32', 'pystray._win32',
'PIL._tkinter_finder', 'PIL._tkinter_finder',
+8 -2
View File
@@ -200,13 +200,19 @@ def parse_dc_ip_list(dc_ip_list: List[str]) -> Dict[int, str]:
dc_redirects: Dict[int, str] = {} dc_redirects: Dict[int, str] = {}
for entry in dc_ip_list: for entry in dc_ip_list:
if ':' not in entry: if ':' not in entry:
raise ValueError( err = ValueError(
f"Invalid --dc-ip format {entry!r}, expected DC:IP") f"Invalid --dc-ip format {entry!r}, expected DC:IP")
err.entry = entry
err.kind = "format"
raise err
dc_s, ip_s = entry.split(':', 1) dc_s, ip_s = entry.split(':', 1)
try: try:
dc_n = int(dc_s) dc_n = int(dc_s)
_socket.inet_aton(ip_s) _socket.inet_aton(ip_s)
except (ValueError, OSError): except (ValueError, OSError):
raise ValueError(f"Invalid --dc-ip {entry!r}") err = ValueError(f"Invalid --dc-ip {entry!r}")
err.entry = entry
err.kind = "invalid"
raise err
dc_redirects[dc_n] = ip_s dc_redirects[dc_n] = ip_s
return dc_redirects return dc_redirects
+11 -8
View File
@@ -809,6 +809,16 @@ def merge_adv_from_form(
base[key] = default_config[key] base[key] = default_config[key]
def _dc_validation_message(error: ValueError) -> str:
exc_entry = getattr(error, "entry", None)
if exc_entry is None:
return str(error)
kind = getattr(error, "kind", "invalid")
if kind == "format":
return t("validation.dc_format", entry=exc_entry)
return t("validation.dc_invalid", entry=exc_entry)
def validate_config_form( def validate_config_form(
widgets: TrayConfigFormWidgets, widgets: TrayConfigFormWidgets,
default_config: dict, default_config: dict,
@@ -838,14 +848,7 @@ def validate_config_form(
try: try:
parse_dc_ip_list(lines) parse_dc_ip_list(lines)
except ValueError as e: except ValueError as e:
msg = str(e) return _dc_validation_message(e)
if "expected DC:IP" in msg:
entry = msg.split("format ", 1)[-1].rstrip(")")
return t("validation.dc_format", entry=entry.strip("'"))
if msg.startswith("Invalid --dc-ip "):
entry = msg.split(" ", 2)[-1]
return t("validation.dc_invalid", entry=entry)
return msg
secret_val = widgets.secret_var.get().strip() secret_val = widgets.secret_var.get().strip()
if len(secret_val) != 32: if len(secret_val) != 32:
+4 -3
View File
@@ -26,7 +26,7 @@ class LocaleEnum(str, Enum):
_LOCALES_DIR = Path(__file__).resolve().parent _LOCALES_DIR = Path(__file__).resolve().parent
_DEFAULT_LOCALE = LocaleEnum.russian _DEFAULT_LOCALE = LocaleEnum.english
_translations: Dict[str, str] = {} _translations: Dict[str, str] = {}
_current_lang: LocaleEnum = _DEFAULT_LOCALE _current_lang: LocaleEnum = _DEFAULT_LOCALE
@@ -137,10 +137,11 @@ def language_option_labels() -> List[Tuple[LocaleEnum, str]]:
def language_label_for_config(value: LocaleInput) -> str: def language_label_for_config(value: LocaleInput) -> str:
loc = LocaleEnum.parse(value) loc = LocaleEnum.parse(value)
for cfg_val, label in language_option_labels(): labels = language_option_labels()
for cfg_val, label in labels:
if cfg_val == loc: if cfg_val == loc:
return label return label
return language_option_labels()[0][1] return labels[0][1] if labels else _DEFAULT_LOCALE.value
def refresh_language_option_maps() -> None: def refresh_language_option_maps() -> None:
+1
View File
@@ -76,6 +76,7 @@
"connectivity.cfproxy_title": "CF Proxy", "connectivity.cfproxy_title": "CF Proxy",
"connectivity.cfworker_title": "CF Worker", "connectivity.cfworker_title": "CF Worker",
"connectivity.timeout": "timeout", "connectivity.timeout": "timeout",
"connectivity.no_response": "no response",
"connectivity.available": "{title}: available", "connectivity.available": "{title}: available",
"connectivity.unavailable": "{title}: unavailable", "connectivity.unavailable": "{title}: unavailable",
"connectivity.all_ok": "{title}: all working", "connectivity.all_ok": "{title}: all working",
+1
View File
@@ -76,6 +76,7 @@
"connectivity.cfproxy_title": "CF-прокси", "connectivity.cfproxy_title": "CF-прокси",
"connectivity.cfworker_title": "CF Worker", "connectivity.cfworker_title": "CF Worker",
"connectivity.timeout": "таймаут", "connectivity.timeout": "таймаут",
"connectivity.no_response": "нет ответа",
"connectivity.available": "{title}: доступен", "connectivity.available": "{title}: доступен",
"connectivity.unavailable": "{title}: недоступен", "connectivity.unavailable": "{title}: недоступен",
"connectivity.all_ok": "{title}: всё работает", "connectivity.all_ok": "{title}: всё работает",
+4 -3
View File
@@ -188,17 +188,18 @@ def _apply_ui_language(cfg: dict) -> None:
def load_config() -> dict: def load_config() -> dict:
ensure_dirs() ensure_dirs()
cfg: Optional[dict] = None
if CONFIG_FILE.exists(): if CONFIG_FILE.exists():
try: try:
with open(CONFIG_FILE, "r", encoding="utf-8") as f: with open(CONFIG_FILE, "r", encoding="utf-8") as f:
data = json.load(f) data = json.load(f)
for k, v in DEFAULT_CONFIG.items(): for k, v in DEFAULT_CONFIG.items():
data.setdefault(k, v) data.setdefault(k, v)
_apply_ui_language(data) cfg = data
return data
except Exception as exc: except Exception as exc:
log.warning("Failed to load config: %s", repr(exc)) log.warning("Failed to load config: %s", repr(exc))
cfg = dict(DEFAULT_CONFIG) if cfg is None:
cfg = dict(DEFAULT_CONFIG)
_apply_ui_language(cfg) _apply_ui_language(cfg)
return cfg return cfg