ruyi 0.44.0b20251219__py3-none-any.whl → 0.45.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. ruyi/__main__.py +16 -4
  2. ruyi/cli/cmd.py +6 -5
  3. ruyi/cli/config_cli.py +14 -11
  4. ruyi/cli/main.py +14 -4
  5. ruyi/cli/oobe.py +7 -3
  6. ruyi/cli/self_cli.py +48 -34
  7. ruyi/cli/user_input.py +42 -12
  8. ruyi/cli/version_cli.py +11 -5
  9. ruyi/config/__init__.py +26 -2
  10. ruyi/config/errors.py +19 -7
  11. ruyi/device/provision.py +116 -55
  12. ruyi/device/provision_cli.py +6 -3
  13. ruyi/i18n/__init__.py +129 -0
  14. ruyi/log/__init__.py +6 -5
  15. ruyi/mux/runtime.py +19 -6
  16. ruyi/mux/venv/maker.py +93 -35
  17. ruyi/mux/venv/venv_cli.py +13 -10
  18. ruyi/pluginhost/plugin_cli.py +4 -3
  19. ruyi/resource_bundle/__init__.py +22 -8
  20. ruyi/resource_bundle/__main__.py +6 -5
  21. ruyi/resource_bundle/data.py +13 -9
  22. ruyi/ruyipkg/admin_checksum.py +4 -1
  23. ruyi/ruyipkg/admin_cli.py +9 -6
  24. ruyi/ruyipkg/augmented_pkg.py +15 -14
  25. ruyi/ruyipkg/checksum.py +8 -2
  26. ruyi/ruyipkg/distfile.py +33 -9
  27. ruyi/ruyipkg/entity.py +12 -2
  28. ruyi/ruyipkg/entity_cli.py +20 -12
  29. ruyi/ruyipkg/entity_provider.py +11 -2
  30. ruyi/ruyipkg/fetcher.py +38 -9
  31. ruyi/ruyipkg/install.py +143 -42
  32. ruyi/ruyipkg/install_cli.py +18 -15
  33. ruyi/ruyipkg/list.py +27 -20
  34. ruyi/ruyipkg/list_cli.py +12 -7
  35. ruyi/ruyipkg/news.py +23 -11
  36. ruyi/ruyipkg/news_cli.py +10 -7
  37. ruyi/ruyipkg/profile_cli.py +8 -2
  38. ruyi/ruyipkg/repo.py +22 -8
  39. ruyi/ruyipkg/unpack.py +42 -8
  40. ruyi/ruyipkg/unpack_method.py +5 -1
  41. ruyi/ruyipkg/update_cli.py +8 -3
  42. ruyi/telemetry/provider.py +74 -29
  43. ruyi/telemetry/telemetry_cli.py +9 -8
  44. ruyi/utils/git.py +18 -11
  45. ruyi/utils/prereqs.py +10 -5
  46. ruyi/utils/ssl_patch.py +2 -1
  47. ruyi/version.py +9 -3
  48. {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/METADATA +2 -1
  49. ruyi-0.45.0.dist-info/RECORD +103 -0
  50. {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/WHEEL +1 -1
  51. ruyi-0.44.0b20251219.dist-info/RECORD +0 -102
  52. {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/entry_points.txt +0 -0
  53. {ruyi-0.44.0b20251219.dist-info → ruyi-0.45.0.dist-info}/licenses/LICENSE-Apache.txt +0 -0
ruyi/ruyipkg/list.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from itertools import chain
2
2
 
3
3
  from ..config import GlobalConfig
4
+ from ..i18n import _
4
5
  from ..log import RuyiLogger
5
6
  from ..utils.porcelain import PorcelainOutput
6
7
  from .augmented_pkg import AugmentedPkg
@@ -22,9 +23,11 @@ def do_list(
22
23
  # all packages either
23
24
  return 1
24
25
 
25
- logger.F("no filter specified for list operation")
26
+ logger.F(_("no filter specified for list operation"))
26
27
  logger.I(
27
- "for the old behavior of listing all packages, try [yellow]ruyi list --name-contains ''[/]"
28
+ _(
29
+ "for the old behavior of listing all packages, try [yellow]ruyi list --name-contains ''[/]"
30
+ )
28
31
  )
29
32
  return 1
30
33
 
@@ -49,7 +52,7 @@ def _do_list_non_verbose(
49
52
  logger: RuyiLogger,
50
53
  augmented_pkgs: list[AugmentedPkg],
51
54
  ) -> int:
52
- logger.stdout("List of available packages:\n")
55
+ logger.stdout(_("List of available packages:\n"))
53
56
 
54
57
  for ap in augmented_pkgs:
55
58
  logger.stdout(f"* [bold green]{ap.category}/{ap.name}[/]")
@@ -82,45 +85,49 @@ def _print_pkg_detail(
82
85
  logger.stdout(f"[bold]## [green]{pm.category}/{pm.name}[/] [blue]{pm.ver}[/][/]\n")
83
86
 
84
87
  if pm.slug is not None:
85
- logger.stdout(f"* Slug: [yellow]{pm.slug}[/]")
88
+ logger.stdout(_("* Slug: [yellow]{slug}[/]").format(slug=pm.slug))
86
89
  else:
87
- logger.stdout("* Slug: (none)")
88
- logger.stdout(f"* Package kind: {sorted(pm.kind)}")
89
- logger.stdout(f"* Vendor: {pm.vendor_name}")
90
+ logger.stdout(_("* Slug: (none)"))
91
+ logger.stdout(_("* Package kind: {kind}").format(kind=sorted(pm.kind)))
92
+ logger.stdout(_("* Vendor: {vendor}").format(vendor=pm.vendor_name))
90
93
  if upstream_ver := pm.upstream_version:
91
- logger.stdout(f"* Upstream version number: {upstream_ver}")
94
+ logger.stdout(
95
+ _("* Upstream version number: {version}").format(version=upstream_ver)
96
+ )
92
97
  else:
93
- logger.stdout("* Upstream version number: (undeclared)")
98
+ logger.stdout(_("* Upstream version number: (undeclared)"))
94
99
  logger.stdout("")
95
100
 
96
101
  sv = pm.service_level
97
102
  if sv.has_known_issues:
98
- logger.stdout("\nPackage has known issue(s):\n")
103
+ logger.stdout(_("\nPackage has known issue(s):\n"))
99
104
  for x in sv.render_known_issues(pm.repo.messages, lang_code):
100
105
  logger.stdout(x, end="\n\n")
101
106
 
102
107
  df = pm.distfiles
103
- logger.stdout(f"Package declares {len(df)} distfile(s):\n")
108
+ logger.stdout(_("Package declares {count} distfile(s):\n").format(count=len(df)))
104
109
  for dd in df.values():
105
110
  logger.stdout(f"* [green]{dd.name}[/]")
106
- logger.stdout(f" - Size: [yellow]{dd.size}[/] bytes")
111
+ logger.stdout(_(" - Size: [yellow]{size}[/] bytes").format(size=dd.size))
107
112
  for kind, csum in dd.checksums.items():
108
113
  logger.stdout(f" - {kind.upper()}: [yellow]{csum}[/]")
109
114
 
110
115
  if bm := pm.binary_metadata:
111
- logger.stdout("\n### Binary artifacts\n")
116
+ logger.stdout(_("\n### Binary artifacts\n"))
112
117
  for host, data in bm.data.items():
113
- logger.stdout(f"* Host [green]{host}[/]:")
114
- logger.stdout(f" - Distfiles: {data['distfiles']}")
118
+ logger.stdout(_("* Host [green]{host}[/]:").format(host=host))
119
+ logger.stdout(
120
+ _(" - Distfiles: {distfiles}").format(distfiles=data["distfiles"])
121
+ )
115
122
  if cmds := data.get("commands"):
116
- logger.stdout(" - Available command(s):")
123
+ logger.stdout(_(" - Available command(s):"))
117
124
  for k in sorted(cmds.keys()):
118
125
  logger.stdout(f" - [green]{k}[/]")
119
126
 
120
127
  if tm := pm.toolchain_metadata:
121
- logger.stdout("\n### Toolchain metadata\n")
122
- logger.stdout(f"* Target: [bold green]{tm.target}[/]")
123
- logger.stdout(f"* Quirks: {tm.quirks}")
124
- logger.stdout("* Components:")
128
+ logger.stdout(_("\n### Toolchain metadata\n"))
129
+ logger.stdout(_("* Target: [bold green]{target}[/]").format(target=tm.target))
130
+ logger.stdout(_("* Quirks: {quirks}").format(quirks=tm.quirks))
131
+ logger.stdout(_("* Components:"))
125
132
  for tc in tm.components:
126
133
  logger.stdout(f' - {tc["name"]} [bold green]{tc["version"]}[/]')
ruyi/ruyipkg/list_cli.py CHANGED
@@ -2,6 +2,7 @@ import argparse
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..cli.cmd import RootCommand
5
+ from ..i18n import _
5
6
  from .list_filter import ListFilter, ListFilterAction
6
7
 
7
8
  if TYPE_CHECKING:
@@ -15,7 +16,7 @@ class ListCommand(
15
16
  has_subcommands=True,
16
17
  is_subcommand_required=False,
17
18
  has_main=True,
18
- help="List available packages in configured repository",
19
+ help=_("List available packages in configured repository"),
19
20
  ):
20
21
  @classmethod
21
22
  def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
@@ -23,7 +24,7 @@ class ListCommand(
23
24
  "--verbose",
24
25
  "-v",
25
26
  action="store_true",
26
- help="Also show details for every package",
27
+ help=_("Also show details for every package"),
27
28
  )
28
29
 
29
30
  # filter expressions
@@ -32,28 +33,32 @@ class ListCommand(
32
33
  action=ListFilterAction,
33
34
  nargs=1,
34
35
  dest="filters",
35
- help="Match packages that are installed (y/true/1) or not installed (n/false/0)",
36
+ help=_(
37
+ "Match packages that are installed (y/true/1) or not installed (n/false/0)"
38
+ ),
36
39
  )
37
40
  p.add_argument(
38
41
  "--category-contains",
39
42
  action=ListFilterAction,
40
43
  nargs=1,
41
44
  dest="filters",
42
- help="Match packages from categories whose names contain the given string",
45
+ help=_(
46
+ "Match packages from categories whose names contain the given string"
47
+ ),
43
48
  )
44
49
  p.add_argument(
45
50
  "--category-is",
46
51
  action=ListFilterAction,
47
52
  nargs=1,
48
53
  dest="filters",
49
- help="Match packages from the given category",
54
+ help=_("Match packages from the given category"),
50
55
  )
51
56
  p.add_argument(
52
57
  "--name-contains",
53
58
  action=ListFilterAction,
54
59
  nargs=1,
55
60
  dest="filters",
56
- help="Match packages whose names contain the given string",
61
+ help=_("Match packages whose names contain the given string"),
57
62
  )
58
63
 
59
64
  if gc.is_experimental:
@@ -62,7 +67,7 @@ class ListCommand(
62
67
  action=ListFilterAction,
63
68
  nargs=1,
64
69
  dest="filters",
65
- help="Match packages related to the given entity",
70
+ help=_("Match packages related to the given entity"),
66
71
  )
67
72
 
68
73
  @classmethod
ruyi/ruyipkg/news.py CHANGED
@@ -2,6 +2,7 @@ from rich import box
2
2
  from rich.table import Table
3
3
 
4
4
  from ..config import GlobalConfig
5
+ from ..i18n import _
5
6
  from ..log import RuyiLogger
6
7
  from ..utils.markdown import RuyiStyledMarkdown
7
8
  from ..utils.porcelain import PorcelainOutput
@@ -14,9 +15,12 @@ def print_news_item_titles(
14
15
  lang: str,
15
16
  ) -> None:
16
17
  tbl = Table(box=box.SIMPLE, show_edge=False)
17
- tbl.add_column("No.")
18
- tbl.add_column("ID")
19
- tbl.add_column("Title")
18
+ # i18n NOTE: used as news item table title
19
+ tbl.add_column(_("No."))
20
+ # i18n NOTE: used as news item table title
21
+ tbl.add_column(_("ID"))
22
+ # i18n NOTE: used as news item table title
23
+ tbl.add_column(_("Title"))
20
24
 
21
25
  for ni in newsitems:
22
26
  unread = not ni.is_read
@@ -40,14 +44,20 @@ def maybe_notify_unread_news(
40
44
 
41
45
  unread_newsitems = gc.repo.news_store().list(True)
42
46
  if unread_newsitems:
43
- gc.logger.stdout(f"\nThere are {len(unread_newsitems)} new news item(s):\n")
47
+ gc.logger.stdout(
48
+ _("\nThere are {count} new news item(s):\n").format(
49
+ count=len(unread_newsitems),
50
+ )
51
+ )
44
52
  print_news_item_titles(gc.logger, unread_newsitems, gc.lang_code)
45
- gc.logger.stdout("\nYou can read them with [yellow]ruyi news read[/].")
53
+ gc.logger.stdout(_("\nYou can read them with [yellow]ruyi news read[/]."))
46
54
  return
47
55
 
48
56
  if prompt_no_unread:
49
57
  gc.logger.stdout(
50
- "\nAll news items have been read. To see a list of them, run [yellow]ruyi news list[/].\n"
58
+ _(
59
+ "\nAll news items have been read. To see a list of them, run [yellow]ruyi news list[/].\n"
60
+ )
51
61
  )
52
62
 
53
63
 
@@ -65,9 +75,9 @@ def do_news_list(
65
75
  po.emit(ni.to_porcelain())
66
76
  return 0
67
77
 
68
- logger.stdout("[bold green]News items:[/]\n")
78
+ logger.stdout(_("[bold green]News items:[/]\n"))
69
79
  if not newsitems:
70
- logger.stdout(" (no unread item)" if only_unread else " (no item)")
80
+ logger.stdout(_(" (no unread item)") if only_unread else _(" (no item)"))
71
81
  return 0
72
82
 
73
83
  print_news_item_titles(logger, newsitems, cfg.lang_code)
@@ -98,7 +108,7 @@ def do_news_read(
98
108
  for ni in items:
99
109
  print_news(logger, ni.get_content_for_lang(cfg.lang_code))
100
110
  else:
101
- logger.stdout("No news to display.")
111
+ logger.stdout(_("No news to display."))
102
112
 
103
113
  # record read statuses
104
114
  store.mark_as_read(*(ni.id for ni in items))
@@ -123,13 +133,15 @@ def filter_news_items_by_specs(
123
133
  try:
124
134
  ni_ord = int(i)
125
135
  if ni_ord not in ni_by_ord:
126
- logger.F(f"there is no news item with ordinal {ni_ord}")
136
+ logger.F(
137
+ _("there is no news item with ordinal {ord}").format(ord=ni_ord)
138
+ )
127
139
  return None
128
140
  items.append(ni_by_ord[ni_ord])
129
141
  except ValueError:
130
142
  # treat i as id
131
143
  if i not in ni_by_id:
132
- logger.F(f"there is no news item with ID '{i}'")
144
+ logger.F(_("there is no news item with ID '{id}'").format(id=i))
133
145
  return None
134
146
  items.append(ni_by_id[i])
135
147
 
ruyi/ruyipkg/news_cli.py CHANGED
@@ -2,6 +2,7 @@ import argparse
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..cli.cmd import RootCommand
5
+ from ..i18n import _
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from ..cli.completion import ArgumentParser
@@ -14,7 +15,7 @@ class NewsCommand(
14
15
  has_subcommands=True,
15
16
  is_subcommand_required=False,
16
17
  has_main=True,
17
- help="List and read news items from configured repository",
18
+ help=_("List and read news items from configured repository"),
18
19
  ):
19
20
  _my_parser: "ArgumentParser | None" = None
20
21
 
@@ -36,14 +37,14 @@ class NewsCommand(
36
37
  class NewsListCommand(
37
38
  NewsCommand,
38
39
  cmd="list",
39
- help="List news items",
40
+ help=_("List news items"),
40
41
  ):
41
42
  @classmethod
42
43
  def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
43
44
  p.add_argument(
44
45
  "--new",
45
46
  action="store_true",
46
- help="List unread news items only",
47
+ help=_("List unread news items only"),
47
48
  )
48
49
 
49
50
  @classmethod
@@ -60,8 +61,10 @@ class NewsListCommand(
60
61
  class NewsReadCommand(
61
62
  NewsCommand,
62
63
  cmd="read",
63
- help="Read news items",
64
- description="Outputs news item(s) to the console and mark as already read. Defaults to reading all unread items if no item is specified.",
64
+ help=_("Read news items"),
65
+ description=_(
66
+ "Outputs news item(s) to the console and mark as already read. Defaults to reading all unread items if no item is specified."
67
+ ),
65
68
  ):
66
69
  @classmethod
67
70
  def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
@@ -69,13 +72,13 @@ class NewsReadCommand(
69
72
  "--quiet",
70
73
  "-q",
71
74
  action="store_true",
72
- help="Do not output anything and only mark as read",
75
+ help=_("Do not output anything and only mark as read"),
73
76
  )
74
77
  p.add_argument(
75
78
  "item",
76
79
  type=str,
77
80
  nargs="*",
78
- help="Ordinal or ID of the news item(s) to read",
81
+ help=_("Ordinal or ID of the news item(s) to read"),
79
82
  )
80
83
 
81
84
  @classmethod
@@ -1,6 +1,7 @@
1
1
  import argparse
2
2
  from typing import TYPE_CHECKING
3
3
 
4
+ from ..i18n import _
4
5
  from .list_cli import ListCommand
5
6
 
6
7
  if TYPE_CHECKING:
@@ -11,7 +12,7 @@ if TYPE_CHECKING:
11
12
  class ListProfilesCommand(
12
13
  ListCommand,
13
14
  cmd="profiles",
14
- help="List all available profiles",
15
+ help=_("List all available profiles"),
15
16
  ):
16
17
  @classmethod
17
18
  def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
@@ -28,6 +29,11 @@ class ListProfilesCommand(
28
29
  logger.stdout(p.id)
29
30
  continue
30
31
 
31
- logger.stdout(f"{p.id} (needs quirks: {p.need_quirks})")
32
+ logger.stdout(
33
+ _("{profile_id} (needs quirks: {need_quirks})").format(
34
+ profile_id=p.id,
35
+ need_quirks=p.need_quirks,
36
+ )
37
+ )
32
38
 
33
39
  return 0
ruyi/ruyipkg/repo.py CHANGED
@@ -19,6 +19,7 @@ from urllib import parse
19
19
  from pygit2 import clone_repository
20
20
  from pygit2.repository import Repository
21
21
 
22
+ from ..i18n import _
22
23
  from ..log import RuyiLogger
23
24
  from ..pluginhost.ctx import PluginHostContext
24
25
  from ..telemetry.scope import TelemetryScopeConfig
@@ -167,7 +168,9 @@ class RepoConfig:
167
168
  return [url]
168
169
  case _:
169
170
  # deny others
170
- logger.W(f"unrecognized dist URL scheme: {u.scheme}")
171
+ logger.W(
172
+ _("unrecognized dist URL scheme: {scheme}").format(scheme=u.scheme)
173
+ )
171
174
  return []
172
175
 
173
176
  def get_mirror_urls_for_file(self, mirror_id: str, path: str) -> list[str]:
@@ -286,9 +289,13 @@ class MetadataRepo(ProvidesPackageManifests):
286
289
  return self.repo
287
290
 
288
291
  self.logger.I(
289
- f"the package repository does not exist at [yellow]{self.root}[/]"
292
+ _("the package repository does not exist at [yellow]{root}[/]").format(
293
+ root=self.root
294
+ )
295
+ )
296
+ self.logger.I(
297
+ _("cloning from [cyan link={remote}]{remote}[/]").format(remote=self.remote)
290
298
  )
291
- self.logger.I(f"cloning from [cyan link={self.remote}]{self.remote}[/]")
292
299
 
293
300
  with RemoteGitProgressIndicator() as pr:
294
301
  repo = clone_repository(
@@ -310,7 +317,7 @@ class MetadataRepo(ProvidesPackageManifests):
310
317
  return self.repo
311
318
 
312
319
  def sync(self) -> None:
313
- self._gc.logger.I("updating the package repository")
320
+ self._gc.logger.I(_("updating the package repository"))
314
321
 
315
322
  repo = self.ensure_git_repo()
316
323
 
@@ -327,7 +334,7 @@ class MetadataRepo(ProvidesPackageManifests):
327
334
  allow_auto_management=allow_auto_management,
328
335
  )
329
336
 
330
- self._gc.logger.I("package repository is updated")
337
+ self._gc.logger.I(_("package repository is updated"))
331
338
 
332
339
  @property
333
340
  def global_config(self) -> "GlobalConfig":
@@ -605,7 +612,9 @@ class MetadataRepo(ProvidesPackageManifests):
605
612
  contents = fp.read()
606
613
  except UnicodeDecodeError:
607
614
  self.logger.W(
608
- f"UnicodeDecodeError: {os.path.join(news_dir, f)}"
615
+ _("UnicodeDecodeError: {path}").format(
616
+ path=os.path.join(news_dir, f)
617
+ )
609
618
  )
610
619
  continue
611
620
  cache.add(f, contents) # may fail but failures are harmless
@@ -638,9 +647,14 @@ class MetadataRepo(ProvidesPackageManifests):
638
647
  ret = self.eval_plugin_fn(plugin_entrypoint, args)
639
648
  if not isinstance(ret, int):
640
649
  self.logger.W(
641
- f"unexpected return type of cmd plugin '{plugin_id}': {type(ret)} is not int."
650
+ _(
651
+ "unexpected return type of cmd plugin '{plugin_id}': {type} is not int."
652
+ ).format(
653
+ plugin_id=plugin_id,
654
+ type=type(ret),
655
+ )
642
656
  )
643
- self.logger.I("forcing return code to 1; the plugin should be fixed")
657
+ self.logger.I(_("forcing return code to 1; the plugin should be fixed"))
644
658
  ret = 1
645
659
  return ret
646
660
 
ruyi/ruyipkg/unpack.py CHANGED
@@ -4,6 +4,7 @@ import shutil
4
4
  import subprocess
5
5
  from typing import Any, BinaryIO, NoReturn, Protocol
6
6
 
7
+ from ..i18n import _
7
8
  from ..log import RuyiLogger
8
9
  from ..utils import ar, prereqs
9
10
  from .unpack_method import (
@@ -192,7 +193,12 @@ def _do_unpack_tar(
192
193
  retcode = p.wait()
193
194
 
194
195
  if retcode != 0:
195
- raise RuntimeError(f"untar failed: command {' '.join(argv)} returned {retcode}")
196
+ raise RuntimeError(
197
+ _("untar failed: command {cmd} returned {retcode}").format(
198
+ cmd=" ".join(argv),
199
+ retcode=retcode,
200
+ )
201
+ )
196
202
 
197
203
 
198
204
  def _do_unpack_zip(
@@ -206,7 +212,12 @@ def _do_unpack_zip(
206
212
  logger.D(f"about to call unzip: argv={argv}")
207
213
  retcode = subprocess.call(argv, cwd=dest)
208
214
  if retcode != 0:
209
- raise RuntimeError(f"unzip failed: command {' '.join(argv)} returned {retcode}")
215
+ raise RuntimeError(
216
+ _("unzip failed: command {cmd} returned {retcode}").format(
217
+ cmd=" ".join(argv),
218
+ retcode=retcode,
219
+ )
220
+ )
210
221
 
211
222
 
212
223
  def _do_unpack_bare_gz(
@@ -226,7 +237,10 @@ def _do_unpack_bare_gz(
226
237
  retcode = subprocess.call(argv, stdout=out)
227
238
  if retcode != 0:
228
239
  raise RuntimeError(
229
- f"gunzip failed: command {' '.join(argv)} returned {retcode}"
240
+ _("gunzip failed: command {cmd} returned {retcode}").format(
241
+ cmd=" ".join(argv),
242
+ retcode=retcode,
243
+ )
230
244
  )
231
245
 
232
246
 
@@ -247,7 +261,10 @@ def _do_unpack_bare_bzip2(
247
261
  retcode = subprocess.call(argv, stdout=out)
248
262
  if retcode != 0:
249
263
  raise RuntimeError(
250
- f"bzip2 failed: command {' '.join(argv)} returned {retcode}"
264
+ _("bzip2 failed: command {cmd} returned {retcode}").format(
265
+ cmd=" ".join(argv),
266
+ retcode=retcode,
267
+ )
251
268
  )
252
269
 
253
270
 
@@ -263,7 +280,12 @@ def _do_unpack_bare_lz4(
263
280
  logger.D(f"about to call lz4: argv={argv}")
264
281
  retcode = subprocess.call(argv, cwd=destdir)
265
282
  if retcode != 0:
266
- raise RuntimeError(f"lz4 failed: command {' '.join(argv)} returned {retcode}")
283
+ raise RuntimeError(
284
+ _("lz4 failed: command {cmd} returned {retcode}").format(
285
+ cmd=" ".join(argv),
286
+ retcode=retcode,
287
+ )
288
+ )
267
289
 
268
290
 
269
291
  def _do_unpack_bare_xz(
@@ -283,7 +305,10 @@ def _do_unpack_bare_xz(
283
305
  retcode = subprocess.call(argv, stdout=out)
284
306
  if retcode != 0:
285
307
  raise RuntimeError(
286
- f"xz failed: command {' '.join(argv)} returned {retcode}"
308
+ _("xz failed: command {cmd} returned {retcode}").format(
309
+ cmd=" ".join(argv),
310
+ retcode=retcode,
311
+ )
287
312
  )
288
313
 
289
314
 
@@ -299,7 +324,12 @@ def _do_unpack_bare_zstd(
299
324
  logger.D(f"about to call zstd: argv={argv}")
300
325
  retcode = subprocess.call(argv, cwd=destdir)
301
326
  if retcode != 0:
302
- raise RuntimeError(f"zstd failed: command {' '.join(argv)} returned {retcode}")
327
+ raise RuntimeError(
328
+ _("zstd failed: command {cmd} returned {retcode}").format(
329
+ cmd=" ".join(argv),
330
+ retcode=retcode,
331
+ )
332
+ )
303
333
 
304
334
 
305
335
  def _do_unpack_deb(
@@ -321,7 +351,11 @@ def _do_unpack_deb(
321
351
  a.open(f),
322
352
  )
323
353
 
324
- raise RuntimeError(f"file '{filename}' does not appear to be a deb")
354
+ raise RuntimeError(
355
+ _("file '{filename}' does not appear to be a deb").format(
356
+ filename=filename,
357
+ )
358
+ )
325
359
 
326
360
 
327
361
  def _get_unpack_cmds_for_method(m: UnpackMethod) -> list[str]:
@@ -3,6 +3,8 @@ import re
3
3
  import sys
4
4
  from typing import Final
5
5
 
6
+ from ..i18n import _
7
+
6
8
  RE_TARBALL: Final = re.compile(r"\.tar(?:\.gz|\.bz2|\.lz4|\.xz|\.zst)?$")
7
9
 
8
10
 
@@ -60,7 +62,9 @@ class UnrecognizedPackFormatError(Exception):
60
62
  self.filename = filename
61
63
 
62
64
  def __str__(self) -> str:
63
- return f"don't know how to unpack file {self.filename}"
65
+ return _("don't know how to unpack file {filename}").format(
66
+ filename=self.filename,
67
+ )
64
68
 
65
69
 
66
70
  def determine_unpack_method(
@@ -2,6 +2,7 @@ import argparse
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ..cli.cmd import RootCommand
5
+ from ..i18n import _
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from ..cli.completion import ArgumentParser
@@ -11,7 +12,7 @@ if TYPE_CHECKING:
11
12
  class UpdateCommand(
12
13
  RootCommand,
13
14
  cmd="update",
14
- help="Update RuyiSDK repo and packages",
15
+ help=_("Update RuyiSDK repo and packages"),
15
16
  ):
16
17
  @classmethod
17
18
  def configure_args(cls, gc: "GlobalConfig", p: "ArgumentParser") -> None:
@@ -32,16 +33,20 @@ class UpdateCommand(
32
33
 
33
34
  if upgradable:
34
35
  logger.stdout(
35
- "\nNewer versions are available for some of your installed packages:\n"
36
+ _(
37
+ "\nNewer versions are available for some of your installed packages:\n"
38
+ )
36
39
  )
37
40
  for pm, new_ver in upgradable:
38
41
  logger.stdout(
39
42
  f" - [bold]{pm.category}/{pm.name}[/]: [yellow]{pm.ver}[/] -> [green]{new_ver}[/]"
40
43
  )
41
44
  logger.stdout(
42
- """
45
+ _(
46
+ """
43
47
  Re-run [yellow]ruyi install[/] to upgrade, and don't forget to re-create any affected
44
48
  virtual environments."""
49
+ )
45
50
  )
46
51
 
47
52
  news.maybe_notify_unread_news(cfg, False)