From 35397c54143f3dba89bebc4e30bc09b0804b5798 Mon Sep 17 00:00:00 2001 From: mofeng-git Date: Wed, 14 Aug 2024 22:54:12 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E7=9A=84=20kvmd=20?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96=EF=BC=88=E6=B1=89=E5=8C=96=EF=BC=89?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=20yaml=20=E9=85=8D=E7=BD=AE=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B=EF=BC=9A=20```=20languages:=20=20=20=20=20console:=20?= =?UTF-8?q?zh=20=20=20=20=20web:=20zh=20```?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kvmd/aiohelpers.py | 6 +- kvmd/aioproc.py | 12 +- kvmd/apps/__init__.py | 32 +++-- kvmd/apps/kvmd/__init__.py | 4 +- kvmd/apps/kvmd/auth.py | 4 +- kvmd/apps/kvmd/server.py | 24 ++-- kvmd/apps/kvmd/streamer.py | 4 +- kvmd/apps/kvmd/ugpio.py | 14 +- kvmd/apps/otg/__init__.py | 6 +- kvmd/apps/otgnet/__init__.py | 4 +- kvmd/apps/vnc/server.py | 46 +++--- kvmd/apps/vnc/vncauth.py | 15 +- kvmd/htclient.py | 4 +- kvmd/htserver.py | 4 +- kvmd/i18n/zh/LC_MESSAGES/message.mo | Bin 12244 -> 15100 bytes kvmd/i18n/zh/LC_MESSAGES/message.po | 198 +++++++++++++++++++++++--- kvmd/inotify.py | 6 +- kvmd/keyboard/keysym.py | 6 +- kvmd/languages.py | 18 +++ kvmd/lanuages.py | 7 - kvmd/plugins/atx/__init__.py | 4 +- kvmd/plugins/atx/gpio.py | 6 +- kvmd/plugins/auth/http.py | 4 +- kvmd/plugins/auth/ldap.py | 6 +- kvmd/plugins/auth/pam.py | 6 +- kvmd/plugins/auth/radius.py | 4 +- kvmd/plugins/hid/_mcu/__init__.py | 4 +- kvmd/plugins/hid/_mcu/gpio.py | 4 +- kvmd/plugins/hid/bt/__init__.py | 4 +- kvmd/plugins/hid/bt/server.py | 4 +- kvmd/plugins/hid/ch9329/__init__.py | 4 +- kvmd/plugins/hid/ch9329/chip.py | 4 +- kvmd/plugins/hid/otg/__init__.py | 4 +- kvmd/plugins/hid/otg/device.py | 4 +- kvmd/plugins/hid/otg/keyboard.py | 4 +- kvmd/plugins/hid/otg/mouse.py | 4 +- kvmd/plugins/hid/spi.py | 2 +- kvmd/plugins/msd/__init__.py | 16 +-- kvmd/plugins/msd/disabled.py | 2 +- kvmd/plugins/msd/otg/__init__.py | 6 +- kvmd/plugins/msd/otg/drive.py | 4 +- kvmd/plugins/msd/otg/storage.py | 4 +- kvmd/plugins/ugpio/anelpwr.py | 4 +- kvmd/usb.py | 6 +- kvmd/validators/languages.py | 34 +++++ message.pot | 182 ++++++++++++++++++++--- testenv/v2-hdmiusb-rpi4.override.yaml | 4 + 47 files changed, 567 insertions(+), 181 deletions(-) create mode 100644 kvmd/languages.py delete mode 100644 kvmd/lanuages.py create mode 100644 kvmd/validators/languages.py diff --git a/kvmd/aiohelpers.py b/kvmd/aiohelpers.py index a0b86399..0ec42cca 100644 --- a/kvmd/aiohelpers.py +++ b/kvmd/aiohelpers.py @@ -22,7 +22,7 @@ import subprocess -from .lanuages import Lanuages +from .languages import Languages from .logging import get_logger @@ -38,13 +38,13 @@ async def remount(name: str, base_cmd: list[str], rw: bool) -> bool: part.format(mode=mode) for part in base_cmd ] - logger.info(Lanuages().gettext("Remounting %s storage to %s: %s ..."), name, mode.upper(), tools.cmdfmt(cmd)) + logger.info(Languages().gettext("Remounting %s storage to %s: %s ..."), name, mode.upper(), tools.cmdfmt(cmd)) try: proc = await aioproc.log_process(cmd, logger) if proc.returncode != 0: assert proc.returncode is not None raise subprocess.CalledProcessError(proc.returncode, cmd) except Exception as err: - logger.error(Lanuages().gettext("Can't remount %s storage: %s"), name, tools.efmt(err)) + logger.error(Languages().gettext("Can't remount %s storage: %s"), name, tools.efmt(err)) return False return True diff --git a/kvmd/aioproc.py b/kvmd/aioproc.py index 39181d0c..70c3ae56 100644 --- a/kvmd/aioproc.py +++ b/kvmd/aioproc.py @@ -26,7 +26,7 @@ import asyncio import asyncio.subprocess import logging -from .lanuages import Lanuages +from .languages import Languages import setproctitle from .logging import get_logger @@ -86,7 +86,7 @@ async def log_stdout_infinite(proc: asyncio.subprocess.Process, logger: logging. else: empty += 1 if empty == 100: # asyncio bug - raise RuntimeError(Lanuages().gettext("Asyncio process: too many empty lines")) + raise RuntimeError(Languages().gettext("Asyncio process: too many empty lines")) async def kill_process(proc: asyncio.subprocess.Process, wait: float, logger: logging.Logger) -> None: # pylint: disable=no-member @@ -101,14 +101,14 @@ async def kill_process(proc: asyncio.subprocess.Process, wait: float, logger: lo if proc.returncode is not None: raise await proc.wait() - logger.info(Lanuages().gettext("Process killed: retcode=%d"), proc.returncode) + logger.info(Languages().gettext("Process killed: retcode=%d"), proc.returncode) except asyncio.CancelledError: pass except Exception: if proc.returncode is None: - logger.exception(Lanuages().gettext("Can't kill process pid=%d"), proc.pid) + logger.exception(Languages().gettext("Can't kill process pid=%d"), proc.pid) else: - logger.info(Lanuages().gettext("Process killed: retcode=%d"), proc.returncode) + logger.info(Languages().gettext("Process killed: retcode=%d"), proc.returncode) def rename_process(suffix: str, prefix: str="kvmd") -> None: @@ -117,7 +117,7 @@ def rename_process(suffix: str, prefix: str="kvmd") -> None: def settle(name: str, suffix: str, prefix: str="kvmd") -> logging.Logger: logger = get_logger(1) - logger.info(Lanuages().gettext("Started %s pid=%d"), name, os.getpid()) + logger.info(Languages().gettext("Started %s pid=%d"), name, os.getpid()) os.setpgrp() rename_process(suffix, prefix) return logger diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index e8410f8a..936ab9f8 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -105,7 +105,9 @@ from ..validators.hw import valid_otg_gadget from ..validators.hw import valid_otg_id from ..validators.hw import valid_otg_ethernet -from ..lanuages import Lanuages +from ..validators.languages import valid_languages + +from ..languages import Languages # ===== def init( @@ -127,16 +129,16 @@ def init( add_help=add_help, formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) - _ = translation(domain="message",localedir="/kvmd/i18n",languages=["zh"]).gettext + parser.add_argument("-c", "--config", default="/etc/kvmd/main.yaml", type=valid_abs_file, - help=_("Set config file path"), metavar="") + help="Set config file path", metavar="") parser.add_argument("-o", "--set-options", default=[], nargs="+", - help=_("Override config options list (like sec/sub/opt=value)"), metavar="",) + help="Override config options list (like sec/sub/opt=value)", metavar="",) parser.add_argument("-m", "--dump-config", action="store_true", - help=_("View current configuration (include all overrides)")) + help="View current configuration (include all overrides)") if check_run: parser.add_argument("--run", dest="run", action="store_true", - help=_("Run the service")) + help="Run the service") (options, remaining) = parser.parse_known_args(argv) if options.dump_config: @@ -151,9 +153,18 @@ def init( )) raise SystemExit() config = _init_config(options.config, options.set_options, **load) - logging.captureWarnings(True) logging.config.dictConfig(config.logging) + + if isinstance(config.get("languages"), dict) and isinstance(config["languages"].get("console"), str): + i18n_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+"/i18n" + Languages.init("message", i18n_path, config["languages"]["console"]) + gettext = Languages().gettext + + logging.addLevelName(20, gettext("INFO")) + logging.addLevelName(30, gettext("WARNING")) + logging.addLevelName(40, gettext("ERROR")) + if cli_logging: logging.getLogger().handlers[0].setFormatter(logging.Formatter( "-- {levelname:>7} -- {message}", @@ -162,7 +173,7 @@ def init( if check_run and not options.run: raise SystemExit( - _("To prevent accidental startup, you must specify the --run option to start.\n")+_("Try the --help option to find out what this service does.\n")+_("Make sure you understand exactly what you are doing!")) + gettext("To prevent accidental startup, you must specify the --run option to start.\n")+gettext("Try the --help option to find out what this service does.\n")+gettext("Make sure you understand exactly what you are doing!")) return (parser, remaining, config) @@ -787,4 +798,9 @@ def _get_config_scheme() -> dict: "timeout": Option(300, type=valid_int_f1), "interval": Option(30, type=valid_int_f1), }, + + "languages": { + "console": Option("default", type=valid_languages), + "web": Option("default", type=valid_languages), + }, } diff --git a/kvmd/apps/kvmd/__init__.py b/kvmd/apps/kvmd/__init__.py index 2d0379c2..1740328f 100644 --- a/kvmd/apps/kvmd/__init__.py +++ b/kvmd/apps/kvmd/__init__.py @@ -26,7 +26,7 @@ from ...plugins.hid import get_hid_class from ...plugins.atx import get_atx_class from ...plugins.msd import get_msd_class -from ...lanuages import Lanuages +from ...languages import Languages from .. import init @@ -112,4 +112,4 @@ def main(argv: (list[str] | None)=None) -> None: stream_forever=config.streamer.forever, ).run(**config.server._unpack()) - get_logger(0).info(Lanuages().gettext("Bye-bye")) + get_logger(0).info(Languages().gettext("Bye-bye")) diff --git a/kvmd/apps/kvmd/auth.py b/kvmd/apps/kvmd/auth.py index 786fdf55..9fe0b730 100644 --- a/kvmd/apps/kvmd/auth.py +++ b/kvmd/apps/kvmd/auth.py @@ -34,7 +34,7 @@ from ...plugins.auth import get_auth_service_class from ...htserver import HttpExposed -from ...lanuages import Lanuages +from ...languages import Languages # ===== class AuthManager: @@ -52,7 +52,7 @@ class AuthManager: totp_secret_path: str, ) -> None: - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext self.__enabled = enabled if not enabled: get_logger().warning(self.gettext("AUTHORIZATION IS DISABLED")) diff --git a/kvmd/apps/kvmd/server.py b/kvmd/apps/kvmd/server.py index 44f33f89..c6a0ce51 100644 --- a/kvmd/apps/kvmd/server.py +++ b/kvmd/apps/kvmd/server.py @@ -33,6 +33,8 @@ from aiohttp.web import Request from aiohttp.web import Response from aiohttp.web import WebSocketResponse +from ...languages import Languages + from ...logging import get_logger from ...errors import OperationError @@ -84,17 +86,17 @@ from .api.redfish import RedfishApi # ===== class StreamerQualityNotSupported(OperationError): def __init__(self) -> None: - super().__init__("This streamer does not support quality settings") + super().__init__(Languages().gettext("This streamer does not support quality settings")) class StreamerResolutionNotSupported(OperationError): def __init__(self) -> None: - super().__init__("This streamer does not support resolution settings") + super().__init__(Languages().gettext("This streamer does not support resolution settings")) class StreamerH264NotSupported(OperationError): def __init__(self) -> None: - super().__init__("This streamer does not support H264") + super().__init__(Languages().gettext("This streamer does not support H264")) # ===== @@ -210,6 +212,8 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins self.__reset_streamer = False self.__new_streamer_params: dict = {} + self.gettext=Languages().gettext + # ===== STREAMER CONTROLLER @exposed_http("POST", "/streamer/set_params") @@ -291,24 +295,24 @@ class KvmdServer(HttpServer): # pylint: disable=too-many-arguments,too-many-ins async def _on_shutdown(self) -> None: logger = get_logger(0) - logger.info("Waiting short tasks ...") + logger.info(self.gettext("Waiting short tasks ...")) await aiotools.wait_all_short_tasks() - logger.info("Stopping system tasks ...") + logger.info(self.gettext("Stopping system tasks ...")) await aiotools.stop_all_deadly_tasks() - logger.info("Disconnecting clients ...") + logger.info(self.gettext("Disconnecting clients ...")) await self._close_all_wss() - logger.info("On-Shutdown complete") + logger.info(self.gettext("On-Shutdown complete")) async def _on_cleanup(self) -> None: logger = get_logger(0) for sub in self.__subsystems: if sub.cleanup: - logger.info("Cleaning up %s ...", sub.name) + logger.info(self.gettext("Cleaning up %s ..."), sub.name) try: await sub.cleanup() # type: ignore except Exception: - logger.exception("Cleanup error on %s", sub.name) - logger.info("On-Cleanup complete") + logger.exception(self.gettext("Cleanup error on %s"), sub.name) + logger.info(self.gettext("On-Cleanup complete")) async def _on_ws_opened(self) -> None: self.__streamer_notifier.notify() diff --git a/kvmd/apps/kvmd/streamer.py b/kvmd/apps/kvmd/streamer.py index 87601a5f..b519aa70 100644 --- a/kvmd/apps/kvmd/streamer.py +++ b/kvmd/apps/kvmd/streamer.py @@ -34,7 +34,7 @@ import aiohttp from PIL import Image as PilImage -from ...lanuages import Lanuages +from ...languages import Languages from ...logging import get_logger @@ -228,7 +228,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes self.__notifier = aiotools.AioNotifier() - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext # ===== diff --git a/kvmd/apps/kvmd/ugpio.py b/kvmd/apps/kvmd/ugpio.py index 2ccad33c..482bcb5e 100644 --- a/kvmd/apps/kvmd/ugpio.py +++ b/kvmd/apps/kvmd/ugpio.py @@ -26,7 +26,7 @@ from typing import AsyncGenerator from typing import Callable from typing import Any -from ...lanuages import Lanuages +from ...languages import Languages from ...logging import get_logger @@ -48,22 +48,22 @@ from ...yamlconf import Section # ===== class GpioChannelNotFoundError(GpioOperationError): def __init__(self) -> None: - super().__init__(Lanuages().gettext("GPIO channel is not found")) + super().__init__(Languages().gettext("GPIO channel is not found")) class GpioSwitchNotSupported(GpioOperationError): def __init__(self) -> None: - super().__init__(Lanuages().gettext("This GPIO channel does not support switching")) + super().__init__(Languages().gettext("This GPIO channel does not support switching")) class GpioPulseNotSupported(GpioOperationError): def __init__(self) -> None: - super().__init__(Lanuages().gettext("This GPIO channel does not support pulsing")) + super().__init__(Languages().gettext("This GPIO channel does not support pulsing")) class GpioChannelIsBusyError(IsBusyError, GpioError): def __init__(self) -> None: - super().__init__(Lanuages().gettext("Performing another GPIO operation on this channel, please try again later")) + super().__init__(Languages().gettext("Performing another GPIO operation on this channel, please try again later")) # ===== @@ -82,7 +82,7 @@ class _GpioInput: self.__driver = driver self.__driver.register_input(self.__pin, config.debounce) - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def get_scheme(self) -> dict: return { @@ -253,7 +253,7 @@ class UserGpio: self.__inputs: dict[str, _GpioInput] = {} self.__outputs: dict[str, _GpioOutput] = {} - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext for (channel, ch_config) in tools.sorted_kvs(config.scheme): driver = self.__drivers[ch_config.driver] diff --git a/kvmd/apps/otg/__init__.py b/kvmd/apps/otg/__init__.py index f054cd4d..504b6a34 100644 --- a/kvmd/apps/otg/__init__.py +++ b/kvmd/apps/otg/__init__.py @@ -29,7 +29,7 @@ import argparse from os.path import join -from ...lanuages import Lanuages +from ...languages import Languages from ...logging import get_logger @@ -203,7 +203,7 @@ def _cmd_start(config: Section) -> None: # pylint: disable=too-many-statements, # https://www.isticktoit.net/?p=1383 logger = get_logger() - gettext=Lanuages().gettext + gettext=Languages().gettext _check_config(config) @@ -296,7 +296,7 @@ def _cmd_stop(config: Section) -> None: gadget_path = usb.get_gadget_path(config.otg.gadget) - logger.info(Lanuages().gettext("Disabling gadget %r ..."), config.otg.gadget) + logger.info(Languages().gettext("Disabling gadget %r ..."), config.otg.gadget) _write(join(gadget_path, "UDC"), "\n") _unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), optional=True) diff --git a/kvmd/apps/otgnet/__init__.py b/kvmd/apps/otgnet/__init__.py index 0306fece..b7438680 100644 --- a/kvmd/apps/otgnet/__init__.py +++ b/kvmd/apps/otgnet/__init__.py @@ -26,7 +26,7 @@ import dataclasses import itertools import argparse -from ...lanuages import Lanuages +from ...languages import Languages from ...logging import get_logger @@ -89,7 +89,7 @@ class _Service: # pylint: disable=too-many-instance-attributes self.__gadget: str = config.otg.gadget self.__driver: str = config.otg.devices.ethernet.driver - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def start(self) -> None: asyncio.run(self.__run(True)) diff --git a/kvmd/apps/vnc/server.py b/kvmd/apps/vnc/server.py index 6ec3960e..eaaf63bc 100644 --- a/kvmd/apps/vnc/server.py +++ b/kvmd/apps/vnc/server.py @@ -28,6 +28,8 @@ import contextlib import aiohttp +from ...languages import Languages + from ...logging import get_logger from ...keyboard.keysym import SymmapModifiers @@ -133,6 +135,8 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes self.__modifiers = 0 + self.gettext=Languages().gettext + # ===== async def run(self) -> None: @@ -156,13 +160,13 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes logger = get_logger(0) await self.__stage1_authorized.wait_passed() - logger.info("%s [kvmd]: Waiting for the SetEncodings message ...", self._remote) + logger.info(self.gettext("%s [kvmd]: Waiting for the SetEncodings message ..."), self._remote) if not (await self.__stage2_encodings_accepted.wait_passed(timeout=5)): - raise RfbError("No SetEncodings message recieved from the client in 5 secs") + raise RfbError(self.gettext("No SetEncodings message recieved from the client in 5 secs")) assert self.__kvmd_session try: - logger.info("%s [kvmd]: Applying HID params: mouse_output=%s ...", self._remote, self.__mouse_output) + logger.info(self.gettext("%s [kvmd]: Applying HID params: mouse_output=%s ..."), self._remote, self.__mouse_output) await self.__kvmd_session.hid.set_params(mouse_output=self.__mouse_output) async with self.__kvmd_session.ws() as self.__kvmd_ws: @@ -170,7 +174,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes self.__stage3_ws_connected.set_passed() async for (event_type, event) in self.__kvmd_ws.communicate(): await self.__process_ws_event(event_type, event) - raise RfbError("KVMD closed the websocket (the server may have been stopped)") + raise RfbError(self.gettext("KVMD closed the websocket (the server may have been stopped)")) finally: self.__kvmd_ws = None @@ -204,19 +208,19 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes while True: frame = await read_frame(not self.__fb_has_key) if not streaming: - logger.info("%s [streamer]: Streaming ...", self._remote) + logger.info(self.gettext("%s [streamer]: Streaming ..."), self._remote) streaming = True if frame["online"]: await self.__queue_frame(frame) else: - await self.__queue_frame("No signal") + await self.__queue_frame(self.gettext("No signal")) except StreamerError as err: if isinstance(err, StreamerPermError): streamer = self.__get_default_streamer() - logger.info("%s [streamer]: Permanent error: %s; switching to %s ...", self._remote, err, streamer) + logger.info(self.gettext("%s [streamer]: Permanent error: %s; switching to %s ..."), self._remote, err, streamer) else: - logger.info("%s [streamer]: Waiting for stream: %s", self._remote, err) - await self.__queue_frame("Waiting for stream ...") + logger.info(self.gettext("%s [streamer]: Waiting for stream: %s"), self._remote, err) + await self.__queue_frame(self.gettext("Waiting for stream ...")) await asyncio.sleep(1) def __get_preferred_streamer(self) -> BaseStreamerClient: @@ -227,13 +231,13 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes streamer: (BaseStreamerClient | None) = None for streamer in self.__streamers: if getattr(self._encodings, formats[streamer.get_format()]): - get_logger(0).info("%s [streamer]: Using preferred %s", self._remote, streamer) + get_logger(0).info(self.gettext("%s [streamer]: Using preferred %s"), self._remote, streamer) return streamer raise RuntimeError("No streamers found") def __get_default_streamer(self) -> BaseStreamerClient: streamer = self.__streamers[-1] - get_logger(0).info("%s [streamer]: Using default %s", self._remote, streamer) + get_logger(0).info(self.gettext("%s [streamer]: Using default %s"), self._remote, streamer) return streamer async def __queue_frame(self, frame: (dict | str)) -> None: @@ -298,13 +302,13 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes await self._send_fb_jpeg(last["data"]) elif last["format"] == StreamFormats.H264: if not self._encodings.has_h264: - raise RfbError("The client doesn't want to accept H264 anymore") + raise RfbError(self.gettext("The client doesn't want to accept H264 anymore")) if self.__fb_has_key: await self._send_fb_h264(last["data"]) else: await self._send_fb_allow_again() else: - raise RuntimeError(f"Unknown format: {last['format']}") + raise RuntimeError(self.gettext(f"Unknown format: {last['format']}")) last["data"] = b"" # ===== @@ -410,7 +414,7 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes has_quality = (await self.__kvmd_session.streamer.get_state())["features"]["quality"] quality = (self._encodings.tight_jpeg_quality if has_quality else None) - get_logger(0).info("%s [main]: Applying streamer params: jpeg_quality=%s; desired_fps=%d ...", + get_logger(0).info(self.gettext("%s [main]: Applying streamer params: jpeg_quality=%s; desired_fps=%d ..."), self._remote, quality, self.__desired_fps) await self.__kvmd_session.streamer.set_params(quality, self.__desired_fps) @@ -456,14 +460,16 @@ class VncServer: # pylint: disable=too-many-instance-attributes shared_params = _SharedParams() + self.gettext=Languages().gettext + async def cleanup_client(writer: asyncio.StreamWriter) -> None: if (await aiotools.close_writer(writer)): - get_logger(0).info("%s [entry]: Connection is closed in an emergency", rfb_format_remote(writer)) + get_logger(0).info(self.gettext("%s [entry]: Connection is closed in an emergency"), rfb_format_remote(writer)) async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None: logger = get_logger(0) remote = rfb_format_remote(writer) - logger.info("%s [entry]: Connected client", remote) + logger.info(self.gettext("%s [entry]: Connected client"), remote) try: sock = writer.get_extra_info("socket") if no_delay: @@ -482,7 +488,7 @@ class VncServer: # pylint: disable=too-many-instance-attributes async with kvmd.make_session("", "") as kvmd_session: none_auth_only = await kvmd_session.auth.check() except (aiohttp.ClientError, asyncio.TimeoutError) as err: - logger.error("%s [entry]: Can't check KVMD auth mode: %s", remote, tools.efmt(err)) + logger.error(self.gettext("%s [entry]: Can't check KVMD auth mode: %s"), remote, tools.efmt(err)) return await _Client( @@ -504,7 +510,7 @@ class VncServer: # pylint: disable=too-many-instance-attributes shared_params=shared_params, ).run() except Exception: - logger.exception("%s [entry]: Unhandled exception in client task", remote) + logger.exception(self.gettext("%s [entry]: Unhandled exception in client task"), remote) finally: await aiotools.shield_fg(cleanup_client(writer)) @@ -514,7 +520,7 @@ class VncServer: # pylint: disable=too-many-instance-attributes if not (await self.__vnc_auth_manager.read_credentials())[1]: raise SystemExit(1) - get_logger(0).info("Listening VNC on TCP [%s]:%d ...", self.__host, self.__port) + get_logger(0).info(self.gettext("Listening VNC on TCP [%s]:%d ..."), self.__host, self.__port) (family, _, _, _, addr) = socket.getaddrinfo(self.__host, self.__port, type=socket.SOCK_STREAM)[0] with contextlib.closing(socket.socket(family, socket.SOCK_STREAM)) as sock: if family == socket.AF_INET6: @@ -532,4 +538,4 @@ class VncServer: # pylint: disable=too-many-instance-attributes def run(self) -> None: aiotools.run(self.__inner_run()) - get_logger().info("Bye-bye") + get_logger().info(self.gettext("Bye-bye")) diff --git a/kvmd/apps/vnc/vncauth.py b/kvmd/apps/vnc/vncauth.py index ebda9ef4..9cdb221a 100644 --- a/kvmd/apps/vnc/vncauth.py +++ b/kvmd/apps/vnc/vncauth.py @@ -22,6 +22,8 @@ import dataclasses +from ...languages import Languages + from ...logging import get_logger from ... import aiotools @@ -30,7 +32,7 @@ from ... import aiotools # ===== class VncAuthError(Exception): def __init__(self, path: str, lineno: int, msg: str) -> None: - super().__init__(f"Syntax error at {path}:{lineno}: {msg}") + super().__init__(Languages().gettext(f"Syntax error at {path}:{lineno}: {msg}")) # ===== @@ -49,6 +51,7 @@ class VncAuthManager: self.__path = path self.__enabled = enabled + self.gettext=Languages().gettext async def read_credentials(self) -> tuple[dict[str, VncAuthKvmdCredentials], bool]: if self.__enabled: @@ -57,7 +60,7 @@ class VncAuthManager: except VncAuthError as err: get_logger(0).error(str(err)) except Exception: - get_logger(0).exception("Unhandled exception while reading VNCAuth passwd file") + get_logger(0).exception(self.gettext("Unhandled exception while reading VNCAuth passwd file")) return ({}, (not self.__enabled)) async def __inner_read_credentials(self) -> dict[str, VncAuthKvmdCredentials]: @@ -68,19 +71,19 @@ class VncAuthManager: continue if " -> " not in line: - raise VncAuthError(self.__path, lineno, "Missing ' -> ' operator") + raise VncAuthError(self.__path, lineno, self.gettext("Missing ' -> ' operator")) (vnc_passwd, kvmd_userpass) = map(str.lstrip, line.split(" -> ", 1)) if ":" not in kvmd_userpass: - raise VncAuthError(self.__path, lineno, "Missing ':' operator in KVMD credentials (right part)") + raise VncAuthError(self.__path, lineno, self.gettext("Missing ':' operator in KVMD credentials (right part)")) (kvmd_user, kvmd_passwd) = kvmd_userpass.split(":") kvmd_user = kvmd_user.strip() if len(kvmd_user) == 0: - raise VncAuthError(self.__path, lineno, "Empty KVMD user (right part)") + raise VncAuthError(self.__path, lineno, self.gettext("Empty KVMD user (right part)")) if vnc_passwd in credentials: - raise VncAuthError(self.__path, lineno, "Duplicating VNC password (left part)") + raise VncAuthError(self.__path, lineno, self.gettext("Duplicating VNC password (left part)")) credentials[vnc_passwd] = VncAuthKvmdCredentials(kvmd_user, kvmd_passwd) return credentials diff --git a/kvmd/htclient.py b/kvmd/htclient.py index df8927d7..56b70d6b 100644 --- a/kvmd/htclient.py +++ b/kvmd/htclient.py @@ -28,7 +28,7 @@ from typing import AsyncGenerator import aiohttp import aiohttp.multipart -from .lanuages import Lanuages +from .languages import Languages from . import __version__ @@ -60,7 +60,7 @@ def get_filename(response: aiohttp.ClientResponse) -> str: try: return os.path.basename(response.url.path) except Exception: - raise aiohttp.ClientError(Lanuages().gettext("Can't determine filename")) + raise aiohttp.ClientError(Languages().gettext("Can't determine filename")) @contextlib.asynccontextmanager diff --git a/kvmd/htserver.py b/kvmd/htserver.py index ee055c15..a3769563 100644 --- a/kvmd/htserver.py +++ b/kvmd/htserver.py @@ -52,7 +52,7 @@ from .errors import IsBusyError from .validators import ValidatorError -from .lanuages import Lanuages +from .languages import Languages from . import aiotools @@ -282,7 +282,7 @@ class HttpServer: self.__ws_bin_handlers: dict[int, Callable] = {} self.__ws_sessions: list[WsSession] = [] self.__ws_sessions_lock = asyncio.Lock() - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def run( self, diff --git a/kvmd/i18n/zh/LC_MESSAGES/message.mo b/kvmd/i18n/zh/LC_MESSAGES/message.mo index 7a1147049a854156464b685b75bf1932396a10ce..8d6c71c92f817a2737b7574d23f560369dad50c0 100644 GIT binary patch delta 5750 zcmb7_d303O8NhF-K%;^}Sd>McD26p+*dx$`>>`MO!VxTvlSeX5GBeDZ5OZofAS6Tt zLYxp(P__`lk_ASSm<{^}wLP}#v8}BeJ$mwHCiGO!u`aDk>F;~*Wuc-y<%In1z3;o< z{@wg>{WrOx3;nOztN3?@|HJrS-cxp6|HfaZ)L6C?pnP8gC&FBq0xRGo_$P;n*DJM% z{VX^Q7DBn#3P-^A;jQp%I7+FI8s0~#0UXSQH^DV}f9hB6+4@F-Oyp>)W2C2PTIDq`8vdG~; z6dr;aY=F`v@4{sGHJlD_!|iL}IyeNnpnQJL_k15zb*0#1gFkRNr4UsGUj>L-rOf@yFy zOoY!slA#VjVp6ps7Smarhl}CAo&6$iPhh_VO4HOpsr{Q!BL4)^rz(*ROLY^J@2QYM zP!B*!^?E27-T@_`7U%pUD5VQsKh!ob7D@*0h5V>>{E~e*HC&721r1)CWCytDO;@~tW`X7Lr%)i%K(EU&%s)bU+x1mJxW6jRU zI4Ct==x_}b$30LKmqKy48j8WUp+tTW^4O`KD90z&-{D=bcZh@KEF^+8kbYBJpeT42 zO4HTCUhq}uOHk@HC_OU`YZ~-J8M_jAH*A8_;FnNRekZdiz4cQl_bQ>te*{Asi|Z(e za6FVTS_-A+`A{4xc31%=buCaFdkLi$$qWY~Ek)GbgVy$g!`a!9pQ4wMo<4W*i2~#lcK>kBzbBEBo_4;l#J|h_D7*O@CKYI^Zx}4so^9#LQ1dUDSz{FlR-CDdFJVdTS3;3{0?vd7A=OYH zz<#g~jU$1Lm`?r&vzWwzG|Lhw9&dsYQ5h5m8la@^&rl5ZV~KBWc=Pmp3`6xifWlmbT=frSmjbZZK2J@ull&+9=~nQJ>eVhulFuU2|u9Q1IQa z?x4r#)jS3QJ_9+AS95taJx32@=w5gJ6&D`#X1cuT*+}V+yLG>P)9a|#f-WP=63^O{ zla8Nr{Qm5Gk2fQvEm}NJ^Sc7B93u@sa}9mHFE{AV4bCF0l$4b2msYzxLGEZ9d;u+( zscS3s;C!#!m(H1?<>-dt%FsLSmK-f8S-em!G-Xk)G`+Y`*k+TB{Z zZg>KE`uYuiW7eqjPE8#*(Y9O<RlEa>ClYWG%J#zmwf zBuDAG09qe3>t{e?Z@WRTBJNiSC)PFN$lCVTr%GM)QjDTt9ou z_}pyWp8A`Ez`A0*B;Y#FZJ{J{d)SC@V0)p4H8FiebwCM+S*aZBWZTE zA_w{{PMDvpO_;4EYd*goa0PtX>DsjPS8R>&H6A(|x)j z!=LA(J?TW3TgFgZlsa`X3iES(0X=+qP<<$qdG2T~O)hCz(~Mld-xsjO)v6zsab|`{ zgVGkp|B?^rhA%tUdJzo0r*$V18GIpO(sy5syi8BFt}&@H)EZ@;Bkz~hbEKPZ;WIZU zMGoIQHKF(FIV+YeUbZltICT8nu6MzX%SL#B)Rmf@ulo( zEC@x~_f`FuXs3#{rY3X8;kJeT)z%X+za~>o14pgd}@#S7g`Lu){1aiIpC-1?*VEb+^74ik4NG z&6_)mZZ}Ft9A^10v#3tSMKfy(V~4O;WY$--wH|NZT4@#)UTS_y*}7EhL?c~ccP*jP zg(6!VxkA?5QzPH1%C!tvdIryON@T;x1wDF2>x!s#_|)wmhOF1qERe=*Yb@+~!O<2y zu#1K?pQ|*BYvNm5XnJ$YfoRo{__;mt!#l;7`{vDAt~p}$XWJT{jy0aME2^CI?JqI+ zoWA0HCpF@@#K_HSvEW1>IWp?Wgix%x(X1^QIbrrl%*3kq#ZFf!%SR@%?Uf3O?ntC* zT@qIby{+MhS$;-lV*>SvSGStw+pX%UDO&9KGiG5y^uWnzX&o)sSq%KT_i96-m_^3J zEUs5(K_#WIt7=y7jMnV!II@#vB~m^V?TO?iXY~kwI%a%i+StJfH(C8+ComRiiZvay z=Uodg7^e^Kct+Y9cFC(!yw85_?OqR`7`G=BuQ}HB+KX6e4Rhp(Rky}ks@2s+;xARU zZ!abR-!DRE#ak<5ySF=1Z4Hg|Li@94DRy*Qu~}YbM(!J5moOt<)5HXJKIe9iM$5Lh zHPuD;)&0P`6-h|RPUsb{sxV7-gpb~t94;R_aCS%6cb~M_!74P_sfjg(qc0qE9;Dd* P60#wa8a>p^yR7~LSn4&G delta 2916 zcmZY9dr*{B7{~FmjE16EA}R^!D~bWSTvoY`S(#dfrKK4*Q--+Y#tO(TWY=Y!Ebx}~ znwsJ?6p7QMmRo^}w;9d6)IT{njap5yT&8ABr+ZD)_a|>tGcwD5&N=V957n^xwufJnoF2 z#}V|eU@8uXF=o6mA(KgC0t53g8lQGtg`?=N!D|zuCOD3{cmb90&_TwG#7s=aS*S#p zq7r-#b>9|LLLVT3%}E?XeDebh-8gu#21o|eaTdN^Brj6mzYd^^9K!;G+{{JjhU#GPDib5k>d(fB8{jDw4xHdgj!KF z`=ClrLM>=MX5eyE$+w~h52NP2f+35>z?+O2iy3$e&cOlaq)u@&3KtASxGkz9TsZN}V1LCMZNtl^z%pLe3GPl`+Q*ak5&?VH0e?=wc zrk=C09Myjh-PnuT!jaTnTak}iz(S-*W(8`#dguDKc2X*78s2d}w z(rQqdPe3JfJ5It1RH?UOF@A`ugo_iZt;xb7EWycWqY^)f+-gD>X=vh~P!kQIW8p-{ zIjBsRqY``>wZeU406vdo!{05cyuYBqLc=m@W%y?u?<~HQmm=e^Ps6gGn29x#v zx6)84Phb{a#Tzh%yES1t>cK+3wAUr56|Terd=+`boIzEl7nMMNZdSq;Dv> zrzVKwLNpp^@QisEHSrl#$s?G9(UvjOun-k!CF=TS)a%-ge9Sj|>AH)HD*YJL@4=m@ zM4mv+UyrK5HVi3n8x8pdYOj7ql{l6SSAV?YRL2EKZpM$Qz&g~{oj@Jd-%yE-CJViG zvoIeYL_N12_1x}p)L#L*7*M8vJEl@6_2-~&4B)j2q0Yt`)M5JxwX!ipRX-OM_%YNL zG@w@c7U~QfMaBCZGx7V>5Wm+nMiGrg8$T|<4XA+MIpa~xs?v?eLY#(r%~s-c-09eh z`bnNdmc%d%kz$xbHEddxT{ip+yKY2Ec>joLE_-G|T<+XDZ%vJFsn;s2sx0?CZdFwW zeN~kK%kK*Wtu(){!fOS*Wm$pRC0Sg$wXW1(>rD?IPbiKWYz3F;VojZ|%xe!yOd0y` z9OXX0*QzcJF0<1Uf3XXbO6;Ra`@+8`UvPzEtXVG4LZA0ZtE{%B##PzQ3#8i*jhz%5EL-NQeB3Ips$zuOmSxyxO1HQ?1xh>g`Z08-{0Ps(G)&7u_VeKm3eX0oEdk_ zv$~qrN7gh)+V*v}ukAi?q-dxkqTVD;=d9BFxp>!|_L zv-8F7?VEd7ukSvzr)%>|od-Yat#9mY-XCe$($l=_az{gC=c?<=ce$f6d^)EhYD8q? z-pHC=UAtTQf}5Tr8+)49*?T?K%|v0&|Igghwl{M41!LcsF+5y7Wq`}xn)`?ypSK}A zB>$8vJhZUFmDlyg+ucoTBZurrSYP>#QaPx# diff --git a/kvmd/i18n/zh/LC_MESSAGES/message.po b/kvmd/i18n/zh/LC_MESSAGES/message.po index 3273b20d..b7b8a062 100644 --- a/kvmd/i18n/zh/LC_MESSAGES/message.po +++ b/kvmd/i18n/zh/LC_MESSAGES/message.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-08-12 22:07+0800\n" -"PO-Revision-Date: 2024-08-12 22:07+0800\n" +"POT-Creation-Date: 2024-08-14 22:40+0800\n" +"PO-Revision-Date: 2024-08-14 22:40+0800\n" "Last-Translator: \n" "Language: zh\n" "Language-Team: zh \n" @@ -90,37 +90,34 @@ msgstr "因收到忽略标识而取消监视 %s" msgid "Can't find any UDC" msgstr "未找到任何 UDC" -#: kvmd/apps/__init__.py:132 -msgid "Set config file path" -msgstr "设置配置文件路径" - -#: kvmd/apps/__init__.py:134 -msgid "Override config options list (like sec/sub/opt=value)" -msgstr "覆盖配置文件选项列表(如 sec/sub/opt=value)" - -#: kvmd/apps/__init__.py:136 -msgid "View current configuration (include all overrides)" -msgstr "查看当前配置(包括所有覆盖配置文件)" - -#: kvmd/apps/__init__.py:139 -msgid "Run the service" -msgstr "启动此服务" +#: kvmd/apps/__init__.py:164 +msgid "INFO" +msgstr "消息" #: kvmd/apps/__init__.py:165 +msgid "WARNING" +msgstr "警告" + +#: kvmd/apps/__init__.py:166 +msgid "ERROR" +msgstr "错误" + +#: kvmd/apps/__init__.py:176 msgid "" "To prevent accidental startup, you must specify the --run option to " "start.\n" msgstr "为了防止意外启动,必须在启动时指定 --run 选项。\n" -#: kvmd/apps/__init__.py:165 +#: kvmd/apps/__init__.py:176 msgid "Try the --help option to find out what this service does.\n" msgstr "尝试使用 --help 选项来了解某项服务的功能。\n" -#: kvmd/apps/__init__.py:165 +#: kvmd/apps/__init__.py:176 msgid "Make sure you understand exactly what you are doing!" msgstr "请确定你自己在做什么!" #: kvmd/apps/kvmd/__init__.py:115 kvmd/apps/otgnet/__init__.py:132 +#: kvmd/apps/vnc/server.py:541 msgid "Bye-bye" msgstr "再见" @@ -172,6 +169,48 @@ msgstr "无法生成新的唯一令牌" msgid "Logged out user %r (%d)" msgstr "已注销用户 %r (%d)" +#: kvmd/apps/kvmd/server.py:89 +msgid "This streamer does not support quality settings" +msgstr "该 streamer 不支持质量设置" + +#: kvmd/apps/kvmd/server.py:94 +msgid "This streamer does not support resolution settings" +msgstr "该 streamer 不支持分辨率设置" + +#: kvmd/apps/kvmd/server.py:99 +msgid "This streamer does not support H264" +msgstr "该 streamer 不支持 H264 设置" + +#: kvmd/apps/kvmd/server.py:298 +msgid "Waiting short tasks ..." +msgstr "正在等待短时任务结束......" + +#: kvmd/apps/kvmd/server.py:300 +msgid "Stopping system tasks ..." +msgstr "正在停止系统任务 ......" + +#: kvmd/apps/kvmd/server.py:302 +msgid "Disconnecting clients ..." +msgstr "断开客户端连接 ......" + +#: kvmd/apps/kvmd/server.py:304 +msgid "On-Shutdown complete" +msgstr "全部服务关闭完成" + +#: kvmd/apps/kvmd/server.py:310 +#, python-format +msgid "Cleaning up %s ..." +msgstr "正在清理 %s ......" + +#: kvmd/apps/kvmd/server.py:314 +#, python-format +msgid "Cleanup error on %s" +msgstr "在 %s 上发生清理错误" + +#: kvmd/apps/kvmd/server.py:315 +msgid "On-Cleanup complete" +msgstr "全部清理完毕" + #: kvmd/apps/kvmd/streamer.py:245 msgid "Streamer stop cancelled" msgstr "Streamer 停止已取消" @@ -375,6 +414,115 @@ msgstr "使用 OTG gadget %r ......" msgid "Using OTG Ethernet interface %r ..." msgstr "使用 OTG 以太网接口 %r ......" +#: kvmd/apps/vnc/server.py:163 +#, python-format +msgid "%s [kvmd]: Waiting for the SetEncodings message ..." +msgstr "%s [kvmd]: 等待 SetEncodings 信息 ......" + +#: kvmd/apps/vnc/server.py:165 +msgid "No SetEncodings message recieved from the client in 5 secs" +msgstr "5 秒内未收到客户端的 SetEncodings 信息" + +#: kvmd/apps/vnc/server.py:169 +#, python-format +msgid "%s [kvmd]: Applying HID params: mouse_output=%s ..." +msgstr "%s [kvmd]: 应用 HID 参数:mouse_output=%s ......" + +#: kvmd/apps/vnc/server.py:177 +msgid "KVMD closed the websocket (the server may have been stopped)" +msgstr "KVMD 关闭了 websocket(服务器可能已停止运行)" + +#: kvmd/apps/vnc/server.py:211 +#, python-format +msgid "%s [streamer]: Streaming ..." +msgstr "%s [streamer]:获取视频流中 ......" + +#: kvmd/apps/vnc/server.py:216 +msgid "No signal" +msgstr "无信号" + +#: kvmd/apps/vnc/server.py:220 +#, python-format +msgid "%s [streamer]: Permanent error: %s; switching to %s ..." +msgstr "%s [streamer]: 持续错误: %s; 切换到 %s ......" + +#: kvmd/apps/vnc/server.py:222 +#, python-format +msgid "%s [streamer]: Waiting for stream: %s" +msgstr "%s [streamer]: 正在等待数据流:%s" + +#: kvmd/apps/vnc/server.py:223 +msgid "Waiting for stream ..." +msgstr "正在启动 streamer ......" + +#: kvmd/apps/vnc/server.py:234 +#, python-format +msgid "%s [streamer]: Using preferred %s" +msgstr "%s [streamer]: 使用首选 %s" + +#: kvmd/apps/vnc/server.py:240 +#, python-format +msgid "%s [streamer]: Using default %s" +msgstr "%s [streamer]: 使用默认 %s" + +#: kvmd/apps/vnc/server.py:305 +msgid "The client doesn't want to accept H264 anymore" +msgstr "客户端不接受 H264 视频" + +#: kvmd/apps/vnc/server.py:311 +msgid "format" +msgstr "格式" + +#: kvmd/apps/vnc/server.py:417 +#, python-format +msgid "%s [main]: Applying streamer params: jpeg_quality=%s; desired_fps=%d ..." +msgstr "%s [main]: 应用流媒体参数: jpeg_quality=%s; desired_fps=%d ..." + +#: kvmd/apps/vnc/server.py:467 +#, python-format +msgid "%s [entry]: Connection is closed in an emergency" +msgstr "%s [entry]: 连接因紧急情况关闭" + +#: kvmd/apps/vnc/server.py:472 +#, python-format +msgid "%s [entry]: Connected client" +msgstr "%s [entry]: 已连接客户端" + +#: kvmd/apps/vnc/server.py:491 +#, python-format +msgid "%s [entry]: Can't check KVMD auth mode: %s" +msgstr "%s [entry]: 无法检查 KVMD 身份验证模式: %s" + +#: kvmd/apps/vnc/server.py:513 +#, python-format +msgid "%s [entry]: Unhandled exception in client task" +msgstr "%s [entry]: 客户端任务中出现无法处理的异常" + +#: kvmd/apps/vnc/server.py:523 +#, python-format +msgid "Listening VNC on TCP [%s]:%d ..." +msgstr "正在监听 TCP [%s]:%d 上的 VNC 服务 ......" + +#: kvmd/apps/vnc/vncauth.py:63 +msgid "Unhandled exception while reading VNCAuth passwd file" +msgstr "读取 VNCAuth 密码文件时出现无法处理的异常" + +#: kvmd/apps/vnc/vncauth.py:74 +msgid "Missing ' -> ' operator" +msgstr "缺少\"->\"运算符" + +#: kvmd/apps/vnc/vncauth.py:78 +msgid "Missing ':' operator in KVMD credentials (right part)" +msgstr "KVMD 证书中缺少\": \"运算符(右侧部分)" + +#: kvmd/apps/vnc/vncauth.py:83 +msgid "Empty KVMD user (right part)" +msgstr "空的 KVMD 用户(右侧部分)" + +#: kvmd/apps/vnc/vncauth.py:86 +msgid "Duplicating VNC password (left part)" +msgstr "复制 VNC 密码(左侧部分)" + #: kvmd/keyboard/keysym.py:69 #, python-format msgid "Invalid modifier key at mapping %s: %s / %s" @@ -668,3 +816,15 @@ msgstr "无法执行重新挂载辅助程序" msgid "Failed ANELPWR POST request to pin %s: %s" msgstr "向引脚 %s 发送 ANELPWR POST 请求失败:%s" +#~ msgid "Set config file path" +#~ msgstr "设置配置文件路径" + +#~ msgid "Override config options list (like sec/sub/opt=value)" +#~ msgstr "覆盖配置文件选项列表(如 sec/sub/opt=value)" + +#~ msgid "View current configuration (include all overrides)" +#~ msgstr "查看当前配置(包括所有覆盖配置文件)" + +#~ msgid "Run the service" +#~ msgstr "启动此服务" + diff --git a/kvmd/inotify.py b/kvmd/inotify.py index c883aac1..c0fa74e6 100644 --- a/kvmd/inotify.py +++ b/kvmd/inotify.py @@ -34,7 +34,7 @@ from typing import Generator from .logging import get_logger -from .lanuages import Lanuages +from .languages import Languages from . import aiotools from . import libc @@ -196,7 +196,7 @@ class Inotify: for path in paths: path = os.path.normpath(path) assert path not in self.__wd_by_path, path - get_logger().info(Lanuages().gettext("Watching for %s"), path) + get_logger().info(Languages().gettext("Watching for %s"), path) # Асинхронно, чтобы не висло на NFS wd = _inotify_check(await aiotools.run_async(libc.inotify_add_watch, self.__fd, _fs_encode(path), mask)) self.__wd_by_path[path] = wd @@ -255,7 +255,7 @@ class Inotify: if event.mask & InotifyMask.IGNORED: ignored_path = self.__path_by_wd[event.wd] if self.__wd_by_path[ignored_path] == event.wd: - logger.info(Lanuages().gettext("Unwatching %s because IGNORED was received"), ignored_path) + logger.info(Languages().gettext("Unwatching %s because IGNORED was received"), ignored_path) del self.__wd_by_path[ignored_path] continue diff --git a/kvmd/keyboard/keysym.py b/kvmd/keyboard/keysym.py index 1683ad53..44de4915 100644 --- a/kvmd/keyboard/keysym.py +++ b/kvmd/keyboard/keysym.py @@ -27,7 +27,7 @@ import importlib.machinery import Xlib.keysymdef -from ..lanuages import Lanuages +from ..languages import Languages from ..logging import get_logger @@ -66,7 +66,7 @@ def build_symmap(path: str) -> dict[int, dict[int, str]]: # x11 keysym -> [(mod or (web_name in WebModifiers.ALTS and key.altgr) or (web_name in WebModifiers.CTRLS and key.ctrl) ): - logger.error(Lanuages().gettext("Invalid modifier key at mapping %s: %s / %s"), src, web_name, key) + logger.error(Languages().gettext("Invalid modifier key at mapping %s: %s / %s"), src, web_name, key) continue modifiers = ( @@ -119,7 +119,7 @@ def _resolve_keysym(name: str) -> int: def _read_keyboard_layout(path: str) -> dict[int, list[At1Key]]: # Keysym to evdev (at1) logger = get_logger(0) - logger.info(Lanuages().gettext("Reading keyboard layout %s ..."), path) + logger.info(Languages().gettext("Reading keyboard layout %s ..."), path) with open(path) as file: lines = list(map(str.strip, file.read().split("\n"))) diff --git a/kvmd/languages.py b/kvmd/languages.py new file mode 100644 index 00000000..01fc2364 --- /dev/null +++ b/kvmd/languages.py @@ -0,0 +1,18 @@ +from gettext import translation + +class Languages: + use_ttranslation = None + languages = "default" + + @classmethod + def gettext(cls, string: str) -> str: + if cls.languages == "default" or cls.languages == "en" : + return string + else: + return cls.use_ttranslation(string) + + @classmethod + def init(cls, domain:str, localedir: str, languages: str) -> None: + cls.languages = languages + cls.use_ttranslation = translation(domain=domain, localedir=localedir, languages=[cls.languages]).gettext + \ No newline at end of file diff --git a/kvmd/lanuages.py b/kvmd/lanuages.py deleted file mode 100644 index c84be57d..00000000 --- a/kvmd/lanuages.py +++ /dev/null @@ -1,7 +0,0 @@ -from gettext import translation - -class Lanuages: - t = translation(domain="message",localedir="/kvmd/i18n",languages=["zh"]).gettext - - def gettext(self,string: str) -> str: - return self.t(string) \ No newline at end of file diff --git a/kvmd/plugins/atx/__init__.py b/kvmd/plugins/atx/__init__.py index 74e0b4c9..e59bd12b 100644 --- a/kvmd/plugins/atx/__init__.py +++ b/kvmd/plugins/atx/__init__.py @@ -25,7 +25,7 @@ from typing import AsyncGenerator from ...errors import OperationError from ...errors import IsBusyError -from ...lanuages import Lanuages +from ...languages import Languages from .. import BasePlugin from .. import get_plugin_class @@ -42,7 +42,7 @@ class AtxOperationError(OperationError, AtxError): class AtxIsBusyError(IsBusyError, AtxError): def __init__(self) -> None: - super().__init__(Lanuages().gettext("Performing another ATX operation, please try again later")) + super().__init__(Languages().gettext("Performing another ATX operation, please try again later")) # ===== diff --git a/kvmd/plugins/atx/gpio.py b/kvmd/plugins/atx/gpio.py index c191fded..b039d003 100644 --- a/kvmd/plugins/atx/gpio.py +++ b/kvmd/plugins/atx/gpio.py @@ -39,7 +39,7 @@ from ...validators.basic import valid_float_f01 from ...validators.os import valid_abs_path from ...validators.hw import valid_gpio_pin -from ...lanuages import Lanuages +from ...languages import Languages from . import AtxIsBusyError from . import BaseAtx @@ -193,7 +193,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes await self.__inner_click(name, pin, delay) else: await aiotools.run_region_task( - Lanuages().gettext(f"Can't perform ATX {name} click or operation was not completed"), + Languages().gettext(f"Can't perform ATX {name} click or operation was not completed"), self.__region, self.__inner_click, name, pin, delay, ) @@ -206,4 +206,4 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes finally: self.__line_request.set_value(pin, gpiod.line.Value(False)) await asyncio.sleep(1) - get_logger(0).info(Lanuages().gettext("Clicked ATX button %r"), name) + get_logger(0).info(Languages().gettext("Clicked ATX button %r"), name) diff --git a/kvmd/plugins/auth/http.py b/kvmd/plugins/auth/http.py index a66740c1..3eac6375 100644 --- a/kvmd/plugins/auth/http.py +++ b/kvmd/plugins/auth/http.py @@ -32,7 +32,7 @@ from ...logging import get_logger from ... import htclient -from ...lanuages import Lanuages +from ...languages import Languages from . import BaseAuthService @@ -91,7 +91,7 @@ class Plugin(BaseAuthService): htclient.raise_not_200(response) return True except Exception: - get_logger().exception(Lanuages().gettext("Failed HTTP auth request for user %r"), user) + get_logger().exception(Languages().gettext("Failed HTTP auth request for user %r"), user) return False async def cleanup(self) -> None: diff --git a/kvmd/plugins/auth/ldap.py b/kvmd/plugins/auth/ldap.py index a7de797e..8d3b6603 100644 --- a/kvmd/plugins/auth/ldap.py +++ b/kvmd/plugins/auth/ldap.py @@ -33,7 +33,7 @@ from ...logging import get_logger from ... import tools from ... import aiotools -from ...lanuages import Lanuages +from ...languages import Languages from . import BaseAuthService @@ -103,9 +103,9 @@ class Plugin(BaseAuthService): except ldap.INVALID_CREDENTIALS: pass except ldap.SERVER_DOWN as err: - get_logger().error(Lanuages().gettext("LDAP server is down: %s"), tools.efmt(err)) + get_logger().error(Languages().gettext("LDAP server is down: %s"), tools.efmt(err)) except Exception as err: - get_logger().error(Lanuages().gettext("Unexpected LDAP error: %s"), tools.efmt(err)) + get_logger().error(Languages().gettext("Unexpected LDAP error: %s"), tools.efmt(err)) finally: if conn is not None: try: diff --git a/kvmd/plugins/auth/pam.py b/kvmd/plugins/auth/pam.py index d16c08fe..bd3da907 100644 --- a/kvmd/plugins/auth/pam.py +++ b/kvmd/plugins/auth/pam.py @@ -34,7 +34,7 @@ from ...logging import get_logger from ... import aiotools -from ...lanuages import Lanuages +from ...languages import Languages from . import BaseAuthService @@ -88,13 +88,13 @@ class Plugin(BaseAuthService): return False else: if uid < self.__allow_uids_at: - get_logger().error(Lanuages().gettext("Unallowed UID of user %r: uid=%d < allow_uids_at=%d"), + get_logger().error(Languages().gettext("Unallowed UID of user %r: uid=%d < allow_uids_at=%d"), user, uid, self.__allow_uids_at) return False pam_obj = pam.pam() if not pam_obj.authenticate(user, passwd, service=self.__service): - get_logger().error(Lanuages().gettext("Can't authorize user %r using PAM: code=%d; reason=%s"), + get_logger().error(Languages().gettext("Can't authorize user %r using PAM: code=%d; reason=%s"), user, pam_obj.code, pam_obj.reason) return False return True diff --git a/kvmd/plugins/auth/radius.py b/kvmd/plugins/auth/radius.py index e4c44d80..8ff35747 100644 --- a/kvmd/plugins/auth/radius.py +++ b/kvmd/plugins/auth/radius.py @@ -36,7 +36,7 @@ from ...logging import get_logger from ... import aiotools -from ...lanuages import Lanuages +from ...languages import Languages from . import BaseAuthService @@ -442,5 +442,5 @@ class Plugin(BaseAuthService): response = client.SendPacket(request) return (response.code == pyrad.packet.AccessAccept) except Exception: - get_logger().exception(Lanuages().gettext("Failed RADIUS auth request for user %r"), user) + get_logger().exception(Languages().gettext("Failed RADIUS auth request for user %r"), user) return False diff --git a/kvmd/plugins/hid/_mcu/__init__.py b/kvmd/plugins/hid/_mcu/__init__.py index 18a6c3a1..3ac2dc51 100644 --- a/kvmd/plugins/hid/_mcu/__init__.py +++ b/kvmd/plugins/hid/_mcu/__init__.py @@ -46,7 +46,7 @@ from ....validators.basic import valid_float_f01 from ....validators.os import valid_abs_path from ....validators.hw import valid_gpio_pin_optional -from ....lanuages import Lanuages +from ....languages import Languages from .. import BaseHid @@ -146,7 +146,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- self.__stop_event = multiprocessing.Event() - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext @classmethod def get_plugin_options(cls) -> dict: diff --git a/kvmd/plugins/hid/_mcu/gpio.py b/kvmd/plugins/hid/_mcu/gpio.py index bbe13f23..54b2e98d 100644 --- a/kvmd/plugins/hid/_mcu/gpio.py +++ b/kvmd/plugins/hid/_mcu/gpio.py @@ -27,7 +27,7 @@ import gpiod from ....logging import get_logger -from ....lanuages import Lanuages +from ....languages import Languages # ===== class Gpio: # pylint: disable=too-many-instance-attributes @@ -51,7 +51,7 @@ class Gpio: # pylint: disable=too-many-instance-attributes self.__line_request: (gpiod.LineRequest | None) = None self.__last_power: (bool | None) = None - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def __enter__(self) -> None: if self.__power_detect_pin >= 0 or self.__reset_pin >= 0: diff --git a/kvmd/plugins/hid/bt/__init__.py b/kvmd/plugins/hid/bt/__init__.py index 0ebffb37..251740e0 100644 --- a/kvmd/plugins/hid/bt/__init__.py +++ b/kvmd/plugins/hid/bt/__init__.py @@ -40,7 +40,7 @@ from .... import aiotools from .... import aiomulti from .... import aioproc -from ....lanuages import Lanuages +from ....languages import Languages from .. import BaseHid @@ -109,7 +109,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes stop_event=self.__stop_event, ) - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext @classmethod def get_plugin_options(cls) -> dict: diff --git a/kvmd/plugins/hid/bt/server.py b/kvmd/plugins/hid/bt/server.py index b325a225..c4147f1e 100644 --- a/kvmd/plugins/hid/bt/server.py +++ b/kvmd/plugins/hid/bt/server.py @@ -38,7 +38,7 @@ from .... import aiomulti from ....keyboard.mappings import UsbKey -from ....lanuages import Lanuages +from ....languages import Languages from ..otg.events import BaseEvent from ..otg.events import ClearEvent @@ -117,7 +117,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes self.__keys: list[UsbKey | None] = [None] * 6 self.__mouse_buttons = 0 - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def run(self) -> None: with self.__iface: diff --git a/kvmd/plugins/hid/ch9329/__init__.py b/kvmd/plugins/hid/ch9329/__init__.py index 05072b2e..c6923084 100644 --- a/kvmd/plugins/hid/ch9329/__init__.py +++ b/kvmd/plugins/hid/ch9329/__init__.py @@ -41,7 +41,7 @@ from ....validators.basic import valid_float_f01 from ....validators.os import valid_abs_path from ....validators.hw import valid_tty_speed -from ....lanuages import Lanuages +from ....languages import Languages from .. import BaseHid @@ -84,7 +84,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst self.__keyboard = Keyboard() self.__mouse = Mouse() - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext @classmethod def get_plugin_options(cls) -> dict: diff --git a/kvmd/plugins/hid/ch9329/chip.py b/kvmd/plugins/hid/ch9329/chip.py index bda399c7..77a6c118 100644 --- a/kvmd/plugins/hid/ch9329/chip.py +++ b/kvmd/plugins/hid/ch9329/chip.py @@ -25,7 +25,7 @@ import contextlib from typing import Generator -from ....lanuages import Lanuages +from ....languages import Languages # ===== @@ -37,7 +37,7 @@ class ChipResponseError(Exception): class ChipConnection: def __init__(self, tty: serial.Serial) -> None: self.__tty = tty - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def xfer(self, cmd: bytes) -> int: self.__send(cmd) diff --git a/kvmd/plugins/hid/otg/__init__.py b/kvmd/plugins/hid/otg/__init__.py index 202dddca..44671191 100644 --- a/kvmd/plugins/hid/otg/__init__.py +++ b/kvmd/plugins/hid/otg/__init__.py @@ -24,7 +24,7 @@ from typing import Iterable from typing import AsyncGenerator from typing import Any -from ....lanuages import Lanuages +from ....languages import Languages from ....logging import get_logger @@ -120,7 +120,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes def sysprep(self) -> None: udc = usb.find_udc(self.__udc) - get_logger(0).info(Lanuages().gettext("Using UDC %s"), udc) + get_logger(0).info(Languages().gettext("Using UDC %s"), udc) self.__keyboard_proc.start(udc) self.__mouse_proc.start(udc) if self.__mouse_alt_proc: diff --git a/kvmd/plugins/hid/otg/device.py b/kvmd/plugins/hid/otg/device.py index f7eef96c..412297fc 100644 --- a/kvmd/plugins/hid/otg/device.py +++ b/kvmd/plugins/hid/otg/device.py @@ -37,7 +37,7 @@ from .... import aiomulti from .... import aioproc from .... import usb -from ....lanuages import Lanuages +from ....languages import Languages from .events import BaseEvent @@ -78,7 +78,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in self.__logger: (logging.Logger | None) = None - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def start(self, udc: str) -> None: # type: ignore # pylint: disable=arguments-differ self.__udc_state_path = usb.get_udc_path(udc, usb.U_STATE) diff --git a/kvmd/plugins/hid/otg/keyboard.py b/kvmd/plugins/hid/otg/keyboard.py index 6842e1df..1b4b7ee8 100644 --- a/kvmd/plugins/hid/otg/keyboard.py +++ b/kvmd/plugins/hid/otg/keyboard.py @@ -41,7 +41,7 @@ from .events import get_led_scroll from .events import get_led_num from .events import make_keyboard_report -from ....lanuages import Lanuages +from ....languages import Languages # ===== class KeyboardProcess(BaseDeviceProcess): @@ -55,7 +55,7 @@ class KeyboardProcess(BaseDeviceProcess): self.__pressed_modifiers: set[UsbKey] = set() self.__pressed_keys: list[UsbKey | None] = [None] * 6 - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def cleanup(self) -> None: self._stop() diff --git a/kvmd/plugins/hid/otg/mouse.py b/kvmd/plugins/hid/otg/mouse.py index 20db2482..b2c9b9c0 100644 --- a/kvmd/plugins/hid/otg/mouse.py +++ b/kvmd/plugins/hid/otg/mouse.py @@ -36,7 +36,7 @@ from .events import MouseRelativeEvent from .events import MouseWheelEvent from .events import make_mouse_report -from ....lanuages import Lanuages +from ....languages import Languages # ===== class MouseProcess(BaseDeviceProcess): @@ -55,7 +55,7 @@ class MouseProcess(BaseDeviceProcess): self.__x = 0 # For absolute self.__y = 0 self.__win98_fix = False - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext def is_absolute(self) -> bool: return self.__absolute diff --git a/kvmd/plugins/hid/spi.py b/kvmd/plugins/hid/spi.py index b7bb748e..4eca835b 100644 --- a/kvmd/plugins/hid/spi.py +++ b/kvmd/plugins/hid/spi.py @@ -41,7 +41,7 @@ from ...validators.basic import valid_int_f1 from ...validators.basic import valid_float_f01 from ...validators.hw import valid_gpio_pin_optional -from ...lanuages import Lanuages +from ...languages import Lanuages from ._mcu import BasePhyConnection from ._mcu import BasePhy diff --git a/kvmd/plugins/msd/__init__.py b/kvmd/plugins/msd/__init__.py index 64997ba0..58c93d42 100644 --- a/kvmd/plugins/msd/__init__.py +++ b/kvmd/plugins/msd/__init__.py @@ -40,7 +40,7 @@ from ... import aiotools from .. import BasePlugin from .. import get_plugin_class -from ...lanuages import Lanuages +from ...languages import Languages # ===== class MsdError(Exception): @@ -53,43 +53,43 @@ class MsdOperationError(OperationError, MsdError): class MsdIsBusyError(IsBusyError, MsdError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("Performing another MSD operation, please try again later")) class MsdOfflineError(MsdOperationError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("MSD is not found")) class MsdConnectedError(MsdOperationError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("MSD is connected to Server, but shouldn't for this operation")) class MsdDisconnectedError(MsdOperationError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("MSD is disconnected from Server, but should be for this operation")) class MsdImageNotSelected(MsdOperationError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("The image is not selected")) class MsdUnknownImageError(MsdOperationError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("The image is not found in the storage")) class MsdImageExistsError(MsdOperationError): def __init__(self) -> None: - gettext=Lanuages().gettext + gettext=Languages().gettext super().__init__(gettext("This image is already exists")) diff --git a/kvmd/plugins/msd/disabled.py b/kvmd/plugins/msd/disabled.py index ad9fe3d0..35be6ad0 100644 --- a/kvmd/plugins/msd/disabled.py +++ b/kvmd/plugins/msd/disabled.py @@ -31,7 +31,7 @@ from . import BaseMsdReader from . import BaseMsdWriter from . import BaseMsd -from ...lanuages import Lanuages +from ...languages import Lanuages # ===== class MsdDisabledError(MsdOperationError): diff --git a/kvmd/plugins/msd/otg/__init__.py b/kvmd/plugins/msd/otg/__init__.py index 43a2a6cd..b850c79c 100644 --- a/kvmd/plugins/msd/otg/__init__.py +++ b/kvmd/plugins/msd/otg/__init__.py @@ -28,7 +28,7 @@ import time from typing import AsyncGenerator -from ....lanuages import Lanuages +from ....languages import Languages from ....logging import get_logger from ....inotify import InotifyMask @@ -44,7 +44,7 @@ from ....validators.kvm import valid_msd_image_name from .... import aiotools from .... import fstab -from ....lanuages import Lanuages +from ....languages import Languages from .. import MsdIsBusyError from .. import MsdOfflineError @@ -142,7 +142,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes self.__notifier = aiotools.AioNotifier() self.__state = _State(self.__notifier) - self.gettext=Lanuages().gettext + self.gettext=Languages().gettext logger = get_logger(0) logger.info(self.gettext("Using OTG gadget %r as MSD"), gadget) diff --git a/kvmd/plugins/msd/otg/drive.py b/kvmd/plugins/msd/otg/drive.py index ca47d7e4..c9a393fc 100644 --- a/kvmd/plugins/msd/otg/drive.py +++ b/kvmd/plugins/msd/otg/drive.py @@ -27,13 +27,13 @@ from .... import usb from .. import MsdOperationError -from ....lanuages import Lanuages +from ....languages import Languages # ===== class MsdDriveLockedError(MsdOperationError): def __init__(self) -> None: - super().__init__(Lanuages().gettext("MSD drive is locked on IO operation")) + super().__init__(Languages().gettext("MSD drive is locked on IO operation")) # ===== diff --git a/kvmd/plugins/msd/otg/storage.py b/kvmd/plugins/msd/otg/storage.py index 0497ca80..ed10db11 100644 --- a/kvmd/plugins/msd/otg/storage.py +++ b/kvmd/plugins/msd/otg/storage.py @@ -31,7 +31,7 @@ from typing import Optional import aiofiles import aiofiles.os -from ....lanuages import Lanuages +from ....languages import Languages from .... import aiotools from .... import aiohelpers @@ -294,4 +294,4 @@ class Storage(_StorageDc): async def remount_rw(self, rw: bool, fatal: bool=True) -> None: if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)): if fatal: - raise MsdError(Lanuages().gettext("Can't execute remount helper")) + raise MsdError(Languages().gettext("Can't execute remount helper")) diff --git a/kvmd/plugins/ugpio/anelpwr.py b/kvmd/plugins/ugpio/anelpwr.py index 647869b1..11cb2693 100644 --- a/kvmd/plugins/ugpio/anelpwr.py +++ b/kvmd/plugins/ugpio/anelpwr.py @@ -41,7 +41,7 @@ from ...validators.basic import valid_bool from ...validators.basic import valid_number from ...validators.basic import valid_float_f01 -from ...lanuages import Lanuages +from ...languages import Languages from . import BaseUserGpioDriver from . import GpioDriverOfflineError @@ -149,7 +149,7 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute htclient.raise_not_200(response) except Exception as err: - get_logger().error(Lanuages().gettext("Failed ANELPWR POST request to pin %s: %s"), pin, tools.efmt(err)) + get_logger().error(Languages().gettext("Failed ANELPWR POST request to pin %s: %s"), pin, tools.efmt(err)) raise GpioDriverOfflineError(self) self.__update_notifier.notify() diff --git a/kvmd/usb.py b/kvmd/usb.py index 53ed486f..23fa82d5 100644 --- a/kvmd/usb.py +++ b/kvmd/usb.py @@ -22,7 +22,7 @@ import os -from .lanuages import Lanuages +from .languages import Languages from . import env @@ -33,10 +33,10 @@ def find_udc(udc: str) -> str: candidates = sorted(os.listdir(path)) if not udc: if len(candidates) == 0: - raise RuntimeError(Lanuages().gettext("Can't find any UDC")) + raise RuntimeError(Languages().gettext("Can't find any UDC")) udc = candidates[0] elif udc not in candidates: - raise RuntimeError(Lanuages().gettext(f"Can't find selected UDC: {udc}")) + raise RuntimeError(Languages().gettext(f"Can't find selected UDC: {udc}")) return udc # fe980000.usb diff --git a/kvmd/validators/languages.py b/kvmd/validators/languages.py new file mode 100644 index 00000000..1455c295 --- /dev/null +++ b/kvmd/validators/languages.py @@ -0,0 +1,34 @@ +# ========================================================================== # +# # +# KVMD - The main PiKVM daemon. # +# # +# Copyright (C) 2018-2024 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +# ========================================================================== # + + +from typing import Any + +from . import check_string_in_list + +from .basic import valid_number + + +# ===== +def valid_languages(arg: Any) -> str: + return check_string_in_list(arg, "Languages", ["zh", "en", "default"]) + + diff --git a/message.pot b/message.pot index 01bec020..af92d405 100644 --- a/message.pot +++ b/message.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-08-12 22:07+0800\n" +"POT-Creation-Date: 2024-08-14 22:40+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -89,37 +89,34 @@ msgstr "" msgid "Can't find any UDC" msgstr "" -#: kvmd/apps/__init__.py:132 -msgid "Set config file path" -msgstr "" - -#: kvmd/apps/__init__.py:134 -msgid "Override config options list (like sec/sub/opt=value)" -msgstr "" - -#: kvmd/apps/__init__.py:136 -msgid "View current configuration (include all overrides)" -msgstr "" - -#: kvmd/apps/__init__.py:139 -msgid "Run the service" +#: kvmd/apps/__init__.py:164 +msgid "INFO" msgstr "" #: kvmd/apps/__init__.py:165 +msgid "WARNING" +msgstr "" + +#: kvmd/apps/__init__.py:166 +msgid "ERROR" +msgstr "" + +#: kvmd/apps/__init__.py:176 msgid "" "To prevent accidental startup, you must specify the --run option to " "start.\n" msgstr "" -#: kvmd/apps/__init__.py:165 +#: kvmd/apps/__init__.py:176 msgid "Try the --help option to find out what this service does.\n" msgstr "" -#: kvmd/apps/__init__.py:165 +#: kvmd/apps/__init__.py:176 msgid "Make sure you understand exactly what you are doing!" msgstr "" #: kvmd/apps/kvmd/__init__.py:115 kvmd/apps/otgnet/__init__.py:132 +#: kvmd/apps/vnc/server.py:541 msgid "Bye-bye" msgstr "" @@ -171,6 +168,48 @@ msgstr "" msgid "Logged out user %r (%d)" msgstr "" +#: kvmd/apps/kvmd/server.py:89 +msgid "This streamer does not support quality settings" +msgstr "" + +#: kvmd/apps/kvmd/server.py:94 +msgid "This streamer does not support resolution settings" +msgstr "" + +#: kvmd/apps/kvmd/server.py:99 +msgid "This streamer does not support H264" +msgstr "" + +#: kvmd/apps/kvmd/server.py:298 +msgid "Waiting short tasks ..." +msgstr "" + +#: kvmd/apps/kvmd/server.py:300 +msgid "Stopping system tasks ..." +msgstr "" + +#: kvmd/apps/kvmd/server.py:302 +msgid "Disconnecting clients ..." +msgstr "" + +#: kvmd/apps/kvmd/server.py:304 +msgid "On-Shutdown complete" +msgstr "" + +#: kvmd/apps/kvmd/server.py:310 +#, python-format +msgid "Cleaning up %s ..." +msgstr "" + +#: kvmd/apps/kvmd/server.py:314 +#, python-format +msgid "Cleanup error on %s" +msgstr "" + +#: kvmd/apps/kvmd/server.py:315 +msgid "On-Cleanup complete" +msgstr "" + #: kvmd/apps/kvmd/streamer.py:245 msgid "Streamer stop cancelled" msgstr "" @@ -374,6 +413,115 @@ msgstr "" msgid "Using OTG Ethernet interface %r ..." msgstr "" +#: kvmd/apps/vnc/server.py:163 +#, python-format +msgid "%s [kvmd]: Waiting for the SetEncodings message ..." +msgstr "" + +#: kvmd/apps/vnc/server.py:165 +msgid "No SetEncodings message recieved from the client in 5 secs" +msgstr "" + +#: kvmd/apps/vnc/server.py:169 +#, python-format +msgid "%s [kvmd]: Applying HID params: mouse_output=%s ..." +msgstr "" + +#: kvmd/apps/vnc/server.py:177 +msgid "KVMD closed the websocket (the server may have been stopped)" +msgstr "" + +#: kvmd/apps/vnc/server.py:211 +#, python-format +msgid "%s [streamer]: Streaming ..." +msgstr "" + +#: kvmd/apps/vnc/server.py:216 +msgid "No signal" +msgstr "" + +#: kvmd/apps/vnc/server.py:220 +#, python-format +msgid "%s [streamer]: Permanent error: %s; switching to %s ..." +msgstr "" + +#: kvmd/apps/vnc/server.py:222 +#, python-format +msgid "%s [streamer]: Waiting for stream: %s" +msgstr "" + +#: kvmd/apps/vnc/server.py:223 +msgid "Waiting for stream ..." +msgstr "" + +#: kvmd/apps/vnc/server.py:234 +#, python-format +msgid "%s [streamer]: Using preferred %s" +msgstr "" + +#: kvmd/apps/vnc/server.py:240 +#, python-format +msgid "%s [streamer]: Using default %s" +msgstr "" + +#: kvmd/apps/vnc/server.py:305 +msgid "The client doesn't want to accept H264 anymore" +msgstr "" + +#: kvmd/apps/vnc/server.py:311 +msgid "format" +msgstr "" + +#: kvmd/apps/vnc/server.py:417 +#, python-format +msgid "%s [main]: Applying streamer params: jpeg_quality=%s; desired_fps=%d ..." +msgstr "" + +#: kvmd/apps/vnc/server.py:467 +#, python-format +msgid "%s [entry]: Connection is closed in an emergency" +msgstr "" + +#: kvmd/apps/vnc/server.py:472 +#, python-format +msgid "%s [entry]: Connected client" +msgstr "" + +#: kvmd/apps/vnc/server.py:491 +#, python-format +msgid "%s [entry]: Can't check KVMD auth mode: %s" +msgstr "" + +#: kvmd/apps/vnc/server.py:513 +#, python-format +msgid "%s [entry]: Unhandled exception in client task" +msgstr "" + +#: kvmd/apps/vnc/server.py:523 +#, python-format +msgid "Listening VNC on TCP [%s]:%d ..." +msgstr "" + +#: kvmd/apps/vnc/vncauth.py:63 +msgid "Unhandled exception while reading VNCAuth passwd file" +msgstr "" + +#: kvmd/apps/vnc/vncauth.py:74 +msgid "Missing ' -> ' operator" +msgstr "" + +#: kvmd/apps/vnc/vncauth.py:78 +msgid "Missing ':' operator in KVMD credentials (right part)" +msgstr "" + +#: kvmd/apps/vnc/vncauth.py:83 +msgid "Empty KVMD user (right part)" +msgstr "" + +#: kvmd/apps/vnc/vncauth.py:86 +msgid "Duplicating VNC password (left part)" +msgstr "" + #: kvmd/keyboard/keysym.py:69 #, python-format msgid "Invalid modifier key at mapping %s: %s / %s" diff --git a/testenv/v2-hdmiusb-rpi4.override.yaml b/testenv/v2-hdmiusb-rpi4.override.yaml index 8d5568f6..cce929cd 100644 --- a/testenv/v2-hdmiusb-rpi4.override.yaml +++ b/testenv/v2-hdmiusb-rpi4.override.yaml @@ -65,3 +65,7 @@ nginx: janus: cmd: - "/bin/true" + +languages: + console: zh + web: zh