refactor: refactor cli module

This commit is contained in:
2026-09-17 18:50:39 +08:00
parent b33d634020
commit deb94dd51a
15 changed files with 706 additions and 648 deletions
+10 -3
View File
@@ -1,12 +1,19 @@
import logging
import sys
from .cli import parse
from .cmds import run
from argparse import ArgumentParser
from .cmds import run, register, parse
def main() -> None:
logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")
opts = parse()
parser = ArgumentParser(
prog="metaglot",
description="A command-line tool that unifies localization metadata for applications shipped on multiple platforms.",
)
register(parser)
args = vars(parser.parse_args())
opts = parse(args)
try:
run(opts)
-624
View File
@@ -1,624 +0,0 @@
import enum
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
# region: Options and Commands
@dataclass(frozen=True)
class RcPotOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
out_pot: Path
"""The path to output POT file."""
@dataclass(frozen=True)
class RcRenderOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
in_po: list[Path]
"""The path to input PO files to read, each may be a glob pattern."""
in_template: Path
"""The path to the input user-provided Liquid for rendering."""
out_rc: Path
"""The path to the output rendered Windows RC file."""
@dataclass(frozen=True)
class RcUpdateOpts:
in_pot: Path
"""The path to the input POT template file."""
in_po: list[Path]
"""The path to input PO files to update, each may be a glob pattern."""
@dataclass(frozen=True)
class RcOpts:
opts: RcPotOpts | RcRenderOpts | RcUpdateOpts
"""The option of one of subcommand."""
class RcCommand(enum.StrEnum):
Pot = "pot"
Render = "render"
Update = "update"
@dataclass(frozen=True)
class DesktopPotOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
out_pot: Path
"""The path to output POT file."""
@dataclass(frozen=True)
class DesktopRenderOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
in_po: list[Path]
"""The path to input PO files to read, each may be a glob pattern."""
in_template: Path
"""The path to the input user-provided Liquid for rendering."""
out_desktop: Path
"""The path to the output rendered desktop entry file."""
@dataclass(frozen=True)
class DesktopUpdateOpts:
in_pot: Path
"""The path to the input POT template file."""
in_po: list[Path]
"""The path to input PO files to update, each may be a glob pattern."""
@dataclass(frozen=True)
class DesktopOpts:
opts: DesktopPotOpts | DesktopRenderOpts | DesktopUpdateOpts
"""The option of one of subcommand."""
class DesktopCommand(enum.StrEnum):
Pot = "pot"
Render = "render"
Update = "update"
@dataclass(frozen=True)
class AppStreamPotOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
out_pot: Path
"""The path to output POT file."""
@dataclass(frozen=True)
class AppStreamRenderOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
in_po: list[Path]
"""The path to input PO files to read, each may be a glob pattern."""
in_template: Path
"""The path to the input user-provided Liquid for rendering."""
out_appstream: Path
"""The path to the output rendered AppStream XML file."""
@dataclass(frozen=True)
class AppStreamUpdateOpts:
in_pot: Path
"""The path to the input POT template file."""
in_po: list[Path]
"""The path to input PO files to update, each may be a glob pattern."""
@dataclass(frozen=True)
class AppStreamOpts:
opts: AppStreamPotOpts | AppStreamRenderOpts | AppStreamUpdateOpts
"""The option of one of subcommand."""
class AppStreamCommand(enum.StrEnum):
Pot = "pot"
Render = "render"
Update = "update"
@dataclass(frozen=True)
class Opts:
"""The root options."""
opts: RcOpts | DesktopOpts | AppStreamOpts
"""The option of one of subcommand."""
class Command(enum.StrEnum):
Rc = "rc"
Desktop = "desktop"
Appstream = "appstream"
# endregion
# region: Parameter Register
_SUBCMD_DEST = "command"
_RC_SUBCMD_DEST = "rc_command"
_DESKTOP_SUBCMD_DEST = "desktop_command"
_RC_POT_MANIFEST_DEST = "manifest"
_RC_POT_OUTPUT_DEST = "output"
_RC_RDR_MANIFEST_DEST = "manifest"
_RC_RDR_PO_DEST = "po"
_RC_RDR_TEMPLATE_DEST = "template"
_RC_RDR_OUTPUT_DEST = "output"
_RC_UPD_POT_DEST = "pot"
_RC_UPD_PO_DEST = "po"
_DESKTOP_POT_MANIFEST_DEST = "manifest"
_DESKTOP_POT_OUTPUT_DEST = "output"
_DESKTOP_RDR_MANIFEST_DEST = "manifest"
_DESKTOP_RDR_PO_DEST = "po"
_DESKTOP_RDR_TEMPLATE_DEST = "template"
_DESKTOP_RDR_OUTPUT_DEST = "output"
_DESKTOP_UPD_POT_DEST = "pot"
_DESKTOP_UPD_PO_DEST = "po"
_APPSTREAM_SUBCMD_DEST = "appstream_command"
_APPSTREAM_POT_MANIFEST_DEST = "manifest"
_APPSTREAM_POT_OUTPUT_DEST = "output"
_APPSTREAM_RDR_MANIFEST_DEST = "manifest"
_APPSTREAM_RDR_PO_DEST = "po"
_APPSTREAM_RDR_TEMPLATE_DEST = "template"
_APPSTREAM_RDR_OUTPUT_DEST = "output"
_APPSTREAM_UPD_POT_DEST = "pot"
_APPSTREAM_UPD_PO_DEST = "po"
def _register_rc_pot_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_RC_POT_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_RC_POT_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to output POT file.",
metavar="FILE",
)
def _register_rc_render_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_RC_RDR_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_RC_RDR_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to read, each may be a glob pattern.",
metavar="GLOB",
)
parser.add_argument(
"-t",
"--template",
dest=_RC_RDR_TEMPLATE_DEST,
action="store",
type=Path,
required=True,
help="The path to the input user-provided Liquid for rendering.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_RC_RDR_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to the output rendered Windows RC file.",
metavar="FILE",
)
def _register_rc_update_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-t",
"--pot",
dest=_RC_UPD_POT_DEST,
action="store",
type=Path,
required=True,
help="The path to the input POT template file.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_RC_UPD_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to update, each may be a glob pattern.",
metavar="GLOB",
)
def _register_rc_param(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_RC_SUBCMD_DEST, required=True)
pot_subcmd = cmds.add_parser(
RcCommand.Pot.value, help="Generate a POT template from given manifest."
)
_register_rc_pot_param(pot_subcmd)
render_subcmd = cmds.add_parser(
RcCommand.Render.value,
help="Render a user-provided template with manifest data and PO translations.",
)
_register_rc_render_param(render_subcmd)
update_subcmd = cmds.add_parser(
RcCommand.Update.value, help="Update PO files against a POT template."
)
_register_rc_update_param(update_subcmd)
def _register_desktop_pot_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_DESKTOP_POT_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_DESKTOP_POT_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to output POT file.",
metavar="FILE",
)
def _register_desktop_render_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_DESKTOP_RDR_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_DESKTOP_RDR_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to read, each may be a glob pattern.",
metavar="GLOB",
)
parser.add_argument(
"-t",
"--template",
dest=_DESKTOP_RDR_TEMPLATE_DEST,
action="store",
type=Path,
required=True,
help="The path to the input user-provided Liquid for rendering.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_DESKTOP_RDR_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to the output rendered desktop entry file.",
metavar="FILE",
)
def _register_desktop_update_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-t",
"--pot",
dest=_DESKTOP_UPD_POT_DEST,
action="store",
type=Path,
required=True,
help="The path to the input POT template file.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_DESKTOP_UPD_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to update, each may be a glob pattern.",
metavar="GLOB",
)
def _register_desktop_param(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_DESKTOP_SUBCMD_DEST, required=True)
pot_subcmd = cmds.add_parser(
DesktopCommand.Pot.value,
help="Generate a POT template from given manifest.",
)
_register_desktop_pot_param(pot_subcmd)
render_subcmd = cmds.add_parser(
DesktopCommand.Render.value,
help="Render a user-provided template with manifest data and PO translations.",
)
_register_desktop_render_param(render_subcmd)
update_subcmd = cmds.add_parser(
DesktopCommand.Update.value, help="Update PO files against a POT template."
)
_register_desktop_update_param(update_subcmd)
def _register_appstream_pot_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_APPSTREAM_POT_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_APPSTREAM_POT_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to output POT file.",
metavar="FILE",
)
def _register_appstream_render_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_APPSTREAM_RDR_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_APPSTREAM_RDR_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to read, each may be a glob pattern.",
metavar="GLOB",
)
parser.add_argument(
"-t",
"--template",
dest=_APPSTREAM_RDR_TEMPLATE_DEST,
action="store",
type=Path,
required=True,
help="The path to the input user-provided Liquid for rendering.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_APPSTREAM_RDR_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to the output rendered AppStream XML file.",
metavar="FILE",
)
def _register_appstream_update_param(parser: ArgumentParser) -> None:
parser.add_argument(
"-t",
"--pot",
dest=_APPSTREAM_UPD_POT_DEST,
action="store",
type=Path,
required=True,
help="The path to the input POT template file.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_APPSTREAM_UPD_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to update, each may be a glob pattern.",
metavar="GLOB",
)
def _register_appstream_param(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_APPSTREAM_SUBCMD_DEST, required=True)
pot_subcmd = cmds.add_parser(
AppStreamCommand.Pot.value,
help="Generate a POT template from given manifest.",
)
_register_appstream_pot_param(pot_subcmd)
render_subcmd = cmds.add_parser(
AppStreamCommand.Render.value,
help="Render a user-provided template with manifest data and PO translations.",
)
_register_appstream_render_param(render_subcmd)
update_subcmd = cmds.add_parser(
AppStreamCommand.Update.value, help="Update PO files against a POT template."
)
_register_appstream_update_param(update_subcmd)
def _register_param(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_SUBCMD_DEST, required=True)
rc_subcmd = cmds.add_parser(
Command.Rc.value, help="Commands for Windows RC resource files."
)
_register_rc_param(rc_subcmd)
desktop_subcmd = cmds.add_parser(
Command.Desktop.value, help="Commands for freedesktop desktop entry files."
)
_register_desktop_param(desktop_subcmd)
appstream_subcmd = cmds.add_parser(
Command.Appstream.value, help="Commands for AppStream metainfo XML files."
)
_register_appstream_param(appstream_subcmd)
# endregion
def parse() -> Opts:
"""
Parse MetaGlot command line options.
:return: The parsed command line options.
"""
parser = ArgumentParser(
prog="metaglot",
description="A command-line tool that unifies localization metadata for applications shipped on multiple platforms.",
)
_register_param(parser)
args = vars(parser.parse_args())
match Command(args[_SUBCMD_DEST]):
case Command.Rc:
match RcCommand(args[_RC_SUBCMD_DEST]):
case RcCommand.Pot:
rc_pot_opts = RcPotOpts(
args[_RC_POT_MANIFEST_DEST], args[_RC_POT_OUTPUT_DEST]
)
rc_opts = RcOpts(rc_pot_opts)
case RcCommand.Render:
rc_rdr_opts = RcRenderOpts(
args[_RC_RDR_MANIFEST_DEST],
args[_RC_RDR_PO_DEST],
args[_RC_RDR_TEMPLATE_DEST],
args[_RC_RDR_OUTPUT_DEST],
)
rc_opts = RcOpts(rc_rdr_opts)
case RcCommand.Update:
rc_upd_opts = RcUpdateOpts(
args[_RC_UPD_POT_DEST], args[_RC_UPD_PO_DEST]
)
rc_opts = RcOpts(rc_upd_opts)
case _:
raise ValueError(f"unhandled rc command: {args[_RC_SUBCMD_DEST]}")
opts = Opts(rc_opts)
case Command.Desktop:
match DesktopCommand(args[_DESKTOP_SUBCMD_DEST]):
case DesktopCommand.Pot:
desktop_pot_opts = DesktopPotOpts(
args[_DESKTOP_POT_MANIFEST_DEST], args[_DESKTOP_POT_OUTPUT_DEST]
)
desktop_opts = DesktopOpts(desktop_pot_opts)
case DesktopCommand.Render:
desktop_rdr_opts = DesktopRenderOpts(
args[_DESKTOP_RDR_MANIFEST_DEST],
args[_DESKTOP_RDR_PO_DEST],
args[_DESKTOP_RDR_TEMPLATE_DEST],
args[_DESKTOP_RDR_OUTPUT_DEST],
)
desktop_opts = DesktopOpts(desktop_rdr_opts)
case DesktopCommand.Update:
desktop_upd_opts = DesktopUpdateOpts(
args[_DESKTOP_UPD_POT_DEST], args[_DESKTOP_UPD_PO_DEST]
)
desktop_opts = DesktopOpts(desktop_upd_opts)
case _:
raise ValueError(
f"unhandled desktop command: {args[_DESKTOP_SUBCMD_DEST]}"
)
opts = Opts(desktop_opts)
case Command.Appstream:
match AppStreamCommand(args[_APPSTREAM_SUBCMD_DEST]):
case AppStreamCommand.Pot:
appstream_pot_opts = AppStreamPotOpts(
args[_APPSTREAM_POT_MANIFEST_DEST],
args[_APPSTREAM_POT_OUTPUT_DEST],
)
appstream_opts = AppStreamOpts(appstream_pot_opts)
case AppStreamCommand.Render:
appstream_rdr_opts = AppStreamRenderOpts(
args[_APPSTREAM_RDR_MANIFEST_DEST],
args[_APPSTREAM_RDR_PO_DEST],
args[_APPSTREAM_RDR_TEMPLATE_DEST],
args[_APPSTREAM_RDR_OUTPUT_DEST],
)
appstream_opts = AppStreamOpts(appstream_rdr_opts)
case AppStreamCommand.Update:
appstream_upd_opts = AppStreamUpdateOpts(
args[_APPSTREAM_UPD_POT_DEST], args[_APPSTREAM_UPD_PO_DEST]
)
appstream_opts = AppStreamOpts(appstream_upd_opts)
case _:
raise ValueError(
f"unhandled appstream command: {args[_APPSTREAM_SUBCMD_DEST]}"
)
opts = Opts(appstream_opts)
case _:
raise ValueError(f"unhandled command: {args[_SUBCMD_DEST]}")
return opts
+54 -1
View File
@@ -1,5 +1,58 @@
from ..cli import Opts, RcOpts, DesktopOpts, AppStreamOpts
import enum
from argparse import ArgumentParser
from dataclasses import dataclass
from typing import Any
from . import appstream, desktop, rc
from .rc import RcOpts
from .desktop import DesktopOpts
from .appstream import AppStreamOpts
@dataclass(frozen=True)
class Opts:
"""The root options."""
opts: RcOpts | DesktopOpts | AppStreamOpts
"""The option of one of subcommand."""
_SUBCMD_DEST = "command"
class _Command(enum.StrEnum):
Rc = "rc"
Desktop = "desktop"
Appstream = "appstream"
def register(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_SUBCMD_DEST, required=True)
subcmd = cmds.add_parser(
_Command.Rc.value, help="Commands for Windows RC resource files."
)
rc.register(subcmd)
subcmd = cmds.add_parser(
_Command.Desktop.value, help="Commands for freedesktop desktop entry files."
)
desktop.register(subcmd)
subcmd = cmds.add_parser(
_Command.Appstream.value, help="Commands for AppStream metainfo XML files."
)
appstream.register(subcmd)
def parse(args: dict[str, Any]) -> Opts:
match _Command(args[_SUBCMD_DEST]):
case _Command.Rc:
opts = Opts(rc.parse(args))
case _Command.Desktop:
opts = Opts(desktop.parse(args))
case _Command.Appstream:
opts = Opts(appstream.parse(args))
case _:
raise ValueError(f"unhandled command: {args[_SUBCMD_DEST]}")
return opts
def run(opts: Opts) -> None:
match opts.opts:
+55 -6
View File
@@ -1,10 +1,59 @@
from ...cli import (
AppStreamOpts,
AppStreamPotOpts,
AppStreamRenderOpts,
AppStreamUpdateOpts,
)
import enum
from argparse import ArgumentParser
from dataclasses import dataclass
from typing import Any
from . import pot, render, update
from .pot import AppStreamPotOpts
from .render import AppStreamRenderOpts
from .update import AppStreamUpdateOpts
_SUBCMD_DEST = "subcommand"
@dataclass(frozen=True)
class AppStreamOpts:
opts: AppStreamPotOpts | AppStreamRenderOpts | AppStreamUpdateOpts
"""The option of one of subcommand."""
class _Command(enum.StrEnum):
Pot = "pot"
Render = "render"
Update = "update"
def register(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_SUBCMD_DEST, required=True)
pot_subcmd = cmds.add_parser(
_Command.Pot.value,
help="Generate a POT template from given manifest.",
)
pot.register(pot_subcmd)
render_subcmd = cmds.add_parser(
_Command.Render.value,
help="Render a user-provided template with manifest data and PO translations.",
)
render.register(render_subcmd)
update_subcmd = cmds.add_parser(
_Command.Update.value, help="Update PO files against a POT template."
)
update.register(update_subcmd)
def parse(args: dict[str, Any]) -> AppStreamOpts:
match _Command(args[_SUBCMD_DEST]):
case _Command.Pot:
opts = AppStreamOpts(pot.parse(args))
case _Command.Render:
opts = AppStreamOpts(render.parse(args))
case _Command.Update:
opts = AppStreamOpts(update.parse(args))
case _:
raise ValueError(
f"unhandled appstream command: {args[_SUBCMD_DEST]}"
)
return opts
def run(opts: AppStreamOpts) -> None:
match opts.opts:
+43 -1
View File
@@ -1,7 +1,49 @@
from ...cli import AppStreamPotOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...manifest import load_manifest
from ...pofile import generate_pot
_MANIFEST_DEST = "manifest"
_OUTPUT_DEST = "output"
@dataclass(frozen=True)
class AppStreamPotOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
out_pot: Path
"""The path to output POT file."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to output POT file.",
metavar="FILE",
)
def parse(args: dict[str, Any]) -> AppStreamPotOpts:
return AppStreamPotOpts(args[_MANIFEST_DEST], args[_OUTPUT_DEST])
def run(opts: AppStreamPotOpts) -> None:
manifest = load_manifest(opts.in_manifest)
generate_pot(manifest, opts.out_pot)
+74 -2
View File
@@ -1,6 +1,8 @@
import logging
from ...cli import AppStreamRenderOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...filters import register_appstream_filters, register_general_filters
from ...langmap import appstream_lang_map
from ...manifest import Manifest, load_manifest
@@ -12,6 +14,76 @@ from ...pofile import (
)
from ...render import create_environment, render
_MANIFEST_DEST = "manifest"
_PO_DEST = "po"
_TEMPLATE_DEST = "template"
_OUTPUT_DEST = "output"
@dataclass(frozen=True)
class AppStreamRenderOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
in_po: list[Path]
"""The path to input PO files to read, each may be a glob pattern."""
in_template: Path
"""The path to the input user-provided Liquid for rendering."""
out_appstream: Path
"""The path of the output rendered AppStream XML file."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to read, each may be a glob pattern.",
metavar="GLOB",
)
parser.add_argument(
"-t",
"--template",
dest=_TEMPLATE_DEST,
action="store",
type=Path,
required=True,
help="The path to the input user-provided Liquid for rendering.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path of the output rendered AppStream XML file.",
metavar="FILE",
)
def parse(args: dict[str, Any]) -> AppStreamRenderOpts:
return AppStreamRenderOpts(
args[_MANIFEST_DEST],
args[_PO_DEST],
args[_TEMPLATE_DEST],
args[_OUTPUT_DEST],
)
def _language_context(pack: LanguagePack, manifest: Manifest) -> dict:
lang = appstream_lang_map.convert(pack.lang)
+43 -1
View File
@@ -1,6 +1,48 @@
from ...cli import AppStreamUpdateOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...pofile import resolve_glob_files, update_po_files
_POT_DEST = "pot"
_PO_DEST = "po"
@dataclass(frozen=True)
class AppStreamUpdateOpts:
in_pot: Path
"""The path to the input POT template file."""
in_po: list[Path]
"""The path to input PO files to update, each may be a glob pattern."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-t",
"--pot",
dest=_POT_DEST,
action="store",
type=Path,
required=True,
help="The path to the input POT template file.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to update, each may be a glob pattern.",
metavar="GLOB",
)
def parse(args: dict[str, Any]) -> AppStreamUpdateOpts:
return AppStreamUpdateOpts(args[_POT_DEST], args[_PO_DEST])
def run(opts: AppStreamUpdateOpts) -> None:
update_po_files(opts.in_pot, resolve_glob_files(opts.in_po))
+55 -1
View File
@@ -1,5 +1,59 @@
from ...cli import DesktopOpts, DesktopPotOpts, DesktopRenderOpts, DesktopUpdateOpts
import enum
from argparse import ArgumentParser
from dataclasses import dataclass
from typing import Any
from . import pot, render, update
from .pot import DesktopPotOpts
from .render import DesktopRenderOpts
from .update import DesktopUpdateOpts
_SUBCMD_DEST = "subcommand"
@dataclass(frozen=True)
class DesktopOpts:
opts: DesktopPotOpts | DesktopRenderOpts | DesktopUpdateOpts
"""The option of one of subcommand."""
class _Command(enum.StrEnum):
Pot = "pot"
Render = "render"
Update = "update"
def register(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_SUBCMD_DEST, required=True)
pot_subcmd = cmds.add_parser(
_Command.Pot.value,
help="Generate a POT template from given manifest.",
)
pot.register(pot_subcmd)
render_subcmd = cmds.add_parser(
_Command.Render.value,
help="Render a user-provided template with manifest data and PO translations.",
)
render.register(render_subcmd)
update_subcmd = cmds.add_parser(
_Command.Update.value, help="Update PO files against a POT template."
)
update.register(update_subcmd)
def parse(args: dict[str, Any]) -> DesktopOpts:
match _Command(args[_SUBCMD_DEST]):
case _Command.Pot:
opts = DesktopOpts(pot.parse(args))
case _Command.Render:
opts = DesktopOpts(render.parse(args))
case _Command.Update:
opts = DesktopOpts(update.parse(args))
case _:
raise ValueError(
f"unhandled desktop command: {args[_SUBCMD_DEST]}"
)
return opts
def run(opts: DesktopOpts) -> None:
match opts.opts:
+43 -1
View File
@@ -1,7 +1,49 @@
from ...cli import DesktopPotOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...manifest import load_manifest
from ...pofile import generate_pot
_MANIFEST_DEST = "manifest"
_OUTPUT_DEST = "output"
@dataclass(frozen=True)
class DesktopPotOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
out_pot: Path
"""The path to output POT file."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to output POT file.",
metavar="FILE",
)
def parse(args: dict[str, Any]) -> DesktopPotOpts:
return DesktopPotOpts(args[_MANIFEST_DEST], args[_OUTPUT_DEST])
def run(opts: DesktopPotOpts) -> None:
manifest = load_manifest(opts.in_manifest)
generate_pot(manifest, opts.out_pot)
+74 -2
View File
@@ -1,6 +1,8 @@
import logging
from ...cli import DesktopRenderOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...filters import register_desktop_filters, register_general_filters
from ...langmap import desktop_lang_map
from ...manifest import Manifest, load_manifest
@@ -12,6 +14,76 @@ from ...pofile import (
)
from ...render import create_environment, render
_MANIFEST_DEST = "manifest"
_PO_DEST = "po"
_TEMPLATE_DEST = "template"
_OUTPUT_DEST = "output"
@dataclass(frozen=True)
class DesktopRenderOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
in_po: list[Path]
"""The path to input PO files to read, each may be a glob pattern."""
in_template: Path
"""The path to the input user-provided Liquid for rendering."""
out_desktop: Path
"""The path of the output rendered desktop entry file."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to read, each may be a glob pattern.",
metavar="GLOB",
)
parser.add_argument(
"-t",
"--template",
dest=_TEMPLATE_DEST,
action="store",
type=Path,
required=True,
help="The path to the input user-provided Liquid for rendering.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path of the output rendered desktop entry file.",
metavar="FILE",
)
def parse(args: dict[str, Any]) -> DesktopRenderOpts:
return DesktopRenderOpts(
args[_MANIFEST_DEST],
args[_PO_DEST],
args[_TEMPLATE_DEST],
args[_OUTPUT_DEST],
)
def _language_context(pack: LanguagePack, manifest: Manifest) -> dict:
locale = desktop_lang_map.convert(pack.lang)
+43 -1
View File
@@ -1,6 +1,48 @@
from ...cli import DesktopUpdateOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...pofile import resolve_glob_files, update_po_files
_POT_DEST = "pot"
_PO_DEST = "po"
@dataclass(frozen=True)
class DesktopUpdateOpts:
in_pot: Path
"""The path to the input POT template file."""
in_po: list[Path]
"""The path to input PO files to update, each may be a glob pattern."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-t",
"--pot",
dest=_POT_DEST,
action="store",
type=Path,
required=True,
help="The path to the input POT template file.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to update, each may be a glob pattern.",
metavar="GLOB",
)
def parse(args: dict[str, Any]) -> DesktopUpdateOpts:
return DesktopUpdateOpts(args[_POT_DEST], args[_PO_DEST])
def run(opts: DesktopUpdateOpts) -> None:
update_po_files(opts.in_pot, resolve_glob_files(opts.in_po))
+52 -1
View File
@@ -1,5 +1,56 @@
from ...cli import RcOpts, RcPotOpts, RcRenderOpts, RcUpdateOpts
import enum
from argparse import ArgumentParser
from dataclasses import dataclass
from typing import Any
from . import pot, render, update
from .pot import RcPotOpts
from .render import RcRenderOpts
from .update import RcUpdateOpts
_SUBCMD_DEST = "subcommand"
@dataclass(frozen=True)
class RcOpts:
opts: RcPotOpts | RcRenderOpts | RcUpdateOpts
"""The option of one of subcommand."""
class _Command(enum.StrEnum):
Pot = "pot"
Render = "render"
Update = "update"
def register(parser: ArgumentParser) -> None:
cmds = parser.add_subparsers(dest=_SUBCMD_DEST, required=True)
pot_subcmd = cmds.add_parser(
_Command.Pot.value, help="Generate a POT template from given manifest."
)
pot.register(pot_subcmd)
render_subcmd = cmds.add_parser(
_Command.Render.value,
help="Render a user-provided template with manifest data and PO translations.",
)
render.register(render_subcmd)
update_subcmd = cmds.add_parser(
_Command.Update.value, help="Update PO files against a POT template."
)
update.register(update_subcmd)
def parse(args: dict[str, Any]) -> RcOpts:
match _Command(args[_SUBCMD_DEST]):
case _Command.Pot:
opts = RcOpts(pot.parse(args))
case _Command.Render:
opts = RcOpts(render.parse(args))
case _Command.Update:
opts = RcOpts(update.parse(args))
case _:
raise ValueError(f"unhandled rc command: {args[_SUBCMD_DEST]}")
return opts
def run(opts: RcOpts) -> None:
match opts.opts:
+43 -1
View File
@@ -1,7 +1,49 @@
from ...cli import RcPotOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...manifest import load_manifest
from ...pofile import generate_pot
_MANIFEST_DEST = "manifest"
_OUTPUT_DEST = "output"
@dataclass(frozen=True)
class RcPotOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
out_pot: Path
"""The path to output POT file."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path to output POT file.",
metavar="FILE",
)
def parse(args: dict[str, Any]) -> RcPotOpts:
return RcPotOpts(args[_MANIFEST_DEST], args[_OUTPUT_DEST])
def run(opts: RcPotOpts) -> None:
manifest = load_manifest(opts.in_manifest)
generate_pot(manifest, opts.out_pot)
+74 -2
View File
@@ -1,6 +1,8 @@
import logging
from ...cli import RcRenderOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...filters import register_general_filters, register_rc_filters
from ...langid import PoLang, WinLcid
from ...langmap import win_lcid_map
@@ -20,6 +22,76 @@ MetaGlot reads and writes everything as UTF-8, so this code page is a fixed
constraint of the pipeline, not a configuration point.
"""
_MANIFEST_DEST = "manifest"
_PO_DEST = "po"
_TEMPLATE_DEST = "template"
_OUTPUT_DEST = "output"
@dataclass(frozen=True)
class RcRenderOpts:
in_manifest: Path
"""The path to input manifest file including translation strings."""
in_po: list[Path]
"""The path to input PO files to read, each may be a glob pattern."""
in_template: Path
"""The path to the input user-provided Liquid for rendering."""
out_rc: Path
"""The path of the output rendered Windows RC file."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-m",
"--manifest",
dest=_MANIFEST_DEST,
action="store",
type=Path,
required=True,
help="The path to input manifest file including translation strings.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to read, each may be a glob pattern.",
metavar="GLOB",
)
parser.add_argument(
"-t",
"--template",
dest=_TEMPLATE_DEST,
action="store",
type=Path,
required=True,
help="The path to the input user-provided Liquid for rendering.",
metavar="FILE",
)
parser.add_argument(
"-o",
"--output",
dest=_OUTPUT_DEST,
action="store",
type=Path,
required=True,
help="The path of the output rendered Windows RC file.",
metavar="FILE",
)
def parse(args: dict[str, Any]) -> RcRenderOpts:
return RcRenderOpts(
args[_MANIFEST_DEST],
args[_PO_DEST],
args[_TEMPLATE_DEST],
args[_OUTPUT_DEST],
)
def _lookup_langid(lang: PoLang) -> WinLcid:
try:
+43 -1
View File
@@ -1,6 +1,48 @@
from ...cli import RcUpdateOpts
from argparse import ArgumentParser
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from ...pofile import resolve_glob_files, update_po_files
_POT_DEST = "pot"
_PO_DEST = "po"
@dataclass(frozen=True)
class RcUpdateOpts:
in_pot: Path
"""The path to the input POT template file."""
in_po: list[Path]
"""The path to input PO files to update, each may be a glob pattern."""
def register(parser: ArgumentParser) -> None:
parser.add_argument(
"-t",
"--pot",
dest=_POT_DEST,
action="store",
type=Path,
required=True,
help="The path to the input POT template file.",
metavar="FILE",
)
parser.add_argument(
"-p",
"--po",
dest=_PO_DEST,
action="extend",
nargs="+",
type=Path,
required=True,
help="The PO files to update, each may be a glob pattern.",
metavar="GLOB",
)
def parse(args: dict[str, Any]) -> RcUpdateOpts:
return RcUpdateOpts(args[_POT_DEST], args[_PO_DEST])
def run(opts: RcUpdateOpts) -> None:
update_po_files(opts.in_pot, resolve_glob_files(opts.in_po))