mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2025-12-12 01:00:29 +08:00
进一步的 kvmd 国际化(汉化)支持,添加配置入口
yaml 配置示例:
```
languages:
console: zh
web: zh
```
This commit is contained in:
parent
5b25b3661f
commit
35397c5414
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from .lanuages import Lanuages
|
from .languages import Languages
|
||||||
|
|
||||||
from .logging import get_logger
|
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)
|
part.format(mode=mode)
|
||||||
for part in base_cmd
|
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:
|
try:
|
||||||
proc = await aioproc.log_process(cmd, logger)
|
proc = await aioproc.log_process(cmd, logger)
|
||||||
if proc.returncode != 0:
|
if proc.returncode != 0:
|
||||||
assert proc.returncode is not None
|
assert proc.returncode is not None
|
||||||
raise subprocess.CalledProcessError(proc.returncode, cmd)
|
raise subprocess.CalledProcessError(proc.returncode, cmd)
|
||||||
except Exception as err:
|
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 False
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import asyncio
|
|||||||
import asyncio.subprocess
|
import asyncio.subprocess
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .lanuages import Lanuages
|
from .languages import Languages
|
||||||
import setproctitle
|
import setproctitle
|
||||||
|
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
@ -86,7 +86,7 @@ async def log_stdout_infinite(proc: asyncio.subprocess.Process, logger: logging.
|
|||||||
else:
|
else:
|
||||||
empty += 1
|
empty += 1
|
||||||
if empty == 100: # asyncio bug
|
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
|
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:
|
if proc.returncode is not None:
|
||||||
raise
|
raise
|
||||||
await proc.wait()
|
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:
|
except asyncio.CancelledError:
|
||||||
pass
|
pass
|
||||||
except Exception:
|
except Exception:
|
||||||
if proc.returncode is None:
|
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:
|
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:
|
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:
|
def settle(name: str, suffix: str, prefix: str="kvmd") -> logging.Logger:
|
||||||
logger = get_logger(1)
|
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()
|
os.setpgrp()
|
||||||
rename_process(suffix, prefix)
|
rename_process(suffix, prefix)
|
||||||
return logger
|
return logger
|
||||||
|
|||||||
@ -105,7 +105,9 @@ from ..validators.hw import valid_otg_gadget
|
|||||||
from ..validators.hw import valid_otg_id
|
from ..validators.hw import valid_otg_id
|
||||||
from ..validators.hw import valid_otg_ethernet
|
from ..validators.hw import valid_otg_ethernet
|
||||||
|
|
||||||
from ..lanuages import Lanuages
|
from ..validators.languages import valid_languages
|
||||||
|
|
||||||
|
from ..languages import Languages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
def init(
|
def init(
|
||||||
@ -127,16 +129,16 @@ def init(
|
|||||||
add_help=add_help,
|
add_help=add_help,
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
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,
|
parser.add_argument("-c", "--config", default="/etc/kvmd/main.yaml", type=valid_abs_file,
|
||||||
help=_("Set config file path"), metavar="<file>")
|
help="Set config file path", metavar="<file>")
|
||||||
parser.add_argument("-o", "--set-options", default=[], nargs="+",
|
parser.add_argument("-o", "--set-options", default=[], nargs="+",
|
||||||
help=_("Override config options list (like sec/sub/opt=value)"), metavar="<k=v>",)
|
help="Override config options list (like sec/sub/opt=value)", metavar="<k=v>",)
|
||||||
parser.add_argument("-m", "--dump-config", action="store_true",
|
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:
|
if check_run:
|
||||||
parser.add_argument("--run", dest="run", action="store_true",
|
parser.add_argument("--run", dest="run", action="store_true",
|
||||||
help=_("Run the service"))
|
help="Run the service")
|
||||||
(options, remaining) = parser.parse_known_args(argv)
|
(options, remaining) = parser.parse_known_args(argv)
|
||||||
|
|
||||||
if options.dump_config:
|
if options.dump_config:
|
||||||
@ -151,9 +153,18 @@ def init(
|
|||||||
))
|
))
|
||||||
raise SystemExit()
|
raise SystemExit()
|
||||||
config = _init_config(options.config, options.set_options, **load)
|
config = _init_config(options.config, options.set_options, **load)
|
||||||
|
|
||||||
logging.captureWarnings(True)
|
logging.captureWarnings(True)
|
||||||
logging.config.dictConfig(config.logging)
|
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:
|
if cli_logging:
|
||||||
logging.getLogger().handlers[0].setFormatter(logging.Formatter(
|
logging.getLogger().handlers[0].setFormatter(logging.Formatter(
|
||||||
"-- {levelname:>7} -- {message}",
|
"-- {levelname:>7} -- {message}",
|
||||||
@ -162,7 +173,7 @@ def init(
|
|||||||
|
|
||||||
if check_run and not options.run:
|
if check_run and not options.run:
|
||||||
raise SystemExit(
|
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)
|
return (parser, remaining, config)
|
||||||
|
|
||||||
@ -787,4 +798,9 @@ def _get_config_scheme() -> dict:
|
|||||||
"timeout": Option(300, type=valid_int_f1),
|
"timeout": Option(300, type=valid_int_f1),
|
||||||
"interval": Option(30, type=valid_int_f1),
|
"interval": Option(30, type=valid_int_f1),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"languages": {
|
||||||
|
"console": Option("default", type=valid_languages),
|
||||||
|
"web": Option("default", type=valid_languages),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ from ...plugins.hid import get_hid_class
|
|||||||
from ...plugins.atx import get_atx_class
|
from ...plugins.atx import get_atx_class
|
||||||
from ...plugins.msd import get_msd_class
|
from ...plugins.msd import get_msd_class
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from .. import init
|
from .. import init
|
||||||
|
|
||||||
@ -112,4 +112,4 @@ def main(argv: (list[str] | None)=None) -> None:
|
|||||||
stream_forever=config.streamer.forever,
|
stream_forever=config.streamer.forever,
|
||||||
).run(**config.server._unpack())
|
).run(**config.server._unpack())
|
||||||
|
|
||||||
get_logger(0).info(Lanuages().gettext("Bye-bye"))
|
get_logger(0).info(Languages().gettext("Bye-bye"))
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from ...plugins.auth import get_auth_service_class
|
|||||||
|
|
||||||
from ...htserver import HttpExposed
|
from ...htserver import HttpExposed
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class AuthManager:
|
class AuthManager:
|
||||||
@ -52,7 +52,7 @@ class AuthManager:
|
|||||||
|
|
||||||
totp_secret_path: str,
|
totp_secret_path: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
self.__enabled = enabled
|
self.__enabled = enabled
|
||||||
if not enabled:
|
if not enabled:
|
||||||
get_logger().warning(self.gettext("AUTHORIZATION IS DISABLED"))
|
get_logger().warning(self.gettext("AUTHORIZATION IS DISABLED"))
|
||||||
|
|||||||
@ -33,6 +33,8 @@ from aiohttp.web import Request
|
|||||||
from aiohttp.web import Response
|
from aiohttp.web import Response
|
||||||
from aiohttp.web import WebSocketResponse
|
from aiohttp.web import WebSocketResponse
|
||||||
|
|
||||||
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
from ...errors import OperationError
|
from ...errors import OperationError
|
||||||
@ -84,17 +86,17 @@ from .api.redfish import RedfishApi
|
|||||||
# =====
|
# =====
|
||||||
class StreamerQualityNotSupported(OperationError):
|
class StreamerQualityNotSupported(OperationError):
|
||||||
def __init__(self) -> None:
|
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):
|
class StreamerResolutionNotSupported(OperationError):
|
||||||
def __init__(self) -> None:
|
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):
|
class StreamerH264NotSupported(OperationError):
|
||||||
def __init__(self) -> None:
|
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.__reset_streamer = False
|
||||||
self.__new_streamer_params: dict = {}
|
self.__new_streamer_params: dict = {}
|
||||||
|
|
||||||
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
# ===== STREAMER CONTROLLER
|
# ===== STREAMER CONTROLLER
|
||||||
|
|
||||||
@exposed_http("POST", "/streamer/set_params")
|
@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:
|
async def _on_shutdown(self) -> None:
|
||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
logger.info("Waiting short tasks ...")
|
logger.info(self.gettext("Waiting short tasks ..."))
|
||||||
await aiotools.wait_all_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()
|
await aiotools.stop_all_deadly_tasks()
|
||||||
logger.info("Disconnecting clients ...")
|
logger.info(self.gettext("Disconnecting clients ..."))
|
||||||
await self._close_all_wss()
|
await self._close_all_wss()
|
||||||
logger.info("On-Shutdown complete")
|
logger.info(self.gettext("On-Shutdown complete"))
|
||||||
|
|
||||||
async def _on_cleanup(self) -> None:
|
async def _on_cleanup(self) -> None:
|
||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
for sub in self.__subsystems:
|
for sub in self.__subsystems:
|
||||||
if sub.cleanup:
|
if sub.cleanup:
|
||||||
logger.info("Cleaning up %s ...", sub.name)
|
logger.info(self.gettext("Cleaning up %s ..."), sub.name)
|
||||||
try:
|
try:
|
||||||
await sub.cleanup() # type: ignore
|
await sub.cleanup() # type: ignore
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Cleanup error on %s", sub.name)
|
logger.exception(self.gettext("Cleanup error on %s"), sub.name)
|
||||||
logger.info("On-Cleanup complete")
|
logger.info(self.gettext("On-Cleanup complete"))
|
||||||
|
|
||||||
async def _on_ws_opened(self) -> None:
|
async def _on_ws_opened(self) -> None:
|
||||||
self.__streamer_notifier.notify()
|
self.__streamer_notifier.notify()
|
||||||
|
|||||||
@ -34,7 +34,7 @@ import aiohttp
|
|||||||
|
|
||||||
from PIL import Image as PilImage
|
from PIL import Image as PilImage
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ class Streamer: # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
self.__notifier = aiotools.AioNotifier()
|
self.__notifier = aiotools.AioNotifier()
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|||||||
@ -26,7 +26,7 @@ from typing import AsyncGenerator
|
|||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
@ -48,22 +48,22 @@ from ...yamlconf import Section
|
|||||||
# =====
|
# =====
|
||||||
class GpioChannelNotFoundError(GpioOperationError):
|
class GpioChannelNotFoundError(GpioOperationError):
|
||||||
def __init__(self) -> None:
|
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):
|
class GpioSwitchNotSupported(GpioOperationError):
|
||||||
def __init__(self) -> None:
|
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):
|
class GpioPulseNotSupported(GpioOperationError):
|
||||||
def __init__(self) -> None:
|
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):
|
class GpioChannelIsBusyError(IsBusyError, GpioError):
|
||||||
def __init__(self) -> None:
|
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 = driver
|
||||||
self.__driver.register_input(self.__pin, config.debounce)
|
self.__driver.register_input(self.__pin, config.debounce)
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def get_scheme(self) -> dict:
|
def get_scheme(self) -> dict:
|
||||||
return {
|
return {
|
||||||
@ -253,7 +253,7 @@ class UserGpio:
|
|||||||
self.__inputs: dict[str, _GpioInput] = {}
|
self.__inputs: dict[str, _GpioInput] = {}
|
||||||
self.__outputs: dict[str, _GpioOutput] = {}
|
self.__outputs: dict[str, _GpioOutput] = {}
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
for (channel, ch_config) in tools.sorted_kvs(config.scheme):
|
for (channel, ch_config) in tools.sorted_kvs(config.scheme):
|
||||||
driver = self.__drivers[ch_config.driver]
|
driver = self.__drivers[ch_config.driver]
|
||||||
|
|||||||
@ -29,7 +29,7 @@ import argparse
|
|||||||
|
|
||||||
from os.path import join
|
from os.path import join
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
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
|
# https://www.isticktoit.net/?p=1383
|
||||||
|
|
||||||
logger = get_logger()
|
logger = get_logger()
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
|
|
||||||
_check_config(config)
|
_check_config(config)
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ def _cmd_stop(config: Section) -> None:
|
|||||||
|
|
||||||
gadget_path = usb.get_gadget_path(config.otg.gadget)
|
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")
|
_write(join(gadget_path, "UDC"), "\n")
|
||||||
|
|
||||||
_unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), optional=True)
|
_unlink(join(gadget_path, "os_desc", usb.G_PROFILE_NAME), optional=True)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import dataclasses
|
|||||||
import itertools
|
import itertools
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ class _Service: # pylint: disable=too-many-instance-attributes
|
|||||||
self.__gadget: str = config.otg.gadget
|
self.__gadget: str = config.otg.gadget
|
||||||
self.__driver: str = config.otg.devices.ethernet.driver
|
self.__driver: str = config.otg.devices.ethernet.driver
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
asyncio.run(self.__run(True))
|
asyncio.run(self.__run(True))
|
||||||
|
|||||||
@ -28,6 +28,8 @@ import contextlib
|
|||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
from ...keyboard.keysym import SymmapModifiers
|
from ...keyboard.keysym import SymmapModifiers
|
||||||
@ -133,6 +135,8 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
self.__modifiers = 0
|
self.__modifiers = 0
|
||||||
|
|
||||||
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|
||||||
async def run(self) -> None:
|
async def run(self) -> None:
|
||||||
@ -156,13 +160,13 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
await self.__stage1_authorized.wait_passed()
|
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)):
|
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
|
assert self.__kvmd_session
|
||||||
try:
|
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)
|
await self.__kvmd_session.hid.set_params(mouse_output=self.__mouse_output)
|
||||||
|
|
||||||
async with self.__kvmd_session.ws() as self.__kvmd_ws:
|
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()
|
self.__stage3_ws_connected.set_passed()
|
||||||
async for (event_type, event) in self.__kvmd_ws.communicate():
|
async for (event_type, event) in self.__kvmd_ws.communicate():
|
||||||
await self.__process_ws_event(event_type, event)
|
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:
|
finally:
|
||||||
self.__kvmd_ws = None
|
self.__kvmd_ws = None
|
||||||
|
|
||||||
@ -204,19 +208,19 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
while True:
|
while True:
|
||||||
frame = await read_frame(not self.__fb_has_key)
|
frame = await read_frame(not self.__fb_has_key)
|
||||||
if not streaming:
|
if not streaming:
|
||||||
logger.info("%s [streamer]: Streaming ...", self._remote)
|
logger.info(self.gettext("%s [streamer]: Streaming ..."), self._remote)
|
||||||
streaming = True
|
streaming = True
|
||||||
if frame["online"]:
|
if frame["online"]:
|
||||||
await self.__queue_frame(frame)
|
await self.__queue_frame(frame)
|
||||||
else:
|
else:
|
||||||
await self.__queue_frame("No signal")
|
await self.__queue_frame(self.gettext("No signal"))
|
||||||
except StreamerError as err:
|
except StreamerError as err:
|
||||||
if isinstance(err, StreamerPermError):
|
if isinstance(err, StreamerPermError):
|
||||||
streamer = self.__get_default_streamer()
|
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:
|
else:
|
||||||
logger.info("%s [streamer]: Waiting for stream: %s", self._remote, err)
|
logger.info(self.gettext("%s [streamer]: Waiting for stream: %s"), self._remote, err)
|
||||||
await self.__queue_frame("Waiting for stream ...")
|
await self.__queue_frame(self.gettext("Waiting for stream ..."))
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
def __get_preferred_streamer(self) -> BaseStreamerClient:
|
def __get_preferred_streamer(self) -> BaseStreamerClient:
|
||||||
@ -227,13 +231,13 @@ class _Client(RfbClient): # pylint: disable=too-many-instance-attributes
|
|||||||
streamer: (BaseStreamerClient | None) = None
|
streamer: (BaseStreamerClient | None) = None
|
||||||
for streamer in self.__streamers:
|
for streamer in self.__streamers:
|
||||||
if getattr(self._encodings, formats[streamer.get_format()]):
|
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
|
return streamer
|
||||||
raise RuntimeError("No streamers found")
|
raise RuntimeError("No streamers found")
|
||||||
|
|
||||||
def __get_default_streamer(self) -> BaseStreamerClient:
|
def __get_default_streamer(self) -> BaseStreamerClient:
|
||||||
streamer = self.__streamers[-1]
|
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
|
return streamer
|
||||||
|
|
||||||
async def __queue_frame(self, frame: (dict | str)) -> None:
|
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"])
|
await self._send_fb_jpeg(last["data"])
|
||||||
elif last["format"] == StreamFormats.H264:
|
elif last["format"] == StreamFormats.H264:
|
||||||
if not self._encodings.has_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:
|
if self.__fb_has_key:
|
||||||
await self._send_fb_h264(last["data"])
|
await self._send_fb_h264(last["data"])
|
||||||
else:
|
else:
|
||||||
await self._send_fb_allow_again()
|
await self._send_fb_allow_again()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(f"Unknown format: {last['format']}")
|
raise RuntimeError(self.gettext(f"Unknown format: {last['format']}"))
|
||||||
last["data"] = b""
|
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"]
|
has_quality = (await self.__kvmd_session.streamer.get_state())["features"]["quality"]
|
||||||
quality = (self._encodings.tight_jpeg_quality if has_quality else None)
|
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)
|
self._remote, quality, self.__desired_fps)
|
||||||
await self.__kvmd_session.streamer.set_params(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()
|
shared_params = _SharedParams()
|
||||||
|
|
||||||
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
async def cleanup_client(writer: asyncio.StreamWriter) -> None:
|
async def cleanup_client(writer: asyncio.StreamWriter) -> None:
|
||||||
if (await aiotools.close_writer(writer)):
|
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:
|
async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
|
||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
remote = rfb_format_remote(writer)
|
remote = rfb_format_remote(writer)
|
||||||
logger.info("%s [entry]: Connected client", remote)
|
logger.info(self.gettext("%s [entry]: Connected client"), remote)
|
||||||
try:
|
try:
|
||||||
sock = writer.get_extra_info("socket")
|
sock = writer.get_extra_info("socket")
|
||||||
if no_delay:
|
if no_delay:
|
||||||
@ -482,7 +488,7 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
async with kvmd.make_session("", "") as kvmd_session:
|
async with kvmd.make_session("", "") as kvmd_session:
|
||||||
none_auth_only = await kvmd_session.auth.check()
|
none_auth_only = await kvmd_session.auth.check()
|
||||||
except (aiohttp.ClientError, asyncio.TimeoutError) as err:
|
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
|
return
|
||||||
|
|
||||||
await _Client(
|
await _Client(
|
||||||
@ -504,7 +510,7 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
shared_params=shared_params,
|
shared_params=shared_params,
|
||||||
).run()
|
).run()
|
||||||
except Exception:
|
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:
|
finally:
|
||||||
await aiotools.shield_fg(cleanup_client(writer))
|
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]:
|
if not (await self.__vnc_auth_manager.read_credentials())[1]:
|
||||||
raise SystemExit(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]
|
(family, _, _, _, addr) = socket.getaddrinfo(self.__host, self.__port, type=socket.SOCK_STREAM)[0]
|
||||||
with contextlib.closing(socket.socket(family, socket.SOCK_STREAM)) as sock:
|
with contextlib.closing(socket.socket(family, socket.SOCK_STREAM)) as sock:
|
||||||
if family == socket.AF_INET6:
|
if family == socket.AF_INET6:
|
||||||
@ -532,4 +538,4 @@ class VncServer: # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
aiotools.run(self.__inner_run())
|
aiotools.run(self.__inner_run())
|
||||||
get_logger().info("Bye-bye")
|
get_logger().info(self.gettext("Bye-bye"))
|
||||||
|
|||||||
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
|
|
||||||
|
from ...languages import Languages
|
||||||
|
|
||||||
from ...logging import get_logger
|
from ...logging import get_logger
|
||||||
|
|
||||||
from ... import aiotools
|
from ... import aiotools
|
||||||
@ -30,7 +32,7 @@ from ... import aiotools
|
|||||||
# =====
|
# =====
|
||||||
class VncAuthError(Exception):
|
class VncAuthError(Exception):
|
||||||
def __init__(self, path: str, lineno: int, msg: str) -> None:
|
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.__path = path
|
||||||
self.__enabled = enabled
|
self.__enabled = enabled
|
||||||
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
async def read_credentials(self) -> tuple[dict[str, VncAuthKvmdCredentials], bool]:
|
async def read_credentials(self) -> tuple[dict[str, VncAuthKvmdCredentials], bool]:
|
||||||
if self.__enabled:
|
if self.__enabled:
|
||||||
@ -57,7 +60,7 @@ class VncAuthManager:
|
|||||||
except VncAuthError as err:
|
except VncAuthError as err:
|
||||||
get_logger(0).error(str(err))
|
get_logger(0).error(str(err))
|
||||||
except Exception:
|
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))
|
return ({}, (not self.__enabled))
|
||||||
|
|
||||||
async def __inner_read_credentials(self) -> dict[str, VncAuthKvmdCredentials]:
|
async def __inner_read_credentials(self) -> dict[str, VncAuthKvmdCredentials]:
|
||||||
@ -68,19 +71,19 @@ class VncAuthManager:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if " -> " not in line:
|
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))
|
(vnc_passwd, kvmd_userpass) = map(str.lstrip, line.split(" -> ", 1))
|
||||||
if ":" not in kvmd_userpass:
|
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_passwd) = kvmd_userpass.split(":")
|
||||||
kvmd_user = kvmd_user.strip()
|
kvmd_user = kvmd_user.strip()
|
||||||
if len(kvmd_user) == 0:
|
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:
|
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)
|
credentials[vnc_passwd] = VncAuthKvmdCredentials(kvmd_user, kvmd_passwd)
|
||||||
return credentials
|
return credentials
|
||||||
|
|||||||
@ -28,7 +28,7 @@ from typing import AsyncGenerator
|
|||||||
import aiohttp
|
import aiohttp
|
||||||
import aiohttp.multipart
|
import aiohttp.multipart
|
||||||
|
|
||||||
from .lanuages import Lanuages
|
from .languages import Languages
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ def get_filename(response: aiohttp.ClientResponse) -> str:
|
|||||||
try:
|
try:
|
||||||
return os.path.basename(response.url.path)
|
return os.path.basename(response.url.path)
|
||||||
except Exception:
|
except Exception:
|
||||||
raise aiohttp.ClientError(Lanuages().gettext("Can't determine filename"))
|
raise aiohttp.ClientError(Languages().gettext("Can't determine filename"))
|
||||||
|
|
||||||
|
|
||||||
@contextlib.asynccontextmanager
|
@contextlib.asynccontextmanager
|
||||||
|
|||||||
@ -52,7 +52,7 @@ from .errors import IsBusyError
|
|||||||
|
|
||||||
from .validators import ValidatorError
|
from .validators import ValidatorError
|
||||||
|
|
||||||
from .lanuages import Lanuages
|
from .languages import Languages
|
||||||
|
|
||||||
from . import aiotools
|
from . import aiotools
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ class HttpServer:
|
|||||||
self.__ws_bin_handlers: dict[int, Callable] = {}
|
self.__ws_bin_handlers: dict[int, Callable] = {}
|
||||||
self.__ws_sessions: list[WsSession] = []
|
self.__ws_sessions: list[WsSession] = []
|
||||||
self.__ws_sessions_lock = asyncio.Lock()
|
self.__ws_sessions_lock = asyncio.Lock()
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def run(
|
def run(
|
||||||
self,
|
self,
|
||||||
|
|||||||
Binary file not shown.
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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: 2024-08-12 22:07+0800\n"
|
"PO-Revision-Date: 2024-08-14 22:40+0800\n"
|
||||||
"Last-Translator: \n"
|
"Last-Translator: \n"
|
||||||
"Language: zh\n"
|
"Language: zh\n"
|
||||||
"Language-Team: zh <LL@li.org>\n"
|
"Language-Team: zh <LL@li.org>\n"
|
||||||
@ -90,37 +90,34 @@ msgstr "因收到忽略标识而取消监视 %s"
|
|||||||
msgid "Can't find any UDC"
|
msgid "Can't find any UDC"
|
||||||
msgstr "未找到任何 UDC"
|
msgstr "未找到任何 UDC"
|
||||||
|
|
||||||
#: kvmd/apps/__init__.py:132
|
#: kvmd/apps/__init__.py:164
|
||||||
msgid "Set config file path"
|
msgid "INFO"
|
||||||
msgstr "设置配置文件路径"
|
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
|
#: kvmd/apps/__init__.py:165
|
||||||
|
msgid "WARNING"
|
||||||
|
msgstr "警告"
|
||||||
|
|
||||||
|
#: kvmd/apps/__init__.py:166
|
||||||
|
msgid "ERROR"
|
||||||
|
msgstr "错误"
|
||||||
|
|
||||||
|
#: kvmd/apps/__init__.py:176
|
||||||
msgid ""
|
msgid ""
|
||||||
"To prevent accidental startup, you must specify the --run option to "
|
"To prevent accidental startup, you must specify the --run option to "
|
||||||
"start.\n"
|
"start.\n"
|
||||||
msgstr "为了防止意外启动,必须在启动时指定 --run 选项。\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"
|
msgid "Try the --help option to find out what this service does.\n"
|
||||||
msgstr "尝试使用 --help 选项来了解某项服务的功能。\n"
|
msgstr "尝试使用 --help 选项来了解某项服务的功能。\n"
|
||||||
|
|
||||||
#: kvmd/apps/__init__.py:165
|
#: kvmd/apps/__init__.py:176
|
||||||
msgid "Make sure you understand exactly what you are doing!"
|
msgid "Make sure you understand exactly what you are doing!"
|
||||||
msgstr "请确定你自己在做什么!"
|
msgstr "请确定你自己在做什么!"
|
||||||
|
|
||||||
#: kvmd/apps/kvmd/__init__.py:115 kvmd/apps/otgnet/__init__.py:132
|
#: kvmd/apps/kvmd/__init__.py:115 kvmd/apps/otgnet/__init__.py:132
|
||||||
|
#: kvmd/apps/vnc/server.py:541
|
||||||
msgid "Bye-bye"
|
msgid "Bye-bye"
|
||||||
msgstr "再见"
|
msgstr "再见"
|
||||||
|
|
||||||
@ -172,6 +169,48 @@ msgstr "无法生成新的唯一令牌"
|
|||||||
msgid "Logged out user %r (%d)"
|
msgid "Logged out user %r (%d)"
|
||||||
msgstr "已注销用户 %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
|
#: kvmd/apps/kvmd/streamer.py:245
|
||||||
msgid "Streamer stop cancelled"
|
msgid "Streamer stop cancelled"
|
||||||
msgstr "Streamer 停止已取消"
|
msgstr "Streamer 停止已取消"
|
||||||
@ -375,6 +414,115 @@ msgstr "使用 OTG gadget %r ......"
|
|||||||
msgid "Using OTG Ethernet interface %r ..."
|
msgid "Using OTG Ethernet interface %r ..."
|
||||||
msgstr "使用 OTG 以太网接口 %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
|
#: kvmd/keyboard/keysym.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Invalid modifier key at mapping %s: %s / %s"
|
msgid "Invalid modifier key at mapping %s: %s / %s"
|
||||||
@ -668,3 +816,15 @@ msgstr "无法执行重新挂载辅助程序"
|
|||||||
msgid "Failed ANELPWR POST request to pin %s: %s"
|
msgid "Failed ANELPWR POST request to pin %s: %s"
|
||||||
msgstr "向引脚 %s 发送 ANELPWR POST 请求失败:%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 "启动此服务"
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from typing import Generator
|
|||||||
|
|
||||||
from .logging import get_logger
|
from .logging import get_logger
|
||||||
|
|
||||||
from .lanuages import Lanuages
|
from .languages import Languages
|
||||||
|
|
||||||
from . import aiotools
|
from . import aiotools
|
||||||
from . import libc
|
from . import libc
|
||||||
@ -196,7 +196,7 @@ class Inotify:
|
|||||||
for path in paths:
|
for path in paths:
|
||||||
path = os.path.normpath(path)
|
path = os.path.normpath(path)
|
||||||
assert path not in self.__wd_by_path, 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
|
# Асинхронно, чтобы не висло на NFS
|
||||||
wd = _inotify_check(await aiotools.run_async(libc.inotify_add_watch, self.__fd, _fs_encode(path), mask))
|
wd = _inotify_check(await aiotools.run_async(libc.inotify_add_watch, self.__fd, _fs_encode(path), mask))
|
||||||
self.__wd_by_path[path] = wd
|
self.__wd_by_path[path] = wd
|
||||||
@ -255,7 +255,7 @@ class Inotify:
|
|||||||
if event.mask & InotifyMask.IGNORED:
|
if event.mask & InotifyMask.IGNORED:
|
||||||
ignored_path = self.__path_by_wd[event.wd]
|
ignored_path = self.__path_by_wd[event.wd]
|
||||||
if self.__wd_by_path[ignored_path] == 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]
|
del self.__wd_by_path[ignored_path]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import importlib.machinery
|
|||||||
|
|
||||||
import Xlib.keysymdef
|
import Xlib.keysymdef
|
||||||
|
|
||||||
from ..lanuages import Lanuages
|
from ..languages import Languages
|
||||||
|
|
||||||
from ..logging import get_logger
|
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.ALTS and key.altgr)
|
||||||
or (web_name in WebModifiers.CTRLS and key.ctrl)
|
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
|
continue
|
||||||
|
|
||||||
modifiers = (
|
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)
|
def _read_keyboard_layout(path: str) -> dict[int, list[At1Key]]: # Keysym to evdev (at1)
|
||||||
logger = get_logger(0)
|
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:
|
with open(path) as file:
|
||||||
lines = list(map(str.strip, file.read().split("\n")))
|
lines = list(map(str.strip, file.read().split("\n")))
|
||||||
|
|||||||
18
kvmd/languages.py
Normal file
18
kvmd/languages.py
Normal file
@ -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
|
||||||
|
|
||||||
@ -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)
|
|
||||||
@ -25,7 +25,7 @@ from typing import AsyncGenerator
|
|||||||
from ...errors import OperationError
|
from ...errors import OperationError
|
||||||
from ...errors import IsBusyError
|
from ...errors import IsBusyError
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from .. import BasePlugin
|
from .. import BasePlugin
|
||||||
from .. import get_plugin_class
|
from .. import get_plugin_class
|
||||||
@ -42,7 +42,7 @@ class AtxOperationError(OperationError, AtxError):
|
|||||||
|
|
||||||
class AtxIsBusyError(IsBusyError, AtxError):
|
class AtxIsBusyError(IsBusyError, AtxError):
|
||||||
def __init__(self) -> None:
|
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"))
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|||||||
@ -39,7 +39,7 @@ from ...validators.basic import valid_float_f01
|
|||||||
from ...validators.os import valid_abs_path
|
from ...validators.os import valid_abs_path
|
||||||
from ...validators.hw import valid_gpio_pin
|
from ...validators.hw import valid_gpio_pin
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from . import AtxIsBusyError
|
from . import AtxIsBusyError
|
||||||
from . import BaseAtx
|
from . import BaseAtx
|
||||||
@ -193,7 +193,7 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
|
|||||||
await self.__inner_click(name, pin, delay)
|
await self.__inner_click(name, pin, delay)
|
||||||
else:
|
else:
|
||||||
await aiotools.run_region_task(
|
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,
|
self.__region, self.__inner_click, name, pin, delay,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -206,4 +206,4 @@ class Plugin(BaseAtx): # pylint: disable=too-many-instance-attributes
|
|||||||
finally:
|
finally:
|
||||||
self.__line_request.set_value(pin, gpiod.line.Value(False))
|
self.__line_request.set_value(pin, gpiod.line.Value(False))
|
||||||
await asyncio.sleep(1)
|
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)
|
||||||
|
|||||||
@ -32,7 +32,7 @@ from ...logging import get_logger
|
|||||||
|
|
||||||
from ... import htclient
|
from ... import htclient
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from . import BaseAuthService
|
from . import BaseAuthService
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ class Plugin(BaseAuthService):
|
|||||||
htclient.raise_not_200(response)
|
htclient.raise_not_200(response)
|
||||||
return True
|
return True
|
||||||
except Exception:
|
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
|
return False
|
||||||
|
|
||||||
async def cleanup(self) -> None:
|
async def cleanup(self) -> None:
|
||||||
|
|||||||
@ -33,7 +33,7 @@ from ...logging import get_logger
|
|||||||
from ... import tools
|
from ... import tools
|
||||||
from ... import aiotools
|
from ... import aiotools
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from . import BaseAuthService
|
from . import BaseAuthService
|
||||||
|
|
||||||
@ -103,9 +103,9 @@ class Plugin(BaseAuthService):
|
|||||||
except ldap.INVALID_CREDENTIALS:
|
except ldap.INVALID_CREDENTIALS:
|
||||||
pass
|
pass
|
||||||
except ldap.SERVER_DOWN as err:
|
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:
|
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:
|
finally:
|
||||||
if conn is not None:
|
if conn is not None:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -34,7 +34,7 @@ from ...logging import get_logger
|
|||||||
|
|
||||||
from ... import aiotools
|
from ... import aiotools
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from . import BaseAuthService
|
from . import BaseAuthService
|
||||||
|
|
||||||
@ -88,13 +88,13 @@ class Plugin(BaseAuthService):
|
|||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if uid < self.__allow_uids_at:
|
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)
|
user, uid, self.__allow_uids_at)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pam_obj = pam.pam()
|
pam_obj = pam.pam()
|
||||||
if not pam_obj.authenticate(user, passwd, service=self.__service):
|
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)
|
user, pam_obj.code, pam_obj.reason)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|||||||
@ -36,7 +36,7 @@ from ...logging import get_logger
|
|||||||
|
|
||||||
from ... import aiotools
|
from ... import aiotools
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from . import BaseAuthService
|
from . import BaseAuthService
|
||||||
|
|
||||||
@ -442,5 +442,5 @@ class Plugin(BaseAuthService):
|
|||||||
response = client.SendPacket(request)
|
response = client.SendPacket(request)
|
||||||
return (response.code == pyrad.packet.AccessAccept)
|
return (response.code == pyrad.packet.AccessAccept)
|
||||||
except Exception:
|
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
|
return False
|
||||||
|
|||||||
@ -46,7 +46,7 @@ from ....validators.basic import valid_float_f01
|
|||||||
from ....validators.os import valid_abs_path
|
from ....validators.os import valid_abs_path
|
||||||
from ....validators.hw import valid_gpio_pin_optional
|
from ....validators.hw import valid_gpio_pin_optional
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from .. import BaseHid
|
from .. import BaseHid
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ class BaseMcuHid(BaseHid, multiprocessing.Process): # pylint: disable=too-many-
|
|||||||
|
|
||||||
self.__stop_event = multiprocessing.Event()
|
self.__stop_event = multiprocessing.Event()
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_options(cls) -> dict:
|
def get_plugin_options(cls) -> dict:
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import gpiod
|
|||||||
|
|
||||||
from ....logging import get_logger
|
from ....logging import get_logger
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class Gpio: # pylint: disable=too-many-instance-attributes
|
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.__line_request: (gpiod.LineRequest | None) = None
|
||||||
self.__last_power: (bool | None) = None
|
self.__last_power: (bool | None) = None
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def __enter__(self) -> None:
|
def __enter__(self) -> None:
|
||||||
if self.__power_detect_pin >= 0 or self.__reset_pin >= 0:
|
if self.__power_detect_pin >= 0 or self.__reset_pin >= 0:
|
||||||
|
|||||||
@ -40,7 +40,7 @@ from .... import aiotools
|
|||||||
from .... import aiomulti
|
from .... import aiomulti
|
||||||
from .... import aioproc
|
from .... import aioproc
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from .. import BaseHid
|
from .. import BaseHid
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
|
|||||||
stop_event=self.__stop_event,
|
stop_event=self.__stop_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_options(cls) -> dict:
|
def get_plugin_options(cls) -> dict:
|
||||||
|
|||||||
@ -38,7 +38,7 @@ from .... import aiomulti
|
|||||||
|
|
||||||
from ....keyboard.mappings import UsbKey
|
from ....keyboard.mappings import UsbKey
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from ..otg.events import BaseEvent
|
from ..otg.events import BaseEvent
|
||||||
from ..otg.events import ClearEvent
|
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.__keys: list[UsbKey | None] = [None] * 6
|
||||||
self.__mouse_buttons = 0
|
self.__mouse_buttons = 0
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
with self.__iface:
|
with self.__iface:
|
||||||
|
|||||||
@ -41,7 +41,7 @@ from ....validators.basic import valid_float_f01
|
|||||||
from ....validators.os import valid_abs_path
|
from ....validators.os import valid_abs_path
|
||||||
from ....validators.hw import valid_tty_speed
|
from ....validators.hw import valid_tty_speed
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from .. import BaseHid
|
from .. import BaseHid
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ class Plugin(BaseHid, multiprocessing.Process): # pylint: disable=too-many-inst
|
|||||||
self.__keyboard = Keyboard()
|
self.__keyboard = Keyboard()
|
||||||
self.__mouse = Mouse()
|
self.__mouse = Mouse()
|
||||||
|
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_options(cls) -> dict:
|
def get_plugin_options(cls) -> dict:
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import contextlib
|
|||||||
|
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
@ -37,7 +37,7 @@ class ChipResponseError(Exception):
|
|||||||
class ChipConnection:
|
class ChipConnection:
|
||||||
def __init__(self, tty: serial.Serial) -> None:
|
def __init__(self, tty: serial.Serial) -> None:
|
||||||
self.__tty = tty
|
self.__tty = tty
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def xfer(self, cmd: bytes) -> int:
|
def xfer(self, cmd: bytes) -> int:
|
||||||
self.__send(cmd)
|
self.__send(cmd)
|
||||||
|
|||||||
@ -24,7 +24,7 @@ from typing import Iterable
|
|||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from ....logging import get_logger
|
from ....logging import get_logger
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ class Plugin(BaseHid): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
def sysprep(self) -> None:
|
def sysprep(self) -> None:
|
||||||
udc = usb.find_udc(self.__udc)
|
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.__keyboard_proc.start(udc)
|
||||||
self.__mouse_proc.start(udc)
|
self.__mouse_proc.start(udc)
|
||||||
if self.__mouse_alt_proc:
|
if self.__mouse_alt_proc:
|
||||||
|
|||||||
@ -37,7 +37,7 @@ from .... import aiomulti
|
|||||||
from .... import aioproc
|
from .... import aioproc
|
||||||
from .... import usb
|
from .... import usb
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from .events import BaseEvent
|
from .events import BaseEvent
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ class BaseDeviceProcess(multiprocessing.Process): # pylint: disable=too-many-in
|
|||||||
|
|
||||||
self.__logger: (logging.Logger | None) = None
|
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
|
def start(self, udc: str) -> None: # type: ignore # pylint: disable=arguments-differ
|
||||||
self.__udc_state_path = usb.get_udc_path(udc, usb.U_STATE)
|
self.__udc_state_path = usb.get_udc_path(udc, usb.U_STATE)
|
||||||
|
|||||||
@ -41,7 +41,7 @@ from .events import get_led_scroll
|
|||||||
from .events import get_led_num
|
from .events import get_led_num
|
||||||
from .events import make_keyboard_report
|
from .events import make_keyboard_report
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class KeyboardProcess(BaseDeviceProcess):
|
class KeyboardProcess(BaseDeviceProcess):
|
||||||
@ -55,7 +55,7 @@ class KeyboardProcess(BaseDeviceProcess):
|
|||||||
|
|
||||||
self.__pressed_modifiers: set[UsbKey] = set()
|
self.__pressed_modifiers: set[UsbKey] = set()
|
||||||
self.__pressed_keys: list[UsbKey | None] = [None] * 6
|
self.__pressed_keys: list[UsbKey | None] = [None] * 6
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
self._stop()
|
self._stop()
|
||||||
|
|||||||
@ -36,7 +36,7 @@ from .events import MouseRelativeEvent
|
|||||||
from .events import MouseWheelEvent
|
from .events import MouseWheelEvent
|
||||||
from .events import make_mouse_report
|
from .events import make_mouse_report
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class MouseProcess(BaseDeviceProcess):
|
class MouseProcess(BaseDeviceProcess):
|
||||||
@ -55,7 +55,7 @@ class MouseProcess(BaseDeviceProcess):
|
|||||||
self.__x = 0 # For absolute
|
self.__x = 0 # For absolute
|
||||||
self.__y = 0
|
self.__y = 0
|
||||||
self.__win98_fix = False
|
self.__win98_fix = False
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
def is_absolute(self) -> bool:
|
def is_absolute(self) -> bool:
|
||||||
return self.__absolute
|
return self.__absolute
|
||||||
|
|||||||
@ -41,7 +41,7 @@ from ...validators.basic import valid_int_f1
|
|||||||
from ...validators.basic import valid_float_f01
|
from ...validators.basic import valid_float_f01
|
||||||
from ...validators.hw import valid_gpio_pin_optional
|
from ...validators.hw import valid_gpio_pin_optional
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Lanuages
|
||||||
|
|
||||||
from ._mcu import BasePhyConnection
|
from ._mcu import BasePhyConnection
|
||||||
from ._mcu import BasePhy
|
from ._mcu import BasePhy
|
||||||
|
|||||||
@ -40,7 +40,7 @@ from ... import aiotools
|
|||||||
from .. import BasePlugin
|
from .. import BasePlugin
|
||||||
from .. import get_plugin_class
|
from .. import get_plugin_class
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class MsdError(Exception):
|
class MsdError(Exception):
|
||||||
@ -53,43 +53,43 @@ class MsdOperationError(OperationError, MsdError):
|
|||||||
|
|
||||||
class MsdIsBusyError(IsBusyError, MsdError):
|
class MsdIsBusyError(IsBusyError, MsdError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("Performing another MSD operation, please try again later"))
|
super().__init__(gettext("Performing another MSD operation, please try again later"))
|
||||||
|
|
||||||
|
|
||||||
class MsdOfflineError(MsdOperationError):
|
class MsdOfflineError(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("MSD is not found"))
|
super().__init__(gettext("MSD is not found"))
|
||||||
|
|
||||||
|
|
||||||
class MsdConnectedError(MsdOperationError):
|
class MsdConnectedError(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("MSD is connected to Server, but shouldn't for this operation"))
|
super().__init__(gettext("MSD is connected to Server, but shouldn't for this operation"))
|
||||||
|
|
||||||
|
|
||||||
class MsdDisconnectedError(MsdOperationError):
|
class MsdDisconnectedError(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("MSD is disconnected from Server, but should be for this operation"))
|
super().__init__(gettext("MSD is disconnected from Server, but should be for this operation"))
|
||||||
|
|
||||||
|
|
||||||
class MsdImageNotSelected(MsdOperationError):
|
class MsdImageNotSelected(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("The image is not selected"))
|
super().__init__(gettext("The image is not selected"))
|
||||||
|
|
||||||
|
|
||||||
class MsdUnknownImageError(MsdOperationError):
|
class MsdUnknownImageError(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("The image is not found in the storage"))
|
super().__init__(gettext("The image is not found in the storage"))
|
||||||
|
|
||||||
|
|
||||||
class MsdImageExistsError(MsdOperationError):
|
class MsdImageExistsError(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
gettext=Lanuages().gettext
|
gettext=Languages().gettext
|
||||||
super().__init__(gettext("This image is already exists"))
|
super().__init__(gettext("This image is already exists"))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ from . import BaseMsdReader
|
|||||||
from . import BaseMsdWriter
|
from . import BaseMsdWriter
|
||||||
from . import BaseMsd
|
from . import BaseMsd
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Lanuages
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class MsdDisabledError(MsdOperationError):
|
class MsdDisabledError(MsdOperationError):
|
||||||
|
|||||||
@ -28,7 +28,7 @@ import time
|
|||||||
|
|
||||||
from typing import AsyncGenerator
|
from typing import AsyncGenerator
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
from ....logging import get_logger
|
from ....logging import get_logger
|
||||||
|
|
||||||
from ....inotify import InotifyMask
|
from ....inotify import InotifyMask
|
||||||
@ -44,7 +44,7 @@ from ....validators.kvm import valid_msd_image_name
|
|||||||
from .... import aiotools
|
from .... import aiotools
|
||||||
from .... import fstab
|
from .... import fstab
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from .. import MsdIsBusyError
|
from .. import MsdIsBusyError
|
||||||
from .. import MsdOfflineError
|
from .. import MsdOfflineError
|
||||||
@ -142,7 +142,7 @@ class Plugin(BaseMsd): # pylint: disable=too-many-instance-attributes
|
|||||||
|
|
||||||
self.__notifier = aiotools.AioNotifier()
|
self.__notifier = aiotools.AioNotifier()
|
||||||
self.__state = _State(self.__notifier)
|
self.__state = _State(self.__notifier)
|
||||||
self.gettext=Lanuages().gettext
|
self.gettext=Languages().gettext
|
||||||
|
|
||||||
logger = get_logger(0)
|
logger = get_logger(0)
|
||||||
logger.info(self.gettext("Using OTG gadget %r as MSD"), gadget)
|
logger.info(self.gettext("Using OTG gadget %r as MSD"), gadget)
|
||||||
|
|||||||
@ -27,13 +27,13 @@ from .... import usb
|
|||||||
|
|
||||||
from .. import MsdOperationError
|
from .. import MsdOperationError
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
class MsdDriveLockedError(MsdOperationError):
|
class MsdDriveLockedError(MsdOperationError):
|
||||||
def __init__(self) -> None:
|
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"))
|
||||||
|
|
||||||
|
|
||||||
# =====
|
# =====
|
||||||
|
|||||||
@ -31,7 +31,7 @@ from typing import Optional
|
|||||||
import aiofiles
|
import aiofiles
|
||||||
import aiofiles.os
|
import aiofiles.os
|
||||||
|
|
||||||
from ....lanuages import Lanuages
|
from ....languages import Languages
|
||||||
|
|
||||||
from .... import aiotools
|
from .... import aiotools
|
||||||
from .... import aiohelpers
|
from .... import aiohelpers
|
||||||
@ -294,4 +294,4 @@ class Storage(_StorageDc):
|
|||||||
async def remount_rw(self, rw: bool, fatal: bool=True) -> None:
|
async def remount_rw(self, rw: bool, fatal: bool=True) -> None:
|
||||||
if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)):
|
if not (await aiohelpers.remount("MSD", self.__remount_cmd, rw)):
|
||||||
if fatal:
|
if fatal:
|
||||||
raise MsdError(Lanuages().gettext("Can't execute remount helper"))
|
raise MsdError(Languages().gettext("Can't execute remount helper"))
|
||||||
|
|||||||
@ -41,7 +41,7 @@ from ...validators.basic import valid_bool
|
|||||||
from ...validators.basic import valid_number
|
from ...validators.basic import valid_number
|
||||||
from ...validators.basic import valid_float_f01
|
from ...validators.basic import valid_float_f01
|
||||||
|
|
||||||
from ...lanuages import Lanuages
|
from ...languages import Languages
|
||||||
|
|
||||||
from . import BaseUserGpioDriver
|
from . import BaseUserGpioDriver
|
||||||
from . import GpioDriverOfflineError
|
from . import GpioDriverOfflineError
|
||||||
@ -149,7 +149,7 @@ class Plugin(BaseUserGpioDriver): # pylint: disable=too-many-instance-attribute
|
|||||||
htclient.raise_not_200(response)
|
htclient.raise_not_200(response)
|
||||||
except Exception as err:
|
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)
|
raise GpioDriverOfflineError(self)
|
||||||
self.__update_notifier.notify()
|
self.__update_notifier.notify()
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from .lanuages import Lanuages
|
from .languages import Languages
|
||||||
|
|
||||||
from . import env
|
from . import env
|
||||||
|
|
||||||
@ -33,10 +33,10 @@ def find_udc(udc: str) -> str:
|
|||||||
candidates = sorted(os.listdir(path))
|
candidates = sorted(os.listdir(path))
|
||||||
if not udc:
|
if not udc:
|
||||||
if len(candidates) == 0:
|
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]
|
udc = candidates[0]
|
||||||
elif udc not in candidates:
|
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
|
return udc # fe980000.usb
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
34
kvmd/validators/languages.py
Normal file
34
kvmd/validators/languages.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# ========================================================================== #
|
||||||
|
# #
|
||||||
|
# KVMD - The main PiKVM daemon. #
|
||||||
|
# #
|
||||||
|
# Copyright (C) 2018-2024 Maxim Devaev <mdevaev@gmail.com> #
|
||||||
|
# #
|
||||||
|
# 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 <https://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
# ========================================================================== #
|
||||||
|
|
||||||
|
|
||||||
|
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"])
|
||||||
|
|
||||||
|
|
||||||
182
message.pot
182
message.pot
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -89,37 +89,34 @@ msgstr ""
|
|||||||
msgid "Can't find any UDC"
|
msgid "Can't find any UDC"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: kvmd/apps/__init__.py:132
|
#: kvmd/apps/__init__.py:164
|
||||||
msgid "Set config file path"
|
msgid "INFO"
|
||||||
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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: kvmd/apps/__init__.py:165
|
#: kvmd/apps/__init__.py:165
|
||||||
|
msgid "WARNING"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: kvmd/apps/__init__.py:166
|
||||||
|
msgid "ERROR"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: kvmd/apps/__init__.py:176
|
||||||
msgid ""
|
msgid ""
|
||||||
"To prevent accidental startup, you must specify the --run option to "
|
"To prevent accidental startup, you must specify the --run option to "
|
||||||
"start.\n"
|
"start.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: kvmd/apps/__init__.py:165
|
#: kvmd/apps/__init__.py:176
|
||||||
msgid "Try the --help option to find out what this service does.\n"
|
msgid "Try the --help option to find out what this service does.\n"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: kvmd/apps/__init__.py:165
|
#: kvmd/apps/__init__.py:176
|
||||||
msgid "Make sure you understand exactly what you are doing!"
|
msgid "Make sure you understand exactly what you are doing!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: kvmd/apps/kvmd/__init__.py:115 kvmd/apps/otgnet/__init__.py:132
|
#: kvmd/apps/kvmd/__init__.py:115 kvmd/apps/otgnet/__init__.py:132
|
||||||
|
#: kvmd/apps/vnc/server.py:541
|
||||||
msgid "Bye-bye"
|
msgid "Bye-bye"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -171,6 +168,48 @@ msgstr ""
|
|||||||
msgid "Logged out user %r (%d)"
|
msgid "Logged out user %r (%d)"
|
||||||
msgstr ""
|
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
|
#: kvmd/apps/kvmd/streamer.py:245
|
||||||
msgid "Streamer stop cancelled"
|
msgid "Streamer stop cancelled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -374,6 +413,115 @@ msgstr ""
|
|||||||
msgid "Using OTG Ethernet interface %r ..."
|
msgid "Using OTG Ethernet interface %r ..."
|
||||||
msgstr ""
|
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
|
#: kvmd/keyboard/keysym.py:69
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Invalid modifier key at mapping %s: %s / %s"
|
msgid "Invalid modifier key at mapping %s: %s / %s"
|
||||||
|
|||||||
@ -65,3 +65,7 @@ nginx:
|
|||||||
janus:
|
janus:
|
||||||
cmd:
|
cmd:
|
||||||
- "/bin/true"
|
- "/bin/true"
|
||||||
|
|
||||||
|
languages:
|
||||||
|
console: zh
|
||||||
|
web: zh
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user