diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 00000000..cd97e8bd --- /dev/null +++ b/babel.cfg @@ -0,0 +1 @@ +[python: kvmd/**.py] \ No newline at end of file diff --git a/kvmd/apps/__init__.py b/kvmd/apps/__init__.py index 035b3cc4..e8410f8a 100644 --- a/kvmd/apps/__init__.py +++ b/kvmd/apps/__init__.py @@ -31,6 +31,8 @@ import pygments import pygments.lexers.data import pygments.formatters +from gettext import translation + from .. import tools from ..mouse import MouseRange @@ -103,6 +105,7 @@ 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 # ===== def init( @@ -124,15 +127,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: @@ -158,10 +162,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!" - ) + _("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!")) return (parser, remaining, config) diff --git a/kvmd/apps/kvmd/auth.py b/kvmd/apps/kvmd/auth.py index 008e8a4f..ec7c8295 100644 --- a/kvmd/apps/kvmd/auth.py +++ b/kvmd/apps/kvmd/auth.py @@ -23,6 +23,8 @@ import secrets import pyotp +from gettext import translation + from ...logging import get_logger from ... import aiotools @@ -49,26 +51,26 @@ class AuthManager: totp_secret_path: str, ) -> None: - + _ = translation(domain="message",localedir="/kvmd/i18n",languages=["zh"]).gettext self.__enabled = enabled if not enabled: - get_logger().warning("AUTHORIZATION IS DISABLED") + get_logger().warning(_("AUTHORIZATION IS DISABLED")) self.__unauth_paths = frozenset(unauth_paths) # To speed up for path in self.__unauth_paths: - get_logger().warning("Authorization is disabled for API %r", path) + get_logger().warning(_("Authorization is disabled for API %r"), path) self.__internal_service: (BaseAuthService | None) = None if enabled: self.__internal_service = get_auth_service_class(internal_type)(**internal_kwargs) - get_logger().info("Using internal auth service %r", self.__internal_service.get_plugin_name()) + get_logger().info(_("Using internal auth service %r"), self.__internal_service.get_plugin_name()) self.__force_internal_users = force_internal_users self.__external_service: (BaseAuthService | None) = None if enabled and external_type: self.__external_service = get_auth_service_class(external_type)(**external_kwargs) - get_logger().info("Using external auth service %r", self.__external_service.get_plugin_name()) + get_logger().info(_("Using external auth service %r"), self.__external_service.get_plugin_name()) self.__totp_secret_path = totp_secret_path @@ -96,7 +98,7 @@ class AuthManager: if secret: code = passwd[-6:] if not pyotp.TOTP(secret).verify(code): - get_logger().error("Got access denied for user %r by TOTP", user) + get_logger().error(_("Got access denied for user %r by TOTP"), user) return False passwd = passwd[:-6] @@ -107,9 +109,9 @@ class AuthManager: ok = (await service.authorize(user, passwd)) if ok: - get_logger().info("Authorized user %r via auth service %r", user, service.get_plugin_name()) + get_logger().info(_("Authorized user %r via auth service %r"), user, service.get_plugin_name()) else: - get_logger().error("Got access denied for user %r from auth service %r", user, service.get_plugin_name()) + get_logger().error(_("Got access denied for user %r from auth service %r"), user, service.get_plugin_name()) return ok async def login(self, user: str, passwd: str) -> (str | None): @@ -119,7 +121,7 @@ class AuthManager: if (await self.authorize(user, passwd)): token = self.__make_new_token() self.__tokens[token] = user - get_logger().info("Logged in user %r", user) + get_logger().info(_("Logged in user %r"), user) return token else: return None @@ -129,7 +131,7 @@ class AuthManager: token = secrets.token_hex(32) if token not in self.__tokens: return token - raise AssertionError("Can't generate new unique token") + raise AssertionError(_("Can't generate new unique token")) def logout(self, token: str) -> None: assert self.__enabled @@ -140,7 +142,7 @@ class AuthManager: if r_user == user: count += 1 del self.__tokens[r_token] - get_logger().info("Logged out user %r (%d)", user, count) + get_logger().info(_("Logged out user %r (%d)"), user, count) def check(self, token: str) -> (str | None): assert self.__enabled diff --git a/kvmd/htclient.py b/kvmd/htclient.py index 724eddcd..df8927d7 100644 --- a/kvmd/htclient.py +++ b/kvmd/htclient.py @@ -28,6 +28,8 @@ from typing import AsyncGenerator import aiohttp import aiohttp.multipart +from .lanuages import Lanuages + from . import __version__ @@ -58,7 +60,7 @@ def get_filename(response: aiohttp.ClientResponse) -> str: try: return os.path.basename(response.url.path) except Exception: - raise aiohttp.ClientError("Can't determine filename") + raise aiohttp.ClientError(Lanuages().gettext("Can't determine filename")) @contextlib.asynccontextmanager diff --git a/kvmd/htserver.py b/kvmd/htserver.py index 4740ac7a..ee055c15 100644 --- a/kvmd/htserver.py +++ b/kvmd/htserver.py @@ -52,6 +52,8 @@ from .errors import IsBusyError from .validators import ValidatorError +from .lanuages import Lanuages + from . import aiotools @@ -280,6 +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 def run( self, @@ -350,7 +353,7 @@ class HttpServer: async with self.__ws_sessions_lock: self.__ws_sessions.append(ws) - get_logger(2).info("Registered new client session: %s; clients now: %d", ws, len(self.__ws_sessions)) + get_logger(2).info(self.gettext("Registered new client session: %s; clients now: %d"), ws, len(self.__ws_sessions)) try: await self._on_ws_opened() @@ -365,20 +368,20 @@ class HttpServer: try: (event_type, event) = parse_ws_event(msg.data) except Exception as err: - logger.error("Can't parse JSON event from websocket: %r", err) + logger.error(self.gettext("Can't parse JSON event from websocket: %r"), err) else: handler = self.__ws_handlers.get(event_type) if handler: await handler(ws, event) else: - logger.error("Unknown websocket event: %r", msg.data) + logger.error(self.gettext("Unknown websocket event: %r"), msg.data) elif msg.type == WSMsgType.BINARY and len(msg.data) >= 1: handler = self.__ws_bin_handlers.get(msg.data[0]) if handler: await handler(ws, msg.data[1:]) else: - logger.error("Unknown websocket binary event: %r", msg.data) + logger.error(self.gettext("Unknown websocket binary event: %r"), msg.data) else: break @@ -409,7 +412,7 @@ class HttpServer: async with self.__ws_sessions_lock: try: self.__ws_sessions.remove(ws) - get_logger(3).info("Removed client socket: %s; clients now: %d", ws, len(self.__ws_sessions)) + get_logger(3).info(self.gettext("Removed client socket: %s; clients now: %d"), ws, len(self.__ws_sessions)) await ws.wsr.close() except Exception: pass diff --git a/kvmd/i18n/zh/LC_MESSAGES/message.mo b/kvmd/i18n/zh/LC_MESSAGES/message.mo new file mode 100644 index 00000000..e43d416b Binary files /dev/null and b/kvmd/i18n/zh/LC_MESSAGES/message.mo differ diff --git a/kvmd/i18n/zh/LC_MESSAGES/message.po b/kvmd/i18n/zh/LC_MESSAGES/message.po new file mode 100644 index 00000000..af3a1f87 --- /dev/null +++ b/kvmd/i18n/zh/LC_MESSAGES/message.po @@ -0,0 +1,399 @@ +# Chinese translations for PROJECT. +# Copyright (C) 2024 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2024-08-06 21:17+0800\n" +"PO-Revision-Date: 2024-08-06 21:17+0800\n" +"Last-Translator: \n" +"Language: zh\n" +"Language-Team: zh \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.15.0\n" + +#: kvmd/htclient.py:63 +msgid "Can't determine filename" +msgstr "无法确定文件名" + +#: kvmd/inotify.py:199 +#, python-format +msgid "Watching for %s" +msgstr "监视 %s" + +#: kvmd/inotify.py:258 +#, python-format +msgid "Unwatching %s because IGNORED was received" +msgstr "因收到忽略标识而取消监视 %s" + +#: kvmd/usb.py:36 +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:165 +msgid "" +"To prevent accidental startup, you must specify the --run option to " +"start.\n" +msgstr "为了防止意外启动,必须在启动时指定 --run 选项。\n" + +#: kvmd/apps/__init__.py:165 +msgid "Try the --help option to find out what this service does.\n" +msgstr "尝试使用 --help 选项来了解某项服务的功能。\n" + +#: kvmd/apps/__init__.py:165 +msgid "Make sure you understand exactly what you are doing!" +msgstr "请确定你自己在做什么!" + +#: kvmd/apps/kvmd/auth.py:57 +msgid "AUTHORIZATION IS DISABLED" +msgstr "身份验证服务已被禁用" + +#: kvmd/apps/kvmd/auth.py:61 +#, python-format +msgid "Authorization is disabled for API %r" +msgstr "由于 API %r 身份验证服务已被禁用" + +#: kvmd/apps/kvmd/auth.py:66 +#, python-format +msgid "Using internal auth service %r" +msgstr "使用内部身份验证服务 %r" + +#: kvmd/apps/kvmd/auth.py:73 +#, python-format +msgid "Using external auth service %r" +msgstr "使用外部身份验证服务 %r" + +#: kvmd/apps/kvmd/auth.py:101 +#, python-format +msgid "Got access denied for user %r by TOTP" +msgstr "用户 %r 被 TOTP 拒绝访问" + +#: kvmd/apps/kvmd/auth.py:112 +#, python-format +msgid "Authorized user %r via auth service %r" +msgstr "用户 %r 已通过身份认证服务 %r 授权" + +#: kvmd/apps/kvmd/auth.py:114 +#, python-format +msgid "Got access denied for user %r from auth service %r" +msgstr "身份验证服务 %r 拒绝了用户 %r 的访问请求" + +#: kvmd/apps/kvmd/auth.py:124 +#, python-format +msgid "Logged in user %r" +msgstr "已登录用户 %r" + +#: kvmd/apps/kvmd/auth.py:134 +msgid "Can't generate new unique token" +msgstr "无法生成新的唯一令牌" + +#: kvmd/apps/kvmd/auth.py:145 +#, python-format +msgid "Logged out user %r (%d)" +msgstr "已注销用户 %r (%d)" + +#: kvmd/plugins/atx/__init__.py:45 +msgid "Performing another ATX operation, please try again later" +msgstr "正在处理另一个 ATX 动作,请稍等" + +#: kvmd/plugins/atx/gpio.py:209 +#, python-format +msgid "Clicked ATX button %r" +msgstr "ATX 按钮 %r 被点击" + +#: kvmd/plugins/auth/http.py:94 +#, python-format +msgid "Failed HTTP auth request for user %r" +msgstr "用户 %r 的 HTTP 验证请求失败" + +#: kvmd/plugins/auth/ldap.py:106 +#, python-format +msgid "LDAP server is down: %s" +msgstr "LDAP 服务已下线: %s" + +#: kvmd/plugins/auth/ldap.py:108 +#, python-format +msgid "Unexpected LDAP error: %s" +msgstr "LDAP 未知错误: %s" + +#: kvmd/plugins/auth/pam.py:91 +#, python-format +msgid "Unallowed UID of user %r: uid=%d < allow_uids_at=%d" +msgstr "未授权 UID user %r: uid=%d < allow_uids_at=%d" + +#: kvmd/plugins/auth/pam.py:97 +#, python-format +msgid "Can't authorize user %r using PAM: code=%d; reason=%s" +msgstr "无法使用 PAM 验证用户 %r :code=%d;reason=%s" + +#: kvmd/plugins/auth/radius.py:445 +#, python-format +msgid "Failed RADIUS auth request for user %r" +msgstr "用户 %r 的 RADIUS 验证请求失败" + +#: kvmd/plugins/hid/bt/__init__.py:137 kvmd/plugins/hid/ch9329/__init__.py:99 +msgid "Starting HID daemon ..." +msgstr "正在启动 HID 守护程序......" + +#: kvmd/plugins/hid/bt/__init__.py:182 kvmd/plugins/hid/ch9329/__init__.py:141 +msgid "Stopping HID daemon ..." +msgstr "正在启停止 HID 守护程序......" + +#: kvmd/plugins/hid/bt/__init__.py:231 +msgid "Unexpected HID error" +msgstr "未知 HID 错误" + +#: kvmd/plugins/hid/bt/server.py:153 +#, python-format +msgid "Listening [%s]:%d for %s ..." +msgstr "监听 [%s]:%d for %s ..." + +#: kvmd/plugins/hid/bt/server.py:190 +#, python-format +msgid "CTL socket error on %s: %s" +msgstr "CTL 套接字错误 : %s: %s" + +#: kvmd/plugins/hid/bt/server.py:204 +#, python-format +msgid "INT socket error on %s: %s" +msgstr "INT 套接字错误 : %s: %s" + +#: kvmd/plugins/hid/bt/server.py:287 +#, python-format +msgid "Can't send %s report to %s: %s" +msgstr "无法向 %s 发送 %s 报告: %s" + +#: kvmd/plugins/hid/bt/server.py:314 +#, python-format +msgid "Can't accept %s client" +msgstr "无法接受 %s 客户端" + +#: kvmd/plugins/hid/bt/server.py:319 +#, python-format +msgid "Refused %s client: %s: max clients reached" +msgstr "拒绝 %s 客户端:%s:已到达最大客户端数量" + +#: kvmd/plugins/hid/bt/server.py:328 +#, python-format +msgid "Accepted %s client: %s" +msgstr "已接受 %s 客户端: %s" + +#: kvmd/plugins/hid/bt/server.py:340 +#, python-format +msgid "Closed %s client %s" +msgstr "关闭 %s 客户端 %s" + +#: kvmd/plugins/hid/bt/server.py:375 +msgid "Publishing ..." +msgstr "广播中......" + +#: kvmd/plugins/hid/bt/server.py:375 +msgid "Unpublishing ..." +msgstr "取消广播......" + +#: kvmd/plugins/hid/bt/server.py:379 +#, python-format +msgid "Can't change public mode: %s" +msgstr "无法更改公共模式:%s" + +#: kvmd/plugins/hid/bt/server.py:383 +#, python-format +msgid "Unpairing %s ..." +msgstr "正在取消配对 %s ......" + +#: kvmd/plugins/hid/bt/server.py:387 +#, python-format +msgid "Can't unpair %s: %s" +msgstr "无法取消配对 %s: %s" + +#: kvmd/plugins/hid/ch9329/__init__.py:178 +#, python-format +msgid "HID : mouse output = %s" +msgstr "HID:鼠标输出 = %s" + +#: kvmd/plugins/hid/ch9329/__init__.py:208 +msgid "Unexpected error in the run loop" +msgstr "运行循环中出现意外错误" + +#: kvmd/plugins/hid/ch9329/__init__.py:231 +msgid "Unexpected error in the HID loop" +msgstr "HID 循环中出现意外错误" + +#: kvmd/plugins/hid/ch9329/chip.py:58 +msgid "Too short response, HID might be disconnected" +msgstr "响应时间太短,HID 可能已断开" + +#: kvmd/plugins/hid/ch9329/chip.py:64 +msgid "Invalid response checksum" +msgstr "响应校验和无效" + +#: kvmd/plugins/hid/otg/device.py:125 +#, python-format +msgid "Unexpected HID-%s error" +msgstr "HID-%s 意外错误" + +#: kvmd/plugins/hid/otg/device.py:152 +#, python-format +msgid "Stopping HID-%s daemon ..." +msgstr "清除 HID-%s 鼠标事件 ......" + +#: kvmd/plugins/hid/otg/device.py:197 +#, python-format +msgid "HID-%s write() error: written (%s) != report length (%d)" +msgstr "HID-%s write() 错误:写入 (%s) != 报告长度 (%d)" + +#: kvmd/plugins/hid/otg/device.py:205 +#, python-format +msgid "HID-%s busy/unplugged (write): %s" +msgstr "HID-%s 忙碌/已拔(写入): %s" + +#: kvmd/plugins/hid/otg/device.py:207 +#, python-format +msgid "Can't write report to HID-%s" +msgstr "无法将内容写入 HID-%s" + +#: kvmd/plugins/hid/otg/device.py:224 +#, python-format +msgid "Can't select() for read HID-%s: %s" +msgstr "读取 HID-%s 时无法选择 %s" + +#: kvmd/plugins/hid/otg/device.py:232 +#, python-format +msgid "HID-%s busy/unplugged (read): %s" +msgstr "HID-%s 忙碌/已拔(读取): %s" + +#: kvmd/plugins/hid/otg/device.py:234 +#, python-format +msgid "Can't read report from HID-%s" +msgstr "无法读取 HID-%s 的回复" + +#: kvmd/plugins/hid/otg/device.py:251 +#, python-format +msgid "Missing HID-%s device: %s" +msgstr "丢失 HID-%s 设备:%s" + +#: kvmd/plugins/hid/otg/device.py:263 +#, python-format +msgid "Can't open HID-%s device %s: %s" +msgstr "无法打开 HID-%s 设备 %s: %s" + +#: kvmd/plugins/hid/otg/device.py:274 +#, python-format +msgid "HID-%s is busy/unplugged (write select)" +msgstr "HID-%s 忙碌/已拔(写入切换)" + +#: kvmd/plugins/hid/otg/device.py:276 +#, python-format +msgid "Can't select() for write HID-%s: %s" +msgstr "写 HID-%s 时无法选择 %s" + +#: kvmd/plugins/hid/otg/keyboard.py:62 +msgid "Clearing HID-keyboard events ..." +msgstr "清除 HID 键盘事件 ......" + +#: kvmd/plugins/hid/otg/mouse.py:71 +msgid "Clearing HID-mouse events ..." +msgstr "清除 HID 鼠标事件 ......" + +#: kvmd/plugins/msd/__init__.py:57 +msgid "Performing another MSD operation, please try again later" +msgstr "正在执行另一项虚拟存储驱动器操作,请稍后再试" + +#: kvmd/plugins/msd/__init__.py:63 +msgid "MSD is not found" +msgstr "虚拟存储驱动器不存在" + +#: kvmd/plugins/msd/__init__.py:69 +msgid "MSD is connected to Server, but shouldn't for this operation" +msgstr "虚拟存储驱动器已连接到服务器,但本操作不应连接到服务器" + +#: kvmd/plugins/msd/__init__.py:75 +msgid "MSD is disconnected from Server, but should be for this operation" +msgstr "虚拟存储驱动器与服务器断开连接,但进行此操作时应断开连接" + +#: kvmd/plugins/msd/__init__.py:81 +msgid "The image is not selected" +msgstr "没有选中任何镜像" + +#: kvmd/plugins/msd/__init__.py:87 +msgid "The image is not found in the storage" +msgstr "存储区中没有找到镜像" + +#: kvmd/plugins/msd/__init__.py:93 +msgid "This image is already exists" +msgstr "此镜像已存在" + +#: kvmd/plugins/msd/disabled.py:40 +msgid "MSD is disabled" +msgstr "虚拟存储驱动器已被禁用" + +#: kvmd/plugins/msd/otg/__init__.py:148 +#, python-format +msgid "Using OTG gadget %r as MSD" +msgstr "使用 OTG gadget %r 作为 MSD" + +#: kvmd/plugins/msd/otg/__init__.py:223 +msgid "Can't reset MSD properly" +msgstr "无法正确重置虚拟存储驱动器" + +#: kvmd/plugins/msd/otg/__init__.py:442 +msgid "Unexpected MSD watcher error" +msgstr "虚拟存储驱动器监视器意外出错" + +#: kvmd/plugins/msd/otg/__init__.py:461 +msgid "Probing to remount storage ..." +msgstr "探测以重新挂载存储 ......" + +#: kvmd/plugins/msd/otg/__init__.py:467 +msgid "Error while reloading MSD state; switching to offline" +msgstr "重新加载 MSD 状态时出错;切换到离线状态" + +#: kvmd/plugins/msd/otg/__init__.py:495 +#, python-format +msgid "Setting up initial image %r ..." +msgstr "设置初始镜像 %r ......" + +#: kvmd/plugins/msd/otg/__init__.py:501 +msgid "Can't setup initial image: ignored" +msgstr "无法设置初始镜像 %r: 已忽略" + +#: kvmd/plugins/msd/otg/__init__.py:503 +#, python-format +msgid "Can't find initial image %r: ignored" +msgstr "找不到初始镜像 %r: 已忽略" + +#: kvmd/plugins/msd/otg/drive.py:36 +msgid "MSD drive is locked on IO operation" +msgstr "虚拟存储驱动器在 IO 操作时被锁定" + +#: kvmd/plugins/msd/otg/storage.py:297 +msgid "Can't execute remount helper" +msgstr "无法执行重新挂载辅助程序" + +#: kvmd/plugins/ugpio/anelpwr.py:152 +#, python-format +msgid "Failed ANELPWR POST request to pin %s: %s" +msgstr "向引脚 %s 发送 ANELPWR POST 请求失败:%s" + diff --git a/kvmd/inotify.py b/kvmd/inotify.py index 700247ef..c883aac1 100644 --- a/kvmd/inotify.py +++ b/kvmd/inotify.py @@ -34,6 +34,8 @@ from typing import Generator from .logging import get_logger +from .lanuages import Lanuages + from . import aiotools from . import libc @@ -194,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("Watching for %s", path) + get_logger().info(Lanuages().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 @@ -253,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("Unwatching %s because IGNORED was received", ignored_path) + logger.info(Lanuages().gettext("Unwatching %s because IGNORED was received"), ignored_path) del self.__wd_by_path[ignored_path] continue diff --git a/kvmd/lanuages.py b/kvmd/lanuages.py new file mode 100644 index 00000000..c84be57d --- /dev/null +++ b/kvmd/lanuages.py @@ -0,0 +1,7 @@ +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 e9496a65..74e0b4c9 100644 --- a/kvmd/plugins/atx/__init__.py +++ b/kvmd/plugins/atx/__init__.py @@ -25,6 +25,8 @@ from typing import AsyncGenerator from ...errors import OperationError from ...errors import IsBusyError +from ...lanuages import Lanuages + from .. import BasePlugin from .. import get_plugin_class @@ -40,7 +42,7 @@ class AtxOperationError(OperationError, AtxError): class AtxIsBusyError(IsBusyError, AtxError): def __init__(self) -> None: - super().__init__("Performing another ATX operation, please try again later") + super().__init__(Lanuages().gettext("Performing another ATX operation, please try again later")) # ===== diff --git a/kvmd/plugins/atx/gpio.py b/kvmd/plugins/atx/gpio.py index 6df57bb3..c191fded 100644 --- a/kvmd/plugins/atx/gpio.py +++ b/kvmd/plugins/atx/gpio.py @@ -39,6 +39,8 @@ 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 . import AtxIsBusyError from . import BaseAtx @@ -88,6 +90,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes notifier=self.__notifier, ) + @classmethod def get_plugin_options(cls) -> dict: return { @@ -190,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( - f"Can't perform ATX {name} click or operation was not completed", + Lanuages().gettext(f"Can't perform ATX {name} click or operation was not completed"), self.__region, self.__inner_click, name, pin, delay, ) @@ -203,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("Clicked ATX button %r", name) + get_logger(0).info(Lanuages().gettext("Clicked ATX button %r"), name) diff --git a/kvmd/plugins/auth/http.py b/kvmd/plugins/auth/http.py index 54308279..a66740c1 100644 --- a/kvmd/plugins/auth/http.py +++ b/kvmd/plugins/auth/http.py @@ -32,6 +32,8 @@ from ...logging import get_logger from ... import htclient +from ...lanuages import Lanuages + from . import BaseAuthService @@ -89,7 +91,7 @@ class Plugin(BaseAuthService): htclient.raise_not_200(response) return True except Exception: - get_logger().exception("Failed HTTP auth request for user %r", user) + get_logger().exception(Lanuages().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 fa6b9fff..a7de797e 100644 --- a/kvmd/plugins/auth/ldap.py +++ b/kvmd/plugins/auth/ldap.py @@ -33,6 +33,8 @@ from ...logging import get_logger from ... import tools from ... import aiotools +from ...lanuages import Lanuages + from . import BaseAuthService @@ -101,9 +103,9 @@ class Plugin(BaseAuthService): except ldap.INVALID_CREDENTIALS: pass except ldap.SERVER_DOWN as err: - get_logger().error("LDAP server is down: %s", tools.efmt(err)) + get_logger().error(Lanuages().gettext("LDAP server is down: %s"), tools.efmt(err)) except Exception as err: - get_logger().error("Unexpected LDAP error: %s", tools.efmt(err)) + get_logger().error(Lanuages().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 119951ab..d16c08fe 100644 --- a/kvmd/plugins/auth/pam.py +++ b/kvmd/plugins/auth/pam.py @@ -34,6 +34,8 @@ from ...logging import get_logger from ... import aiotools +from ...lanuages import Lanuages + from . import BaseAuthService @@ -86,13 +88,13 @@ class Plugin(BaseAuthService): return False else: if uid < self.__allow_uids_at: - get_logger().error("Unallowed UID of user %r: uid=%d < allow_uids_at=%d", + get_logger().error(Lanuages().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("Can't authorize user %r using PAM: code=%d; reason=%s", + get_logger().error(Lanuages().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 92c4632d..e4c44d80 100644 --- a/kvmd/plugins/auth/radius.py +++ b/kvmd/plugins/auth/radius.py @@ -36,6 +36,8 @@ from ...logging import get_logger from ... import aiotools +from ...lanuages import Lanuages + from . import BaseAuthService @@ -440,5 +442,5 @@ class Plugin(BaseAuthService): response = client.SendPacket(request) return (response.code == pyrad.packet.AccessAccept) except Exception: - get_logger().exception("Failed RADIUS auth request for user %r", user) + get_logger().exception(Lanuages().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 01411621..18a6c3a1 100644 --- a/kvmd/plugins/hid/_mcu/__init__.py +++ b/kvmd/plugins/hid/_mcu/__init__.py @@ -46,6 +46,8 @@ 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 .. import BaseHid from .gpio import Gpio @@ -144,6 +146,8 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- self.__stop_event = multiprocessing.Event() + self.gettext=Lanuages().gettext + @classmethod def get_plugin_options(cls) -> dict: return { @@ -247,7 +251,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- @aiotools.atomic_fg async def cleanup(self) -> None: if self.is_alive(): - get_logger(0).info("Stopping HID daemon ...") + get_logger(0).info(self.gettext("Stopping HID daemon ...")) self.__stop_event.set() if self.is_alive() or self.exitcode is not None: self.join() @@ -316,14 +320,14 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- with self.__gpio: self.__hid_loop() if self.__phy.has_device(): - logger.info("Clearing HID events ...") + logger.info(self.gettext("Clearing HID events ...")) try: with self.__phy.connected() as conn: self.__process_request(conn, ClearEvent().make_request()) except Exception: - logger.exception("Can't clear HID events") + logger.exception(self.gettext("Can't clear HID events")) except Exception: - logger.exception("Unexpected error in the GPIO loop") + logger.exception(self.gettext("Unexpected error in the GPIO loop")) time.sleep(1) def __hid_loop(self) -> None: @@ -353,24 +357,24 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- reset = False except Exception: self.clear_events() - get_logger(0).exception("Unexpected error in the HID loop") + get_logger(0).exception(self.gettext("Unexpected error in the HID loop")) time.sleep(1) def __hid_loop_wait_device(self, reset: bool) -> bool: logger = get_logger(0) if reset: - logger.info("Initial HID reset and wait for %s ...", self.__phy) + logger.info(self.gettext("Initial HID reset and wait for %s ..."), self.__phy) self.__gpio.reset() # На самом деле SPI и Serial-девайсы не пропадают, # а вот USB CDC (Pico HID Bridge) вполне себе пропадает for _ in range(10): if self.__phy.has_device(): - logger.info("Physical HID interface found: %s", self.__phy) + logger.info(self.gettext("Physical HID interface found: %s"), self.__phy) return True if self.__stop_event.is_set(): break time.sleep(1) - logger.error("Missing physical HID interface: %s", self.__phy) + logger.error(self.gettext("Missing physical HID interface: %s"), self.__phy) self.__set_state_online(False) return False @@ -388,28 +392,28 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- try: if len(response) < 4: read_retries -= 1 - raise _TempRequestError(f"No response from HID: request={request!r}") + raise _TempRequestError(self.gettext(f"No response from HID: request={request!r}")) if not check_response(response): request = REQUEST_REPEAT - raise _TempRequestError("Invalid response CRC; requesting response again ...") + raise _TempRequestError(self.gettext("Invalid response CRC; requesting response again ...")) code = response[1] if code == 0x48: # Request timeout # pylint: disable=no-else-raise - raise _TempRequestError(f"Got request timeout from HID: request={request!r}") + raise _TempRequestError(self.gettext(f"Got request timeout from HID: request={request!r}")) elif code == 0x40: # CRC Error - raise _TempRequestError(f"Got CRC error of request from HID: request={request!r}") + raise _TempRequestError(self.gettext(f"Got CRC error of request from HID: request={request!r}")) elif code == 0x45: # Unknown command - raise _PermRequestError(f"HID did not recognize the request={request!r}") + raise _PermRequestError(self.gettext(f"HID did not recognize the request={request!r}")) elif code == 0x24: # Rebooted? - raise _PermRequestError("No previous command state inside HID, seems it was rebooted") + raise _PermRequestError(self.gettext("No previous command state inside HID, seems it was rebooted")) elif code == 0x20: # Legacy done self.__set_state_online(True) return True elif code & 0x80: # Pong/Done with state self.__set_state_pong(response) return True - raise _TempRequestError(f"Invalid response from HID: request={request!r}, response=0x{response!r}") + raise _TempRequestError(self.gettext(f"Invalid response from HID: request={request!r}, response=0x{response!r}")) except _RequestError as err: common_retries -= 1 @@ -440,7 +444,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many- for msg in error_messages: logger.error(msg) if not (common_retries and read_retries): - logger.error("Can't process HID request due many errors: %r", request) + logger.error(self.gettext("Can't process HID request due many errors: %r", request)) return error_retval def __set_state_online(self, online: bool) -> None: diff --git a/kvmd/plugins/hid/_mcu/gpio.py b/kvmd/plugins/hid/_mcu/gpio.py index 0afbcc69..bbe13f23 100644 --- a/kvmd/plugins/hid/_mcu/gpio.py +++ b/kvmd/plugins/hid/_mcu/gpio.py @@ -27,6 +27,7 @@ import gpiod from ....logging import get_logger +from ....lanuages import Lanuages # ===== class Gpio: # pylint: disable=too-many-instance-attributes @@ -50,6 +51,8 @@ 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 + def __enter__(self) -> None: if self.__power_detect_pin >= 0 or self.__reset_pin >= 0: assert self.__line_request is None @@ -91,7 +94,7 @@ class Gpio: # pylint: disable=too-many-instance-attributes assert self.__line_request power = bool(self.__line_request.get_value(self.__power_detect_pin).value) if power != self.__last_power: - get_logger(0).info("HID power state changed: %s -> %s", self.__last_power, power) + get_logger(0).info(self.gettext("HID power state changed: %s -> %s"), self.__last_power, power) self.__last_power = power return power return True @@ -105,4 +108,4 @@ class Gpio: # pylint: disable=too-many-instance-attributes finally: self.__line_request.set_value(self.__reset_pin, gpiod.line.Value(self.__reset_inverted)) time.sleep(1) - get_logger(0).info("Reset HID performed") + get_logger(0).info(self.gettext("Reset HID performed")) diff --git a/kvmd/plugins/hid/bt/__init__.py b/kvmd/plugins/hid/bt/__init__.py index ece6b59b..0ebffb37 100644 --- a/kvmd/plugins/hid/bt/__init__.py +++ b/kvmd/plugins/hid/bt/__init__.py @@ -40,6 +40,8 @@ from .... import aiotools from .... import aiomulti from .... import aioproc +from ....lanuages import Lanuages + from .. import BaseHid from ..otg.events import ResetEvent @@ -107,6 +109,8 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes stop_event=self.__stop_event, ) + self.gettext=Lanuages().gettext + @classmethod def get_plugin_options(cls) -> dict: return { @@ -130,7 +134,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes } def sysprep(self) -> None: - get_logger(0).info("Starting HID daemon ...") + get_logger(0).info(self.gettext("Starting HID daemon ...")) self.__proc = multiprocessing.Process(target=self.__server_worker, daemon=True) self.__proc.start() @@ -175,7 +179,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes async def cleanup(self) -> None: if self.__proc is not None: if self.__proc.is_alive(): - get_logger(0).info("Stopping HID daemon ...") + get_logger(0).info(self.gettext("Stopping HID daemon ...")) self.__stop_event.set() if self.__proc.is_alive() or self.__proc.exitcode is not None: self.__proc.join() @@ -224,5 +228,5 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes try: self.__server.run() except Exception: - logger.exception("Unexpected HID error") + logger.exception(self.gettext("Unexpected HID error")) time.sleep(5) diff --git a/kvmd/plugins/hid/bt/server.py b/kvmd/plugins/hid/bt/server.py index 2b6a4307..b325a225 100644 --- a/kvmd/plugins/hid/bt/server.py +++ b/kvmd/plugins/hid/bt/server.py @@ -38,6 +38,8 @@ from .... import aiomulti from ....keyboard.mappings import UsbKey +from ....lanuages import Lanuages + from ..otg.events import BaseEvent from ..otg.events import ClearEvent from ..otg.events import ResetEvent @@ -115,6 +117,8 @@ class BtServer: # pylint: disable=too-many-instance-attributes self.__keys: list[UsbKey | None] = [None] * 6 self.__mouse_buttons = 0 + self.gettext=Lanuages().gettext + def run(self) -> None: with self.__iface: self.__iface.configure() @@ -146,7 +150,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes @contextlib.contextmanager def __listen(self, role: _RoleT, addr: str, port: int) -> Generator[socket.socket, None, None]: - get_logger(0).info("Listening [%s]:%d for %s ...", addr, port, role) + get_logger(0).info(self.gettext("Listening [%s]:%d for %s ..."), addr, port, role) with socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP) as sock: # type: ignore sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(self.__socket_timeout) @@ -183,7 +187,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes elif data == b"\x71": sock.send(b"\x00") except Exception as err: - get_logger(0).exception("CTL socket error on %s: %s", client.addr, tools.efmt(err)) + get_logger(0).exception(self.gettext("CTL socket error on %s: %s"), client.addr, tools.efmt(err)) self.__close_client("CTL", client, "ctl_sock") continue @@ -197,7 +201,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes elif data[:2] == b"\xA2\x01": self.__process_leds(data[2]) except Exception as err: - get_logger(0).exception("INT socket error on %s: %s", client.addr, tools.efmt(err)) + get_logger(0).exception(self.gettext("INT socket error on %s: %s"), client.addr, tools.efmt(err)) self.__close_client("INT", client, "ctl_sock") if qr in ready_read: @@ -280,7 +284,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes try: client.int_sock.send(report) except Exception as err: - get_logger(0).info("Can't send %s report to %s: %s", name, client.addr, tools.efmt(err)) + get_logger(0).info(self.gettext("Can't send %s report to %s: %s"), name, client.addr, tools.efmt(err)) self.__close_client_pair(client) def __clear_modifiers(self) -> None: @@ -307,12 +311,12 @@ class BtServer: # pylint: disable=too-many-instance-attributes (sock, peer) = server_sock.accept() sock.setblocking(True) except Exception: - get_logger(0).exception("Can't accept %s client", role) + get_logger(0).exception(self.gettext("Can't accept %s client"), role) else: if peer[0] not in self.__clients: if len(self.__clients) >= self.__max_clients: self.__close_sock(sock) - get_logger(0).info("Refused %s client: %s: max clients reached", role, peer[0]) + get_logger(0).info(self.gettext("Refused %s client: %s: max clients reached"), role, peer[0]) return self.__clients[peer[0]] = _BtClient(peer[0]) client = self.__clients[peer[0]] @@ -321,7 +325,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes setattr(client, sock_attr, sock) self.__to_read.add(sock) - get_logger(0).info("Accepted %s client: %s", role, peer[0]) + get_logger(0).info(self.gettext("Accepted %s client: %s"), role, peer[0]) self.__state_flags.update(online=True) self.__set_public(len(self.__clients) < self.__max_clients) @@ -333,7 +337,7 @@ class BtServer: # pylint: disable=too-many-instance-attributes setattr(client, sock_attr, None) self.__to_read.remove(sock) - get_logger(0).info("Closed %s client %s", role, client.addr) + get_logger(0).info(self.gettext("Closed %s client %s"), role, client.addr) if client.ctl_sock is None and client.int_sock is None: self.__clients.pop(client.addr) @@ -368,16 +372,16 @@ class BtServer: # pylint: disable=too-many-instance-attributes def __set_public(self, public: bool) -> None: logger = get_logger(0) if self.__control_public: - logger.info("Publishing ..." if public else "Unpublishing ...") + logger.info(self.gettext("Publishing ...") if public else self.gettext("Unpublishing ...")) try: self.__iface.set_public(public) except Exception as err: - logger.error("Can't change public mode: %s", tools.efmt(err)) + logger.error(self.gettext("Can't change public mode: %s"), tools.efmt(err)) def __unpair_client(self, client: _BtClient) -> None: logger = get_logger(0) - logger.info("Unpairing %s ...", client.addr) + logger.info(self.gettext("Unpairing %s ..."), client.addr) try: self.__iface.unpair(client.addr) except Exception as err: - logger.error("Can't unpair %s: %s", client.addr, tools.efmt(err)) + logger.error(self.gettext("Can't unpair %s: %s"), client.addr, tools.efmt(err)) diff --git a/kvmd/plugins/hid/ch9329/__init__.py b/kvmd/plugins/hid/ch9329/__init__.py index 4e2be8c9..05072b2e 100644 --- a/kvmd/plugins/hid/ch9329/__init__.py +++ b/kvmd/plugins/hid/ch9329/__init__.py @@ -41,6 +41,8 @@ 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 .. import BaseHid from .chip import ChipResponseError @@ -82,6 +84,8 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst self.__keyboard = Keyboard() self.__mouse = Mouse() + self.gettext=Lanuages().gettext + @classmethod def get_plugin_options(cls) -> dict: return { @@ -92,7 +96,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst } def sysprep(self) -> None: - get_logger(0).info("Starting HID daemon ...") + get_logger(0).info(self.gettext("Starting HID daemon ...")) self.start() async def get_state(self) -> dict: @@ -134,7 +138,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst @aiotools.atomic_fg async def cleanup(self) -> None: if self.is_alive(): - get_logger(0).info("Stopping HID daemon ...") + get_logger(0).info(self.gettext("Stopping HID daemon ...")) self.__stop_event.set() if self.is_alive() or self.exitcode is not None: self.join() @@ -171,7 +175,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst _ = keyboard_output if mouse_output is not None: - get_logger(0).info("HID : mouse output = %s", mouse_output) + get_logger(0).info(self.gettext("HID : mouse output = %s"), mouse_output) absolute = (mouse_output == "usb") self.__mouse.set_absolute(absolute) self._set_jiggler_absolute(absolute) @@ -201,7 +205,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst try: self.__hid_loop() except Exception: - logger.exception("Unexpected error in the run loop") + logger.exception(self.gettext("Unexpected error in the run loop")) time.sleep(1) def __hid_loop(self) -> None: @@ -224,7 +228,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst self.__process_cmd(conn, cmd) except Exception: self.clear_events() - get_logger(0).exception("Unexpected error in the HID loop") + get_logger(0).exception(self.gettext("Unexpected error in the HID loop")) time.sleep(2) def __process_cmd(self, conn: ChipConnection, cmd: bytes) -> bool: # pylint: disable=too-many-branches diff --git a/kvmd/plugins/hid/ch9329/chip.py b/kvmd/plugins/hid/ch9329/chip.py index b8631ec0..bda399c7 100644 --- a/kvmd/plugins/hid/ch9329/chip.py +++ b/kvmd/plugins/hid/ch9329/chip.py @@ -25,6 +25,8 @@ import contextlib from typing import Generator +from ....lanuages import Lanuages + # ===== class ChipResponseError(Exception): @@ -35,6 +37,7 @@ class ChipResponseError(Exception): class ChipConnection: def __init__(self, tty: serial.Serial) -> None: self.__tty = tty + self.gettext=Lanuages().gettext def xfer(self, cmd: bytes) -> int: self.__send(cmd) @@ -52,16 +55,16 @@ class ChipConnection: def __recv(self) -> int: data = self.__tty.read(5) if len(data) < 5: - raise ChipResponseError("Too short response, HID might be disconnected") + raise ChipResponseError(self.gettext("Too short response, HID might be disconnected")) if data and data[4]: data += self.__tty.read(data[4] + 1) if self.__make_checksum(data[:-1]) != data[-1]: - raise ChipResponseError("Invalid response checksum") + raise ChipResponseError(self.gettext("Invalid response checksum")) if data[4] == 1 and data[5] != 0: - raise ChipResponseError(f"Response error code = {data[5]!r}") + raise ChipResponseError(self.gettext(f"Response error code = {data[5]!r}")) # led_byte (info) response return (data[7] if data[3] == 0x81 else -1) diff --git a/kvmd/plugins/hid/otg/device.py b/kvmd/plugins/hid/otg/device.py index dfab658d..f7eef96c 100644 --- a/kvmd/plugins/hid/otg/device.py +++ b/kvmd/plugins/hid/otg/device.py @@ -37,6 +37,8 @@ from .... import aiomulti from .... import aioproc from .... import usb +from ....lanuages import Lanuages + from .events import BaseEvent @@ -75,6 +77,8 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in self.__no_device_reported = False self.__logger: (logging.Logger | None) = None + + self.gettext=Lanuages().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) @@ -118,7 +122,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in retries -= 1 except Exception: - self.__logger.exception("Unexpected HID-%s error", self.__name) + self.__logger.exception(self.gettext("Unexpected HID-%s error"), self.__name) time.sleep(1) self.__close_device() @@ -145,7 +149,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in def _stop(self) -> None: if self.is_alive(): - get_logger().info("Stopping HID-%s daemon ...", self.__name) + get_logger().info(self.gettext("Stopping HID-%s daemon ..."), self.__name) self.__stop_event.set() if self.is_alive() or self.exitcode is not None: self.join() @@ -190,7 +194,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in self.__state_flags.update(online=True) return True else: - logger.error("HID-%s write() error: written (%s) != report length (%d)", + logger.error(self.gettext("HID-%s write() error: written (%s) != report length (%d)"), self.__name, written, len(report)) except Exception as err: if isinstance(err, OSError) and ( @@ -198,9 +202,9 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in err.errno == errno.EAGAIN # pylint: disable=no-member or err.errno == errno.ESHUTDOWN # pylint: disable=no-member ): - logger.debug("HID-%s busy/unplugged (write): %s", self.__name, tools.efmt(err)) + logger.debug(self.gettext("HID-%s busy/unplugged (write): %s"), self.__name, tools.efmt(err)) else: - logger.exception("Can't write report to HID-%s", self.__name) + logger.exception(self.gettext("Can't write report to HID-%s"), self.__name) self.__state_flags.update(online=False) return False @@ -217,7 +221,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in try: read = bool(select.select([self.__fd], [], [], 0)[0]) except Exception as err: - logger.error("Can't select() for read HID-%s: %s", self.__name, tools.efmt(err)) + logger.error(self.gettext("Can't select() for read HID-%s: %s"), self.__name, tools.efmt(err)) break if read: @@ -225,9 +229,9 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in report = os.read(self.__fd, self.__read_size) except Exception as err: if isinstance(err, OSError) and err.errno == errno.EAGAIN: # pylint: disable=no-member - logger.debug("HID-%s busy/unplugged (read): %s", self.__name, tools.efmt(err)) + logger.debug(self.gettext("HID-%s busy/unplugged (read): %s"), self.__name, tools.efmt(err)) else: - logger.exception("Can't read report from HID-%s", self.__name) + logger.exception(self.gettext("Can't read report from HID-%s"), self.__name) else: self._process_read_report(report) @@ -244,7 +248,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in self.__close_device() self.__state_flags.update(online=False) if not self.__no_device_reported: - logger.error("Missing HID-%s device: %s", self.__name, self.__device_path) + logger.error(self.gettext("Missing HID-%s device: %s"), self.__name, self.__device_path) self.__no_device_reported = True return False self.__no_device_reported = False @@ -256,7 +260,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in flags |= (os.O_RDWR if self.__read_size else os.O_WRONLY) self.__fd = os.open(self.__device_path, flags) except Exception as err: - logger.error("Can't open HID-%s device %s: %s", + logger.error(self.gettext("Can't open HID-%s device %s: %s"), self.__name, self.__device_path, tools.efmt(err)) if self.__fd >= 0: @@ -267,9 +271,9 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in return True else: # Если запись недоступна, то скорее всего устройство отключено - logger.debug("HID-%s is busy/unplugged (write select)", self.__name) + logger.debug(self.gettext("HID-%s is busy/unplugged (write select)"), self.__name) except Exception as err: - logger.error("Can't select() for write HID-%s: %s", self.__name, tools.efmt(err)) + logger.error(self.gettext("Can't select() for write HID-%s: %s"), self.__name, tools.efmt(err)) self.__state_flags.update(online=False) return False diff --git a/kvmd/plugins/hid/otg/keyboard.py b/kvmd/plugins/hid/otg/keyboard.py index 9008db06..6842e1df 100644 --- a/kvmd/plugins/hid/otg/keyboard.py +++ b/kvmd/plugins/hid/otg/keyboard.py @@ -41,6 +41,7 @@ from .events import get_led_scroll from .events import get_led_num from .events import make_keyboard_report +from ....lanuages import Lanuages # ===== class KeyboardProcess(BaseDeviceProcess): @@ -54,10 +55,11 @@ class KeyboardProcess(BaseDeviceProcess): self.__pressed_modifiers: set[UsbKey] = set() self.__pressed_keys: list[UsbKey | None] = [None] * 6 + self.gettext=Lanuages().gettext def cleanup(self) -> None: self._stop() - get_logger().info("Clearing HID-keyboard events ...") + get_logger().info(self.gettext("Clearing HID-keyboard events ...")) self._cleanup_write(b"\x00" * 8) # Release all keys and modifiers def send_clear_event(self) -> None: diff --git a/kvmd/plugins/hid/otg/mouse.py b/kvmd/plugins/hid/otg/mouse.py index 7d3bd2e6..20db2482 100644 --- a/kvmd/plugins/hid/otg/mouse.py +++ b/kvmd/plugins/hid/otg/mouse.py @@ -36,6 +36,7 @@ from .events import MouseRelativeEvent from .events import MouseWheelEvent from .events import make_mouse_report +from ....lanuages import Lanuages # ===== class MouseProcess(BaseDeviceProcess): @@ -54,6 +55,7 @@ class MouseProcess(BaseDeviceProcess): self.__x = 0 # For absolute self.__y = 0 self.__win98_fix = False + self.gettext=Lanuages().gettext def is_absolute(self) -> bool: return self.__absolute @@ -66,7 +68,7 @@ class MouseProcess(BaseDeviceProcess): def cleanup(self) -> None: self._stop() - get_logger().info("Clearing HID-mouse events ...") + get_logger().info(self.gettext("Clearing HID-mouse events ...")) report = make_mouse_report( absolute=self.__absolute, buttons=0, diff --git a/kvmd/plugins/hid/spi.py b/kvmd/plugins/hid/spi.py index dadcc77e..b7bb748e 100644 --- a/kvmd/plugins/hid/spi.py +++ b/kvmd/plugins/hid/spi.py @@ -41,6 +41,8 @@ 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 ._mcu import BasePhyConnection from ._mcu import BasePhy from ._mcu import BaseMcuHid @@ -56,6 +58,7 @@ class _SpiPhyConnection(BasePhyConnection): self.__xfer = xfer self.__read_timeout = read_timeout + self.gettext=Lanuages().gettext def send(self, request: bytes) -> bytes: assert len(request) == 8 diff --git a/kvmd/plugins/msd/__init__.py b/kvmd/plugins/msd/__init__.py index e0210921..64997ba0 100644 --- a/kvmd/plugins/msd/__init__.py +++ b/kvmd/plugins/msd/__init__.py @@ -40,6 +40,7 @@ from ... import aiotools from .. import BasePlugin from .. import get_plugin_class +from ...lanuages import Lanuages # ===== class MsdError(Exception): @@ -52,37 +53,44 @@ class MsdOperationError(OperationError, MsdError): class MsdIsBusyError(IsBusyError, MsdError): def __init__(self) -> None: - super().__init__("Performing another MSD operation, please try again later") + gettext=Lanuages().gettext + super().__init__(gettext("Performing another MSD operation, please try again later")) class MsdOfflineError(MsdOperationError): def __init__(self) -> None: - super().__init__("MSD is not found") + gettext=Lanuages().gettext + super().__init__(gettext("MSD is not found")) class MsdConnectedError(MsdOperationError): def __init__(self) -> None: - super().__init__("MSD is connected to Server, but shouldn't for this operation") + gettext=Lanuages().gettext + super().__init__(gettext("MSD is connected to Server, but shouldn't for this operation")) class MsdDisconnectedError(MsdOperationError): def __init__(self) -> None: - super().__init__("MSD is disconnected from Server, but should be for this operation") + gettext=Lanuages().gettext + super().__init__(gettext("MSD is disconnected from Server, but should be for this operation")) class MsdImageNotSelected(MsdOperationError): def __init__(self) -> None: - super().__init__("The image is not selected") + gettext=Lanuages().gettext + super().__init__(gettext("The image is not selected")) class MsdUnknownImageError(MsdOperationError): def __init__(self) -> None: - super().__init__("The image is not found in the storage") + gettext=Lanuages().gettext + super().__init__(gettext("The image is not found in the storage")) class MsdImageExistsError(MsdOperationError): def __init__(self) -> None: - super().__init__("This image is already exists") + gettext=Lanuages().gettext + super().__init__(gettext("This image is already exists")) # ===== diff --git a/kvmd/plugins/msd/disabled.py b/kvmd/plugins/msd/disabled.py index ad49875e..ad9fe3d0 100644 --- a/kvmd/plugins/msd/disabled.py +++ b/kvmd/plugins/msd/disabled.py @@ -31,11 +31,13 @@ from . import BaseMsdReader from . import BaseMsdWriter from . import BaseMsd +from ...lanuages import Lanuages # ===== class MsdDisabledError(MsdOperationError): def __init__(self) -> None: - super().__init__("MSD is disabled") + gettext=Lanuages().gettext + super().__init__(gettext("MSD is disabled")) # ===== diff --git a/kvmd/plugins/msd/otg/__init__.py b/kvmd/plugins/msd/otg/__init__.py index 18f2a006..43a2a6cd 100644 --- a/kvmd/plugins/msd/otg/__init__.py +++ b/kvmd/plugins/msd/otg/__init__.py @@ -28,6 +28,7 @@ import time from typing import AsyncGenerator +from ....lanuages import Lanuages from ....logging import get_logger from ....inotify import InotifyMask @@ -43,6 +44,8 @@ from ....validators.kvm import valid_msd_image_name from .... import aiotools from .... import fstab +from ....lanuages import Lanuages + from .. import MsdIsBusyError from .. import MsdOfflineError from .. import MsdConnectedError @@ -58,7 +61,6 @@ from .storage import Image from .storage import Storage from .drive import Drive - # ===== @dataclasses.dataclass(frozen=True) class _DriveState: @@ -140,9 +142,10 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes self.__notifier = aiotools.AioNotifier() self.__state = _State(self.__notifier) + self.gettext=Lanuages().gettext logger = get_logger(0) - logger.info("Using OTG gadget %r as MSD", gadget) + logger.info(self.gettext("Using OTG gadget %r as MSD"), gadget) aiotools.run_sync(self.__reload_state(notify=False)) @classmethod @@ -217,7 +220,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes self.__drive.set_rw_flag(False) await self.__storage.remount_rw(False) except Exception: - get_logger(0).exception("Can't reset MSD properly") + get_logger(0).exception(self.gettext("Can't reset MSD properly")) @aiotools.atomic_fg async def cleanup(self) -> None: @@ -436,7 +439,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if need_reload_state: await self.__reload_state() except Exception: - logger.exception("Unexpected MSD watcher error") + logger.exception(self.gettext("Unexpected MSD watcher error")) time.sleep(1) async def __reload_state(self, notify: bool=True) -> None: @@ -455,13 +458,13 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes if self.__state.vd is None and drive_state.image is None: # Если только что включились и образ не подключен - попробовать # перемонтировать хранилище (и создать images и meta). - logger.info("Probing to remount storage ...") + logger.info(self.gettext("Probing to remount storage ...")) await self.__storage.remount_rw(True) await self.__storage.remount_rw(False) await self.__setup_initial() except Exception: - logger.exception("Error while reloading MSD state; switching to offline") + logger.exception(self.gettext("Error while reloading MSD state; switching to offline")) self.__state.storage = None self.__state.vd = None @@ -489,12 +492,12 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes logger = get_logger(0) image = await self.__storage.make_image_by_name(self.__initial_image) if (await image.exists()): - logger.info("Setting up initial image %r ...", self.__initial_image) + logger.info(self.gettext("Setting up initial image %r ..."), self.__initial_image) try: self.__drive.set_rw_flag(False) self.__drive.set_cdrom_flag(self.__initial_cdrom) self.__drive.set_image_path(image.path) except Exception: - logger.exception("Can't setup initial image: ignored") + logger.exception(self.gettext("Can't setup initial image: ignored")) else: - logger.error("Can't find initial image %r: ignored", self.__initial_image) + logger.error(self.gettext("Can't find initial image %r: ignored"), self.__initial_image) diff --git a/kvmd/plugins/msd/otg/drive.py b/kvmd/plugins/msd/otg/drive.py index 1bae91e5..ca47d7e4 100644 --- a/kvmd/plugins/msd/otg/drive.py +++ b/kvmd/plugins/msd/otg/drive.py @@ -27,11 +27,13 @@ from .... import usb from .. import MsdOperationError +from ....lanuages import Lanuages + # ===== class MsdDriveLockedError(MsdOperationError): def __init__(self) -> None: - super().__init__("MSD drive is locked on IO operation") + super().__init__(Lanuages().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 32f52a2d..0497ca80 100644 --- a/kvmd/plugins/msd/otg/storage.py +++ b/kvmd/plugins/msd/otg/storage.py @@ -31,6 +31,8 @@ from typing import Optional import aiofiles import aiofiles.os +from ....lanuages import Lanuages + from .... import aiotools from .... import aiohelpers @@ -292,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("Can't execute remount helper") + raise MsdError(Lanuages().gettext("Can't execute remount helper")) diff --git a/kvmd/plugins/ugpio/anelpwr.py b/kvmd/plugins/ugpio/anelpwr.py index a9cc432b..647869b1 100644 --- a/kvmd/plugins/ugpio/anelpwr.py +++ b/kvmd/plugins/ugpio/anelpwr.py @@ -41,6 +41,8 @@ from ...validators.basic import valid_bool from ...validators.basic import valid_number from ...validators.basic import valid_float_f01 +from ...lanuages import Lanuages + from . import BaseUserGpioDriver from . import GpioDriverOfflineError @@ -146,7 +148,8 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute ) as response: htclient.raise_not_200(response) except Exception as err: - get_logger().error("Failed ANELPWR POST request to pin %s: %s", pin, tools.efmt(err)) + + get_logger().error(Lanuages().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 54309c2c..53ed486f 100644 --- a/kvmd/usb.py +++ b/kvmd/usb.py @@ -22,6 +22,8 @@ import os +from .lanuages import Lanuages + from . import env @@ -31,10 +33,10 @@ def find_udc(udc: str) -> str: candidates = sorted(os.listdir(path)) if not udc: if len(candidates) == 0: - raise RuntimeError("Can't find any UDC") + raise RuntimeError(Lanuages().gettext("Can't find any UDC")) udc = candidates[0] elif udc not in candidates: - raise RuntimeError(f"Can't find selected UDC: {udc}") + raise RuntimeError(Lanuages().gettext(f"Can't find selected UDC: {udc}")) return udc # fe980000.usb diff --git a/make-zh_translation.sh b/make-zh_translation.sh new file mode 100644 index 00000000..dbe230b4 --- /dev/null +++ b/make-zh_translation.sh @@ -0,0 +1,3 @@ +pybabel extract -F babel.cfg -o message.pot . +pybabel update -d kvmd/i18n -l zh -D message -i message.pot +pybabel compile -i kvmd/i18n/zh/LC_MESSAGES/message.po -o kvmd/i18n/zh/LC_MESSAGES/message.mo \ No newline at end of file diff --git a/message.pot b/message.pot new file mode 100644 index 00000000..13862a32 --- /dev/null +++ b/message.pot @@ -0,0 +1,398 @@ +# Translations template for PROJECT. +# Copyright (C) 2024 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# FIRST AUTHOR , 2024. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PROJECT VERSION\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2024-08-06 21:17+0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.15.0\n" + +#: kvmd/htclient.py:63 +msgid "Can't determine filename" +msgstr "" + +#: kvmd/inotify.py:199 +#, python-format +msgid "Watching for %s" +msgstr "" + +#: kvmd/inotify.py:258 +#, python-format +msgid "Unwatching %s because IGNORED was received" +msgstr "" + +#: kvmd/usb.py:36 +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" +msgstr "" + +#: kvmd/apps/__init__.py:165 +msgid "" +"To prevent accidental startup, you must specify the --run option to " +"start.\n" +msgstr "" + +#: kvmd/apps/__init__.py:165 +msgid "Try the --help option to find out what this service does.\n" +msgstr "" + +#: kvmd/apps/__init__.py:165 +msgid "Make sure you understand exactly what you are doing!" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:57 +msgid "AUTHORIZATION IS DISABLED" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:61 +#, python-format +msgid "Authorization is disabled for API %r" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:66 +#, python-format +msgid "Using internal auth service %r" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:73 +#, python-format +msgid "Using external auth service %r" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:101 +#, python-format +msgid "Got access denied for user %r by TOTP" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:112 +#, python-format +msgid "Authorized user %r via auth service %r" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:114 +#, python-format +msgid "Got access denied for user %r from auth service %r" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:124 +#, python-format +msgid "Logged in user %r" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:134 +msgid "Can't generate new unique token" +msgstr "" + +#: kvmd/apps/kvmd/auth.py:145 +#, python-format +msgid "Logged out user %r (%d)" +msgstr "" + +#: kvmd/plugins/atx/__init__.py:45 +msgid "Performing another ATX operation, please try again later" +msgstr "" + +#: kvmd/plugins/atx/gpio.py:209 +#, python-format +msgid "Clicked ATX button %r" +msgstr "" + +#: kvmd/plugins/auth/http.py:94 +#, python-format +msgid "Failed HTTP auth request for user %r" +msgstr "" + +#: kvmd/plugins/auth/ldap.py:106 +#, python-format +msgid "LDAP server is down: %s" +msgstr "" + +#: kvmd/plugins/auth/ldap.py:108 +#, python-format +msgid "Unexpected LDAP error: %s" +msgstr "" + +#: kvmd/plugins/auth/pam.py:91 +#, python-format +msgid "Unallowed UID of user %r: uid=%d < allow_uids_at=%d" +msgstr "" + +#: kvmd/plugins/auth/pam.py:97 +#, python-format +msgid "Can't authorize user %r using PAM: code=%d; reason=%s" +msgstr "" + +#: kvmd/plugins/auth/radius.py:445 +#, python-format +msgid "Failed RADIUS auth request for user %r" +msgstr "" + +#: kvmd/plugins/hid/bt/__init__.py:137 kvmd/plugins/hid/ch9329/__init__.py:99 +msgid "Starting HID daemon ..." +msgstr "" + +#: kvmd/plugins/hid/bt/__init__.py:182 kvmd/plugins/hid/ch9329/__init__.py:141 +msgid "Stopping HID daemon ..." +msgstr "" + +#: kvmd/plugins/hid/bt/__init__.py:231 +msgid "Unexpected HID error" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:153 +#, python-format +msgid "Listening [%s]:%d for %s ..." +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:190 +#, python-format +msgid "CTL socket error on %s: %s" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:204 +#, python-format +msgid "INT socket error on %s: %s" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:287 +#, python-format +msgid "Can't send %s report to %s: %s" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:314 +#, python-format +msgid "Can't accept %s client" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:319 +#, python-format +msgid "Refused %s client: %s: max clients reached" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:328 +#, python-format +msgid "Accepted %s client: %s" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:340 +#, python-format +msgid "Closed %s client %s" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:375 +msgid "Publishing ..." +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:375 +msgid "Unpublishing ..." +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:379 +#, python-format +msgid "Can't change public mode: %s" +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:383 +#, python-format +msgid "Unpairing %s ..." +msgstr "" + +#: kvmd/plugins/hid/bt/server.py:387 +#, python-format +msgid "Can't unpair %s: %s" +msgstr "" + +#: kvmd/plugins/hid/ch9329/__init__.py:178 +#, python-format +msgid "HID : mouse output = %s" +msgstr "" + +#: kvmd/plugins/hid/ch9329/__init__.py:208 +msgid "Unexpected error in the run loop" +msgstr "" + +#: kvmd/plugins/hid/ch9329/__init__.py:231 +msgid "Unexpected error in the HID loop" +msgstr "" + +#: kvmd/plugins/hid/ch9329/chip.py:58 +msgid "Too short response, HID might be disconnected" +msgstr "" + +#: kvmd/plugins/hid/ch9329/chip.py:64 +msgid "Invalid response checksum" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:125 +#, python-format +msgid "Unexpected HID-%s error" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:152 +#, python-format +msgid "Stopping HID-%s daemon ..." +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:197 +#, python-format +msgid "HID-%s write() error: written (%s) != report length (%d)" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:205 +#, python-format +msgid "HID-%s busy/unplugged (write): %s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:207 +#, python-format +msgid "Can't write report to HID-%s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:224 +#, python-format +msgid "Can't select() for read HID-%s: %s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:232 +#, python-format +msgid "HID-%s busy/unplugged (read): %s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:234 +#, python-format +msgid "Can't read report from HID-%s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:251 +#, python-format +msgid "Missing HID-%s device: %s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:263 +#, python-format +msgid "Can't open HID-%s device %s: %s" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:274 +#, python-format +msgid "HID-%s is busy/unplugged (write select)" +msgstr "" + +#: kvmd/plugins/hid/otg/device.py:276 +#, python-format +msgid "Can't select() for write HID-%s: %s" +msgstr "" + +#: kvmd/plugins/hid/otg/keyboard.py:62 +msgid "Clearing HID-keyboard events ..." +msgstr "" + +#: kvmd/plugins/hid/otg/mouse.py:71 +msgid "Clearing HID-mouse events ..." +msgstr "" + +#: kvmd/plugins/msd/__init__.py:57 +msgid "Performing another MSD operation, please try again later" +msgstr "" + +#: kvmd/plugins/msd/__init__.py:63 +msgid "MSD is not found" +msgstr "" + +#: kvmd/plugins/msd/__init__.py:69 +msgid "MSD is connected to Server, but shouldn't for this operation" +msgstr "" + +#: kvmd/plugins/msd/__init__.py:75 +msgid "MSD is disconnected from Server, but should be for this operation" +msgstr "" + +#: kvmd/plugins/msd/__init__.py:81 +msgid "The image is not selected" +msgstr "" + +#: kvmd/plugins/msd/__init__.py:87 +msgid "The image is not found in the storage" +msgstr "" + +#: kvmd/plugins/msd/__init__.py:93 +msgid "This image is already exists" +msgstr "" + +#: kvmd/plugins/msd/disabled.py:40 +msgid "MSD is disabled" +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:148 +#, python-format +msgid "Using OTG gadget %r as MSD" +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:223 +msgid "Can't reset MSD properly" +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:442 +msgid "Unexpected MSD watcher error" +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:461 +msgid "Probing to remount storage ..." +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:467 +msgid "Error while reloading MSD state; switching to offline" +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:495 +#, python-format +msgid "Setting up initial image %r ..." +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:501 +msgid "Can't setup initial image: ignored" +msgstr "" + +#: kvmd/plugins/msd/otg/__init__.py:503 +#, python-format +msgid "Can't find initial image %r: ignored" +msgstr "" + +#: kvmd/plugins/msd/otg/drive.py:36 +msgid "MSD drive is locked on IO operation" +msgstr "" + +#: kvmd/plugins/msd/otg/storage.py:297 +msgid "Can't execute remount helper" +msgstr "" + +#: kvmd/plugins/ugpio/anelpwr.py:152 +#, python-format +msgid "Failed ANELPWR POST request to pin %s: %s" +msgstr "" +