From 68f13bc369a8dcac6c3e7d06e9d154fe2230215b Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 09:13:08 +0800 Subject: [PATCH 01/28] Theme preview server for theme authors tools/theme_server/serve.py renders every PortMaster screen with a theme and shows them in a web page that refreshes when a theme file is saved: any resolution, colour scheme and language, a view of one screen at every common size, and region boxes showing each region's area and its final theme values. --export writes the screens as PNGs instead. Each render process (worker.py) is a headless pugwash for one theme version, scheme, resolution and language, since SDL allows one GUI per process and those are fixed when pugwash starts. Screens use the real ports list and screenshots, downloaded once into a cache. pugwash: - PM_PRETEND_RESOLUTION=WxH sets the screen size in HM_TESTING mode, in place of the commented-out pretend_resolution. - create_screenshot() takes a file name, and reads the pixels before switching back to the window: it saved the window's last frame. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 8 + PortMaster/pylibs/pugwash/app.py | 35 ++- THEME.md | 32 ++ tests/test_theme_server.py | 269 ++++++++++++++++ tools/theme_server/serve.py | 454 +++++++++++++++++++++++++++ tools/theme_server/shots.py | 354 +++++++++++++++++++++ tools/theme_server/static/index.html | 421 +++++++++++++++++++++++++ tools/theme_server/worker.py | 248 +++++++++++++++ 8 files changed, 1808 insertions(+), 13 deletions(-) create mode 100644 tests/test_theme_server.py create mode 100644 tools/theme_server/serve.py create mode 100644 tools/theme_server/shots.py create mode 100644 tools/theme_server/static/index.html create mode 100644 tools/theme_server/worker.py diff --git a/CLAUDE.md b/CLAUDE.md index cfd2f97b..06cbb83d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,6 +71,14 @@ Guard tests for restructuring: It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes `pmtest/reports/--.zip`, scrubbed of MAC/private IP addresses, serials, Wi-Fi names and the hostname (`suite.scrub`). The tester's own `pugwash.txt`/`harbourmaster.txt`/`log.txt` are set aside in `pmtest/saved-logs/` for the run and put back afterwards (or by the next run after a crash), so a report only holds the run's logs. pugwash and the GUI tests run in the environment the installed launcher gives the GUI (`pm_launch.txt` with `PM_LAUNCH_ENV_ONLY=Y`). The build also writes `dist/pmtest.zip.md5`, for `harbourmaster install /pmtest.zip.md5`. `python3 -I tools/test_suite/matrix.py --out DIR` turns collected reports into `matrix.json` and a self-contained `matrix.html` coverage page, starting with a device × CFW release heat map. `tools/test_suite/devices.py` names the hardware behind each CFW's device string (`DEVICES`, `FAMILIES`) and reads a vendored copy of PortMaster-Info's `device_info.json` (`--refresh` updates it) for supported-but-untested cells; a report it can't place is printed and listed under Other; it and `receive.py` must keep running on Python 3.7 with only the standard library. A build made with `PM_PMTEST_UPLOAD=http://:8770/` carries `pmtest/upload.txt` and, if the tester agrees on the start screen, POSTs each finished report there, where `python3 -I tools/test_suite/receive.py ` saves it on the LAN. The public endpoint is the `pmtest-reports` Cloudflare Worker (its own repo, with `collect.py` to download reports); HTTPS uses PortMaster's certifi bundle. Builds without the variable send nothing. Tested in `tests/test_test_suite.py` and `tests/test_build.py`. +**Theme preview server** (`tools/theme_server/`, for theme authors, documented in `THEME.md`): +- `serve.py` serves a page showing every screen rendered with a theme, at any resolution, colour scheme and language, and exports PNGs with `--export`. +- Each render process, `worker.py`, is a headless pugwash for one theme version, scheme, resolution and language. SDL allows one GUI per process, and the screen size, the theme's `[capability]` overrides and the language are fixed at start. A new one starts when the theme's files change, so no state carries over. + - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode. + - It links a shared ports cache (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`). +- `shots.py` holds the screen catalogue: one builder per screen, pushing scenes with real port data. +- Tested in `tests/test_theme_server.py`. + Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is fixed and the marker should then be removed. pugwash's `main()` is wrapped in `logger.catch`, so it exits 0 even after a crash; check its output for tracebacks rather than relying on the exit code. ## Architecture diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index 64b907bc..dcb49729 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -30,6 +30,15 @@ from pugwash import lang +def parse_resolution(text): + """(width, height) from "1024x768", None for anything else.""" + width, _x, height = text.strip().lower().partition('x') + if not (width.isdigit() and height.isdigit() and int(width) > 0 and int(height) > 0): + return None + + return (int(width), int(height)) + + class PortMasterGUI(CallbackMixin, CommandsMixin, FifoControlMixin, PortInfoMixin, sdl.GUI, harbourmaster.Callback): """ The GUI. Methods are grouped into the mixins in app_*.py. @@ -91,22 +100,22 @@ def nice_version(v): # Print the display width and height logger.info(f"Display size: {self.display_width}x{self.display_height}") + pretend_resolution = None if harbourmaster.HM_TESTING: ## To pretend to be a different device set the device_info environment - ## variables, eg: DEVICE_NAME=RG353V CFW_NAME=ArkOS DEVICE_ARCH=aarch64 - pretend_resolution = None - ## Uncomment to choose a particular resolution - # pretend_resolution = (1024, 768) + ## variables, eg: DEVICE_NAME=RG353V CFW_NAME=ArkOS DEVICE_ARCH=aarch64, + ## and PM_PRETEND_RESOLUTION=1024x768 for its screen (tools/theme_server). + pretend_resolution = parse_resolution(os.environ.get('PM_PRETEND_RESOLUTION', '')) capabilities = harbourmaster.expand_info(dict(capabilities), override_resolution=pretend_resolution) - if capabilities['device'] == 'default': + if capabilities['device'] == 'default' and pretend_resolution is None: device = harbourmaster.find_device_by_resolution((self.display_width, self.display_height)) logger.info(f"FOUND: {device}") if device != 'default': capabilities = harbourmaster.device_info(device) - if capabilities['device'] == 'default': + if capabilities['device'] == 'default' and pretend_resolution is None: capabilities = harbourmaster.expand_info(dict(capabilities), override_resolution=(self.display_width, self.display_height)) capabilities['capabilities'].append(lang.CURRENT_LANG) @@ -437,9 +446,9 @@ def do_draw(self): self.clean() - def create_screenshot(self): + def create_screenshot(self, file_name='screenshot.png'): """ - Creates a screenshot and saves it to screenshot.png + Creates a screenshot and saves it to file_name (screenshot.png) """ # Create a texture to render onto self.in_screenshot = True @@ -460,10 +469,7 @@ def create_screenshot(self): # Draw the GUI self.do_draw() - # Reset the rendering target to the default (the window) - sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None) - - # Capture screenshot + # Capture screenshot, from the texture: read before the target goes back to the window. width, height = ctypes.c_int(0), ctypes.c_int(0) sdl2.SDL_QueryTexture(texture, None, None, ctypes.byref(width), ctypes.byref(height)) pixels = (ctypes.c_uint8 * (width.value * height.value * 4))() @@ -474,8 +480,11 @@ def create_screenshot(self): pixels, width.value * 4) + # Reset the rendering target to the default (the window) + sdl2.SDL_SetRenderTarget(self.renderer.sdlrenderer, None) + # Save the screenshot using pypng - with open('screenshot.png', 'wb') as f: + with open(file_name, 'wb') as f: writer = png.Writer(width=width.value, height=height.value, greyscale=False, alpha=True) writer.write_array(f, pixels) diff --git a/THEME.md b/THEME.md index 8d753f3b..aec6a6d1 100644 --- a/THEME.md +++ b/THEME.md @@ -4,6 +4,38 @@ PortMasters new gui has a flexible theme system build around a `theme.json` file This is currently a WIP documenting the theme format. +## Previewing your theme + +The theme preview server renders every PortMaster screen with your theme, at any screen size, colour scheme and language, and refreshes the page each time you save a file. It runs from a PortMaster-GUI checkout on Linux or macOS: + +```bash +python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt # once, gets SDL2 +.venv/bin/python tools/theme_server/serve.py --themes ~/my-themes +``` + +Then open http://localhost:8090/. `--themes` is one theme folder (it has a `theme.json`) or a folder of them; the default theme is always there too. + +- **The screens:** + - main menu, all, ready-to-run and installed ports, port info (and its popup) + - filters, featured ports, options, runtimes, themes, colour schemes, language + - on-screen keyboard, messages with a progress bar, message box + - the autoinstall and dialog screens, which always use the default theme on a device + - They show the real ports list and screenshots, downloaded once into `~/.cache/portmaster-theme-server`. `--refresh-ports` gets the current ones. +- **Region boxes:** draws each region's area with its name. In a screen's own view, hover over or click a box (or a name in the list) to see the region's final theme values, after `#base`, `#element` and `[capability]` overrides are applied. **Save PNG** saves the screen, with the boxes if they're on. +- **One screen, every size:** one screen at every common resolution, to check your `[hires]`, `[4:3]` and other overrides. +- A screen your theme has no section for says so, rather than failing. Errors pugwash logged while drawing (a missing image, a bad colour) are listed on the screen. + +To write the screens to files instead, for a theme's README or to compare before and after a change: + +```bash +.venv/bin/python tools/theme_server/serve.py --themes ~/my-themes --export out/ \ + --resolutions 640x480,1280x720 --schemes all --langs en_US,de_DE +``` + +That writes `out/////.png`, with a `.json` next to each listing its regions. + +Every theme needs `main_menu`, `option_menu`, `ports_list` and `message_window` sections: pugwash fills in the other screens from those when a theme leaves them out. + ## Scene Format The theme has a few different sections: diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py new file mode 100644 index 00000000..c3c1e712 --- /dev/null +++ b/tests/test_theme_server.py @@ -0,0 +1,269 @@ +# SPDX-License-Identifier: MIT +# +# tools/theme_server: the theme dev server. Its renderer (worker.py, a headless pugwash) +# draws every screen of a theme; serve.py runs renderers, serves the page and exports. +# These use an empty ports cache, so no network. + +import json +import sys +import threading +import urllib.error +import urllib.request + +import pytest + +from conftest import PM_DIR, REPO_DIR + +sys.path.insert(0, str(REPO_DIR / "tools" / "theme_server")) +import serve # noqa: E402 +import worker # noqa: E402 + + +DEFAULT_THEME = PM_DIR / "pylibs" / "default_theme" + +## The renderers rely on harbourmaster's testing mode (windowed, PM_PRETEND_RESOLUTION), +## which needs a git checkout: not the Test Suite's copy on a device. +needs_checkout = pytest.mark.skipif(not (REPO_DIR / ".git").exists(), reason="the theme server runs from a git checkout") + + +@pytest.mark.parametrize("size, expected, absent", [ + ((640, 480), {"640x480", "4:3"}, {"hires", "lowres", "wide", "square"}), + ((1280, 720), {"1280x720", "16:9", "hires", "wide"}, {"lowres", "square"}), + ((720, 720), {"720x720", "1:1", "hires", "square"}, {"wide"}), + ((480, 320), {"480x320", "3:2", "lowres", "wide"}, {"hires"}), + ((1280, 800), {"1280x800", "16:10", "16:9", "hires", "wide"}, set()), + ((720, 480), {"720x480", "3:2", "wide"}, {"hires", "lowres"}), + ]) +def test_screen_capabilities_match_device_info(size, expected, absent): + ## The same rules as device_info.txt, so a theme's [hires] / [4:3] overrides apply. + caps = set(worker.screen_capabilities(*size)) + assert expected <= caps and not (absent & caps) + + +def test_prepare_tools_dir(tmp_path): + cache_config = tmp_path / "cache" / "tools" / "PortMaster" / "config" + (cache_config / "images_pm").mkdir(parents=True) + (cache_config / "020_portmaster.source.json").write_text("{}") + (cache_config / "config.json").write_text(json.dumps({'version': 2, 'theme': "other"})) + theme = tmp_path / "My Theme" + theme.mkdir() + (theme / "theme.json").write_text("{}") + + work = tmp_path / "work" + assert worker.prepare_tools_dir(work, tmp_path / "cache", theme, "Dark") == "My Theme" + config = work / "tools" / "PortMaster" / "config" + ## The cache is linked, not copied; config.json is the renderer's own, with the cache's + ## version (else harbourmaster "upgrades" it by deleting the image folders). + assert (config / "images_pm").is_symlink() and (config / "020_portmaster.source.json").is_symlink() + cfg = json.loads((config / "config.json").read_text()) + assert (cfg['version'], cfg['theme'], cfg['theme-scheme'], cfg['disclaimer']) == (2, "My Theme", "Dark", True) + assert (work / "tools" / "PortMaster" / "themes" / "My Theme").resolve() == theme.resolve() + + ## The built-in theme isn't linked, pugwash finds it by name. + assert worker.prepare_tools_dir(work, tmp_path / "cache", DEFAULT_THEME, None) == "default_theme" + assert not any((work / "tools" / "PortMaster" / "themes").iterdir()) + + +def test_themes(tmp_path): + for name, data in (("Alpha", {"#info": {"name": "Alpha Theme"}, "#schemes": {"#base": {}, "Dark": {}, "Light": {}}}), + ("Beta", {"#info": {"name": "Beta", "default-scheme": "Light"}, "#schemes": {"Dark": {}, "Light": {}}})): + (tmp_path / name).mkdir() + (tmp_path / name / "theme.json").write_text(json.dumps(data)) + (tmp_path / "Broken").mkdir() + (tmp_path / "Broken" / "theme.json").write_text("{ not json") + (tmp_path / "not-a-theme").mkdir() + + themes = serve.Themes(tmp_path) + assert list(themes.all()) == ["default_theme", "Alpha", "Beta", "Broken"] + alpha = themes.info("Alpha", tmp_path / "Alpha") + assert (alpha['name'], alpha['schemes'], alpha['default_scheme']) == ("Alpha Theme", ["Dark", "Light"], "Dark") + assert themes.info("Beta", tmp_path / "Beta")['default_scheme'] == "Light" + assert themes.info("Broken", tmp_path / "Broken")['error'] + + ## A single theme folder works too. + assert list(serve.Themes(tmp_path / "Alpha").all()) == ["default_theme", "Alpha"] + + ## The fingerprint changes when a file is saved or added. + before = serve.Themes.fingerprint(tmp_path / "Alpha") + assert serve.Themes.fingerprint(tmp_path / "Alpha") == before + (tmp_path / "Alpha" / "image.png").write_bytes(b"x") + assert serve.Themes.fingerprint(tmp_path / "Alpha") != before + + +def test_languages_and_resolutions(): + codes = dict(serve.languages()) + assert codes["en_US"] == "English" and "de_DE" in codes + assert serve.valid_resolution("640x480") and serve.valid_resolution("1920x1152") + for bad in ("640", "640x", "x480", "0x480", "640x480; rm", "99999x10", "../x"): + assert not serve.valid_resolution(bad) + + +################################################################################ +## Rendering: real renderers, an empty ports cache. +@pytest.fixture(scope="module") +def pool(tmp_path_factory): + tmp = tmp_path_factory.mktemp("theme_server") + themes_dir = tmp / "themes" + ## A theme with only the sections pugwash needs (it copies the rest from these): the + ## other screens say what's missing. + (themes_dir / "Tiny").mkdir(parents=True) + tiny = json.loads((DEFAULT_THEME / "theme.json").read_text()) + tiny = {key: value for key, value in tiny.items() if key.startswith("#") or key in ("main_menu", "option_menu", "ports_list", "message_window")} + (themes_dir / "Tiny" / "theme.json").write_text(json.dumps(tiny)) + + themes = serve.Themes(themes_dir) + pool = serve.RenderPool(themes, tmp / "cache", tmp / "work") + try: + pool.shots() + except serve.WorkerError as error: + pool.stop() + pytest.skip(f"the renderer can't start here: {str(error).strip().splitlines()[-1]}") + + yield pool + pool.stop() + + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +@pytest.mark.parametrize("resolution", ["640x480", "1280x720"]) +def test_every_screen_renders(pool, resolution): + for shot, _ in pool.shots(): + png, info = pool.render("default_theme", None, resolution, "en_US", shot) + assert info['errors'] == [], (shot, info['errors']) + if shot in ("port_info", "port_info_popup", "featured", "featured_list"): + ## No ports or featured lists in an empty cache (and the default theme has no popup section). + assert info['missing'] and png == b"" + continue + + assert png.startswith(b"\x89PNG"), shot + assert info['regions'], shot + width, height = (int(part) for part in resolution.split("x")) + for region in info['regions']: + x, y, w, h = region['rect'] + assert 0 <= x <= width and 0 <= y <= height, (shot, region['name']) + assert region['section'] or region['scene'] == "EmptyScene" + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_png_size_and_caching(pool): + import png as pypng + + png_bytes, info = pool.render("default_theme", "Dark Mode", "854x480", "de_DE", "main_menu") + width, height, _, _ = pypng.Reader(bytes=png_bytes).read() + assert (width, height) == (854, 480) + assert info['scheme'] == "Dark Mode" + ## Rendered once per theme version. + assert pool.render("default_theme", "Dark Mode", "854x480", "de_DE", "main_menu")[0] is png_bytes + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_missing_sections_and_default_only_screens(pool): + _, info = pool.render("Tiny", None, "640x480", "en_US", "main_menu") + assert not info['missing'] and info['regions'] + _, info = pool.render("Tiny", None, "640x480", "en_US", "filters") + assert info['missing'] == "this theme has no filter_list section" + ## fifo_control (autoinstall, PortMasterDialog) always uses the default theme. + _, info = pool.render("Tiny", None, "640x480", "en_US", "selection_list") + assert "default theme" in info['missing'] + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_a_saved_theme_gets_a_new_renderer(pool): + theme_json = pool.themes.get("Tiny") / "theme.json" + _, before = pool.render("Tiny", None, "640x480", "en_US", "main_menu") + data = json.loads(theme_json.read_text()) + data["main_menu"]["#element:option_list"]["area"] = [0.0, 0.0, 0.5, 0.5] + theme_json.write_text(json.dumps(data)) + _, after = pool.render("Tiny", None, "640x480", "en_US", "main_menu") + + assert after['version'] != before['version'] + rect = next(region['rect'] for region in after['regions'] if region['tag'] == "option_list") + assert rect == [0, 0, 320, 240] + ## The old renderer for those settings is gone. + assert sum(1 for key in pool.workers if key[0] == "Tiny" and key[3] == "640x480") == 1 + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_http_api(pool): + server = serve.Server(("127.0.0.1", 0), serve.Handler) + server.themes, server.pool, server.shots, server.verbose = pool.themes, pool, pool.shots(), False + thread = threading.Thread(target=server.serve_forever, daemon=True) + thread.start() + base = f"http://127.0.0.1:{server.server_address[1]}" + + def get(path): + try: + with urllib.request.urlopen(base + path, timeout=60) as response: + return response.status, response.headers['Content-Type'], response.read() + except urllib.error.HTTPError as error: + return error.code, error.headers['Content-Type'], error.read() + + try: + status, kind, body = get("/") + assert status == 200 and kind.startswith("text/html") and b"Theme Preview" in body + + status, _, body = get("/api/info") + info = json.loads(body) + assert [theme['id'] for theme in info['themes']] == ["default_theme", "Tiny"] + assert ["main_menu", "Main menu"] in info['shots'] and "640x480" in info['resolutions'] + + status, kind, body = get("/api/render.png?theme=default_theme&res=640x480&shot=options") + assert status == 200 and kind == "image/png" and body.startswith(b"\x89PNG") + status, _, body = get("/api/render.json?theme=default_theme&res=640x480&shot=options") + assert json.loads(body)['regions'] + + assert get("/api/render.png?theme=default_theme&res=640x480&shot=no_such_screen")[0] == 400 + assert get("/api/render.png?theme=default_theme&res=huge&shot=options")[0] == 400 + assert get("/api/render.png?theme=nope&res=640x480&shot=options")[0] == 500 + assert get("/api/render.png?theme=Tiny&res=640x480&shot=filters")[0] == 404 + assert json.loads(get("/api/version?theme=Tiny")[2])['version'] + assert get("/api/version?theme=nope")[0] == 404 + assert get("/etc/passwd")[0] == 404 + + finally: + server.shutdown() + server.server_close() + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_export(pool, tmp_path): + shots = [shot for shot in pool.shots() if shot[0] in ("main_menu", "filters")] + assert serve.export(pool, pool.themes, tmp_path, ["Tiny"], ["640x480"], [], ["en_US"], shots) == 0 + ## /// + folder = tmp_path / "Tiny" / "640x480" / pool.themes.info("Tiny", pool.themes.get("Tiny"))['default_scheme'] / "en_US" + assert (folder / "main_menu.png").read_bytes().startswith(b"\x89PNG") + assert json.loads((folder / "filters.json").read_text())['missing'] == "this theme has no filter_list section" + assert not (folder / "filters.png").exists() + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_port_screens_with_port_data(online_hm, hm_dirs, tmp_path): + ## online_hm saved tests/data/ports.json's ports into hm_dirs' config: a ports cache. + cache = hm_dirs['tools_dir'].parent + assert (cache / "tools" / "PortMaster" / "config" / "020_portmaster.source.json").is_file() + pool = serve.RenderPool(serve.Themes(None), cache, tmp_path / "work") + try: + _, info = pool.render("default_theme", None, "640x480", "en_US", "port_info") + assert not info['missing'] and not info['errors'] + title = next(region for region in info['regions'] if region['tag'] == "port_title") + assert title['data']['text'] == "{port_info.title}" + + _, info = pool.render("default_theme", None, "640x480", "en_US", "ports_all") + assert not info['errors'] and any(region['tag'] == "ports_list" for region in info['regions']) + + finally: + pool.stop() diff --git a/tools/theme_server/serve.py b/tools/theme_server/serve.py new file mode 100644 index 00000000..d9e7487c --- /dev/null +++ b/tools/theme_server/serve.py @@ -0,0 +1,454 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +# +# The theme dev server: renders every PortMaster screen with your theme, at any resolution, +# colour scheme and language, and shows them in a web page that refreshes when you save. +# +# python3 tools/theme_server/serve.py --themes DIR [--port 8090] +# DIR is a theme (it has a theme.json) or a folder of themes. The built-in default +# theme is always there too. Then open http://localhost:8090/ +# +# python3 tools/theme_server/serve.py --themes DIR --export OUT +# [--theme NAME] [--resolutions 640x480,1280x720] [--schemes all] [--langs en_US] +# Writes OUT/////.png and .json, no server. +# +# --refresh-ports downloads the ports list and screenshots again (they're cached in +# ~/.cache/portmaster-theme-server, or --cache DIR). +# +# Needs what running pugwash from a checkout needs (SDL2: pip install pysdl2-dll on macOS) +# and the network the first time, for the real ports list. + +import argparse +import hashlib +import json +import os +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import time +import urllib.parse +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path + + +HERE = Path(__file__).resolve().parent +REPO_DIR = HERE.parents[1] +PM_DIR = REPO_DIR / "PortMaster" +DEFAULT_THEME_DIR = PM_DIR / "pylibs" / "default_theme" +WORKER = HERE / "worker.py" + +RESOLUTIONS = ( + "480x320", "640x480", "720x480", "720x720", "854x480", "960x544", "960x720", + "1024x768", "1280x720", "1280x800", "1920x1080", "1920x1152", + ) + +## Render processes kept running at once (each is a pugwash, ~60 MB). +MAX_WORKERS = 6 + + +def default_cache(): + base = os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache") + return Path(base) / "portmaster-theme-server" + + +def valid_resolution(text): + match = re.match(r"^(\d{2,4})x(\d{2,4})$", text or "") + return bool(match) and 0 < int(match.group(1)) <= 4096 and 0 < int(match.group(2)) <= 4096 + + +################################################################################ +## Themes and languages +class Themes: + """The themes the server shows: the default theme, then each theme in --themes.""" + def __init__(self, themes_dir): + self.themes_dir = Path(themes_dir).resolve() if themes_dir else None + + def all(self): + found = {"default_theme": DEFAULT_THEME_DIR} + if self.themes_dir is not None: + if (self.themes_dir / "theme.json").is_file(): + found[self.themes_dir.name] = self.themes_dir + elif self.themes_dir.is_dir(): + for theme_file in sorted(self.themes_dir.glob("*/theme.json")): + found[theme_file.parent.name] = theme_file.parent + + return found + + def get(self, theme_id): + return self.all().get(theme_id) + + def info(self, theme_id, theme_dir): + """Name, schemes and the default scheme, or the error reading theme.json.""" + try: + data = json.loads((theme_dir / "theme.json").read_text(encoding="utf-8")) + except (OSError, ValueError) as error: + return {'id': theme_id, 'name': theme_id, 'schemes': [], 'default_scheme': None, 'error': str(error)} + + info = data.get("#info", {}) + schemes = [name for name in data.get("#schemes", {}) if not name.startswith("#")] + default = info.get("default-scheme") + if default not in schemes: + default = schemes[0] if schemes else None + + return {'id': theme_id, 'name': info.get("name") or theme_id, 'creator': info.get("creator", ""), + 'schemes': schemes, 'default_scheme': default, 'error': None} + + @staticmethod + def fingerprint(theme_dir): + """Changes whenever a file in the theme is added, removed or saved.""" + digest = hashlib.md5() + for root, dirs, files in os.walk(str(theme_dir)): + dirs.sort() + for name in sorted(files): + path = os.path.join(root, name) + try: + stat = os.stat(path) + except OSError: + continue + digest.update(f"{path}\0{stat.st_size}\0{stat.st_mtime_ns}\n".encode("utf-8", "replace")) + + return digest.hexdigest()[:12] + + +def languages(): + """[(code, name)]: the languages pugwash has translations for.""" + text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") + names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) + locales = PM_DIR / "pylibs" / "locales" + codes = {"en_US"} | {path.name for path in locales.iterdir() if (path / "LC_MESSAGES").is_dir()} + return [(code, names.get(code, code)) for code in names if code in codes] + \ + [(code, code) for code in sorted(codes - set(names))] + + +################################################################################ +## Render workers +class WorkerError(Exception): + pass + + +class Worker: + """One worker.py: a pugwash for one theme version, scheme, resolution and language.""" + def __init__(self, key, theme_dir, scheme, resolution, lang, cache, work): + self.key = key + self.lock = threading.Lock() + self.work = Path(work) + command = [sys.executable, str(WORKER), "--theme-dir", str(theme_dir), "--resolution", resolution, + "--lang", lang, "--cache", str(cache), "--work", str(self.work)] + if scheme: + command += ["--scheme", scheme] + + self.log_file = open(str(self.work.with_suffix(".log")), "w") + self.proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=self.log_file, universal_newlines=True, bufsize=1) + self.ready = self._read() + if not self.ready.get('ok'): + self.stop() + raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + str(self.log_file.name)) + + self.used = time.monotonic() + + def _read(self): + line = self.proc.stdout.readline() + if not line: + return {'ok': False, 'error': "the renderer stopped, see " + str(self.log_file.name)} + return json.loads(line) + + def request(self, **command): + with self.lock: + if self.proc.poll() is not None: + raise WorkerError("the renderer stopped, see " + str(self.log_file.name)) + + self.used = time.monotonic() + self.proc.stdin.write(json.dumps(command) + "\n") + self.proc.stdin.flush() + return self._read() + + def stop(self): + try: + if self.proc.poll() is None: + self.proc.stdin.write(json.dumps({'cmd': "quit"}) + "\n") + self.proc.stdin.flush() + self.proc.wait(timeout=5) + except (OSError, ValueError, subprocess.TimeoutExpired): + self.proc.kill() + finally: + self.log_file.close() + shutil.rmtree(str(self.work), ignore_errors=True) + try: + os.unlink(self.log_file.name) + except OSError: + pass + + +class RenderPool: + """Starts workers as they're needed, replaces them when their theme changes, and caches + what they rendered.""" + def __init__(self, themes, cache, work_dir): + self.themes = themes + self.cache = cache + self.work_dir = Path(work_dir) + self.work_dir.mkdir(parents=True, exist_ok=True) + self.workers = {} + self.renders = {} + self.lock = threading.Lock() + self.start_lock = threading.Lock() + self.counter = 0 + + def _worker(self, theme_id, scheme, resolution, lang): + theme_dir = self.themes.get(theme_id) + if theme_dir is None: + raise WorkerError(f"no theme {theme_id!r}") + + fingerprint = Themes.fingerprint(theme_dir) + key = (theme_id, fingerprint, scheme or "", resolution, lang) + with self.lock: + worker = self.workers.get(key) + if worker is not None: + return worker, fingerprint + + ## One start at a time: the page asks for every screen at once. + with self.start_lock: + with self.lock: + worker = self.workers.get(key) + if worker is not None: + return worker, fingerprint + + ## The same settings for an older version of the theme, then the least + ## recently used if there are too many. + stale = [self.workers.pop(other) for other in list(self.workers) + if other[0] == theme_id and other[2:] == key[2:]] + while len(self.workers) >= MAX_WORKERS: + stale.append(self.workers.pop(min(self.workers, key=lambda other: self.workers[other].used))) + + self.counter += 1 + work = self.work_dir / f"worker-{self.counter}" + + for old in stale: + old.stop() + + worker = Worker(key, theme_dir, scheme, resolution, lang, self.cache, work) + with self.lock: + self.workers[key] = worker + + return worker, fingerprint + + def render(self, theme_id, scheme, resolution, lang, shot): + """(png bytes, info dict) for one screen, cached until the theme changes.""" + worker, fingerprint = self._worker(theme_id, scheme, resolution, lang) + render_key = worker.key + (shot,) + with self.lock: + if render_key in self.renders: + return self.renders[render_key] + + out_file = worker.work / f"{shot}.png" + answer = worker.request(cmd="render", shot=shot, out=str(out_file)) + if not answer.get('ok'): + raise WorkerError(answer.get('error', "render failed")) + + png = out_file.read_bytes() if not answer.get('missing') else b"" + info = { + 'shot': shot, 'theme': theme_id, 'scheme': worker.ready.get('scheme'), 'resolution': resolution, + 'lang': lang, 'version': fingerprint, 'regions': answer.get('regions', []), + 'errors': answer.get('errors', []), 'missing': answer.get('missing'), 'ms': answer.get('ms'), + } + with self.lock: + ## Drop renders of older theme versions. + for old_key in [k for k in self.renders if k[0] == theme_id and k[1] != fingerprint]: + del self.renders[old_key] + self.renders[render_key] = (png, info) + + return png, info + + def shots(self): + worker, _ = self._worker("default_theme", None, "640x480", "en_US") + return worker.request(cmd="shots")['shots'] + + def stop(self): + with self.lock: + workers, self.workers = list(self.workers.values()), {} + for worker in workers: + worker.stop() + + +################################################################################ +## The ports cache +def ensure_cache(cache, refresh=False): + source = cache / "tools" / "PortMaster" / "config" / "020_portmaster.source.json" + if source.is_file() and not refresh: + try: + if json.loads(source.read_text()).get('data', {}).get('ports'): + return + except ValueError: + pass + + print(f"Downloading the ports list and screenshots into {cache} ...", flush=True) + if (cache / "tools").exists(): + shutil.rmtree(str(cache / "tools")) + + result = subprocess.run([sys.executable, str(WORKER), "--prepare-cache", str(cache)]) + if result.returncode != 0: + sys.exit("Couldn't download the ports list (no network?). Screens will show no ports.") + + +################################################################################ +## The web server +class Handler(BaseHTTPRequestHandler): + server_version = "PortMasterThemeServer/1" + + def log_message(self, format, *args): + if self.server.verbose: + super().log_message(format, *args) + + def send(self, status, body, content_type="application/json", cache=False): + if isinstance(body, (dict, list)): + body = json.dumps(body).encode("utf-8") + elif isinstance(body, str): + body = body.encode("utf-8") + + self.send_response(status) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "max-age=3600" if cache else "no-store") + self.end_headers() + self.wfile.write(body) + + def do_GET(self): + url = urllib.parse.urlparse(self.path) + query = {key: values[-1] for key, values in urllib.parse.parse_qs(url.query).items()} + try: + if url.path in ("/", "/index.html"): + self.send(200, (HERE / "static" / "index.html").read_bytes(), "text/html; charset=utf-8") + + elif url.path == "/api/info": + themes = self.server.themes + self.send(200, { + 'themes': [themes.info(theme_id, theme_dir) for theme_id, theme_dir in themes.all().items()], + 'languages': languages(), + 'resolutions': list(RESOLUTIONS), + 'shots': self.server.shots, + }) + + elif url.path == "/api/version": + theme_dir = self.server.themes.get(query.get('theme')) + self.send(200 if theme_dir else 404, {'version': Themes.fingerprint(theme_dir) if theme_dir else None}) + + elif url.path in ("/api/render.png", "/api/render.json"): + theme_id = query.get('theme', "default_theme") + resolution = query.get('res', "640x480") + lang = query.get('lang', "en_US") + shot = query.get('shot', "main_menu") + if not valid_resolution(resolution) or not re.match(r"^\w\w_\w\w$", lang) or \ + shot not in {name for name, _ in self.server.shots}: + self.send(400, {'error': "bad theme, resolution, language or screen"}) + return + + png, info = self.server.pool.render(theme_id, query.get('scheme') or None, resolution, lang, shot) + if url.path.endswith(".png"): + if not png: + self.send(404, {'error': info['missing']}) + else: + ## The URL carries the theme version, so it can be cached. + self.send(200, png, "image/png", cache='v' in query) + else: + self.send(200, info) + + else: + self.send(404, {'error': "not found"}) + + except WorkerError as error: + self.send(500, {'error': str(error)}) + + except (BrokenPipeError, ConnectionResetError): + pass + + +class Server(ThreadingHTTPServer): + daemon_threads = True + + +################################################################################ +## Export +def export(pool, themes, out, theme_ids, resolutions, schemes, langs, shots): + failures = 0 + for theme_id in theme_ids: + info = themes.info(theme_id, themes.get(theme_id)) + theme_schemes = info['schemes'] if schemes == ["all"] else (schemes or [info['default_scheme']]) + for resolution in resolutions: + for scheme in theme_schemes or [None]: + for lang in langs: + folder = out / theme_id / resolution / (scheme or "default") / lang + folder.mkdir(parents=True, exist_ok=True) + for shot, _ in shots: + try: + png, info = pool.render(theme_id, scheme, resolution, lang, shot) + except WorkerError as error: + print(f"FAIL {theme_id} {resolution} {scheme} {lang} {shot}: {error}", file=sys.stderr) + failures += 1 + continue + + if png: + (folder / f"{shot}.png").write_bytes(png) + (folder / f"{shot}.json").write_text(json.dumps(info, indent=1)) + for error in info['errors']: + print(f"{theme_id} {resolution} {scheme} {lang} {shot}: {error}", file=sys.stderr) + + print(f"{folder}", flush=True) + + return 1 if failures else 0 + + +def main(argv=None): + parser = argparse.ArgumentParser(description="Renders PortMaster's screens with your theme.") + parser.add_argument("--themes", help="a theme folder, or a folder of themes") + parser.add_argument("--port", type=int, default=8090) + parser.add_argument("--host", default="127.0.0.1", help="use 0.0.0.0 to let other machines connect") + parser.add_argument("--cache", type=Path, default=default_cache()) + parser.add_argument("--refresh-ports", action="store_true", help="download the ports list and screenshots again") + parser.add_argument("--verbose", action="store_true") + parser.add_argument("--export", type=Path, metavar="OUT", help="write PNGs instead of serving them") + parser.add_argument("--theme", action="append", help="export: these themes (default: all)") + parser.add_argument("--resolutions", default="640x480", help="export: comma separated, or 'all'") + parser.add_argument("--schemes", default="", help="export: comma separated, 'all', or the theme's default") + parser.add_argument("--langs", default="en_US", help="export: comma separated") + options = parser.parse_args(argv) + + ensure_cache(options.cache, options.refresh_ports) + themes = Themes(options.themes) + work_dir = Path(tempfile.mkdtemp(prefix="pm-theme-server-")) + pool = RenderPool(themes, options.cache, work_dir) + + try: + shots = pool.shots() + if options.export: + resolutions = list(RESOLUTIONS) if options.resolutions == "all" else options.resolutions.split(",") + bad = [resolution for resolution in resolutions if not valid_resolution(resolution)] + if bad: + sys.exit(f"Not a resolution: {', '.join(bad)}") + + return export(pool, themes, options.export, options.theme or list(themes.all()), resolutions, + [scheme for scheme in options.schemes.split(",") if scheme], options.langs.split(","), shots) + + try: + server = Server((options.host, options.port), Handler) + except OSError as error: + sys.exit(f"Can't listen on {options.host}:{options.port} ({error.strerror}), try another --port.") + + server.themes, server.pool, server.shots, server.verbose = themes, pool, shots, options.verbose + print(f"Theme server on http://{'localhost' if options.host == '127.0.0.1' else options.host}:{options.port}/" + f" ({len(themes.all())} theme{'s' if len(themes.all()) != 1 else ''}), Ctrl+C to stop.", flush=True) + try: + server.serve_forever() + except KeyboardInterrupt: + pass + return 0 + + finally: + pool.stop() + shutil.rmtree(str(work_dir), ignore_errors=True) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/theme_server/shots.py b/tools/theme_server/shots.py new file mode 100644 index 00000000..8cd6fb72 --- /dev/null +++ b/tools/theme_server/shots.py @@ -0,0 +1,354 @@ +# SPDX-License-Identifier: MIT +# +# The theme dev server's scenes: every screen pugwash has, set up with real port data, and +# the renderer that draws one into a PNG with the regions it drew. Imported by worker.py, +# after it has set up the environment pugwash and harbourmaster read on import. + +import json +from pathlib import Path + +import harbourmaster +from loguru import logger + +from pugwash import scenes as pug_scenes +from pugwash.app import PortMasterGUI +from pugwash.scenes.base import BaseScene + + +class MissingSection(Exception): + """A screen needs a theme section this theme doesn't have.""" + + +## Which theme section a scene was made from, for the debug boxes; a missing one is +## reported as such instead of pugwash's KeyError. +_load_regions = BaseScene.load_regions + + +def _recording_load_regions(self, section, required_tags): + if section not in self.gui.theme_data: + raise MissingSection(f"this theme has no {section} section") + + self.theme_section = section + return _load_regions(self, section, required_tags) + + +BaseScene.load_regions = _recording_load_regions + + +class EmptyScene(BaseScene): + """Nothing on screen: the scene under the dialogs (not every theme has a "blank" section).""" + def __init__(self, gui): + super().__init__(gui) + self.theme_section = "" + + +class NoThemeDownloads: + """The themes list without the online theme list: installed themes only.""" + def get_theme_list(self): + return {} + + +## pugwash's fifo_control (autoinstall, PortMasterDialog) always uses the default theme. +DEFAULT_THEME_ONLY = {"selection_list", "selection_description", "selection_images", "selection_description_images"} + + +## Ports picked for the port info screens, the first one that's in the cache wins. +PORT_INFO_PORTS = ("2048.zip", "cave-story.zip", "celeste.zip", "stardewvalley.zip") + + +################################################################################ +## The shots: (name, title, builder). A builder sets up gui.scenes. +def _root(gui, scene): + gui.scenes = [('root', [scene])] + + +def _ports_list(gui, mode='install', base_filters=(), name=None): + scene = pug_scenes.PortsListScene(gui, {'mode': mode, 'base_filters': list(base_filters), 'name': name}) + _root(gui, pug_scenes.MainMenuScene(gui)) + gui.push_scene('ports', scene) + return scene + + +def _info_port(gui, required=True): + for name in PORT_INFO_PORTS: + if gui.hm.port_info(name) is not None: + return name + + name = next(iter(gui.hm.list_ports()), None) + if name is None and required: + raise MissingSection("no ports downloaded yet, start the server once with the network on") + + return name + + +def shot_main_menu(gui): + _root(gui, pug_scenes.MainMenuScene(gui)) + + +def shot_ports_all(gui): + _ports_list(gui, 'install') + + +def shot_ports_rtr(gui): + _ports_list(gui, 'install', ['rtr'], "Ready to Run Ports") + + +def shot_ports_manage(gui): + _ports_list(gui, 'uninstall', ['installed'], "Manage Ports") + + +def shot_port_info(gui): + list_scene = _ports_list(gui, 'install') + gui.push_scene('port_info', pug_scenes.PortInfoScene(gui, _info_port(gui), 'install', list_scene)) + + +def shot_port_info_popup(gui): + from pugwash.scenes.port_info import PortInfoPopup + shot_port_info(gui) + info_scene = gui.scenes[-1][1][-1] + gui.push_scene('port_info', PortInfoPopup(gui, info_scene)) + + +def shot_filters(gui): + list_scene = _ports_list(gui, 'install') + gui.push_scene('ports', pug_scenes.FiltersScene(gui, list_scene)) + + +def _need_featured(gui): + if not gui.hm.featured_ports(pre_load=True): + raise MissingSection("no featured ports downloaded yet, start the server once with the network on") + + +def shot_featured(gui): + from pugwash.scenes import FeaturedPortsNavigationScene + _need_featured(gui) + _root(gui, pug_scenes.MainMenuScene(gui)) + gui.push_scene('featured-ports', FeaturedPortsNavigationScene(gui)) + + +def shot_featured_list(gui): + from pugwash.scenes.ports import FeaturedPortsScene + _need_featured(gui) + _root(gui, pug_scenes.MainMenuScene(gui)) + items = gui.hm.featured_ports() + + def first_list(items): + for item in items: + if item.get('ports'): + return item + found = first_list(item.get('children') or []) + if found: + return found + return None + + item = first_list(items) + if item is None: + raise RuntimeError("no featured ports list in the cache") + + gui.push_scene('port-list', FeaturedPortsScene(gui, item)) + + +def shot_options(gui): + _root(gui, pug_scenes.MainMenuScene(gui)) + gui.push_scene('option', pug_scenes.OptionScene(gui)) + + +def shot_runtimes(gui): + shot_options(gui) + gui.push_scene('runtime-manager', pug_scenes.RuntimesScene(gui)) + + +def shot_themes(gui): + gui.theme_downloader = NoThemeDownloads() + shot_options(gui) + gui.push_scene('select-theme', pug_scenes.ThemesScene(gui)) + + +def shot_theme_schemes(gui): + shot_options(gui) + gui.push_scene('select-scheme', pug_scenes.ThemeSchemeScene(gui)) + + +def shot_language(gui): + shot_options(gui) + gui.push_scene('select-language', pug_scenes.LanguageScene(gui)) + + +def shot_keyboard(gui): + shot_options(gui) + gui.push_scene('osk', pug_scenes.OnScreenKeyboard(gui)) + + +def shot_messages(gui): + _ports_list(gui, 'install') + gui.messages_begin(internal=True) + for text in ("Downloading cave-story.zip", "Checking cave-story.zip", "Installing cave-story.zip", + "Extracting Cave Story/gamedata/data.pak", "Downloading runtime: love_11.5.squashfs"): + gui.message(text) + gui.progress("Downloading love_11.5.squashfs", 7340032, 12582912, 'data') + + +def shot_message_box(gui): + _ports_list(gui, 'install') + gui.push_scene('message_box', pug_scenes.MessageBoxScene( + gui, "Do you want to install Cave Story?\n\nIt needs the love_11.5 runtime (12 MB).", + title_text="Install", want_cancel=True)) + + +SUMMARY = { + 'cave-story.zip': {'title': "Cave Story: installed", 'description': "Installed from autoinstall.\n\nIt is in your Ports list now."}, + 'broken.zip': {'title': "broken.zip: not installed", 'description': "Not a zip file.\n\nIt was kept in the autoinstall folder."}, + 'love_11.5.squashfs': {'title': "love_11.5: runtime installed", 'description': "Moved to PortMaster/libs."}, + } + + +def _selection(gui, want_description, want_images): + from pugwash.scenes import DialogSelectionList + _root(gui, EmptyScene(gui)) + register = json.loads(json.dumps(SUMMARY)) + if want_images: + port = _info_port(gui, required=False) + image = gui.hm.port_images(port).get('screenshot') if port else None + for value in register.values(): + value['image'] = str(image) if image else "NO_IMAGE" + + gui.push_scene('selection_list', DialogSelectionList(gui, { + 'title': "Autoinstall", 'want_description': want_description, 'want_images': want_images, + 'want_cancel': False, 'ok_text': "Okay"}, register)) + + +def shot_selection_list(gui): + _selection(gui, False, False) + + +def shot_selection_description(gui): + _selection(gui, True, False) + + +def shot_selection_images(gui): + _selection(gui, False, True) + + +def shot_selection_description_images(gui): + _selection(gui, True, True) + + +def shot_disclaimer(gui): + from pugwash.scenes import DisclaimerScene + _root(gui, DisclaimerScene(gui)) + + +SHOTS = ( + ("main_menu", "Main menu", shot_main_menu), + ("ports_all", "All ports", shot_ports_all), + ("ports_rtr", "Ready to run ports", shot_ports_rtr), + ("ports_manage", "Manage ports", shot_ports_manage), + ("port_info", "Port info", shot_port_info), + ("port_info_popup", "Port info popup", shot_port_info_popup), + ("filters", "Filters", shot_filters), + ("featured", "Featured ports", shot_featured), + ("featured_list", "Featured ports list", shot_featured_list), + ("options", "Options", shot_options), + ("runtimes", "Runtimes", shot_runtimes), + ("themes", "Themes", shot_themes), + ("theme_schemes", "Colour schemes", shot_theme_schemes), + ("language", "Language", shot_language), + ("keyboard", "On-screen keyboard", shot_keyboard), + ("messages", "Messages and progress", shot_messages), + ("message_box", "Message box", shot_message_box), + ("selection_list", "Selection list", shot_selection_list), + ("selection_description", "Selection list + description", shot_selection_description), + ("selection_images", "Selection list + images", shot_selection_images), + ("selection_description_images", "Selection list + description + images", shot_selection_description_images), + ("disclaimer", "Disclaimer", shot_disclaimer), + ) + +SHOT_BUILDERS = {name: builder for name, _, builder in SHOTS} + + +################################################################################ +## The renderer +def _json_safe(value): + try: + json.dumps(value) + return value + except (TypeError, ValueError): + return str(value) + + +def scene_regions(gui): + """The regions on screen, bottom to top, as the debug boxes show them.""" + found = [] + for depth, scene in enumerate(gui.scenes[-1][1]): + tags = {id(region): tag for tag, region in scene.tags.items()} + for region in scene.regions: + area = region.area + found.append({ + 'scene': type(scene).__name__, + 'section': getattr(scene, 'theme_section', ""), + 'depth': depth, + 'name': region.name, + 'tag': tags.get(id(region)), + 'visible': bool(region.visible), + 'z': [region.z_index, region.z_position], + 'rect': [area.x, area.y, area.width, area.height], + 'data': {key: _json_safe(value) for key, value in region._dict.items()}, + }) + + return found + + +class Renderer: + def __init__(self, work, theme_name): + self.work = Path(work) + self.errors = [] + ## pugwash's errors (the theme's), not harbourmaster's (the port data's). + logger.add(lambda message: self.errors.append(message.record["message"]), level="ERROR", + filter=lambda record: not record["name"].startswith("harbourmaster")) + + self.theme_name = theme_name + self.gui = PortMasterGUI(first_scene=EmptyScene, force_theme=theme_name) + self.scheme = self.gui.themes.get_current_theme_scheme() + self.gui.hm = harbourmaster.HarbourMaster( + {'offline': True, 'no-check': True}, + temp_dir=self.work / "temp", callback=self.gui) + ## Screenshots load as they're drawn, so every image is there in the first frame. + self.gui.images.disable_async() + ## No sounds or music in a screenshot. + self.gui.sounds.sound_is_disabled = True + self.gui.sounds.music_is_disabled = True + + def settle(self, frames=3): + """A few frames, so scenes fill in their lists and text.""" + gui = self.gui + for _ in range(frames): + gui.updated = True + gui.draw_counter = 0 + gui.do_loop(no_delay=True) + + def render(self, shot, out_file): + builder = SHOT_BUILDERS.get(shot) + if builder is None: + raise KeyError(f"no shot called {shot!r}") + + self.errors.clear() + gui = self.gui + ## A screen that "quit" (eg: a scene with nothing to show) mustn't cancel the next one. + gui.events.running = True + if shot in DEFAULT_THEME_ONLY and self.theme_name != "default_theme": + raise MissingSection("autoinstall and PortMasterDialog screens always use the default theme") + + gui.callback_messages = [] + gui.message_box_scene = None + gui.callback_amount = 0 + builder(gui) + self.settle() + gui.create_screenshot(str(out_file)) + regions = scene_regions(gui) + if gui.message_box_scene is not None: + gui.messages_end(internal=True) + + return regions, list(self.errors) + + def quit(self): + self.gui.quit() diff --git a/tools/theme_server/static/index.html b/tools/theme_server/static/index.html new file mode 100644 index 00000000..9ff6d1a6 --- /dev/null +++ b/tools/theme_server/static/index.html @@ -0,0 +1,421 @@ + + + + + +Theme Preview + + + +
+

Theme Preview

+ + + + + + Show + + + + +
Loading…
+
+
+ +
+
+ +
+
+

+ + + + + + +
+
+ +
+
+ + + diff --git a/tools/theme_server/worker.py b/tools/theme_server/worker.py new file mode 100644 index 00000000..653c6a6b --- /dev/null +++ b/tools/theme_server/worker.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +# +# The theme dev server's renderer: a headless pugwash that draws one scene at a time. +# +# SDL allows one GUI per process, and the screen size, the theme's [hires]/[4:3] overrides +# and the language are fixed when pugwash starts, so serve.py runs one of these for each +# theme, colour scheme, resolution and language, and starts a new one when the theme's +# files change. Commands are JSON lines on stdin, answers JSON lines on stdout; anything +# else pugwash prints goes to stderr. +# +# worker.py --theme-dir DIR --resolution 640x480 [--scheme NAME] [--lang en_US] +# --cache DIR --work DIR +# +# {"cmd": "render", "shot": "main_menu", "out": "/tmp/x.png"} +# -> {"ok": true, "regions": [...], "errors": [...]} +# {"cmd": "shots"} -> {"ok": true, "shots": [[name, title], ...]} +# {"cmd": "quit"} +# +# worker.py --prepare-cache DIR +# Downloads the PortMaster sources (ports.json, port screenshots, featured ports) +# into DIR/tools/PortMaster/config, where every renderer finds them. + +import argparse +import json +import math +import os +import shutil +import sys +import time +import traceback +from pathlib import Path + + +REPO_DIR = Path(__file__).resolve().parents[2] +PM_DIR = REPO_DIR / "PortMaster" + + +def screen_capabilities(width, height): + """The screen capabilities device_info.txt gives a device with this screen.""" + gcd = math.gcd(width, height) or 1 + aspect_x, aspect_y = width // gcd, height // gcd + if (aspect_x, aspect_y) == (8, 5): + aspect_x, aspect_y = 16, 10 + + caps = [f"{width}x{height}", f"{aspect_x}:{aspect_y}"] + if (aspect_x, aspect_y) == (16, 10): + caps.append("16:9") + + if width >= 960 or height >= 720: + caps.append("hires") + elif width < 640 or height < 480: + caps.append("lowres") + + if aspect_x >= 16 or aspect_x * 10 // aspect_y >= 15: + caps.append("wide") + elif aspect_x == aspect_y: + caps.append("square") + + return caps + + +def device_env(width, height, lang): + """A pretend aarch64 device with this screen, as the env device_info.txt would write.""" + caps = ["aarch64", "armhf", "power", "opengl", "2gb", "1gb", "analog_0", "analog_1", "analog_2"] + caps += screen_capabilities(width, height) + return { + 'DEVICE_NAME': "Theme Preview", + 'DEVICE_CPU': "rk3566", + 'DEVICE_ARCH': "aarch64", + 'DEVICE_RAM': "2", + 'CFW_NAME': "PortMaster", + 'CFW_VERSION': "theme-server", + 'DISPLAY_WIDTH': str(width), + 'DISPLAY_HEIGHT': str(height), + 'ANALOG_STICKS': "2", + 'DEVICE_CAPABILITIES': " ".join(caps), + 'PM_PRETEND_RESOLUTION': f"{width}x{height}", + 'LANG': lang, + } + + +def sdl_env(): + """Headless SDL, unless the environment already picked a driver (the tests on a device + whose SDL has no dummy driver).""" + if "SDL_VIDEODRIVER" in os.environ: + return {'SDL_AUDIODRIVER': os.environ.get('SDL_AUDIODRIVER', "dummy")} + + return {'SDL_VIDEODRIVER': "dummy", 'SDL_AUDIODRIVER': "dummy", 'SDL_RENDER_DRIVER': "software"} + + +def prepare_tools_dir(work, cache, theme_dir, scheme): + """work/tools: the cached ports (linked, not copied), the theme, and a config.json.""" + tools = work / "tools" + config = tools / "PortMaster" / "config" + if tools.exists(): + shutil.rmtree(str(tools)) + + config.mkdir(parents=True) + (tools / "PortMaster" / "themes").mkdir() + (tools / "PortMaster" / "libs").mkdir() + (work / "ports").mkdir(exist_ok=True) + (work / "temp").mkdir(exist_ok=True) + + cached = cache / "tools" / "PortMaster" / "config" + if cached.is_dir(): + for entry in cached.iterdir(): + if entry.name != "config.json": + (config / entry.name).symlink_to(entry) + + default_theme = PM_DIR / "pylibs" / "default_theme" + if theme_dir.resolve() == default_theme.resolve(): + theme_name = "default_theme" + else: + theme_name = theme_dir.name + (tools / "PortMaster" / "themes" / theme_name).symlink_to(theme_dir.resolve()) + + ## The cache's own config.json (its "version", else harbourmaster upgrades the config by + ## deleting the image folders, which here are links into the cache). + cfg_data = {} + if (cached / "config.json").is_file(): + cfg_data = json.loads((cached / "config.json").read_text()) + + (config / "config.json").write_text(json.dumps({ + **cfg_data, + 'theme': theme_name, + 'theme-scheme': scheme, + 'disclaimer': True, + 'warning_time': "9999-12-31", + 'sfx-disabled': True, + 'music-disabled': True, + }, indent=2)) + + return theme_name + + +def prepare_cache(cache): + """Fill the cache like a first PortMaster start online would, then exit.""" + cache = Path(cache) + for name in ("tools", "ports", "temp"): + (cache / name).mkdir(parents=True, exist_ok=True) + + os.environ['HM_TOOLS_DIR'] = str(cache / "tools") + os.environ['HM_PORTS_DIR'] = str(cache / "ports") + os.environ['HM_SCRIPTS_DIR'] = str(cache / "ports") + os.environ.update({key: value for key, value in device_env(640, 480, "en_US").items() if key.startswith(("DEVICE_", "CFW_"))}) + sys.path[:0] = [str(PM_DIR / "pylibs"), str(PM_DIR / "exlibs")] + + import harbourmaster + from loguru import logger + logger.remove() + logger.add(sys.stderr, level="WARNING") + + class Progress(harbourmaster.Callback): + def message(self, message): + print(message.strip(), file=sys.stderr) + + hm = harbourmaster.HarbourMaster({'offline': False, 'no-check': False}, + temp_dir=cache / "temp", callback=Progress()) + hm.featured_ports(pre_load=True) + counts = {name: len(source.ports) for name, source in hm.sources.items()} + print(json.dumps({'ok': sum(counts.values()) > 0, 'ports': counts})) + return 0 if sum(counts.values()) > 0 else 1 + + +def main(argv=None): + if argv is None: + argv = sys.argv[1:] + if argv[:1] == ["--prepare-cache"] and len(argv) == 2: + return prepare_cache(argv[1]) + + parser = argparse.ArgumentParser(description="theme dev server renderer") + parser.add_argument("--theme-dir", required=True) + parser.add_argument("--scheme", default=None) + parser.add_argument("--resolution", default="640x480") + parser.add_argument("--lang", default="en_US") + parser.add_argument("--cache", required=True) + parser.add_argument("--work", required=True) + options = parser.parse_args(argv) + + ## Answers go to the real stdout, everything pugwash prints to stderr. + reply_file = os.fdopen(os.dup(1), 'w') + os.dup2(2, 1) + + def reply(**answer): + reply_file.write(json.dumps(answer) + "\n") + reply_file.flush() + + width, height = (int(part) for part in options.resolution.lower().split("x")) + work = Path(options.work) + work.mkdir(parents=True, exist_ok=True) + theme_name = prepare_tools_dir(work, Path(options.cache), Path(options.theme_dir), options.scheme) + + os.environ.update(device_env(width, height, options.lang)) + os.environ.update(sdl_env()) + for key in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES'): + os.environ.pop(key, None) + os.environ['HM_TOOLS_DIR'] = str(work / "tools") + os.environ['HM_PORTS_DIR'] = str(work / "ports") + os.environ['HM_SCRIPTS_DIR'] = str(work / "ports") + + ## From the checkout: harbourmaster's testing mode (windowed, no update checks). + os.chdir(str(REPO_DIR)) + sys.path[:0] = [str(PM_DIR / "pylibs"), str(PM_DIR / "exlibs")] + + import shots + + try: + renderer = shots.Renderer(work, theme_name) + + except Exception: + reply(ok=False, error=traceback.format_exc()) + return 1 + + reply(ok=True, ready=True, resolution=[width, height], theme=theme_name, scheme=renderer.scheme) + + for line in sys.stdin: + try: + command = json.loads(line) + if command.get('cmd') == "quit": + break + + elif command.get('cmd') == "shots": + reply(ok=True, shots=[[name, title] for name, title, _ in shots.SHOTS]) + + elif command.get('cmd') == "render": + started = time.monotonic() + try: + regions, errors = renderer.render(command['shot'], command['out']) + + except shots.MissingSection as missing: + reply(ok=True, missing=str(missing), regions=[], errors=[]) + continue + + reply(ok=True, regions=regions, errors=errors, ms=int((time.monotonic() - started) * 1000)) + + else: + reply(ok=False, error=f"unknown command {command.get('cmd')!r}") + + except Exception: + reply(ok=False, error=traceback.format_exc()) + + renderer.quit() + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From d556bd908fbf3c126effa83dcbc12d26349e5aba Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 12:24:51 +0800 Subject: [PATCH 02/28] Theme server: the startup warning screens The unsupported-system warning, as a TrimUI-based OS gets it, and with the longest set of warnings (spruce on a device with under 1 GB of RAM). The renderer pretends to be such a device for get_startup_warnings(). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- THEME.md | 1 + tools/theme_server/shots.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/THEME.md b/THEME.md index aec6a6d1..2759bd63 100644 --- a/THEME.md +++ b/THEME.md @@ -20,6 +20,7 @@ Then open http://localhost:8090/. `--themes` is one theme folder (it has a `them - filters, featured ports, options, runtimes, themes, colour schemes, language - on-screen keyboard, messages with a progress bar, message box - the autoinstall and dialog screens, which always use the default theme on a device + - the disclaimer and the unsupported-system warning, which are built in and look the same in every theme - They show the real ports list and screenshots, downloaded once into `~/.cache/portmaster-theme-server`. `--refresh-ports` gets the current ones. - **Region boxes:** draws each region's area with its name. In a screen's own view, hover over or click a box (or a name in the list) to see the region's final theme values, after `#base`, `#element` and `[capability]` overrides are applied. **Save PNG** saves the screen, with the boxes if they're on. - **One screen, every size:** one screen at every common resolution, to check your `[hires]`, `[4:3]` and other overrides. diff --git a/tools/theme_server/shots.py b/tools/theme_server/shots.py index 8cd6fb72..09fe4c99 100644 --- a/tools/theme_server/shots.py +++ b/tools/theme_server/shots.py @@ -238,6 +238,31 @@ def shot_disclaimer(gui): _root(gui, DisclaimerScene(gui)) +def _startup_warning(gui, keywords): + """The unsupported-system warning with these of get_startup_warnings()'s warnings.""" + from pugwash.scenes import StartupWarningScene + from pugwash.scenes import startup + import unittest.mock + + ## Its warnings come from looking at the device; pretend to be one that gets these. + def exists(path): + return {"/mnt/SDCARD/spruce": "spruce", "/etc/trimui_device.txt": "trimui"}.get(str(path).rstrip("/")) in keywords + + with unittest.mock.patch.object(startup.Path, "exists", lambda self: exists(self)): + info = dict(harbourmaster.device_info(), ram=512 if "lowram" in keywords else 2048) + warnings = startup.get_startup_warnings(info) + + _root(gui, StartupWarningScene(gui, warnings)) + + +def shot_warning_trimui(gui): + _startup_warning(gui, {"trimui"}) + + +def shot_warning_long(gui): + _startup_warning(gui, {"spruce", "lowram"}) + + SHOTS = ( ("main_menu", "Main menu", shot_main_menu), ("ports_all", "All ports", shot_ports_all), @@ -261,6 +286,8 @@ def shot_disclaimer(gui): ("selection_images", "Selection list + images", shot_selection_images), ("selection_description_images", "Selection list + description + images", shot_selection_description_images), ("disclaimer", "Disclaimer", shot_disclaimer), + ("warning_trimui", "Unsupported system warning (TrimUI)", shot_warning_trimui), + ("warning_long", "Unsupported system warning (spruce, low RAM)", shot_warning_long), ) SHOT_BUILDERS = {name: builder for name, _, builder in SHOTS} From 1809cc3d4dda5793f48fcb730b7f4a509b1ebc5e Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 12:45:36 +0800 Subject: [PATCH 03/28] Translations as JSON instead of gettext PortMaster's translations move from gettext .po/.mo files (synced with Crowdin) to plain JSON in PortMaster/pylibs/locales, two per language like messages.po and themes.po were: .json {"English": "translation"} the code's _("...") .themes.json {"English": "translation"} themes' "text" values strings.json and strings.themes.json list every English string and where it's used; tools/i18n.py extract keeps them in step with the code (an ast scan for _("..."), the same 298 strings xgettext found) and the themes, and tools/i18n.py check finds unused strings and lost {placeholders}. - harbourmaster/i18n.py: _() and theme_text(), no dependencies; every module imports _ from it instead of gettext. A theme can carry its own "#translations", which win over PortMaster's. - All 15 languages converted from the .po files: every one of the 4920 lookups gives what gettext gave. Fuzzy entries were already none; the 5 translations of strings the code no longer has are dropped. - German "Unable to download {runtime}, ..." had lost {runtime} (and had a typo): fixed. - The build no longer needs msgfmt, CI no longer installs gettext, and do_i18n.sh, theme_msgfmt.py and the xgettext snapshot test are gone (tests/test_i18n.py replaces it and runs everywhere, devices too). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- .github/workflows/release.yaml | 4 - .github/workflows/tests.yaml | 4 +- CLAUDE.md | 11 +- PortMaster/pylibs/harbourmaster/captain.py | 2 +- PortMaster/pylibs/harbourmaster/harbour.py | 2 +- PortMaster/pylibs/harbourmaster/hm_info.py | 2 +- PortMaster/pylibs/harbourmaster/hm_install.py | 2 +- PortMaster/pylibs/harbourmaster/hm_ports.py | 2 +- .../pylibs/harbourmaster/hm_runtimes.py | 2 +- PortMaster/pylibs/harbourmaster/i18n.py | 82 ++ .../harbourmaster/platform/retrodeck.py | 2 +- .../pylibs/harbourmaster/source/__init__.py | 2 +- .../pylibs/harbourmaster/source/base.py | 2 +- .../pylibs/harbourmaster/source/portmaster.py | 2 +- PortMaster/pylibs/harbourmaster/util/net.py | 2 +- PortMaster/pylibs/locales/de_DE.json | 254 ++++ PortMaster/pylibs/locales/de_DE.themes.json | 23 + .../locales/de_DE/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/de_DE/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/en_US.json | 1 + PortMaster/pylibs/locales/en_US.themes.json | 1 + .../locales/en_US/LC_MESSAGES/messages.po | 528 ------- .../locales/en_US/LC_MESSAGES/themes.po | 117 -- PortMaster/pylibs/locales/es_ES.json | 254 ++++ PortMaster/pylibs/locales/es_ES.themes.json | 23 + .../locales/es_ES/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/es_ES/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/fr_FR.json | 254 ++++ PortMaster/pylibs/locales/fr_FR.themes.json | 23 + .../locales/fr_FR/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/fr_FR/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/it_IT.json | 208 +++ PortMaster/pylibs/locales/it_IT.themes.json | 16 + .../locales/it_IT/LC_MESSAGES/messages.po | 1240 ---------------- .../locales/it_IT/LC_MESSAGES/themes.po | 176 --- PortMaster/pylibs/locales/ja_JP.json | 207 +++ PortMaster/pylibs/locales/ja_JP.themes.json | 16 + .../locales/ja_JP/LC_MESSAGES/messages.po | 1239 ---------------- .../locales/ja_JP/LC_MESSAGES/themes.po | 172 --- PortMaster/pylibs/locales/ko_KR.json | 257 ++++ PortMaster/pylibs/locales/ko_KR.themes.json | 23 + .../locales/ko_KR/LC_MESSAGES/messages.po | 1252 ---------------- .../locales/ko_KR/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/nl_NL.json | 254 ++++ PortMaster/pylibs/locales/nl_NL.themes.json | 23 + .../locales/nl_NL/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/nl_NL/LC_MESSAGES/themes.po | 192 --- PortMaster/pylibs/locales/pl_PL.json | 257 ++++ PortMaster/pylibs/locales/pl_PL.themes.json | 23 + .../locales/pl_PL/LC_MESSAGES/messages.po | 1252 ---------------- .../locales/pl_PL/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/pt_BR.json | 257 ++++ PortMaster/pylibs/locales/pt_BR.themes.json | 23 + .../locales/pt_BR/LC_MESSAGES/messages.po | 1252 ---------------- .../locales/pt_BR/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/ro_RO.json | 247 ++++ PortMaster/pylibs/locales/ro_RO.themes.json | 23 + .../locales/ro_RO/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/ro_RO/LC_MESSAGES/themes.po | 196 --- PortMaster/pylibs/locales/ru_RU.json | 257 ++++ PortMaster/pylibs/locales/ru_RU.themes.json | 23 + .../locales/ru_RU/LC_MESSAGES/messages.po | 1253 ----------------- .../locales/ru_RU/LC_MESSAGES/themes.po | 193 --- PortMaster/pylibs/locales/strings.json | 1035 ++++++++++++++ PortMaster/pylibs/locales/strings.themes.json | 127 ++ PortMaster/pylibs/locales/sv_SE.json | 232 +++ PortMaster/pylibs/locales/sv_SE.themes.json | 23 + .../locales/sv_SE/LC_MESSAGES/messages.po | 1244 ---------------- .../locales/sv_SE/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/uk_UA.json | 254 ++++ PortMaster/pylibs/locales/uk_UA.themes.json | 23 + .../locales/uk_UA/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/uk_UA/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/locales/zh_CN.json | 253 ++++ PortMaster/pylibs/locales/zh_CN.themes.json | 23 + .../locales/zh_CN/LC_MESSAGES/messages.po | 1248 ---------------- .../locales/zh_CN/LC_MESSAGES/themes.po | 194 --- PortMaster/pylibs/pugwash/app.py | 2 +- PortMaster/pylibs/pugwash/app_callbacks.py | 2 +- PortMaster/pylibs/pugwash/app_commands.py | 2 +- PortMaster/pylibs/pugwash/app_fifo.py | 2 +- PortMaster/pylibs/pugwash/app_ports.py | 2 +- PortMaster/pylibs/pugwash/autoinstall.py | 2 +- PortMaster/pylibs/pugwash/lang.py | 23 +- PortMaster/pylibs/pugwash/main.py | 2 +- PortMaster/pylibs/pugwash/scenes/base.py | 4 +- PortMaster/pylibs/pugwash/scenes/dialogs.py | 2 +- PortMaster/pylibs/pugwash/scenes/featured.py | 2 +- PortMaster/pylibs/pugwash/scenes/filters.py | 2 +- PortMaster/pylibs/pugwash/scenes/keyboard.py | 2 +- PortMaster/pylibs/pugwash/scenes/language.py | 2 +- PortMaster/pylibs/pugwash/scenes/main_menu.py | 2 +- PortMaster/pylibs/pugwash/scenes/options.py | 2 +- PortMaster/pylibs/pugwash/scenes/port_info.py | 2 +- PortMaster/pylibs/pugwash/scenes/ports.py | 2 +- PortMaster/pylibs/pugwash/scenes/runtimes.py | 2 +- PortMaster/pylibs/pugwash/scenes/startup.py | 2 +- PortMaster/pylibs/pugwash/scenes/themes.py | 2 +- PortMaster/pylibs/pugwash/theme.py | 9 +- PortMaster/pylibs/pugwash/update.py | 2 +- do_i18n.sh | 75 - do_release.sh | 21 +- tests/data/msgids.txt | 1028 -------------- tests/test_build.py | 16 +- tests/test_i18n.py | 112 ++ tests/test_i18n_snapshot.py | 47 - theme_msgfmt.py | 132 -- tools/i18n.py | 211 +++ tools/theme_server/serve.py | 2 +- 109 files changed, 5391 insertions(+), 22157 deletions(-) create mode 100644 PortMaster/pylibs/harbourmaster/i18n.py create mode 100644 PortMaster/pylibs/locales/de_DE.json create mode 100644 PortMaster/pylibs/locales/de_DE.themes.json delete mode 100644 PortMaster/pylibs/locales/de_DE/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/de_DE/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/en_US.json create mode 100644 PortMaster/pylibs/locales/en_US.themes.json delete mode 100644 PortMaster/pylibs/locales/en_US/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/en_US/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/es_ES.json create mode 100644 PortMaster/pylibs/locales/es_ES.themes.json delete mode 100644 PortMaster/pylibs/locales/es_ES/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/es_ES/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/fr_FR.json create mode 100644 PortMaster/pylibs/locales/fr_FR.themes.json delete mode 100644 PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/it_IT.json create mode 100644 PortMaster/pylibs/locales/it_IT.themes.json delete mode 100644 PortMaster/pylibs/locales/it_IT/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/it_IT/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/ja_JP.json create mode 100644 PortMaster/pylibs/locales/ja_JP.themes.json delete mode 100644 PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/ko_KR.json create mode 100644 PortMaster/pylibs/locales/ko_KR.themes.json delete mode 100644 PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/nl_NL.json create mode 100644 PortMaster/pylibs/locales/nl_NL.themes.json delete mode 100644 PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/pl_PL.json create mode 100644 PortMaster/pylibs/locales/pl_PL.themes.json delete mode 100644 PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/pt_BR.json create mode 100644 PortMaster/pylibs/locales/pt_BR.themes.json delete mode 100644 PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/ro_RO.json create mode 100644 PortMaster/pylibs/locales/ro_RO.themes.json delete mode 100644 PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/ru_RU.json create mode 100644 PortMaster/pylibs/locales/ru_RU.themes.json delete mode 100644 PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/strings.json create mode 100644 PortMaster/pylibs/locales/strings.themes.json create mode 100644 PortMaster/pylibs/locales/sv_SE.json create mode 100644 PortMaster/pylibs/locales/sv_SE.themes.json delete mode 100644 PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/uk_UA.json create mode 100644 PortMaster/pylibs/locales/uk_UA.themes.json delete mode 100644 PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/themes.po create mode 100644 PortMaster/pylibs/locales/zh_CN.json create mode 100644 PortMaster/pylibs/locales/zh_CN.themes.json delete mode 100644 PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/messages.po delete mode 100644 PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/themes.po delete mode 100755 do_i18n.sh delete mode 100644 tests/data/msgids.txt create mode 100644 tests/test_i18n.py delete mode 100644 tests/test_i18n_snapshot.py delete mode 100644 theme_msgfmt.py create mode 100644 tools/i18n.py diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index cf42194b..0298d828 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -79,10 +79,6 @@ jobs: echo "version=$(date -u +'%Y.%m.%d-%H%M')" >> $GITHUB_OUTPUT echo "release=${{ inputs.channel }}" >> $GITHUB_OUTPUT - - name: Install gettext - run: | - sudo apt-get install -y gettext - - name: Create PortMaster.zip id: create-zip run: | diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0c967bae..409bc664 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -35,8 +35,8 @@ jobs: - name: Install dev requirements run: | pip install -r requirements-dev.txt - # xgettext for the translatable strings snapshot test, busybox for device_info.txt under ash - sudo apt-get install -y gettext busybox + # busybox for device_info.txt under ash + sudo apt-get install -y busybox - name: Lint (errors only) run: | diff --git a/CLAUDE.md b/CLAUDE.md index 06cbb83d..15e3916a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,7 +29,8 @@ flake8 --select=E9,F63,F7,F82,F811 PortMaster/pugwash PortMaster/harbourmaster P ./do_release.sh testsuite # a test build + dist/pmtest.zip, the "PortMaster Test Suite" port for testers (see below) ./do_release.sh alpha|beta|stable [VER] # a release in dist/ (+ version.json); stable (or MAKE_INSTALL=Y) also builds the makeself installers ./do_muos_release.sh [stable] # dist/muos.portmaster.zip (likewise do_trimui_release.sh, do_x86_64_release.sh) -./do_i18n.sh # extract strings with xgettext, sync with Crowdin, compile .mo (needs crowdin CLI) +python3 -I tools/i18n.py extract # after changing translatable strings: rewrite locales/strings*.json +python3 -I tools/i18n.py check # language files: unused strings, lost {placeholders} ``` pugwash flags: `--quiet --no-check --debug --no-colour --force-colour --no-log --offline --no-harbour`. @@ -162,7 +163,13 @@ The version lives in the `## -- BEGIN PORTMASTER INFO --` block at the top of th ## Translations -Wrap user-facing strings in `_()` (gettext). `do_i18n.sh` extracts strings from the `pugwash` script and every `.py` under `pylibs/harbourmaster` and `pylibs/pugwash` into `pylibs/locales/messages.pot`; theme strings go into `themes.pot` via `theme_msgfmt.py`. Translations are managed on Crowdin, so don't hand-edit `.po` files for other languages. If you change strings on purpose, regenerate the snapshot with `PM_UPDATE_I18N_SNAPSHOT=1 pytest tests/test_i18n_snapshot.py`. +Wrap user-facing strings in `_()` from `harbourmaster.i18n`. Translations live in `PortMaster/pylibs/locales` as plain JSON, two files per language: `.json` for the code's strings and `.themes.json` for themes' `"text"` values (`{"English": "translation"}`; a missing string shows in English). +- `strings.json` and `strings.themes.json` list every English string and where it's used. +- `tools/i18n.py extract` rewrites both lists from the code and the themes. Pass `--themes DIR` to rescan other themes too; themes it doesn't scan keep their strings. +- `tests/test_i18n.py` fails when the code's strings and `strings.json` differ, so run `extract` after changing strings on purpose. +- A theme can carry its own translations in a `#translations` section of `theme.json` (`{lang: {text: translation}}`), which win over PortMaster's. +- `pugwash.lang` picks the language on import and loads it with `harbourmaster.i18n.load_language`. +- There's no gettext, `.po`/`.mo` or Crowdin any more: translators edit the language files, for example with the theme server's translation editor. ## Style diff --git a/PortMaster/pylibs/harbourmaster/captain.py b/PortMaster/pylibs/harbourmaster/captain.py index 1794de54..033bacbb 100644 --- a/PortMaster/pylibs/harbourmaster/captain.py +++ b/PortMaster/pylibs/harbourmaster/captain.py @@ -9,7 +9,7 @@ import subprocess import zipfile -from gettext import gettext as _ +from harbourmaster.i18n import _ from pathlib import Path # Included imports diff --git a/PortMaster/pylibs/harbourmaster/harbour.py b/PortMaster/pylibs/harbourmaster/harbour.py index 2af6a2f4..081bcb72 100644 --- a/PortMaster/pylibs/harbourmaster/harbour.py +++ b/PortMaster/pylibs/harbourmaster/harbour.py @@ -4,7 +4,7 @@ import pathlib import shutil from pathlib import Path -from gettext import gettext as _ +from harbourmaster.i18n import _ from loguru import logger from .config import HM_PORTS_DIR, HM_SCRIPTS_DIR, HM_SOURCE_DEFAULTS, HM_TESTING, HM_TOOLS_DIR from .hardware import device_info diff --git a/PortMaster/pylibs/harbourmaster/hm_info.py b/PortMaster/pylibs/harbourmaster/hm_info.py index ac695634..1482dd0b 100644 --- a/PortMaster/pylibs/harbourmaster/hm_info.py +++ b/PortMaster/pylibs/harbourmaster/hm_info.py @@ -3,7 +3,7 @@ import datetime import shutil import zipfile -from gettext import gettext as _ +from harbourmaster.i18n import _ from loguru import logger from .util import calculate_git_blob_sha_data, calculate_git_blob_sha_file, datetime_compare, download, fetch_data, fetch_text, json_safe_load, json_safe_loads diff --git a/PortMaster/pylibs/harbourmaster/hm_install.py b/PortMaster/pylibs/harbourmaster/hm_install.py index dda43cd3..545dd19c 100644 --- a/PortMaster/pylibs/harbourmaster/hm_install.py +++ b/PortMaster/pylibs/harbourmaster/hm_install.py @@ -7,7 +7,7 @@ import subprocess import zipfile from pathlib import Path -from gettext import gettext as _ +from harbourmaster.i18n import _ from loguru import logger from .console import cprint from .config import HM_ACCEPTABLE_NON_BASH_TOP_LEVEL_FILES diff --git a/PortMaster/pylibs/harbourmaster/hm_ports.py b/PortMaster/pylibs/harbourmaster/hm_ports.py index ddc4dd98..6131c66b 100644 --- a/PortMaster/pylibs/harbourmaster/hm_ports.py +++ b/PortMaster/pylibs/harbourmaster/hm_ports.py @@ -2,7 +2,7 @@ import json from pathlib import Path -from gettext import gettext as _ +from harbourmaster.i18n import _ from loguru import logger from . import config as hm_config from .config import HM_SORT_ORDER diff --git a/PortMaster/pylibs/harbourmaster/hm_runtimes.py b/PortMaster/pylibs/harbourmaster/hm_runtimes.py index ff2b4fd6..3f86dc80 100644 --- a/PortMaster/pylibs/harbourmaster/hm_runtimes.py +++ b/PortMaster/pylibs/harbourmaster/hm_runtimes.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT import hashlib -from gettext import gettext as _ +from harbourmaster.i18n import _ from loguru import logger from .console import cprint from .util import download, hash_file diff --git a/PortMaster/pylibs/harbourmaster/i18n.py b/PortMaster/pylibs/harbourmaster/i18n.py new file mode 100644 index 00000000..b522aeb5 --- /dev/null +++ b/PortMaster/pylibs/harbourmaster/i18n.py @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: MIT +# +# Translations, in PortMaster/pylibs/locales, two files per language: +# +# .json {"English text": "translation", ...} the code's _("...") strings +# .themes.json {"English text": "translation", ...} themes' "text" values +# +# A string with no translation shows in English. strings.json and strings.themes.json +# list every string and where it's used (tools/i18n.py extract keeps them in step with +# the code and the themes); the theme server's translation editor edits the language files. + +import json +from pathlib import Path + + +LANGUAGE = None +_messages = {} +_themes = {} +_theme_own = {} + + +def _(text): + """The translation of a string from the code, or the string itself.""" + return _messages.get(text) or text + + +def theme_text(text): + """The translation of a theme's "text": the theme's own #translations first, then + PortMaster's.""" + return _theme_own.get(text) or _themes.get(text) or text + + +DOMAINS = ('messages', 'themes') + + +def language_file(locales_dir, lang, domain='messages'): + """locales/.json, or .themes.json for the themes' strings.""" + return Path(locales_dir) / (f"{lang}.json" if domain == 'messages' else f"{lang}.{domain}.json") + + +def _read(path): + try: + with open(str(path), encoding="utf-8") as fh: + data = json.load(fh) + except (OSError, ValueError): + return {} + + if not isinstance(data, dict): + return {} + + return {key: value for key, value in data.items() if isinstance(key, str) and isinstance(value, str) and value} + + +def read_language(locales_dir, lang): + """{'messages': {...}, 'themes': {...}} for a language, empty where a file can't be read.""" + return {domain: _read(language_file(locales_dir, lang, domain)) for domain in DOMAINS} + + +def load_language(locales_dir, lang): + """Use this language from now on.""" + global LANGUAGE, _messages, _themes + data = read_language(locales_dir, lang) + LANGUAGE = lang + _messages, _themes = data['messages'], data['themes'] + + +def set_theme_translations(translations): + """A theme's own "#translations" ({lang: {text: translation}}) for the current language.""" + global _theme_own + own = translations.get(LANGUAGE, {}) if isinstance(translations, dict) and LANGUAGE else {} + _theme_own = {key: value for key, value in own.items() if isinstance(key, str) and isinstance(value, str) and value} if isinstance(own, dict) else {} + + +__all__ = ( + '_', + 'theme_text', + 'load_language', + 'read_language', + 'language_file', + 'DOMAINS', + 'set_theme_translations', + ) diff --git a/PortMaster/pylibs/harbourmaster/platform/retrodeck.py b/PortMaster/pylibs/harbourmaster/platform/retrodeck.py index 2466e966..a6c5a106 100644 --- a/PortMaster/pylibs/harbourmaster/platform/retrodeck.py +++ b/PortMaster/pylibs/harbourmaster/platform/retrodeck.py @@ -3,7 +3,7 @@ # RetroDECK. import shutil -from gettext import gettext as _ +from harbourmaster.i18n import _ from pathlib import Path from loguru import logger from ..util import json_safe_load, replace_file diff --git a/PortMaster/pylibs/harbourmaster/source/__init__.py b/PortMaster/pylibs/harbourmaster/source/__init__.py index b0192e17..54789f05 100644 --- a/PortMaster/pylibs/harbourmaster/source/__init__.py +++ b/PortMaster/pylibs/harbourmaster/source/__init__.py @@ -3,7 +3,7 @@ # Port sources. HM_SOURCE_APIS maps the "api" value in *.source.json files to a class, # PortMasterV3 is the only one left; raw_download handles installs from a plain url. -from gettext import gettext as _ +from harbourmaster.i18n import _ from urllib.parse import urlparse, urlunparse from loguru import logger from ..info import port_info_load diff --git a/PortMaster/pylibs/harbourmaster/source/base.py b/PortMaster/pylibs/harbourmaster/source/base.py index b101840e..93626384 100644 --- a/PortMaster/pylibs/harbourmaster/source/base.py +++ b/PortMaster/pylibs/harbourmaster/source/base.py @@ -2,7 +2,7 @@ # # BaseSource, the interface every port source implements. -from gettext import gettext as _ +from harbourmaster.i18n import _ from ..config import HM_UPDATE_FREQUENCY from ..util import datetime_compare, name_cleaner diff --git a/PortMaster/pylibs/harbourmaster/source/portmaster.py b/PortMaster/pylibs/harbourmaster/source/portmaster.py index 3ff1e8d7..81cd121d 100644 --- a/PortMaster/pylibs/harbourmaster/source/portmaster.py +++ b/PortMaster/pylibs/harbourmaster/source/portmaster.py @@ -5,7 +5,7 @@ import datetime import json import zipfile -from gettext import gettext as _ +from harbourmaster.i18n import _ from loguru import logger from ..config import HM_MAX_TEMP_SIZE from ..info import port_info_load, port_info_merge diff --git a/PortMaster/pylibs/harbourmaster/util/net.py b/PortMaster/pylibs/harbourmaster/util/net.py index 1c8f8ca4..b6f19c29 100644 --- a/PortMaster/pylibs/harbourmaster/util/net.py +++ b/PortMaster/pylibs/harbourmaster/util/net.py @@ -4,7 +4,7 @@ import hashlib import sys -from gettext import gettext as _ +from harbourmaster.i18n import _ import requests from loguru import logger from ..console import cprint diff --git a/PortMaster/pylibs/locales/de_DE.json b/PortMaster/pylibs/locales/de_DE.json new file mode 100644 index 00000000..5a36eb89 --- /dev/null +++ b/PortMaster/pylibs/locales/de_DE.json @@ -0,0 +1,254 @@ +{ + "Loading...": "Lädt...", + "Loading Sources.": "Lade Quellen.", + "Download validation failed.": "Download Überprüfung fehlgeschlagen", + "Loading Info.": "Informationen werden geladen.", + "Fetching latest featured ports.": "Lade neueste Ports.", + "Installing Theme {download_name}.": "Installiere Theme {download_name}.", + "Installing": "Wird installiert", + "Theme {download_name!r} installed successfully.": "Theme {download_name!r} erfolgreich installiert.", + "Port {download_name} installed failed.": "Port {download_name} Installation fehlgeschlagen.", + "Installing {download_name}.": "Installiere {download_name}.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} erfolgreich installiert.", + "Installation failed, removing files...": "Installation fehlgeschlagen, entferne Dateien...", + "Okay": "Okay", + "Cancel": "Abbrechen", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} und {runtime_name!r} erfolgreich installiert.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} wurde erfolgreich installiert, Installation von {runtime_name!r} ist fehlgeschlagen\nInstalliere den Port erneut um es nochmal zu versuchen oder besuche das Wiki für Hilfe.", + "Unable to download in offline mode.": "Download im Offline-Modus nicht möglich.", + "Unable to find a source for {port_name}": "Es konnte keine Quelle für {port_name} gefunden werden.", + "Unknown port {port_name}": "Unbekannter Port {port_name}", + "Removing {port_name}": "Entferne {port_name}", + "Successfully uninstalled {port_name}": "Erfolgreich {port_name} deinstalliert", + "Loading Ports.": "Lade Ports.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} enthält eine fehlerhafte Runtime, möglicherweise funktioniert das Spiel nicht korrekt.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Port {runtime} enthält eine unbekannte Runtime, möglicherweise funktioniert das Spiel nicht korrekt.", + "Not Installed": "Nicht installiert", + "Update Available": "Update verfügbar", + "Verified": "Überprüft", + "Unverified": "Nicht verifiziert", + "Broken": "Fehlerhaft", + "Unable do download a runtime when in offline mode.": "Runtimes lassen sich nicht im Offline Modus herunterladen.", + "Verifying runtime {runtime}": "Überprüfe Runtime {runtime}", + "Verifying": "Überprüfe", + "Unable to find a download for {runtime}.": "Es konnte kein Download für {runtime} gefunden werden.", + "Unable to download {runtime} in {device_arch}.": "Kann {runtime} in {device_arch} nicht herunterladen.", + "Verified {runtime} successfully.": "{runtime} erfolgreich überprüft.", + "Verified {runtime}.": "{runtime} verifiziert.", + "Updating {runtime}.": "Aktualisiere {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Laufzeit- {runtime} ist defekt, Installiere neu.", + "Downloading runtime {runtime}.": "Lade runtime {runtime} herunter", + "Unable to download {runtime}, game may not run correctly.": "{runtime} konnte nicht heruntergeladen werden, möglicherweise funktioniert das Spiel nicht korrekt.", + "Successfully downloaded {runtime}.": "Erfolgreich {runtime} heruntergeladen", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nUm dies zu tun:\nMENÜ→-> UTILITIES -> Rom-Verzeichnis erneut scannen", + "Unable to download verification file.": "Überprüfungsdatei konnte nicht heruntergeladen werden", + "Unable to download non zip files.": "Nur Zip Dateien können heruntergeladen werden.", + "Cache out of date.": "Cache veraltet", + "First check.": "Erste Überprüfung.", + "Auto Update.": "Automatische Aktualisierung", + "Updating": "Aktualisiere", + "Up to date already": "Neuste Version bereits installiert", + "Fetching latest info": "Lade neuste Informationen", + "Done.": "Fertig", + "Fetching info": "Lade Informationen", + "Unable to find {port_name}.": "{port_name} konnte nicht gefunden werden", + "Unable to download file. [{status_code}]": "[{status_code}] konnte nicht heruntergeladen werden.", + "Downloading {file_url} - ({total_length_mb})": "Lade {file_url} - ({total_length_mb}) herunter.", + "Downloading file.": "Lade Datei herunter", + "Download failed: {err}": "Download fehlgeschlagen: {err}", + "Passed file validation.": "Dateiüberprüfung erfolgreich", + "Unable to validate download.": "Download konnte nicht überprüft werden.", + "Unknown IP": "Unbekannte IP", + "N/A": "N/V", + "Fetching latest gameinfo.xml files and cover images.": "Lade aktuelle gameinfo.xml Dateien und Cover-Bilder herunter.", + "Downloading gameinfo.xml for {source_name}": "Lade gameinfo.xml für {source_name} herunter", + "Installing {port_name}": "Installation von {port_name}", + "Uninstalling {port_name}": "Deinstallation von {port_name}", + "Updating all port sources:": "Aktualisieren aller Port-Quellen:", + "Checking {runtime_name}": "Prüfe {runtime_name}", + "** NO PORT **": "** KEIN PORT **", + "Ready to Run": "Bereit zum Laufen", + "Requires Files": "Benötigt Dateien", + "Installed": "Installiert", + "Missing": "Fehlt", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Ports wurden hinzugefügt oder entfernt, aktualisieren Sie Ihre Spiele-Liste, um die neuen Spiele zu sehen oder entfernen Sie die deinstallierten.", + "Danish": "Dänisch", + "German": "Deutsch", + "English": "Englisch", + "Spanish": "Spanisch", + "Finnish": "Finnisch", + "French": "Französisch", + "Italian": "Italienisch", + "Japanese": "Japanisch", + "Korean": "Koreanisch", + "Dutch": "Niederländisch", + "Polish": "Polnisch", + "Portuguese (Portugal)": "Portugiesisch (Portugal)", + "Portuguese (Brazil)": "Portugiesisch (Brasilien)", + "Russian": "Russisch", + "Swedish": "Schwedisch", + "Ukrainian": "Ukrainisch", + "Chinese Simplified": "Chinesisch (vereinfacht)", + "Messages": "Meldungen", + "Are you sure you want to cancel?": "Wirklich abbrechen?", + "Featured Ports": "Empfohlene Ports", + "Select": "Auswählen", + "Back": "Zurück", + "No featured ports found, internet connection is required.": "Keine empfohlenen Ports gefunden, Internetverbindung erforderlich.", + "Filters Scene": "Filter-Szene", + "Alphabetical": "Alphabetisch", + "Recently Added": "Zuletzt hinzugefügt", + "Recently Updated": "Vor kurzem aktualisiert", + "Action": "Aktion", + "Adventure": "Adventure", + "Arcade": "Arcade", + "Casino/Card": "Casino/Card", + "First Person Shooter": "First Person Shooter", + "Platformer": "Platformer", + "Puzzle": "Puzzle", + "Racing": "Racing", + "Rhythm": "Rhythm", + "Role Playing Game": "Role Playing Game", + "Simulation": "Simulation", + "Sports": "Sports", + "Strategy": "Strategie", + "Visual Novel": "Visual Novel", + "Other": "Sonstiges", + "Files Required": "Erforderliche Dateien", + "Experimental": "Experimentell", + "Broken Ports": "Defekte Ports", + "Free game, all files included.": "Kostenloses Spiel, alle Dateien enthalten.", + "Demo files included.": "Demo-Dateien enthalten.", + "Free external assets needed.": "Kostenlose externe Assets erforderlich.", + "Paid external assets needed.": "Kostenpflichtige externe Assets erforderlich.", + "{runtime_name} Runtime": "{runtime_name} Runtime", + "ARM 32bit": "ARM 32Bit", + "ARM 64bit": "ARM 64Bit", + "x86 64bit": "x86 64Bit", + "Sort:": "Sortieren:", + "Clear Filters": "Filter löschen", + "Genres:": "Genres:", + "Attributes:": "Attribute:", + "Architecture:": "Architektur:", + "Availability:": "Verfügbarkeit:", + "Porters:": "Porter:", + "Delete": "Löschen", + "Upper Case": "Großbuchstaben", + "Space": "Leerzeichen", + "Enter": "Eingabe", + "Lower Case": "Kleinbuchstaben", + "Language Select": "Sprachauswahl", + "{lang_name} (Selected)": "{lang_name} (Ausgewählt)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Sprache wechseln?\n\nNeustart erforderlich, damit Änderungen wirksam werden.", + "Main Menu": "Hauptmenü", + "Hand curated lists of ports": "Von Hand ausgewählte Ports", + "All Ports": "Alle Ports", + "List all ports available on PortMaster.": "Alle verfügbaren PortMaster Ports auflisten.", + "Ready to Run Ports": "Ready to Run Ports", + "List all ports that are ready to play!": "Alle Ports auflisten, die zum Spielen bereit sind!", + "Manage Ports": "Ports verwalten", + "Update / Uninstall Ports": "Ports aktualisieren / deinstallieren", + "Options": "Optionen", + "PortMaster Options": "PortMaster Optionen", + "Exit": "Beenden", + "Quit PortMaster": "PortMaster beenden", + "Quit": "Verlassen", + "Secret Mode {secret_mode}": "Geheim-Modus {secret_mode}", + "Enabled": "Aktiviert", + "Disabled": "Deaktiviert", + "Are you sure you want to exit PortMaster?": "Bist du sicher, dass du PortMaster verlassen möchtest?", + "Options Menu": "Optionen", + "Interface": "Benutzeroberfläche", + "Choose Language": "Sprache wählen", + "Select the language PortMaster uses.": "Sprache auswählen.", + "Select Theme": "Theme auswählen", + "Select a theme for PortMaster.": "Theme auswählen.", + "Select Color Scheme": "Farbschema auswählen", + "Select a colour scheme for PortMaster": "Farbschema auswählen", + "Audio": "Audio", + "Music: ": "Musik", + "Enable or Disable background music in PortMaster.": "Hintergrundmusik in PortMaster aktivieren oder deaktivieren.", + "Sound FX: ": "Sound FX", + "Enable or Disable soundfx in PortMaster.": "Soundfx in PortMaster aktivieren oder deaktivieren.", + "Port Metadata": "Port-Metadaten", + "Metadata Update: ": "Metadaten Update: ", + "Auto": "Automatisch", + "Manual": "Manuell", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster aktualisiert Metadaten, wenn Ports installiert werden, sodass Bilder und Beschreibungen beim Durchsuchen von Ports auf dem Gerät angezeigt werden.", + "Metadata Refresh": "Metadaten aktualisieren", + "Manually update port metadata with missing/updated information and artwork.": "Port-Metadaten manuell mit fehlenden/aktualisierten Informationen und Grafiken aktualisieren.", + "Ports Location: ": "Ports-Speicherort: ", + "Roms section": "Rom-Bereich", + "Ports tab": "Ports-Tab", + "Location where ports should be installed to.": "Speicherort an dem Ports installiert werden sollen.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "System", + "Runtime Manager": "Runtime-Verwaltung", + "Manage port runtimes.": "Port Runtimes verwalten.", + "Experimental Ports: ": "Experimentelle Ports: ", + "Show or hide experimental ports.": "Zeige oder verstecke experimentelle Ports.", + "Update Ports": "Ports aktualisieren", + "Fetch latest ports information.": "Neueste Port-Informationen abrufen.", + "Update PortMaster": "PortMaster aktualisieren", + "Force check for a new PortMaster version.": "Auf neue PortMaster Version prüfen.", + "Restore PortMaster": "PortMaster wiederherstellen", + "This will restore PortMaster to the latest stable version of PortMaster.": "Dadurch wird PortMaster auf die aktuelle stabile Version von PortMaster zurückgesetzt.", + "Release Channel: {channel}": "Release-Kanal: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Ändert den Release-Kanal von PortMaster, entweder Beta oder Stable.", + "Enable Alpha Releases": "Alpha-Versionen aktivieren", + "Change release channel of PortMaster to alpha.": "Ändert den Release-Kanal von PortMaster auf \"Alpha\".", + "Controller Mode: {controller_mode}": "Controller-Modus: {controller_mode}", + "Toggle between various controller layouts.": "Zwischen verschiedenen Controller-Layouts wechseln.", + "Secret Options": "Geheime Optionen", + "All Ports: ": "Alle Ports: ", + "Show all ports, ignoring requirements.": "Zeige alle Ports, ignoriere Voraussetzungen.", + "CWTBE Mode: ": "CWTBE Modus: ", + "Enable gptokeyb2 by default.": "Aktiviere gptokeyb2 standardmäßig.", + "Delete PortMaster Config": "PortMaster Konfiguration löschen", + "This can break stuff, don't touch unless you know what you are doing.": "Griffel weg wenn du kein Plan hast, sonst machst du noch alles kaputt!", + "Delete PortMaster Runtimes": "PortMaster Laufzeiten löschen", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Bist du sicher, dass du PortMaster wiederherstellen möchtest?\n\nDies wird erfordern, dass du einen speziell erstellten Port ausführst, der installiert wird.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Ein spezieller Port mit dem Namen \"Restore PortMaster.sh\" wurde installiert.\n\nStarte diesen Port, um die PortMaster Installation wiederherzustellen.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster wird nun beendet, damit du den Wiederherstellungs-Port ausführen kannst.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Bist du sicher, dass du zum Alpha-Release-Kanal wechseln möchtest?\n\nPortMaster wird entsprechend upgraden oder downgraden.\n\nWARNUNG: DIES KANN UND WIRD PROBLEME VERURSACHEN", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Bist du sicher, dass du den Release-Kanal von {current_channel} auf {new_channel} ändern möchtest?\n\nPortMaster wird entsprechend upgraden oder downgraden.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Möchten sie wirklich gamelist.xml aktualisieren?\n\n\n\nDadurch werden gespeicherte Designs oder Port-Informationen überschrieben und es wird ein 50+ MB Download benötigt.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Sind Sie sicher, dass Sie Ports von \"{from_mode}\" nach \"{to_mode}\" verschieben möchten", + "SDCARD 1": "SDKARTE 1", + "SDCARD 2": "SDKARTE 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Möchten sie wirklich Ports auf {to_loc} installieren und verwalten?\n\n\nBereits installiere Ports werden nicht verschoben.\n\nPortMaster wird neu starten um die Änderungen anzuwenden.", + "Deleting Runtimes:": "Lösche Runtimes:", + "Source Manager": "Quellen-Manager", + "Port Info Popup": "Port-Info-Popup", + "Hide Info": "Info ausblenden", + "Reinstall": "Neuinstallieren", + "Uninstall": "Deinstallieren", + "Install": "Installieren", + "Port Info": "Port Info", + "Show Info": "Info anzeigen", + "Are you sure you want to uninstall {port_name}?": "{port_name} wirklich deinstallieren?", + "NO PORTS": "KEINE PORTS", + "** NO PORTS FOUND **": "** KEINE PORTS GEFUNDEN **", + "Download ports first.": "Zuerst Ports herunterladen.", + "Try removing some filters.": "Zuerst Filter entfernen", + "Ports List": "Portliste", + "Filters": "Filter", + "Download All": "Alles herunterladen", + "Used": "Benutzt", + "Not Used": "Nicht verwendet", + "Check": "Überprüfen", + "Are you sure you want to download and verify all runtimes?": "Alle runtimes herunterladen und verifizieren?", + "Deleted runtime {runtime}": "Runtime- {runtime} entfernt", + "Disclaimer": "Haftungsausschluss", + "Accept": "Akzeptieren", + "{theme_name} (Selected)": "{theme_name} (Ausgewählt)", + "Download": "Herunterladen", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Theme wechseln?\n\nNeustart erforderlich, damit Änderungen wirksam werden.", + "Select Colour Scheme": "Farbschema auswählen", + "{item_name} (Selected)": "{item_name} (Ausgewählt)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Farbschema für das Theme ändern?\n\nNeustart erforderlich, damit Änderungen wirksam werden.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Es wird von {old_release_channel} zur {new_release_channel} Version gewechselt.\n\nSoll das Downgrade durchgeführt werden?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Es gibt eine neue Version von PortMaster ({portmaster_version})\n\nSoll das Upgrade durchgeführt werden?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "PortMaster wieder installieren?\n\nDies wird vom {release_channel} Kanal zur Version {portmaster_version} installiert." +} diff --git a/PortMaster/pylibs/locales/de_DE.themes.json b/PortMaster/pylibs/locales/de_DE.themes.json new file mode 100644 index 00000000..cd61552c --- /dev/null +++ b/PortMaster/pylibs/locales/de_DE.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Uhrzeit: {system.time_24hr}\nBatterie: {system.battery_level}\nSpeicher: {system.free_space} frei", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres} Download-Größe: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Verfügbare Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilter: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Freier Speicher: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownloadgröße: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nAnleitung: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpeicher: {system.free_space} frei / {system.total_space} insgesamt \nUhrzeit: {system.time_24hr}\nBatterie: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Verfasser: {theme_info.creator}\nStatus: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpeicher: {system.free_space} frei", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filter: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres} \nDownload-Größe: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Zeit: {system.time_24hr} Batterie: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Titel: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownloadgröße: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nBeschreibung: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Verfügbare Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres}\n{if:port_info.install_size}Installierte Größe: {port_info.install_size}{else}Downloadgröße: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Zeit: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nGerät: {system.device_name}\nBatterie: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpeicherplatz: {system.free_space} frei / {system.total_space} Gesamt", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Beschreibung: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genres: {port_info.genres}\n• Downloadgröße: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Beschreibung: {port_info.description}\n• Anleitungen:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Ersteller: {theme_info.creator}\n• Status: {theme_info.status}\n• Beschreibung:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownloadgröße: {runtime_info.download_size}\nInstallationsgröße: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/de_DE/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/de_DE/LC_MESSAGES/messages.po deleted file mode 100644 index c17f8c81..00000000 --- a/PortMaster/pylibs/locales/de_DE/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: German\n" -"Language: de_DE\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: de\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Dänisch" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Deutsch" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Englisch" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Spanisch" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finnisch" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Französisch" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italienisch" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japanisch" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Koreanisch" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Niederländisch" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polnisch" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugiesisch (Portugal)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugiesisch (Brasilien)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Russisch" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Schwedisch" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ukrainisch" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chinesisch (vereinfacht)" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "Unbekannte IP" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/V" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** KEIN PORT **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Bereit zum Laufen" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Benötigt Dateien" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Installiert" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Fehlt" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Update verfügbar" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Nicht installiert" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Okay" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Abbrechen" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Lade aktuelle gameinfo.xml Dateien und Cover-Bilder herunter." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Lade gameinfo.xml für {source_name} herunter" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Installation von {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Deinstallation von {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Aktualisieren aller Port-Quellen:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Prüfe {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Es wird von {old_release_channel} zur {new_release_channel} Version gewechselt.\n\n" -"Soll das Downgrade durchgeführt werden?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Es gibt eine neue Version von PortMaster ({portmaster_version})\n\n" -"Soll das Upgrade durchgeführt werden?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "PortMaster wieder installieren?\n\n" -"Dies wird vom {release_channel} Kanal zur Version {portmaster_version} installiert." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Ports wurden hinzugefügt oder entfernt, aktualisieren Sie Ihre Spiele-Liste, um die neuen Spiele zu sehen oder entfernen Sie die deinstallierten." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Lädt..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Download Überprüfung fehlgeschlagen" - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Informationen werden geladen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Lade neueste Ports." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Lade Quellen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Lade Ports." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Installiere Theme {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Wird installiert" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Theme {download_name!r} erfolgreich installiert." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Installiere {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Port {download_name!r} erfolgreich installiert." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Installation fehlgeschlagen, entferne Dateien..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Port {download_name} Installation fehlgeschlagen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Port {download_name!r} und {runtime_name!r} erfolgreich installiert." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Port {download_name!r} wurde erfolgreich installiert, Installation von {runtime_name!r} ist fehlgeschlagen\n" -"Installiere den Port erneut um es nochmal zu versuchen oder besuche das Wiki für Hilfe." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Port {runtime} enthält eine fehlerhafte Runtime, möglicherweise funktioniert das Spiel nicht korrekt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Port {runtime} enthält eine unbekannte Runtime, möglicherweise funktioniert das Spiel nicht korrekt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Überprüft" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Nicht verifiziert" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Fehlerhaft" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Runtimes lassen sich nicht im Offline Modus herunterladen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Überprüfe Runtime {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Überprüfe" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Es konnte kein Download für {runtime} gefunden werden." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Kann {runtime} in {device_arch} nicht herunterladen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} erfolgreich überprüft." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} verifiziert." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Aktualisiere {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Laufzeit- {runtime} ist defekt, Installiere neu." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Lade runtime {runtime} herunter" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Runtime konnte nicht runtergeladen werden, öglicherweise funktioniert das Spiel nicht korrekt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "Erfolgreich {runtime} heruntergeladen" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Download im Offline-Modus nicht möglich." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Es konnte keine Quelle für {port_name} gefunden werden." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Unbekannter Port {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Entferne {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "Erfolgreich {port_name} deinstalliert" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Um dies zu tun:\n" -"MENÜ→-> UTILITIES -> Rom-Verzeichnis erneut scannen" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache veraltet" - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Erste Überprüfung." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Automatische Aktualisierung" - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Aktualisiere" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Neuste Version bereits installiert" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Lade neuste Informationen" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Fertig" - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "{port_name} konnte nicht gefunden werden" - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Für {port_name} konnten keine Überprüfungsinformationen gefunden werden." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Für {port_name} konnten keine Überprüfungsinformationen heruntergeladen werden." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Erfolgreich!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Lade Informationen" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "Lade neuste Ports" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Lade Informationen" - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Überprüfungsdatei konnte nicht heruntergeladen werden" - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Nur Zip Dateien können heruntergeladen werden." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "[{status_code}] konnte nicht heruntergeladen werden." - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Lade {file_url} - ({total_length_mb}) herunter." - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Lade Datei herunter" - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Download fehlgeschlagen: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Dateiüberprüfung erfolgreich" - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Download konnte nicht überprüft werden." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Hauptmenü" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Haftungsausschluss" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Akzeptieren" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Verlassen" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Bist du sicher, dass du PortMaster verlassen möchtest?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Empfohlene Ports" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Von Hand ausgewählte Ports" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Alle Ports" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Alle verfügbaren PortMaster Ports auflisten." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Ready to Run Ports" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Alle Ports auflisten, die zum Spielen bereit sind!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Ports verwalten" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Ports aktualisieren / deinstallieren" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Optionen" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "PortMaster Optionen" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Beenden" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "PortMaster beenden" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Eingabe" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Geheim-Modus {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Aktiviert" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Deaktiviert" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Optionen" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Benutzeroberfläche" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Sprache wählen" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Sprache auswählen." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Theme auswählen" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Theme auswählen." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Farbschema auswählen" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Farbschema auswählen" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Audio" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Musik" - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Hintergrundmusik in PortMaster aktivieren oder deaktivieren." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Sound FX" - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Soundfx in PortMaster aktivieren oder deaktivieren." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Port-Metadaten" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Metadaten Update: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Automatisch" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manuell" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster aktualisiert Metadaten, wenn Ports installiert werden, sodass Bilder und Beschreibungen beim Durchsuchen von Ports auf dem Gerät angezeigt werden." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Metadaten aktualisieren" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Port-Metadaten manuell mit fehlenden/aktualisierten Informationen und Grafiken aktualisieren." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Ports-Speicherort: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Rom-Bereich" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Ports-Tab" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Speicherort an dem Ports installiert werden sollen." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "System" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Runtime-Verwaltung" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Port Runtimes verwalten." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Experimentelle Ports: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Zeige oder verstecke experimentelle Ports." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Ports aktualisieren" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Neueste Port-Informationen abrufen." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "PortMaster aktualisieren" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Auf neue PortMaster Version prüfen." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "PortMaster wiederherstellen" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Dadurch wird PortMaster auf die aktuelle stabile Version von PortMaster zurückgesetzt." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Release-Kanal: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Ändert den Release-Kanal von PortMaster, entweder Beta oder Stable." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Alpha-Versionen aktivieren" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Ändert den Release-Kanal von PortMaster auf \"Alpha\"." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Controller-Modus: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Zwischen verschiedenen Controller-Layouts wechseln." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Geheime Optionen" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Alle Ports: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Zeige alle Ports, ignoriere Voraussetzungen." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "CWTBE Modus: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Aktiviere gptokeyb2 standardmäßig." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "PortMaster Konfiguration löschen" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Griffel weg wenn du kein Plan hast, sonst machst du noch alles kaputt!" - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "PortMaster Laufzeiten löschen" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Zurück" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Bist du sicher, dass du PortMaster wiederherstellen möchtest?\n\n" -"Dies wird erfordern, dass du einen speziell erstellten Port ausführst, der installiert wird." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Ein spezieller Port mit dem Namen \"Restore PortMaster.sh\" wurde installiert.\n\n" -"Starte diesen Port, um die PortMaster Installation wiederherzustellen." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster wird nun beendet, damit du den Wiederherstellungs-Port ausführen kannst." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Bist du sicher, dass du zum Alpha-Release-Kanal wechseln möchtest?\n\n" -"PortMaster wird entsprechend upgraden oder downgraden.\n\n" -"WARNUNG: DIES KANN UND WIRD PROBLEME VERURSACHEN" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Bist du sicher, dass du den Release-Kanal von {current_channel} auf {new_channel} ändern möchtest?\n\n" -"PortMaster wird entsprechend upgraden oder downgraden." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Möchten sie wirklich gamelist.xml aktualisieren?\n\n\n\n" -"Dadurch werden gespeicherte Designs oder Port-Informationen überschrieben und es wird ein 50+ MB Download benötigt." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Sind Sie sicher, dass Sie Ports von \"{from_mode}\" nach \"{to_mode}\" verschieben möchten" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SDKARTE 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SDKARTE 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Möchten sie wirklich Ports auf {to_loc} installieren und verwalten?\n\n\n" -"Bereits installiere Ports werden nicht verschoben.\n\n" -"PortMaster wird neu starten um die Änderungen anzuwenden." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Lösche Runtimes:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Quellen-Manager" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Alles herunterladen" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Benutzt" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Nicht verwendet" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Überprüfen" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Deinstallieren" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Installieren" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Alle runtimes herunterladen und verifizieren?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Runtime- {runtime} entfernt" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Ausgewählt)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Auswählen" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Herunterladen" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Theme wechseln?\n\n" -"Neustart erforderlich, damit Änderungen wirksam werden." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Farbschema auswählen" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Ausgewählt)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Farbschema für das Theme ändern?\n\n" -"Neustart erforderlich, damit Änderungen wirksam werden." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Sprachauswahl" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Ausgewählt)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Sprache wechseln?\n\n" -"Neustart erforderlich, damit Änderungen wirksam werden." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Löschen" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Großbuchstaben" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Leerzeichen" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Kleinbuchstaben" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Keine empfohlenen Ports gefunden, Internetverbindung erforderlich." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "KEINE PORTS" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** KEINE PORTS GEFUNDEN **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Zuerst Ports herunterladen." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Zuerst Filter entfernen" - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Info anzeigen" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Portliste" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filter" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Port-Info-Popup" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Info ausblenden" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Neuinstallieren" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Port Info" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "{port_name} wirklich deinstallieren?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Filter-Szene" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alphabetisch" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Zuletzt hinzugefügt" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Vor kurzem aktualisiert" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Aktion" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Adventure" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Casino/Card" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "First Person Shooter" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Platformer" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Puzzle" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Racing" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Rhythm" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Role Playing Game" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulation" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sports" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Strategie" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Visual Novel" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Sonstiges" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Erforderliche Dateien" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Experimentell" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Defekte Ports" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Kostenloses Spiel, alle Dateien enthalten." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Demo-Dateien enthalten." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Kostenlose externe Assets erforderlich." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Kostenpflichtige externe Assets erforderlich." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Runtime" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32Bit" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64Bit" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64Bit" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Sortieren:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Filter löschen" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Genres:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Attribute:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Architektur:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Verfügbarkeit:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porter:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Meldungen" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Wirklich abbrechen?" - diff --git a/PortMaster/pylibs/locales/de_DE/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/de_DE/LC_MESSAGES/themes.po deleted file mode 100644 index e24b6f64..00000000 --- a/PortMaster/pylibs/locales/de_DE/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: German\n" -"Language: de_DE\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: de\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres: {port_info.genres} Download-Größe: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Verfügbare Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filter: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Freier Speicher: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Downloadgröße: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Anleitung: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Speicher: {system.free_space} frei / {system.total_space} insgesamt \n" -"Uhrzeit: {system.time_24hr}\n" -"Batterie: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Verfasser: {theme_info.creator}\n" -"Status: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Zeit: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Gerät: {system.device_name}\n" -"Batterie: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Speicher: {system.free_space} frei" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filter: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres: {port_info.genres} \n" -"Download-Größe: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Zeit: {system.time_24hr} Batterie: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Speicherplatz: {system.free_space} frei / {system.total_space} Gesamt" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Beschreibung: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Genres: {port_info.genres}\n" -"• Downloadgröße: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Beschreibung: {port_info.description}\n" -"• Anleitungen:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Ersteller: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Beschreibung:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Downloadgröße: {runtime_info.download_size}\n" -"Installationsgröße: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Uhrzeit: {system.time_24hr}\n" -"Batterie: {system.battery_level}\n" -"Speicher: {system.free_space} frei" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Titel: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Downloadgröße: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Beschreibung: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Verfügbare Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installierte Größe: {port_info.install_size}{else}Downloadgröße: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/en_US.json b/PortMaster/pylibs/locales/en_US.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/PortMaster/pylibs/locales/en_US.json @@ -0,0 +1 @@ +{} diff --git a/PortMaster/pylibs/locales/en_US.themes.json b/PortMaster/pylibs/locales/en_US.themes.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/PortMaster/pylibs/locales/en_US.themes.json @@ -0,0 +1 @@ +{} diff --git a/PortMaster/pylibs/locales/en_US/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/en_US/LC_MESSAGES/messages.po deleted file mode 100644 index 3e65938f..00000000 --- a/PortMaster/pylibs/locales/en_US/LC_MESSAGES/messages.po +++ /dev/null @@ -1,528 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-08-27 10:34+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -#: pugwash:184 -msgid "German" -msgstr "" - -#: pugwash:185 -msgid "English" -msgstr "" - -#: pugwash:186 -msgid "Spanish" -msgstr "" - -#: pugwash:187 -msgid "French" -msgstr "" - -#: pugwash:188 -msgid "Italian" -msgstr "" - -#: pugwash:189 -msgid "Polish" -msgstr "" - -#: pugwash:282 -msgid "Unknown IP" -msgstr "" - -#: pugwash:519 pugwash:643 -msgid "N/A" -msgstr "" - -#: pugwash:613 -msgid "** NO PORT **" -msgstr "" - -#: pugwash:630 -msgid "Ready to Run" -msgstr "" - -#: pugwash:630 -msgid "Requires Files" -msgstr "" - -#: pugwash:637 pugwash:658 -msgid "Installed" -msgstr "" - -#: pugwash:639 -msgid "Missing" -msgstr "" - -#: pugwash:642 -msgid "None" -msgstr "" - -#: pugwash:659 -msgid "Update Available" -msgstr "" - -#: pugwash:660 -msgid "Not Installed" -msgstr "" - -#: pugwash:760 pylibs/pugscene.py:1155 -msgid "Okay" -msgstr "" - -#: pugwash:763 pylibs/pugscene.py:699 pylibs/pugscene.py:707 -#: pylibs/pugscene.py:1126 pylibs/pugscene.py:1158 -msgid "Cancel" -msgstr "" - -#: pugwash:957 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "" - -#: pugwash:966 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "" - -#: pugwash:976 -msgid "Updating all port sources:" -msgstr "" - -#: pugwash:1007 -#, python-brace-format -msgid "" -"There is a new version of PortMaster {portmaster_version}\n" -"\n" -"Do you want to upgrade?" -msgstr "" - -#: pylibs/harbourmaster/harbour.py:101 -msgid "Loading..." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:151 -msgid "Loading Info." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:169 pylibs/harbourmaster/harbour.py:185 -msgid "Fetching latest info." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:197 -msgid "Fetching latest porters." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:209 -msgid "Loading Sources." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:365 -msgid "Loading Ports." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:853 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:860 pylibs/harbourmaster/harbour.py:898 -#: pylibs/harbourmaster/harbour.py:955 -msgid "Installing" -msgstr "" - -#: pylibs/harbourmaster/harbour.py:870 -msgid "Theme {download_name!r} installed successfully." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:889 pylibs/harbourmaster/harbour.py:946 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:914 pylibs/harbourmaster/harbour.py:1054 -msgid "Port {download_name!r} installed successfully." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1021 -msgid "Installation failed, removing files..." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1033 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1044 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1049 -msgid "" -"Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to " -"install!!\n" -"\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1062 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1077 -msgid "Unable do download a runtime when in offline mode." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1086 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1101 pylibs/harbourmaster/harbour.py:1111 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1120 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1126 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1136 pylibs/harbourmaster/harbour.py:1215 -msgid "Unable do download a port when in offline mode." -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1233 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1247 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1280 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "" - -#: pylibs/harbourmaster/harbour.py:1312 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "" - -#: pylibs/harbourmaster/source.py:52 -msgid "Cache out of date." -msgstr "" - -#: pylibs/harbourmaster/source.py:57 -msgid "First check." -msgstr "" - -#: pylibs/harbourmaster/source.py:60 -msgid "Auto Update." -msgstr "" - -#: pylibs/harbourmaster/source.py:143 -msgid "Updating" -msgstr "" - -#: pylibs/harbourmaster/source.py:154 -msgid "Up to date already" -msgstr "" - -#: pylibs/harbourmaster/source.py:159 -msgid "Fetching latest info" -msgstr "" - -#: pylibs/harbourmaster/source.py:204 -msgid "Done." -msgstr "" - -#: pylibs/harbourmaster/source.py:212 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "" - -#: pylibs/harbourmaster/source.py:223 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "" - -#: pylibs/harbourmaster/source.py:230 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "" - -#: pylibs/harbourmaster/source.py:240 -msgid "Success!" -msgstr "" - -#: pylibs/harbourmaster/source.py:295 pylibs/pugtheme.py:328 -msgid "Fetching info" -msgstr "" - -#: pylibs/harbourmaster/source.py:475 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "" - -#: pylibs/harbourmaster/source.py:514 -msgid "Fetching info." -msgstr "" - -#: pylibs/harbourmaster/source.py:586 -msgid "Unable to download verification file." -msgstr "" - -#: pylibs/harbourmaster/source.py:599 -msgid "Unable to download non zip files." -msgstr "" - -#: pylibs/harbourmaster/util.py:259 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "" - -#: pylibs/harbourmaster/util.py:275 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "" - -#: pylibs/harbourmaster/util.py:287 pylibs/harbourmaster/util.py:301 -msgid "Downloading file." -msgstr "" - -#: pylibs/harbourmaster/util.py:310 -msgid "Download validation failed." -msgstr "" - -#: pylibs/harbourmaster/util.py:316 -msgid "Passed file validation." -msgstr "" - -#: pylibs/harbourmaster/util.py:321 -msgid "Unable to validate download." -msgstr "" - -#: pylibs/pugscene.py:306 -msgid "All Ports" -msgstr "" - -#: pylibs/pugscene.py:307 -msgid "Ready to Run Ports" -msgstr "" - -#: pylibs/pugscene.py:308 -msgid "Uninstall Ports" -msgstr "" - -#: pylibs/pugscene.py:310 -msgid "Options" -msgstr "" - -#: pylibs/pugscene.py:311 -msgid "Exit" -msgstr "" - -#: pylibs/pugscene.py:312 pylibs/pugscene.py:378 pylibs/pugscene.py:699 -#: pylibs/pugscene.py:707 -msgid "Enter" -msgstr "" - -#: pylibs/pugscene.py:312 -msgid "Quit" -msgstr "" - -#: pylibs/pugscene.py:347 -msgid "System" -msgstr "" - -#: pylibs/pugscene.py:349 -msgid "Update Ports" -msgstr "" - -#: pylibs/pugscene.py:350 -msgid "Update PortMaster" -msgstr "" - -#: pylibs/pugscene.py:356 pylibs/pugscene.py:439 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "" - -#: pylibs/pugscene.py:358 -msgid "Audio" -msgstr "" - -#: pylibs/pugscene.py:362 pylibs/pugscene.py:412 -msgid "Music: " -msgstr "" - -#: pylibs/pugscene.py:362 pylibs/pugscene.py:365 pylibs/pugscene.py:412 -#: pylibs/pugscene.py:421 -msgid "Disabled" -msgstr "" - -#: pylibs/pugscene.py:362 pylibs/pugscene.py:365 pylibs/pugscene.py:412 -#: pylibs/pugscene.py:421 -msgid "Enabled" -msgstr "" - -#: pylibs/pugscene.py:365 pylibs/pugscene.py:421 -msgid "Sound FX: " -msgstr "" - -#: pylibs/pugscene.py:367 -msgid "Interface" -msgstr "" - -#: pylibs/pugscene.py:369 -msgid "Choose Language" -msgstr "" - -#: pylibs/pugscene.py:370 -msgid "Select Theme" -msgstr "" - -#: pylibs/pugscene.py:373 -msgid "Select Color Scheme" -msgstr "" - -#: pylibs/pugscene.py:378 pylibs/pugscene.py:505 pylibs/pugscene.py:586 -#: pylibs/pugscene.py:587 pylibs/pugscene.py:639 pylibs/pugscene.py:640 -#: pylibs/pugscene.py:764 pylibs/pugscene.py:766 pylibs/pugscene.py:899 -#: pylibs/pugscene.py:901 -msgid "Back" -msgstr "" - -#: pylibs/pugscene.py:489 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "" - -#: pylibs/pugscene.py:503 pylibs/pugscene.py:587 pylibs/pugscene.py:640 -#: pylibs/pugscene.py:699 pylibs/pugscene.py:707 -msgid "Select" -msgstr "" - -#: pylibs/pugscene.py:508 -msgid "Download" -msgstr "" - -#: pylibs/pugscene.py:527 -msgid "" -"Do you want to change theme?\n" -"\n" -"You will have to restart for it to take affect." -msgstr "" - -#: pylibs/pugscene.py:581 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "" - -#: pylibs/pugscene.py:604 -msgid "" -"Do you want to change the themes color scheme?\n" -"\n" -"You will have to restart for it to take affect." -msgstr "" - -#: pylibs/pugscene.py:634 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "" - -#: pylibs/pugscene.py:657 -msgid "" -"Do you want to change language?\n" -"\n" -"You will have to restart for it to take affect." -msgstr "" - -#: pylibs/pugscene.py:699 pylibs/pugscene.py:707 -msgid "Delete" -msgstr "" - -#: pylibs/pugscene.py:699 -msgid "Upper Case" -msgstr "" - -#: pylibs/pugscene.py:699 pylibs/pugscene.py:707 -msgid "Space" -msgstr "" - -#: pylibs/pugscene.py:707 -msgid "Lower Case" -msgstr "" - -#: pylibs/pugscene.py:764 pylibs/pugscene.py:766 -msgid "Show Info" -msgstr "" - -#: pylibs/pugscene.py:764 -msgid "Filters" -msgstr "" - -#: pylibs/pugscene.py:787 -msgid "NO PORTS" -msgstr "" - -#: pylibs/pugscene.py:794 -msgid "** NO PORTS FOUND **" -msgstr "" - -#: pylibs/pugscene.py:797 -msgid "Download ports first." -msgstr "" - -#: pylibs/pugscene.py:799 -msgid "Try removing some filters." -msgstr "" - -#: pylibs/pugscene.py:899 -msgid "Install" -msgstr "" - -#: pylibs/pugscene.py:901 -msgid "Uninstall" -msgstr "" - -#: pylibs/pugscene.py:1007 -msgid "Genres:" -msgstr "" - -#: pylibs/pugscene.py:1035 -msgid "Attributes:" -msgstr "" - -#: pylibs/pugscene.py:1062 -msgid "Porters:" -msgstr "" - -#: pylibs/pugscene.py:1145 -msgid "Are you sure you want to cancel?" -msgstr "" diff --git a/PortMaster/pylibs/locales/en_US/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/en_US/LC_MESSAGES/themes.po deleted file mode 100644 index 5edadbea..00000000 --- a/PortMaster/pylibs/locales/en_US/LC_MESSAGES/themes.po +++ /dev/null @@ -1,117 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - - -#: default_theme/theme.json:254 -msgid "" -"{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "" - -#: default_theme/theme.json:281 -msgid "" -"Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "" - -#: default_theme/theme.json:351, Zelda.theme.zip/theme.json:457 -msgid "" -"Free Space: {system.free_space}\n" -"\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"\n" -"Instruction: {port_info.instructions}" -msgstr "" - -#: default_theme/theme.json:380, ff_VII.theme.zip/theme.json:446 -msgid "" -"Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "" - -#: default_theme/theme.json:388, ff_VII.theme.zip/theme.json:454 -msgid "" -"PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "" - -#: default_theme/theme.json:411, ff_VII.theme.zip/theme.json:489, Zelda.theme.zip/theme.json:523 -msgid "IP: {system.ip_address}" -msgstr "" - -#: default_theme/theme.json:465, ff_VII.theme.zip/theme.json:569, Zelda.theme.zip/theme.json:602 -msgid "" -"Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "" - -#: basic_theme.theme.zip/theme.json:77 -msgid "" -"Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "" - -#: ff_VII.theme.zip/theme.json:243 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "" - -#: ff_VII.theme.zip/theme.json:343 -msgid "" -"{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "" - -#: ff_VII.theme.zip/theme.json:410 -msgid "" -"Free Space: {system.free_space}\n" -"\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"\n" -"Instruction: \n" -"Place file x in folder y then dance until tomorrow" -msgstr "" - -#: Zelda.theme.zip/theme.json:308 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "" - -#: Zelda.theme.zip/theme.json:357 -msgid "" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "" - -#: Zelda.theme.zip/theme.json:494 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "" - diff --git a/PortMaster/pylibs/locales/es_ES.json b/PortMaster/pylibs/locales/es_ES.json new file mode 100644 index 00000000..a6137224 --- /dev/null +++ b/PortMaster/pylibs/locales/es_ES.json @@ -0,0 +1,254 @@ +{ + "Loading...": "Cargando...", + "Loading Sources.": "Cargando Fuentes.", + "Download validation failed.": "Error al validar la descarga.", + "Loading Info.": "Cargando información.", + "Fetching latest featured ports.": "Obteniendo los últimos ports destacados.", + "Installing Theme {download_name}.": "Instalando Tema {download_name}.", + "Installing": "Instalando", + "Theme {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", + "Port {download_name} installed failed.": "La instalación del Port {download_name} ha fallado.", + "Installing {download_name}.": "Instalando {download_name}.", + "Port {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", + "Installation failed, removing files...": "Instalación fallida, eliminando archivos...", + "Okay": "Aceptar", + "Cancel": "Cancelar", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} y {runtime_name!r} instalados correctamente.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "¡¡Port {download_name!r} instalado correctamente, pero la instalación de {runtime_name!r} ha fallado!!\n\nReintenta la instalación o revisa la wiki para obtener ayuda.", + "Unable to download in offline mode.": "No se puede descargar en modo desconectado.", + "Unable to find a source for {port_name}": "No se ha podido encontrar una fuente para {port_name}", + "Unknown port {port_name}": "Port desconocido {port_name}", + "Removing {port_name}": "Eliminando {port_name}", + "Successfully uninstalled {port_name}": "{port_name} Desinstalado correctamente", + "Loading Ports.": "Cargando Ports.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "El Port {runtime} contiene un tiempo de ejecución defectuoso, el juego puede no ejecutarse correctamente.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "El Port {runtime} contiene un tiempo de ejecución defectuoso, el juego puede no ejecutarse correctamente.", + "Not Installed": "No Instalado", + "Update Available": "Actualización Disponible", + "Verified": "Verificado", + "Unverified": "Sin verificar", + "Broken": "Dañado", + "Unable do download a runtime when in offline mode.": "No se puede descargar un tiempo de ejecución en modo sin conexión.", + "Verifying runtime {runtime}": "Verificando el runtime {runtime}", + "Verifying": "Verificando", + "Unable to find a download for {runtime}.": "No se ha podido encontrar una descarga para {runtime}.", + "Unable to download {runtime} in {device_arch}.": "No se puede descargar {runtime} en {device_arch}.", + "Verified {runtime} successfully.": "Se verificó el {runtime} exitosamente.", + "Verified {runtime}.": "{runtime} verificado.", + "Updating {runtime}.": "Actualizando el {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "El runtime {runtime} esta dañado, reinstalando.", + "Downloading runtime {runtime}.": "Descargando tiempo de ejecución {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "No se puede descargar {runtime}, es posible que el juego no funcione correctamente.", + "Successfully downloaded {runtime}.": "{runtime} Descargado correctamente.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\nPara hacerlo:\n\nMENÚ > UTILIDADES > REESCANEAR DIRECTORIO DE ROMS", + "Unable to download verification file.": "No se puede descargar el archivo de verificación.", + "Unable to download non zip files.": "No se pueden descargar archivos de tipo no zip.", + "Cache out of date.": "Caché Obsoleta.", + "First check.": "Primera verificación.", + "Auto Update.": "Actualización automática.", + "Updating": "Actualizando", + "Up to date already": "Ya está actualizado", + "Fetching latest info": "Obteniendo información más reciente", + "Done.": "Hecho.", + "Fetching info": "Obteniendo información", + "Unable to find {port_name}.": "No se puede encontrar {port_name}.", + "Unable to download file. [{status_code}]": "No se ha podido descargar el archivo. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Descargando {file_url} - ({total_length_mb})", + "Downloading file.": "Descargando archivo.", + "Download failed: {err}": "Descarga fallida: {err}", + "Passed file validation.": "Validación de archivo correcta.", + "Unable to validate download.": "Error al validar la descarga.", + "Unknown IP": "IP Desconocida", + "N/A": "N/D", + "Fetching latest gameinfo.xml files and cover images.": "Obteniendo los últimos archivos de gameinfo.xml y portadas de imágenes.", + "Downloading gameinfo.xml for {source_name}": "Descargando gameinfo.xml para {source_name}", + "Installing {port_name}": "Instalando {port_name}", + "Uninstalling {port_name}": "Desinstalando {port_name}", + "Updating all port sources:": "Actualizando todas las fuentes de ports:", + "Checking {runtime_name}": "Checkeando {runtime_name}", + "** NO PORT **": "** SIN PORT**", + "Ready to Run": "Listo para Funcionar", + "Requires Files": "Requiere Archivos", + "Installed": "Instalado", + "Missing": "No Disponible", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Se han agregado o eliminado ports, actualiza la lista para ver los nuevos juegos o eliminar los juegos desinstalados.", + "Danish": "Danés", + "German": "Alemán", + "English": "Inglés", + "Spanish": "Español", + "Finnish": "Finlandés", + "French": "Francés", + "Italian": "Italiano", + "Japanese": "Japonés", + "Korean": "Coreano", + "Dutch": "Holandés", + "Polish": "Polaco", + "Portuguese (Portugal)": "Portugués (Portugal)", + "Portuguese (Brazil)": "Portugués (Brasil)", + "Russian": "Ruso", + "Swedish": "Sueco", + "Ukrainian": "Ucraniano", + "Chinese Simplified": "Chino Simplificado", + "Messages": "Mensajes", + "Are you sure you want to cancel?": "¿Está seguro de que quiere cancelar?", + "Featured Ports": "Ports Destacados", + "Select": "Seleccionar", + "Back": "Atrás", + "No featured ports found, internet connection is required.": "No se encontraron ports destacados, se requiere conexión a internet.", + "Filters Scene": "Escena de Filtros", + "Alphabetical": "Alfabético", + "Recently Added": "Recientemente Añadidos", + "Recently Updated": "Recientemente Actualizados", + "Action": "Acción", + "Adventure": "Aventura", + "Arcade": "Arcade", + "Casino/Card": "Casino/Cartas", + "First Person Shooter": "Disparos en Primera Persona", + "Platformer": "Plataformero", + "Puzzle": "Puzzle", + "Racing": "Carreras", + "Rhythm": "Ritmo", + "Role Playing Game": "Juegos de Rol", + "Simulation": "Simulación", + "Sports": "Deportes", + "Strategy": "Estrategia", + "Visual Novel": "Novela Visual", + "Other": "Otros", + "Files Required": "Archivos Requeridos", + "Experimental": "Experimental", + "Broken Ports": "Ports Rotos", + "Free game, all files included.": "Juego gratis, todos los archivos incluidos.", + "Demo files included.": "Archivos de demostración incluidos.", + "Free external assets needed.": "Se necesitan archivos externos gratis.", + "Paid external assets needed.": "Se necesitan archivos externos pagos.", + "{runtime_name} Runtime": "{runtime_name} Runtime", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Sort:": "Ordenar:", + "Clear Filters": "Limpiar Filtros", + "Genres:": "Géneros:", + "Attributes:": "Atributos:", + "Architecture:": "Arquitectura:", + "Availability:": "Disponibilidad:", + "Porters:": "Porters:", + "Delete": "Eliminar", + "Upper Case": "Mayúsculas", + "Space": "Espacio", + "Enter": "Entrar", + "Lower Case": "Minúsculas", + "Language Select": "Eligir Idioma", + "{lang_name} (Selected)": "{lang_name} (seleccionado)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "¿Quieres cambiar de idioma?\n\nTendrás que reiniciar para que surta efecto.", + "Main Menu": "Menú Principal", + "Hand curated lists of ports": "Lista de ports supervisados a mano", + "All Ports": "Todos los ports", + "List all ports available on PortMaster.": "Lista de todos los ports disponibles en PortMaster.", + "Ready to Run Ports": "Ports listos para ejecutarse", + "List all ports that are ready to play!": "Lista de todos los ports que estan listos para jugar!", + "Manage Ports": "Administrar Ports", + "Update / Uninstall Ports": "Actualizar / Desinstalar Ports", + "Options": "Opciones", + "PortMaster Options": "Opciones de PortMaster", + "Exit": "Salir", + "Quit PortMaster": "Salir de PortMaster", + "Quit": "Salir", + "Secret Mode {secret_mode}": "Modo Secreto {secret_mode}", + "Enabled": "Habilitado", + "Disabled": "Deshabilitado", + "Are you sure you want to exit PortMaster?": "Estás seguro que deseas salir de PortMaster?", + "Options Menu": "Menu de Opciones", + "Interface": "Interfaz", + "Choose Language": "Seleccionar idioma", + "Select the language PortMaster uses.": "Elige el idioma que PortMaster usa.", + "Select Theme": "Seleccionar Tema", + "Select a theme for PortMaster.": "Elige el tema de PortMaster.", + "Select Color Scheme": "Seleccione el Esquema de Color", + "Select a colour scheme for PortMaster": "Elige un esquema de color para PortMaster", + "Audio": "Audio", + "Music: ": "Música: ", + "Enable or Disable background music in PortMaster.": "Activa o Desactiva la música de fondo de PortMaster.", + "Sound FX: ": "Efectos: ", + "Enable or Disable soundfx in PortMaster.": "Habilita o Deshabilita los efectos de sonido en PortMaster.", + "Port Metadata": "Metadatos del Port", + "Metadata Update: ": "Actualización de metadatos: ", + "Auto": "Auto", + "Manual": "Manual", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster actualiza los metadatos de los Ports instalados para que muestre imágenes y descripciones al navegar por los Ports del dispositivo.", + "Metadata Refresh": "Actualizar metadatos", + "Manually update port metadata with missing/updated information and artwork.": "Actualiza manualmente los metadatos del Port con información faltante/actualizada y artwork.", + "Ports Location: ": "Ubicación de Ports: ", + "Roms section": "Sección de Roms", + "Ports tab": "Pestaña de ports", + "Location where ports should be installed to.": "Ubicación donde deberían instalarse los ports.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "Sistema", + "Runtime Manager": "Administrador de Runtimes", + "Manage port runtimes.": "Administrar runtimes del port.", + "Experimental Ports: ": "Ports experimentales: ", + "Show or hide experimental ports.": "Ocultar o mostrar los ports experimentales.", + "Update Ports": "Actualizar Ports", + "Fetch latest ports information.": "Obtener información de ports más reciente.", + "Update PortMaster": "Actualizar PortMaster", + "Force check for a new PortMaster version.": "Chequear forzosamente por una nueva versión de PortMaster.", + "Restore PortMaster": "Restaurar PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Esto restaurará PortMaster a su última versión estable.", + "Release Channel: {channel}": "Canal de Lanzamiento: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Cambiar el canal de lanzamiento de PortMaster, ya sea beta o estable.", + "Enable Alpha Releases": "Activar versiones alfa", + "Change release channel of PortMaster to alpha.": "Cambiar el canal de lanzamientos de PortMaster a alfa.", + "Controller Mode: {controller_mode}": "Modo Controlador: {controller_mode}", + "Toggle between various controller layouts.": "Alterna entre varios layouts de controles.", + "Secret Options": "Opciones Secretas", + "All Ports: ": "Todos los ports: ", + "Show all ports, ignoring requirements.": "Muestra todos los ports, sin importar los requisitos.", + "CWTBE Mode: ": "Modo CWTBE: ", + "Enable gptokeyb2 by default.": "Activar gptokeyb2 por defecto.", + "Delete PortMaster Config": "Eliminar la Configuración de PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "Esto puede romper cosas, no lo toque a menos que sepas lo que estés haciendo.", + "Delete PortMaster Runtimes": "Eliminar Runtimes de PortMaster", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "¿Está seguro de que desea restaurar PortMaster?\n\nEsto requerirá que ejecute un Port especialmente elaborado que se instalará.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Un Port especial ha sido instalado llamado \"Restaurar PortMaster.sh\"\n\nEjecute este Port para restaurar su instalación de PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster se cerrará ahora para que puedas ejecutar el Port de restauración.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "¿Estás seguro de que deseas cambiar al canal de lanzamiento alfa?\n\nPortMaster actualizará o descenderá de versión dependiendo de lo que sea necesario.\n\nESTO PUEDE ROMPER (Y ROMPERÁ) COSAS, HAS SIDO ADVERTIDO", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Estas seguro que quieres cambiar el canal de lanzamiento de {current_channel} a {new_channel}?\n\nPortMaster se actualizará o hará downgrade en consecuencia.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "¿Estás seguro de que quieres actualizar el gamelist.xml?\n\nEsto anulará cualquier artwork o información de ports obtenidos y requerirá una descarga de más de 50 MB.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "¿Estás seguro de que quieres mover los ports de {from_mode} a {to_mode} ?", + "SDCARD 1": "SDCARD 1", + "SDCARD 2": "SDCARD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "¿Estás seguro de que quieres administrar e instalar ports en {to_loc}?\n\nLos ports ya instalados no se moverán.\nPortMaster se reiniciará para que surta efecto.", + "Deleting Runtimes:": "Eliminando Runtimes:", + "Source Manager": "Administrador de Recursos", + "Port Info Popup": "Popup de Info del Port", + "Hide Info": "Esconder Info", + "Reinstall": "Reinstalar", + "Uninstall": "Desinstalar", + "Install": "Instalar", + "Port Info": "Info del Port", + "Show Info": "Mostrar información", + "Are you sure you want to uninstall {port_name}?": "Estas seguro que quieres desinstalar el {port_name}?", + "NO PORTS": "SIN PORTS", + "** NO PORTS FOUND **": "** NO SE HAN ENCONTRADO PORTS **", + "Download ports first.": "Descarga ports primero.", + "Try removing some filters.": "Prueba a eliminar algunos filtros.", + "Ports List": "Lista de Ports", + "Filters": "Filtros", + "Download All": "Descargar Todo", + "Used": "Usado", + "Not Used": "Sin Usar", + "Check": "Comprobado", + "Are you sure you want to download and verify all runtimes?": "Estás seguro que quieres descargar y verificar todos los runtimes?", + "Deleted runtime {runtime}": "Se elimino el runtime {runtime}", + "Disclaimer": "Aviso legal", + "Accept": "Aceptar", + "{theme_name} (Selected)": "{theme_name} (seleccionado)", + "Download": "Descargar", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "¿Quieres cambiar de tema?\n\nTendrás que reiniciar para que surta efecto.", + "Select Colour Scheme": "Elige el Esquema de Color", + "{item_name} (Selected)": "{item_name} (seleccionado)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "¿Quieres cambiar el esquema de color del tema?\n\nTendrás que reiniciar para que surta efecto.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Estas cambiando de la version {old_release_channel} a la version {new_release_channel}.\n\nQuieres hacer el downgrade?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Hay una nueva version de PortMaster ({portmaster_version})\n\nTe gustaría actualizarla?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Te gustaría reinstalar PortMaster?\n\nEsto va a reinstalar desde el canal {release_channel} al canal {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/es_ES.themes.json b/PortMaster/pylibs/locales/es_ES.themes.json new file mode 100644 index 00000000..c8ddd2ec --- /dev/null +++ b/PortMaster/pylibs/locales/es_ES.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Hora: {system.time_24hr}\nBatería: {system.battery_level}\nEspacio libre: {system.free_space}", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Géneros: {port_info.genres} Tamaño de Descarga: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports Disponibles: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltros: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Espacio Libre: {system.free_space}\n\nPorteador: {port_info.porter}\nGeneros: {port_info.genres}\nTamaño de Descarga: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstrucciones: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nEspacio libre: {system.free_space} / {system.total_space} total\nHora: {system.time_24hr}\nBatería: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Creador: {theme_info.creator}\nEstado: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nEspacio libre: {system.free_space}", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtros: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Géneros: {port_info.genres}\nTamaño de Descarga: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Tiempo: {system.time_24hr} Batería: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Título: {port_info.title}\nPorteador: {port_info.porter}\nGéneros: {port_info.genres}\nTamaño de Descarga: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescripción: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports Disponibles: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltros: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Géneros: {port_info.genres}\n{if:port_info.install_size}Tamaño de Instalación: {port_info.install_size}{else}Tamaño de Descarga: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Tiempo: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDispositivo: {system.device_name}\nBatería: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nEspacio: {system.free_space} libre / {system.total_space} total", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porteado por: {port_info.porter}\n• Géneros: {port_info.genres}\n• Descripción: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Géneros: {port_info.genres}\n• Tamaño descargado: {port_info.download_size}{if:port_info.runtime}\n• Tiempo de ejecución: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descripción: {port_info.description}\n• Instrucciones:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Creador: {theme_info.creator}\n• Estado: {theme_info.status}\n• Descripción:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nombre: {runtime_info.name}\nEstado: {runtime_info.status}\nPorts: {runtime_info.ports}\nTamaño de Descarga: {runtime_info.download_size}\nTamaño de Instalación: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/es_ES/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/es_ES/LC_MESSAGES/messages.po deleted file mode 100644 index d256cbfb..00000000 --- a/PortMaster/pylibs/locales/es_ES/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Spanish\n" -"Language: es_ES\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: es-ES\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Danés" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Alemán" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Inglés" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Español" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finlandés" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Francés" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italiano" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japonés" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Coreano" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Holandés" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polaco" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugués (Portugal)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugués (Brasil)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Ruso" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Sueco" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ucraniano" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chino Simplificado" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "IP Desconocida" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/D" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** SIN PORT**" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Listo para Funcionar" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Requiere Archivos" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Instalado" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "No Disponible" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Actualización Disponible" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "No Instalado" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Aceptar" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Cancelar" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Obteniendo los últimos archivos de gameinfo.xml y portadas de imágenes." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Descargando gameinfo.xml para {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Instalando {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Desinstalando {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Actualizando todas las fuentes de ports:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Checkeando {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Estas cambiando de la version {old_release_channel} a la version {new_release_channel}.\n\n" -"Quieres hacer el downgrade?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Hay una nueva version de PortMaster ({portmaster_version})\n\n" -"Te gustaría actualizarla?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Te gustaría reinstalar PortMaster?\n\n" -"Esto va a reinstalar desde el canal {release_channel} al canal {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Se han agregado o eliminado ports, actualiza la lista para ver los nuevos juegos o eliminar los juegos desinstalados." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Cargando..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Error al validar la descarga." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Cargando información." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Obteniendo los últimos ports destacados." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Cargando Fuentes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Cargando Ports." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Instalando Tema {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Instalando" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Tema {download_name!r} instalado correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Instalando {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Tema {download_name!r} instalado correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Instalación fallida, eliminando archivos..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "La instalación del Port {download_name} ha fallado." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Port {download_name!r} y {runtime_name!r} instalados correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "¡¡Port {download_name!r} instalado correctamente, pero la instalación de {runtime_name!r} ha fallado!!\n\n" -"Reintenta la instalación o revisa la wiki para obtener ayuda." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "El Port {runtime} contiene un tiempo de ejecución defectuoso, el juego puede no ejecutarse correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "El Port {runtime} contiene un tiempo de ejecución defectuoso, el juego puede no ejecutarse correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Verificado" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Sin verificar" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Dañado" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "No se puede descargar un tiempo de ejecución en modo sin conexión." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Verificando el runtime {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Verificando" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "No se ha podido encontrar una descarga para {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "No se puede descargar {runtime} en {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "Se verificó el {runtime} exitosamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} verificado." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Actualizando el {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "El runtime {runtime} esta dañado, reinstalando." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Descargando tiempo de ejecución {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "No se puede descargar {runtime}, es posible que el juego no funcione correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} Descargado correctamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "No se puede descargar en modo desconectado." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "No se ha podido encontrar una fuente para {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Port desconocido {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Eliminando {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} Desinstalado correctamente" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n" -"Para hacerlo:\n\n" -"MENÚ > UTILIDADES > REESCANEAR DIRECTORIO DE ROMS" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Caché Obsoleta." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Primera verificación." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Actualización automática." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Actualizando" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Ya está actualizado" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Obteniendo información más reciente" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Hecho." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "No se puede encontrar {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "No se ha podido encontrar la información de verificación para {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "No se puede descargar la información de verificación para {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "¡Éxito!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Obteniendo información" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Obteniendo los ports más recientes" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Obteniendo información." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "No se puede descargar el archivo de verificación." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "No se pueden descargar archivos de tipo no zip." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "No se ha podido descargar el archivo. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Descargando {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Descargando archivo." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Descarga fallida: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Validación de archivo correcta." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Error al validar la descarga." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Menú Principal" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Aviso legal" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Aceptar" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Salir" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Estás seguro que deseas salir de PortMaster?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Ports Destacados" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Lista de ports supervisados a mano" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Todos los ports" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Lista de todos los ports disponibles en PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Ports listos para ejecutarse" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Lista de todos los ports que estan listos para jugar!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Administrar Ports" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Actualizar / Desinstalar Ports" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Opciones" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Opciones de PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Salir" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Salir de PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Entrar" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Modo Secreto {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Habilitado" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Deshabilitado" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Menu de Opciones" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interfaz" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Seleccionar idioma" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Elige el idioma que PortMaster usa." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Seleccionar Tema" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Elige el tema de PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Seleccione el Esquema de Color" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Elige un esquema de color para PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Audio" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Música: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Activa o Desactiva la música de fondo de PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Efectos: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Habilita o Deshabilita los efectos de sonido en PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Metadatos del Port" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Actualización de metadatos: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Auto" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manual" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster actualiza los metadatos de los Ports instalados para que muestre imágenes y descripciones al navegar por los Ports del dispositivo." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Actualizar metadatos" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Actualiza manualmente los metadatos del Port con información faltante/actualizada y artwork." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Ubicación de Ports: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Sección de Roms" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Pestaña de ports" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Ubicación donde deberían instalarse los ports." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Sistema" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Administrador de Runtimes" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Administrar runtimes del port." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Ports experimentales: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Ocultar o mostrar los ports experimentales." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Actualizar Ports" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Obtener información de ports más reciente." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Actualizar PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Chequear forzosamente por una nueva versión de PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Restaurar PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Esto restaurará PortMaster a su última versión estable." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Canal de Lanzamiento: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Cambiar el canal de lanzamiento de PortMaster, ya sea beta o estable." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Activar versiones alfa" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Cambiar el canal de lanzamientos de PortMaster a alfa." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Modo Controlador: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Alterna entre varios layouts de controles." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Opciones Secretas" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Todos los ports: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Muestra todos los ports, sin importar los requisitos." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Modo CWTBE: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Activar gptokeyb2 por defecto." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Eliminar la Configuración de PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Esto puede romper cosas, no lo toque a menos que sepas lo que estés haciendo." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Eliminar Runtimes de PortMaster" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Atrás" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "¿Está seguro de que desea restaurar PortMaster?\n\n" -"Esto requerirá que ejecute un Port especialmente elaborado que se instalará." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Un Port especial ha sido instalado llamado \"Restaurar PortMaster.sh\"\n\n" -"Ejecute este Port para restaurar su instalación de PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster se cerrará ahora para que puedas ejecutar el Port de restauración." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "¿Estás seguro de que deseas cambiar al canal de lanzamiento alfa?\n\n" -"PortMaster actualizará o descenderá de versión dependiendo de lo que sea necesario.\n\n" -"ESTO PUEDE ROMPER (Y ROMPERÁ) COSAS, HAS SIDO ADVERTIDO" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Estas seguro que quieres cambiar el canal de lanzamiento de {current_channel} a {new_channel}?\n\n" -"PortMaster se actualizará o hará downgrade en consecuencia." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "¿Estás seguro de que quieres actualizar el gamelist.xml?\n\n" -"Esto anulará cualquier artwork o información de ports obtenidos y requerirá una descarga de más de 50 MB." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "¿Estás seguro de que quieres mover los ports de {from_mode} a {to_mode} ?" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SDCARD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SDCARD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "¿Estás seguro de que quieres administrar e instalar ports en {to_loc}?\n\n" -"Los ports ya instalados no se moverán.\n" -"PortMaster se reiniciará para que surta efecto." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Eliminando Runtimes:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Administrador de Recursos" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Descargar Todo" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Usado" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Sin Usar" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Comprobado" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Desinstalar" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Instalar" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Estás seguro que quieres descargar y verificar todos los runtimes?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Se elimino el runtime {runtime}" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (seleccionado)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Seleccionar" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Descargar" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "¿Quieres cambiar de tema?\n\n" -"Tendrás que reiniciar para que surta efecto." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Elige el Esquema de Color" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (seleccionado)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "¿Quieres cambiar el esquema de color del tema?\n\n" -"Tendrás que reiniciar para que surta efecto." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Eligir Idioma" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (seleccionado)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "¿Quieres cambiar de idioma?\n\n" -"Tendrás que reiniciar para que surta efecto." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Eliminar" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Mayúsculas" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Espacio" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Minúsculas" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "No se encontraron ports destacados, se requiere conexión a internet." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "SIN PORTS" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** NO SE HAN ENCONTRADO PORTS **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Descarga ports primero." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Prueba a eliminar algunos filtros." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Mostrar información" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Lista de Ports" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filtros" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Popup de Info del Port" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Esconder Info" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Reinstalar" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Info del Port" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Estas seguro que quieres desinstalar el {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Escena de Filtros" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabético" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Recientemente Añadidos" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Recientemente Actualizados" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Acción" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Aventura" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Casino/Cartas" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Disparos en Primera Persona" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Plataformero" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Puzzle" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Carreras" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Ritmo" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Juegos de Rol" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulación" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Deportes" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Estrategia" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Novela Visual" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Otros" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Archivos Requeridos" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Experimental" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Ports Rotos" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Juego gratis, todos los archivos incluidos." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Archivos de demostración incluidos." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Se necesitan archivos externos gratis." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Se necesitan archivos externos pagos." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Runtime" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32bit" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64bit" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64bit" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Ordenar:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Limpiar Filtros" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Géneros:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Atributos:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Arquitectura:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Disponibilidad:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porters:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Mensajes" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "¿Está seguro de que quiere cancelar?" - diff --git a/PortMaster/pylibs/locales/es_ES/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/es_ES/LC_MESSAGES/themes.po deleted file mode 100644 index 7b356dc2..00000000 --- a/PortMaster/pylibs/locales/es_ES/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Spanish\n" -"Language: es_ES\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: es-ES\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Géneros: {port_info.genres} Tamaño de Descarga: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Ports Disponibles: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtros: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Espacio Libre: {system.free_space}\n\n" -"Porteador: {port_info.porter}\n" -"Generos: {port_info.genres}\n" -"Tamaño de Descarga: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instrucciones: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Espacio libre: {system.free_space} / {system.total_space} total\n" -"Hora: {system.time_24hr}\n" -"Batería: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Creador: {theme_info.creator}\n" -"Estado: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Tiempo: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Dispositivo: {system.device_name}\n" -"Batería: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Espacio libre: {system.free_space}" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filtros: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Géneros: {port_info.genres}\n" -"Tamaño de Descarga: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Tiempo: {system.time_24hr} Batería: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Espacio: {system.free_space} libre / {system.total_space} total" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porteado por: {port_info.porter}\n" -"• Géneros: {port_info.genres}\n" -"• Descripción: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Géneros: {port_info.genres}\n" -"• Tamaño descargado: {port_info.download_size}{if:port_info.runtime}\n" -"• Tiempo de ejecución: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Descripción: {port_info.description}\n" -"• Instrucciones:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Creador: {theme_info.creator}\n" -"• Estado: {theme_info.status}\n" -"• Descripción:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Nombre: {runtime_info.name}\n" -"Estado: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Tamaño de Descarga: {runtime_info.download_size}\n" -"Tamaño de Instalación: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Hora: {system.time_24hr}\n" -"Batería: {system.battery_level}\n" -"Espacio libre: {system.free_space}" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Título: {port_info.title}\n" -"Porteador: {port_info.porter}\n" -"Géneros: {port_info.genres}\n" -"Tamaño de Descarga: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Descripción: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Ports Disponibles: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtros: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Géneros: {port_info.genres}\n" -"{if:port_info.install_size}Tamaño de Instalación: {port_info.install_size}{else}Tamaño de Descarga: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/fr_FR.json b/PortMaster/pylibs/locales/fr_FR.json new file mode 100644 index 00000000..aa265623 --- /dev/null +++ b/PortMaster/pylibs/locales/fr_FR.json @@ -0,0 +1,254 @@ +{ + "Loading...": "Chargement...", + "Loading Sources.": "Chargement des sources.", + "Download validation failed.": "Échec de la validation du téléchargement.", + "Loading Info.": "Chargement des informations.", + "Fetching latest featured ports.": "Récupération des derniers ports recommandés.", + "Installing Theme {download_name}.": "Installation du thème {download_name}.", + "Installing": "Installation en cours", + "Theme {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", + "Port {download_name} installed failed.": "Échec de l'installation du port {download_name}.", + "Installing {download_name}.": "Installation de {download_name}.", + "Port {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", + "Installation failed, removing files...": "Échec de l'installation, suppression des fichiers en cours...", + "Okay": "D'accord", + "Cancel": "Annuler", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} et {runtime_name!r} installé avec succès.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Le port {download_name!r} a été installé avec succès, mais {runtime_name!r} n'a pas pu être installé!!\n\nRéinstallez pour essayer à nouveau, ou consultez le wiki pour obtenir de l'aide.", + "Unable to download in offline mode.": "Impossible de télécharger un runtime en mode hors ligne.", + "Unable to find a source for {port_name}": "Impossible de trouver une source pour {port_name}", + "Unknown port {port_name}": "Port inconnu {port_name}", + "Removing {port_name}": "Suppression de {port_name}", + "Successfully uninstalled {port_name}": "{port_name} désinstallé avec succès", + "Loading Ports.": "Chargement des ports.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Le port {runtime} contient un runtime corrompu, le jeu pourrait ne pas s'exécuter correctement.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Le port {runtime} contient un runtime corrompu, le jeu pourrait ne pas s'exécuter correctement.", + "Not Installed": "Non installé", + "Update Available": "Nouvelle mise à jour disponible", + "Verified": "Vérifié", + "Unverified": "Non vérifié", + "Broken": "Cassé", + "Unable do download a runtime when in offline mode.": "Impossible de télécharger un runtime en mode hors ligne.", + "Verifying runtime {runtime}": "Vérification du runtime {runtime}", + "Verifying": "Vérification", + "Unable to find a download for {runtime}.": "Impossible de trouver un téléchargement pour {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Impossible de télécharger {runtime} dans {device_arch}.", + "Verified {runtime} successfully.": "{runtime} vérifié avec succès.", + "Verified {runtime}.": "{runtime} vérifié.", + "Updating {runtime}.": "Mise à jour de {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Le runtime {runtime} est cassé, réinstallation en cours.", + "Downloading runtime {runtime}.": "Téléchargement du runtime {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Impossible de télécharger {runtime}, le jeu pourrait ne pas s'exécuter correctement.", + "Successfully downloaded {runtime}.": "Téléchargement de {runtime} réussi.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nPour ce faire :\nMENU -> UTILITIES -> Rescan Rom Directory", + "Unable to download verification file.": "Impossible de télécharger le fichier de vérification.", + "Unable to download non zip files.": "Impossible de télécharger des fichiers non compressés.", + "Cache out of date.": "Cache obsolète.", + "First check.": "Première vérification.", + "Auto Update.": "Mise à jour automatique.", + "Updating": "Mise à jour en cours", + "Up to date already": "Déjà à jour", + "Fetching latest info": "Récupération des dernières informations", + "Done.": "Terminé.", + "Fetching info": "Récupération des informations", + "Unable to find {port_name}.": "Impossible de trouver {port_name}.", + "Unable to download file. [{status_code}]": "Impossible de télécharger le fichier. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Téléchargement de {file_url} - ({total_length_mb})", + "Downloading file.": "Téléchargement du fichier.", + "Download failed: {err}": "Échec du téléchargement : {err}", + "Passed file validation.": "Validation du fichier réussie.", + "Unable to validate download.": "Impossible de valider le téléchargement.", + "Unknown IP": "IP inconnue", + "N/A": "N/A", + "Fetching latest gameinfo.xml files and cover images.": "Récupération des derniers fichiers gameinfo.xml et des images de couverture.", + "Downloading gameinfo.xml for {source_name}": "Téléchargement de gameinfo.xml en cours pour {source_name}", + "Installing {port_name}": "Installation de {port_name}", + "Uninstalling {port_name}": "Désinstallation de {port_name}", + "Updating all port sources:": "Mise à jour de toutes les sources de ports :", + "Checking {runtime_name}": "Vérification de {runtime_name}", + "** NO PORT **": "** AUCUN RÉSULTAT **", + "Ready to Run": "Prêt à Exécuter", + "Requires Files": "Fichiers Requis", + "Installed": "Installé", + "Missing": "Manquant", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Les ports ont été ajoutés ou supprimés, mettez à jour votre liste de jeux pour voir les nouveaux jeux ou supprimez ceux désinstallés.", + "Danish": "Danois", + "German": "Allemand", + "English": "Anglais", + "Spanish": "Espagnol", + "Finnish": "Finnois", + "French": "Français", + "Italian": "Italien", + "Japanese": "Japonais", + "Korean": "Coréen", + "Dutch": "Néerlandais", + "Polish": "Polonais", + "Portuguese (Portugal)": "Portugais (Portugal)", + "Portuguese (Brazil)": "Portugais (Brésil)", + "Russian": "Russe", + "Swedish": "Suédois", + "Ukrainian": "Ukrainien", + "Chinese Simplified": "Chinois simplifié", + "Messages": "Messages", + "Are you sure you want to cancel?": "Êtes-vous sûr de vouloir annuler ?", + "Featured Ports": "Ports recommandés", + "Select": "Sélectionner", + "Back": "Retour", + "No featured ports found, internet connection is required.": "Aucun port en vedette trouvé, une connexion internet est requise.", + "Filters Scene": "Filtre Scène", + "Alphabetical": "Ordre alphabétique", + "Recently Added": "Ajouts récents", + "Recently Updated": "Récemment mis à jour", + "Action": "Action", + "Adventure": "Aventure", + "Arcade": "Arcade", + "Casino/Card": "Casino/Jeu de cartes", + "First Person Shooter": "Tir à la première personne", + "Platformer": "Jeu de plates-formes", + "Puzzle": "Réflexion", + "Racing": "Jeu de course", + "Rhythm": "Rythme", + "Role Playing Game": "Jeu de rôle", + "Simulation": "Simulation", + "Sports": "Sports", + "Strategy": "Stratégie", + "Visual Novel": "Visual Novel", + "Other": "Autres", + "Files Required": "Fichiers nécessaires", + "Experimental": "Expérimental", + "Broken Ports": "Ports endommagés", + "Free game, all files included.": "Jeu gratuit, tous les fichiers sont inclus.", + "Demo files included.": "Fichiers démo inclus.", + "Free external assets needed.": "Des ressources externes gratuites sont nécessaires.", + "Paid external assets needed.": "Des ressources externes payantes sont nécessaires.", + "{runtime_name} Runtime": "{runtime_name} runtime", + "ARM 32bit": "ARM 32bits", + "ARM 64bit": "ARM 64bits", + "x86 64bit": "x86 64bits", + "Sort:": "Ordre :", + "Clear Filters": "Réinitialiser les filtres", + "Genres:": "Genres :", + "Attributes:": "Attributs :", + "Architecture:": "Architecture :", + "Availability:": "Disponibilité :", + "Porters:": "Porteurs :", + "Delete": "Supprimer", + "Upper Case": "Majuscule", + "Space": "Espace", + "Enter": "Entrer", + "Lower Case": "Minuscule", + "Language Select": "Sélection de la langue", + "{lang_name} (Selected)": "{lang_name} (Sélectionné)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Voulez-vous changer de langue ?\n\nVous devrez redémarrer pour que cela prenne effet.", + "Main Menu": "Menu principal", + "Hand curated lists of ports": "Ports sélectionnés", + "All Ports": "Tous les ports", + "List all ports available on PortMaster.": "Lister tous les ports disponibles sur PortMaster.", + "Ready to Run Ports": "Ports Prêts à Exécuter", + "List all ports that are ready to play!": "Lister tous les ports qui sont prêts à jouer !", + "Manage Ports": "Gestion des ports", + "Update / Uninstall Ports": "Mise à jour / Désinstallation des ports", + "Options": "Options", + "PortMaster Options": "Options de PortMaster", + "Exit": "Quitter", + "Quit PortMaster": "Quitter PortMaster", + "Quit": "Quitter", + "Secret Mode {secret_mode}": "Mode secret {secret_mode}", + "Enabled": "Activé", + "Disabled": "Désactivé", + "Are you sure you want to exit PortMaster?": "Êtes-vous sûr de vouloir quitter PortMaster ?", + "Options Menu": "Options", + "Interface": "Interface", + "Choose Language": "Choisir la Langue", + "Select the language PortMaster uses.": "Sélectionnez la langue utilisée par PortMaster.", + "Select Theme": "Sélection du thème", + "Select a theme for PortMaster.": "Sélectionnez un thème pour PortMaster.", + "Select Color Scheme": "Sélection de la palette de couleurs", + "Select a colour scheme for PortMaster": "Sélectionnez un schéma de couleurs pour PortMaster", + "Audio": "Audio", + "Music: ": "Musique :", + "Enable or Disable background music in PortMaster.": "Activer ou désactiver la musique de fond dans PortMaster.", + "Sound FX: ": "Effets sonores :", + "Enable or Disable soundfx in PortMaster.": "Activer ou désactiver les effets sonores dans PortMaster.", + "Port Metadata": "Métadonnées du port", + "Metadata Update: ": "Mise à jour des métadonnées :", + "Auto": "Auto", + "Manual": "Manuel", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster met à jour les métadonnées pour les ports installés, pour que les illustrations et les descriptions de port soient affichées lors de la navigation sur votre appareil.", + "Metadata Refresh": "Mise à jour des métadonnées", + "Manually update port metadata with missing/updated information and artwork.": "Mettre à jour manuellement les métadonnées du port avec les informations manquantes/mises à jour et les illustrations.", + "Ports Location: ": "Emplacement des ports :", + "Roms section": "Section des roms", + "Ports tab": "Onglet des ports", + "Location where ports should be installed to.": "Emplacement où les ports doivent être installés.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "Système", + "Runtime Manager": "Gestion des runtimes", + "Manage port runtimes.": "Gérer les runtimes des ports.", + "Experimental Ports: ": "Ports expérimentaux :", + "Show or hide experimental ports.": "Afficher/Masquer les ports expérimentaux.", + "Update Ports": "Mettre à Jour les Ports", + "Fetch latest ports information.": "Récupérer les dernières informations sur les ports.", + "Update PortMaster": "Mettre à jour PortMaster", + "Force check for a new PortMaster version.": "Forcer la vérification d'une nouvelle version de PortMaster.", + "Restore PortMaster": "Restaurer PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Cela restaurera PortMaster à sa dernière version stable.", + "Release Channel: {channel}": "Canal de publication: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Changer le canal de publication de PortMaster, soit bêta, soit stable.", + "Enable Alpha Releases": "Activer les versions alpha", + "Change release channel of PortMaster to alpha.": "Changer le canal de publication de PortMaster en alpha.", + "Controller Mode: {controller_mode}": "Mode contrôleur : {controller_mode}", + "Toggle between various controller layouts.": "Basculer entre les différentes mises en page du contrôleur.", + "Secret Options": "Options secrètes", + "All Ports: ": "Tous les ports : ", + "Show all ports, ignoring requirements.": "Afficher tous les ports, ignorer les exigences.", + "CWTBE Mode: ": "Mode CWTBE : ", + "Enable gptokeyb2 by default.": "Activer gptokeyb2 par défaut.", + "Delete PortMaster Config": "Supprimer la configuration de PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "Cela peut rendre votre installation inutilisable, n'y touchez pas à moins de savoir ce que vous faites.", + "Delete PortMaster Runtimes": "Supprimer les runtimes de PortMaster", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Êtes-vous sûr de vouloir restaurer PortMaster ?\n\nCela nécessitera que vous exécutiez un port spécialement conçu qui sera installé.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Un port spécial a été installé appelé \"Restorer PortMaster.sh\"\n\nExécutez ce port pour restaurer votre installation PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster va maintenant quitter pour que vous puissiez exécuter le port de restauration.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Êtes-vous sûr de vouloir changer le canal de publication de PortMaster en alpha ?\n\nPortMaster effectuera une mise à jour ou passera à une version antérieure en conséquence.\n\nAVERTISSEMENT : CETTE MANIPULATION PEUT ET VA CRÉER DES PROBLÈMES.", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Êtes-vous sûr de vouloir changer le canal de publication de {current_channel} à {new_channel}?\n\nPortMaster va être mis à jour ou rétrogradé en conséquence.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Êtes-vous sûr de vouloir mettre à jour votre gamelist.xml ?\n\nCeci remplacera toutes les informations ou images des ports et nécessitera un téléchargement de plus de 50 Mo.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Êtes-vous sûr de vouloir déplacer les ports de la {from_mode} vers la {to_mode}", + "SDCARD 1": "Carte SD 1", + "SDCARD 2": "Carte SD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Êtes-vous sûr de vouloir gérer et installer des ports sur {to_loc}?\n\nLes ports déjà installés ne seront pas déplacés.\nPortMaster redémarrera pour que cela prenne effet.", + "Deleting Runtimes:": "Suppression des runtimes :", + "Source Manager": "Gestion des sources", + "Port Info Popup": "Popup d'informations sur le port", + "Hide Info": "Masquer les informations", + "Reinstall": "Réinstaller", + "Uninstall": "Désinstaller", + "Install": "Installer", + "Port Info": "Informations sur le port", + "Show Info": "Afficher les Infos", + "Are you sure you want to uninstall {port_name}?": "Êtes-vous sûr de vouloir désinstaller {port_name}?", + "NO PORTS": "AUCUN RÉSULTAT", + "** NO PORTS FOUND **": "** AUCUN RÉSULTAT **", + "Download ports first.": "Téléchargez d'abord les ports.", + "Try removing some filters.": "Essayez de supprimer certains filtres.", + "Ports List": "Liste des Ports", + "Filters": "Filtres", + "Download All": "Tout télécharger", + "Used": "Utilisé", + "Not Used": "Inutilisé", + "Check": "Vérifier", + "Are you sure you want to download and verify all runtimes?": "Êtes-vous sûr de vouloir télécharger et vérifier tous les runtimes ?", + "Deleted runtime {runtime}": "Runtime {runtime} supprimé", + "Disclaimer": "Avertissement", + "Accept": "Accepter", + "{theme_name} (Selected)": "{theme_name} (Sélectionné)", + "Download": "Télécharger", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Voulez-vous changer de thème ?\n\nVous devrez redémarrer pour que ce changement prenne effet.", + "Select Colour Scheme": "Sélection de la palette de couleurs", + "{item_name} (Selected)": "{item_name} (Sélectionné)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Voulez-vous changer le schéma de couleurs du thème ?\n\nVous devrez redémarrer pour que ce changement prenne effet.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Vous passez de l' {old_release_channel} à la version {new_release_channel} .\n\nVoulez-vous rétrograder ?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Il y a une nouvelle version de PortMaster ({portmaster_version})\n\nVoulez-vous mettre à jour ?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Voulez-vous réinstaller PortMaster ?\n\nCela va réinstaller depuis le canal {release_channel} vers {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/fr_FR.themes.json b/PortMaster/pylibs/locales/fr_FR.themes.json new file mode 100644 index 00000000..e330bdef --- /dev/null +++ b/PortMaster/pylibs/locales/fr_FR.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Heure : {system.time_24hr}\nBatterie : {system.battery_level}\nEspace : {system.free_space} disponible", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres : {port_info.genres} Taille de téléchargement : {port_info.download_size}{if:port_info.runtime} Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibles : {ports_list.filter_ports} / {ports_list.total_ports} \nFiltres : {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Espace libre : {system.free_space}\n\nPorter : {port_info.porter}\nGenres : {port_info.genres}\nTaille du téléchargement : {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstructions : {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM : {system.portmaster_version}\\n\nHM : {system.harbourmaster_version}\\n\nEspace : {system.free_space} disponible / {system.total_space} total\\n\nHeure : {system.time_24hr}\\n\nBatterie : {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Créateur : {theme_info.creator}\nStatut : {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM : {system.portmaster_version}\\n\nHM : {system.harbourmaster_version}\\n\nEspace : {system.free_space} disponible", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtres : {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres : {port_info.genres} \nTaille de téléchargement : {port_info.download_size}{if:port_info.runtime} \nRuntime : {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Heure : {system.time_24hr} Batterie : {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Espace libre : {port_info.title}\nPorter : {port_info.porter}\nGenres : {port_info.genres}\nTaille du téléchargement : {port_info.download_size}{if:port_info.runtime}\nRuntime : {port_info.runtime} ({port_info.runtime_status}){endif}\nInstructions : {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibles : {ports_list.filter_ports} / {ports_list.total_ports} \nFiltres : {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres : {port_info.genres}\n{if:port_info.install_size}Taille installée : {port_info.install_size}{else}Taille de téléchargement : {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Heure: {system.time_24hr}\nCFW : {system.cfw_name} ({system.cfw_version})\nPériphérique : {system.device_name}\nBatterie : {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM : {system.portmaster_version}\nHM : {system.harbourmaster_version}\nEspace : {system.free_space} disponible / {system.total_space} total", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porteur : {port_info.porter}\n• Genres : {port_info.genres}\n• Description : {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genres : {port_info.genres}\n• Taille du téléchargement : {port_info.download_size}{if:port_info.runtime}\n• Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description : {port_info.description}\n• Instructions :\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM : {system.portmaster_version}, HM : {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Créateur : {theme_info.creator}\n• Statut : {theme_info.status}\n• Description :\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nom : {runtime_info.name}\nStatut: {runtime_info.status}\nPorts : {runtime_info.ports}\nTaille du téléchargement : {runtime_info.download_size}\nTaille de l'installation : {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/messages.po deleted file mode 100644 index 6da6c16c..00000000 --- a/PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: French\n" -"Language: fr_FR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: fr\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Danois" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Allemand" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Anglais" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Espagnol" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finnois" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Français" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italien" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japonais" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Coréen" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Néerlandais" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polonais" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugais (Portugal)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugais (Brésil)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Russe" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Suédois" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ukrainien" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chinois simplifié" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "IP inconnue" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/A" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** AUCUN RÉSULTAT **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Prêt à Exécuter" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Fichiers Requis" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Installé" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Manquant" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Nouvelle mise à jour disponible" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Non installé" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "D'accord" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Annuler" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Récupération des derniers fichiers gameinfo.xml et des images de couverture." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Téléchargement de gameinfo.xml en cours pour {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Installation de {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Désinstallation de {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Mise à jour de toutes les sources de ports :" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Vérification de {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Vous passez de l' {old_release_channel} à la version {new_release_channel} .\n\n" -"Voulez-vous rétrograder ?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Il y a une nouvelle version de PortMaster ({portmaster_version})\n\n" -"Voulez-vous mettre à jour ?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Voulez-vous réinstaller PortMaster ?\n\n" -"Cela va réinstaller depuis le canal {release_channel} vers {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Les ports ont été ajoutés ou supprimés, mettez à jour votre liste de jeux pour voir les nouveaux jeux ou supprimez ceux désinstallés." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Chargement..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Échec de la validation du téléchargement." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Chargement des informations." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Récupération des derniers ports recommandés." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Chargement des sources." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Chargement des ports." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Installation du thème {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Installation en cours" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Le thème {download_name!r} a été installé avec succès." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Installation de {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Le thème {download_name!r} a été installé avec succès." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Échec de l'installation, suppression des fichiers en cours..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Échec de l'installation du port {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Port {download_name!r} et {runtime_name!r} installé avec succès." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Le port {download_name!r} a été installé avec succès, mais {runtime_name!r} n'a pas pu être installé!!\n\n" -"Réinstallez pour essayer à nouveau, ou consultez le wiki pour obtenir de l'aide." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Le port {runtime} contient un runtime corrompu, le jeu pourrait ne pas s'exécuter correctement." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Le port {runtime} contient un runtime corrompu, le jeu pourrait ne pas s'exécuter correctement." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Vérifié" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Non vérifié" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Cassé" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Impossible de télécharger un runtime en mode hors ligne." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Vérification du runtime {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Vérification" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Impossible de trouver un téléchargement pour {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Impossible de télécharger {runtime} dans {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} vérifié avec succès." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} vérifié." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Mise à jour de {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Le runtime {runtime} est cassé, réinstallation en cours." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Téléchargement du runtime {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Impossible de télécharger {runtime}, le jeu pourrait ne pas s'exécuter correctement." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "Téléchargement de {runtime} réussi." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Impossible de télécharger un runtime en mode hors ligne." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Impossible de trouver une source pour {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Port inconnu {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Suppression de {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} désinstallé avec succès" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Pour ce faire :\n" -"MENU -> UTILITIES -> Rescan Rom Directory" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache obsolète." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Première vérification." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Mise à jour automatique." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Mise à jour en cours" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Déjà à jour" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Récupération des dernières informations" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Terminé." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Impossible de trouver {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Impossible de trouver les informations de vérification pour {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Impossible de télécharger les informations de vérification pour {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Succès !" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Récupération des informations" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name} : Récupération" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Récupération des informations." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Impossible de télécharger le fichier de vérification." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Impossible de télécharger des fichiers non compressés." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Impossible de télécharger le fichier. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Téléchargement de {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Téléchargement du fichier." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Échec du téléchargement : {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Validation du fichier réussie." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Impossible de valider le téléchargement." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Menu principal" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Avertissement" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Accepter" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Quitter" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Êtes-vous sûr de vouloir quitter PortMaster ?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Ports recommandés" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Ports sélectionnés" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Tous les ports" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Lister tous les ports disponibles sur PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Ports Prêts à Exécuter" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Lister tous les ports qui sont prêts à jouer !" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Gestion des ports" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Mise à jour / Désinstallation des ports" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Options" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Options de PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Quitter" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Quitter PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Entrer" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Mode secret {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Activé" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Désactivé" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Options" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interface" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Choisir la Langue" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Sélectionnez la langue utilisée par PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Sélection du thème" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Sélectionnez un thème pour PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Sélection de la palette de couleurs" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Sélectionnez un schéma de couleurs pour PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Audio" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Musique :" - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Activer ou désactiver la musique de fond dans PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Effets sonores :" - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Activer ou désactiver les effets sonores dans PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Métadonnées du port" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Mise à jour des métadonnées :" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Auto" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manuel" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster met à jour les métadonnées pour les ports installés, pour que les illustrations et les descriptions de port soient affichées lors de la navigation sur votre appareil." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Mise à jour des métadonnées" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Mettre à jour manuellement les métadonnées du port avec les informations manquantes/mises à jour et les illustrations." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Emplacement des ports :" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Section des roms" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Onglet des ports" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Emplacement où les ports doivent être installés." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Système" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Gestion des runtimes" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Gérer les runtimes des ports." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Ports expérimentaux :" - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Afficher/Masquer les ports expérimentaux." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Mettre à Jour les Ports" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Récupérer les dernières informations sur les ports." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Mettre à jour PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Forcer la vérification d'une nouvelle version de PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Restaurer PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Cela restaurera PortMaster à sa dernière version stable." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Canal de publication: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Changer le canal de publication de PortMaster, soit bêta, soit stable." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Activer les versions alpha" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Changer le canal de publication de PortMaster en alpha." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Mode contrôleur : {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Basculer entre les différentes mises en page du contrôleur." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Options secrètes" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Tous les ports : " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Afficher tous les ports, ignorer les exigences." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Mode CWTBE : " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Activer gptokeyb2 par défaut." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Supprimer la configuration de PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Cela peut rendre votre installation inutilisable, n'y touchez pas à moins de savoir ce que vous faites." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Supprimer les runtimes de PortMaster" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Retour" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Êtes-vous sûr de vouloir restaurer PortMaster ?\n\n" -"Cela nécessitera que vous exécutiez un port spécialement conçu qui sera installé." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Un port spécial a été installé appelé \"Restorer PortMaster.sh\"\n\n" -"Exécutez ce port pour restaurer votre installation PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster va maintenant quitter pour que vous puissiez exécuter le port de restauration." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Êtes-vous sûr de vouloir changer le canal de publication de PortMaster en alpha ?\n\n" -"PortMaster effectuera une mise à jour ou passera à une version antérieure en conséquence.\n\n" -"AVERTISSEMENT : CETTE MANIPULATION PEUT ET VA CRÉER DES PROBLÈMES." - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Êtes-vous sûr de vouloir changer le canal de publication de {current_channel} à {new_channel}?\n\n" -"PortMaster va être mis à jour ou rétrogradé en conséquence." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Êtes-vous sûr de vouloir mettre à jour votre gamelist.xml ?\n\n" -"Ceci remplacera toutes les informations ou images des ports et nécessitera un téléchargement de plus de 50 Mo." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Êtes-vous sûr de vouloir déplacer les ports de la {from_mode} vers la {to_mode}" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "Carte SD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "Carte SD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Êtes-vous sûr de vouloir gérer et installer des ports sur {to_loc}?\n\n" -"Les ports déjà installés ne seront pas déplacés.\n" -"PortMaster redémarrera pour que cela prenne effet." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Suppression des runtimes :" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Gestion des sources" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Tout télécharger" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Utilisé" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Inutilisé" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Vérifier" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Désinstaller" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Installer" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Êtes-vous sûr de vouloir télécharger et vérifier tous les runtimes ?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Runtime {runtime} supprimé" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Sélectionné)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Sélectionner" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Télécharger" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Voulez-vous changer de thème ?\n\n" -"Vous devrez redémarrer pour que ce changement prenne effet." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Sélection de la palette de couleurs" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Sélectionné)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Voulez-vous changer le schéma de couleurs du thème ?\n\n" -"Vous devrez redémarrer pour que ce changement prenne effet." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Sélection de la langue" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Sélectionné)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Voulez-vous changer de langue ?\n\n" -"Vous devrez redémarrer pour que cela prenne effet." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Supprimer" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Majuscule" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Espace" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Minuscule" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Aucun port en vedette trouvé, une connexion internet est requise." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "AUCUN RÉSULTAT" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** AUCUN RÉSULTAT **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Téléchargez d'abord les ports." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Essayez de supprimer certains filtres." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Afficher les Infos" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Liste des Ports" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filtres" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Popup d'informations sur le port" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Masquer les informations" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Réinstaller" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Informations sur le port" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Êtes-vous sûr de vouloir désinstaller {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Filtre Scène" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Ordre alphabétique" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Ajouts récents" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Récemment mis à jour" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Action" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Aventure" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Casino/Jeu de cartes" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Tir à la première personne" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Jeu de plates-formes" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Réflexion" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Jeu de course" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Rythme" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Jeu de rôle" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulation" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sports" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Stratégie" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Visual Novel" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Autres" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Fichiers nécessaires" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Expérimental" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Ports endommagés" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Jeu gratuit, tous les fichiers sont inclus." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Fichiers démo inclus." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Des ressources externes gratuites sont nécessaires." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Des ressources externes payantes sont nécessaires." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} runtime" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32bits" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64bits" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64bits" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Ordre :" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Réinitialiser les filtres" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Genres :" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Attributs :" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Architecture :" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Disponibilité :" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porteurs :" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Messages" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Êtes-vous sûr de vouloir annuler ?" - diff --git a/PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/themes.po deleted file mode 100644 index 36436193..00000000 --- a/PortMaster/pylibs/locales/fr_FR/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: French\n" -"Language: fr_FR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: fr\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres : {port_info.genres} Taille de téléchargement : {port_info.download_size}{if:port_info.runtime} Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibles : {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtres : {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Espace libre : {system.free_space}\n\n" -"Porter : {port_info.porter}\n" -"Genres : {port_info.genres}\n" -"Taille du téléchargement : {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instructions : {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM : {system.portmaster_version}\\n\n" -"HM : {system.harbourmaster_version}\\n\n" -"Espace : {system.free_space} disponible / {system.total_space} total\\n\n" -"Heure : {system.time_24hr}\\n\n" -"Batterie : {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Créateur : {theme_info.creator}\n" -"Statut : {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Heure: {system.time_24hr}\n" -"CFW : {system.cfw_name} ({system.cfw_version})\n" -"Périphérique : {system.device_name}\n" -"Batterie : {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM : {system.portmaster_version}\\n\n" -"HM : {system.harbourmaster_version}\\n\n" -"Espace : {system.free_space} disponible" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filtres : {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres : {port_info.genres} \n" -"Taille de téléchargement : {port_info.download_size}{if:port_info.runtime} \n" -"Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Heure : {system.time_24hr} Batterie : {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM : {system.portmaster_version}\n" -"HM : {system.harbourmaster_version}\n" -"Espace : {system.free_space} disponible / {system.total_space} total" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porteur : {port_info.porter}\n" -"• Genres : {port_info.genres}\n" -"• Description : {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Genres : {port_info.genres}\n" -"• Taille du téléchargement : {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description : {port_info.description}\n" -"• Instructions :\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM : {system.portmaster_version}, HM : {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Créateur : {theme_info.creator}\n" -"• Statut : {theme_info.status}\n" -"• Description :\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Nom : {runtime_info.name}\n" -"Statut: {runtime_info.status}\n" -"Ports : {runtime_info.ports}\n" -"Taille du téléchargement : {runtime_info.download_size}\n" -"Taille de l'installation : {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Heure : {system.time_24hr}\n" -"Batterie : {system.battery_level}\n" -"Espace : {system.free_space} disponible" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Espace libre : {port_info.title}\n" -"Porter : {port_info.porter}\n" -"Genres : {port_info.genres}\n" -"Taille du téléchargement : {port_info.download_size}{if:port_info.runtime}\n" -"Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Instructions : {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibles : {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtres : {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres : {port_info.genres}\n" -"{if:port_info.install_size}Taille installée : {port_info.install_size}{else}Taille de téléchargement : {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/it_IT.json b/PortMaster/pylibs/locales/it_IT.json new file mode 100644 index 00000000..9bf8f192 --- /dev/null +++ b/PortMaster/pylibs/locales/it_IT.json @@ -0,0 +1,208 @@ +{ + "Loading...": "Caricamento...", + "Loading Sources.": "Caricamento Fonti.", + "Download validation failed.": "Validazione del download fallita.", + "Loading Info.": "Caricamento Informazioni", + "Fetching latest featured ports.": "Recupero informazioni sugli ultimi ports Raccomandati dal Team.", + "Installing Theme {download_name}.": "Installo Tema {download_name}.", + "Installing": "Installo", + "Theme {download_name!r} installed successfully.": "Tema {download_name!r} installato con successo.", + "Port {download_name} installed failed.": "{download_name} fallisce l'installazione.", + "Installing {download_name}.": "Installazione {download_name}.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} installato con successo.", + "Installation failed, removing files...": "Installazione fallita, rimozione file in corso...", + "Okay": "Ok", + "Cancel": "Annulla", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "{download_name!r} e {runtime_name!r} sono stati installati con successo", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} e {runtime_name!r} installazione fallita!\n\nRiprova o controlla la Fiji per ulteriori informazioni.", + "Unable to find a source for {port_name}": "Impossibile trovare una fonte per {port_name}", + "Unknown port {port_name}": "Port sconosciuto {port_name}", + "Removing {port_name}": "Rimuovo {port_name}", + "Successfully uninstalled {port_name}": "{port_name} disinstallato con successo.", + "Loading Ports.": "Caricamento Ports.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} sembra avere una runtime corrota, il gioco potrebbe avere problemi", + "Not Installed": "Non Installato", + "Update Available": "Aggiornamento Disponibile", + "Verified": "Verificato", + "Unverified": "Non verificato", + "Broken": "Corrotto", + "Unable do download a runtime when in offline mode.": "Impossibile scaricare la runtime in modalità offline", + "Verifying runtime {runtime}": "Verifico runtime {runtime}", + "Verifying": "Verifica in corso", + "Unable to find a download for {runtime}.": "Impossibile trovare il download per {runtime}", + "Verified {runtime} successfully.": "{runtime} verificata con successo.", + "Verified {runtime}.": "{runtime} Verificata.", + "Updating {runtime}.": "Aggiornamento {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Runtime {runtime} è corrotta, reinstallazione.", + "Downloading runtime {runtime}.": "Scarico {runtime}", + "Unable to download {runtime}, game may not run correctly.": "Impossibile scaricare la runtime {runtime}, il gioco potrebbe presentare dei problemi.", + "Successfully downloaded {runtime}.": "Scaricato con successo {runtime}", + "Unable to download verification file.": "Non posso scaricare i file di verifica", + "Unable to download non zip files.": "Non posso scaricare i file non zip", + "Cache out of date.": "Cache Obsoleta.", + "First check.": "Controllo d'avvio", + "Auto Update.": "Aggiornamento automatico.", + "Updating": "Aggiorno", + "Up to date already": "Già aggiornata", + "Fetching latest info": "Cerco nuove informazioni", + "Done.": "Fatto.", + "Fetching info": "Cerco Informazioni", + "Unable to find {port_name}.": "Impossibile trovare {port_name}.", + "Unable to download file. [{status_code}]": "Impossibile Scaricare. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Scarico {file_url} - ({total_length_mb})", + "Downloading file.": "Scarico i file", + "Download failed: {err}": "Download fallito: {err}", + "Passed file validation.": "File Validati", + "Unable to validate download.": "Impossibile validare il download.", + "Unknown IP": "IP Ignoto", + "N/A": "N/A", + "Installing {port_name}": "Installo {port_name}", + "Uninstalling {port_name}": "Disinstallo {port_name}", + "Updating all port sources:": "Aggiorno le fonti...", + "Checking {runtime_name}": "Verifico {runtime_name}", + "** NO PORT **": "Nessun Risultato", + "Ready to Run": "Ready to Run", + "Requires Files": "File richiesti", + "Installed": "Installato", + "Missing": "Mancante", + "Danish": "Danese", + "German": "Tedesco", + "English": "Inglese", + "Spanish": "Spagnolo", + "Finnish": "Finlandese", + "French": "Francese", + "Italian": "Italiano", + "Japanese": "Giapponese", + "Korean": "Coreano", + "Dutch": "Olandese", + "Polish": "Polacco", + "Portuguese (Portugal)": "Portoghese (Portogallo)", + "Portuguese (Brazil)": "Portoghese (Brasile)", + "Russian": "Russo", + "Swedish": "Svedese", + "Ukrainian": "Ucraino", + "Chinese Simplified": "Cinese Semplificato", + "Messages": "Avviso", + "Are you sure you want to cancel?": "Sicuro di voler annullare?", + "Featured Ports": "Raccomandati dal Team", + "Select": "Seleziona", + "Back": "Indietro", + "Filters Scene": "Filtri", + "Alphabetical": "Alfabetico", + "Recently Added": "Aggiunti di Recente", + "Recently Updated": "Aggiornati di recente", + "Action": "Azione", + "Adventure": "Avventura", + "Arcade": "Arcade", + "Casino/Card": "Casino/Carta", + "First Person Shooter": "Fps", + "Platformer": "Platform", + "Puzzle": "Rompicapo", + "Racing": "Racing", + "Rhythm": "Musicale", + "Role Playing Game": "Gioco Di Ruolo", + "Simulation": "Simulazione", + "Sports": "Sport", + "Strategy": "Strategia", + "Visual Novel": "Visual Novel", + "Other": "Altro", + "Broken Ports": "Ports Problematici", + "{runtime_name} Runtime": "{runtime_name} Runtime", + "Sort:": "Ordina per:", + "Clear Filters": "Rimuovi Filtri", + "Genres:": "Genere", + "Attributes:": "Runtime", + "Porters:": "Porter", + "Delete": "Elimina", + "Upper Case": "Maiuscolo", + "Space": "Spazio", + "Enter": "Accetta", + "Lower Case": "Minuscolo", + "Language Select": "Selezione Lingua", + "{lang_name} (Selected)": "{lang_name} (Attuale)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Vuoi cambiare lingua?\n\nRiavvia per applicare le modifiche.", + "Main Menu": "Menu principale", + "Hand curated lists of ports": "Selezione di ports raccomandati dal Team", + "All Ports": "Tutti i Ports", + "List all ports available on PortMaster.": "Lista con tutti i Ports disponibili su PortMaster.", + "Ready to Run Ports": "Ready To Run", + "List all ports that are ready to play!": "Selezione di ports già compresivi di tutto il necessario per giocare!", + "Manage Ports": "Gestione Ports", + "Update / Uninstall Ports": "Aggiorna / Disinstalla Ports", + "Options": "Opzioni", + "PortMaster Options": "Configura PortMaster secondo le tue preferenze", + "Exit": "Esci", + "Quit PortMaster": "Esci Da PortMaster", + "Quit": "Indietro", + "Secret Mode {secret_mode}": "Modalità Segreta {secret_mode}", + "Enabled": "Attiva", + "Disabled": "Disattiva", + "Options Menu": "Impostazioni", + "Interface": "Interfaccia", + "Choose Language": "Lingua", + "Select the language PortMaster uses.": "Seleziona la lingua usata da PortMaster.", + "Select Theme": "Seleziona Tema", + "Select a theme for PortMaster.": "Cambia il tuo attuale tema o scaricane di nuovi.", + "Select Color Scheme": "Seleziona Modalità Tema", + "Select a colour scheme for PortMaster": "Cambia schema colori dell'attuale tema", + "Audio": "Audio", + "Music: ": "Musica: ", + "Enable or Disable background music in PortMaster.": "Abilita o disabilita la musica di sottofondo in PortMaster.", + "Sound FX: ": "Effetti Sonori: ", + "Enable or Disable soundfx in PortMaster.": "Abilita o disabilita gli effetti sonori in PortMaster.", + "Auto": "Auto", + "Manual": "Manuale", + "Metadata Refresh": "Aggiorna Metadati", + "Roms section": "Sezione Roms", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "Sistema", + "Runtime Manager": "Runtime Manager", + "Manage port runtimes.": "Gestisci Runtimes.", + "Experimental Ports: ": "Porte Sperimentali: ", + "Show or hide experimental ports.": "Mostra o nascondi le porte sperimentali.", + "Update Ports": "Aggiorna Ports", + "Update PortMaster": "Aggiorna PortMaster", + "Force check for a new PortMaster version.": "Forza il controllo per una nuova versione di PortMaster.", + "Controller Mode: {controller_mode}": "Modalità Controller: {controller_mode}", + "Toggle between various controller layouts.": "Cambia Schema comandi per la navigazione dei menu.", + "Secret Options": "Opzioni Segrete", + "All Ports: ": "Tutte Le Porte: ", + "Show all ports, ignoring requirements.": "Mostra tutte le porte, ignorando i requisiti.", + "CWTBE Mode: ": "Modo CWTBE ", + "Delete PortMaster Config": "Elimina Configurazione Attuale", + "This can break stuff, don't touch unless you know what you are doing.": "Attenzione, non toccare a meno che tu non sappia cosa stai facendo.", + "Delete PortMaster Runtimes": "Elimina Runtimes installate", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Sei sicuro di voler passare al canale di rilascio alpha?\n\nPortMaster verrà aggiornato o retrocesso di conseguenza.\n\nQUESTO PUÒ EFFETTIVAMENTE ROMPERE IL SISTEMA, SEI STATO AVVISATO", + "SDCARD 1": "SDCARD 1", + "SDCARD 2": "SDCARD 2", + "Deleting Runtimes:": "Eliminazione Runtimes:", + "Source Manager": "Gestione Fonti", + "Reinstall": "Reinstalla", + "Uninstall": "Disinstalla", + "Install": "Installa", + "Port Info": "Port Info", + "Show Info": "Mostra info", + "Are you sure you want to uninstall {port_name}?": "Sei sicuro di voler disinstallare {port_name}?", + "NO PORTS": "Nessun Risultato", + "** NO PORTS FOUND **": "** Nessun Risultato **", + "Download ports first.": "Scarica qualcosa prima.", + "Try removing some filters.": "Rimuovi qualche filtro.", + "Ports List": "Lista dei Ports", + "Filters": "Filtri", + "Download All": "Scarica tutto", + "Used": "In uso", + "Not Used": "Non utilizzato", + "Check": "Verifica", + "Are you sure you want to download and verify all runtimes?": "Sei sicuro di voler scaricare tutte le runtimes?", + "Deleted runtime {runtime}": "Runtime {runtime} eliminata", + "{theme_name} (Selected)": "{theme_name} (Attuale)", + "Download": "Download", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Vuoi cambiare Tema?\n\nRiavvia per applicare le modifiche.", + "Select Colour Scheme": "Selezionare schema colori", + "{item_name} (Selected)": "{item_name} (Attuale)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Vuoi Cambiare lo Schema Colore?\n\nDovrai riavviare per applicare le modifiche.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Stai passando dalla versione {old_release_channel} alla versione {new_release_channel} .\n\nVuoi tornare alla versione precedente?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "C'è una nuova versione di PortMaster ({portmaster_version})\n\nVuoi aggiornare?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Vuoi reinstallare PortMaster?\n\nProseguendo scaricherai da {release_channel} la versione {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/it_IT.themes.json b/PortMaster/pylibs/locales/it_IT.themes.json new file mode 100644 index 00000000..2b6a89d0 --- /dev/null +++ b/PortMaster/pylibs/locales/it_IT.themes.json @@ -0,0 +1,16 @@ +{ + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genere: {port_info.genres} Dimensione Download: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibili: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltri: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Spazio libero: {system.free_space}\n\nPorter: {port_info.porter}\nGenere: {port_info.genres}\nDimensione: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nIstruzioni: {port_info.instructions}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Autore: {theme_info.creator}\nStato: {theme_info.status}", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtri: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genere: {port_info.genres}\nDimensione: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Ora: {system.time_24hr} Batteria: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Titolo: {port_info.title}\nPorter: {port_info.porter}\nGenere: {port_info.genres}\nDimensione Download: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescrizione: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibili: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltri: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Generi: {port_info.genres}\n{if:port_info.install_size}Dimensione Installazione: {port_info.install_size}{else}Dimensione Download: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Ora: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDispositivo: {system.device_name}\nBatteria: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpazio: {system.free_space} liberi / {system.total_space} totali", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nome: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports} \nDimensione Download: {runtime_info.download_size}\nDimensione Installazione: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/it_IT/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/it_IT/LC_MESSAGES/messages.po deleted file mode 100644 index 0427a4af..00000000 --- a/PortMaster/pylibs/locales/it_IT/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1240 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Italian\n" -"Language: it_IT\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: it\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Danese" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Tedesco" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Inglese" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Spagnolo" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finlandese" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Francese" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italiano" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Giapponese" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Coreano" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Olandese" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polacco" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portoghese (Portogallo)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portoghese (Brasile)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Russo" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Svedese" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ucraino" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Cinese Semplificato" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "IP Ignoto" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/A" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "Nessun Risultato" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Ready to Run" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "File richiesti" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Installato" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Mancante" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Aggiornamento Disponibile" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Non Installato" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Ok" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Annulla" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "" - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Installo {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Disinstallo {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Aggiorno le fonti..." - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Verifico {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Stai passando dalla versione {old_release_channel} alla versione {new_release_channel} .\n\n" -"Vuoi tornare alla versione precedente?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "C'è una nuova versione di PortMaster ({portmaster_version})\n\n" -"Vuoi aggiornare?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Vuoi reinstallare PortMaster?\n\n" -"Proseguendo scaricherai da {release_channel} la versione {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Caricamento..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Validazione del download fallita." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Caricamento Informazioni" - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Recupero informazioni sugli ultimi ports Raccomandati dal Team." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Caricamento Fonti." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Caricamento Ports." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Installo Tema {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Installo" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Tema {download_name!r} installato con successo." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Installazione {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Port {download_name!r} installato con successo." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Installazione fallita, rimozione file in corso..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "{download_name} fallisce l'installazione." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "{download_name!r} e {runtime_name!r} sono stati installati con successo" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "{download_name!r} e {runtime_name!r} installazione fallita!\n\n" -"Riprova o controlla la Fiji per ulteriori informazioni." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Port {runtime} sembra avere una runtime corrota, il gioco potrebbe avere problemi" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Verificato" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Non verificato" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Corrotto" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Impossibile scaricare la runtime in modalità offline" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Verifico runtime {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Verifica in corso" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Impossibile trovare il download per {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} verificata con successo." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} Verificata." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Aggiornamento {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Runtime {runtime} è corrotta, reinstallazione." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Scarico {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Impossibile scaricare la runtime {runtime}, il gioco potrebbe presentare dei problemi." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "Scaricato con successo {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Impossibile trovare una fonte per {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Port sconosciuto {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Rimuovo {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} disinstallato con successo." - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache Obsoleta." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Controllo d'avvio" - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Aggiornamento automatico." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Aggiorno" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Già aggiornata" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Cerco nuove informazioni" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Fatto." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Impossibile trovare {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Non posso verificare {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Non posso scaricare le informazioni per verificare {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Successo!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Cerco Informazioni" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Controllo Nuovi ports" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Cerco Informazioni" - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Non posso scaricare i file di verifica" - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Non posso scaricare i file non zip" - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Impossibile Scaricare. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Scarico {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Scarico i file" - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Download fallito: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "File Validati" - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Impossibile validare il download." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Menu principale" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Indietro" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Raccomandati dal Team" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Selezione di ports raccomandati dal Team" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Tutti i Ports" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Lista con tutti i Ports disponibili su PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Ready To Run" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Selezione di ports già compresivi di tutto il necessario per giocare!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Gestione Ports" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Aggiorna / Disinstalla Ports" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Opzioni" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Configura PortMaster secondo le tue preferenze" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Esci" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Esci Da PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Accetta" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Modalità Segreta {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Attiva" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Disattiva" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Impostazioni" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interfaccia" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Lingua" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Seleziona la lingua usata da PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Seleziona Tema" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Cambia il tuo attuale tema o scaricane di nuovi." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Seleziona Modalità Tema" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Cambia schema colori dell'attuale tema" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Audio" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Musica: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Abilita o disabilita la musica di sottofondo in PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Effetti Sonori: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Abilita o disabilita gli effetti sonori in PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Auto" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manuale" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Aggiorna Metadati" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Sezione Roms" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Sistema" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Runtime Manager" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Gestisci Runtimes." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Porte Sperimentali: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Mostra o nascondi le porte sperimentali." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Aggiorna Ports" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Aggiorna PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Forza il controllo per una nuova versione di PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Modalità Controller: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Cambia Schema comandi per la navigazione dei menu." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Opzioni Segrete" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Tutte Le Porte: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Mostra tutte le porte, ignorando i requisiti." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Modo CWTBE " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Elimina Configurazione Attuale" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Attenzione, non toccare a meno che tu non sappia cosa stai facendo." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Elimina Runtimes installate" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Indietro" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Sei sicuro di voler passare al canale di rilascio alpha?\n\n" -"PortMaster verrà aggiornato o retrocesso di conseguenza.\n\n" -"QUESTO PUÒ EFFETTIVAMENTE ROMPERE IL SISTEMA, SEI STATO AVVISATO" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SDCARD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SDCARD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Eliminazione Runtimes:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Gestione Fonti" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Scarica tutto" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "In uso" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Non utilizzato" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Verifica" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Disinstalla" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Installa" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Sei sicuro di voler scaricare tutte le runtimes?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Runtime {runtime} eliminata" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Attuale)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Seleziona" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Download" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Vuoi cambiare Tema?\n\n" -"Riavvia per applicare le modifiche." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Selezionare schema colori" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Attuale)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Vuoi Cambiare lo Schema Colore?\n\n" -"Dovrai riavviare per applicare le modifiche." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Selezione Lingua" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Attuale)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Vuoi cambiare lingua?\n\n" -"Riavvia per applicare le modifiche." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Elimina" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Maiuscolo" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Spazio" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Minuscolo" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "Nessun Risultato" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** Nessun Risultato **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Scarica qualcosa prima." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Rimuovi qualche filtro." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Mostra info" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Lista dei Ports" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filtri" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Reinstalla" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Port Info" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Sei sicuro di voler disinstallare {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Filtri" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabetico" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Aggiunti di Recente" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Aggiornati di recente" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Azione" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Avventura" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Casino/Carta" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Fps" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Platform" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Rompicapo" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Racing" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Musicale" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Gioco Di Ruolo" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulazione" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sport" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Strategia" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Visual Novel" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Altro" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Ports Problematici" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Runtime" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Ordina per:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Rimuovi Filtri" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Genere" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Runtime" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porter" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Avviso" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Sicuro di voler annullare?" - diff --git a/PortMaster/pylibs/locales/it_IT/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/it_IT/LC_MESSAGES/themes.po deleted file mode 100644 index b3ff925b..00000000 --- a/PortMaster/pylibs/locales/it_IT/LC_MESSAGES/themes.po +++ /dev/null @@ -1,176 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Italian\n" -"Language: it_IT\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: it\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genere: {port_info.genres} Dimensione Download: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibili: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtri: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Spazio libero: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genere: {port_info.genres}\n" -"Dimensione: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Istruzioni: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Autore: {theme_info.creator}\n" -"Stato: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Ora: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Dispositivo: {system.device_name}\n" -"Batteria: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filtri: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genere: {port_info.genres}\n" -"Dimensione: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Ora: {system.time_24hr} Batteria: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Spazio: {system.free_space} liberi / {system.total_space} totali" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Nome: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports} \n" -"Dimensione Download: {runtime_info.download_size}\n" -"Dimensione Installazione: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Titolo: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genere: {port_info.genres}\n" -"Dimensione Download: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Descrizione: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibili: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtri: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Generi: {port_info.genres}\n" -"{if:port_info.install_size}Dimensione Installazione: {port_info.install_size}{else}Dimensione Download: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/ja_JP.json b/PortMaster/pylibs/locales/ja_JP.json new file mode 100644 index 00000000..5797a8f1 --- /dev/null +++ b/PortMaster/pylibs/locales/ja_JP.json @@ -0,0 +1,207 @@ +{ + "Loading...": "読み込み中...", + "Loading Sources.": "ソースの読み込み中", + "Download validation failed.": "ダウンロードの検証に失敗しました。", + "Loading Info.": "情報を読み込み中", + "Fetching latest featured ports.": "最新の注目ポートを取得しています", + "Installing Theme {download_name}.": "テーマ {download_name} のインストール中。", + "Installing": "インストール中", + "Theme {download_name!r} installed successfully.": "テーマ {download_name!r} のインストールに成功しました。", + "Port {download_name} installed failed.": "ポート {download_name} がインストールされませんでした。", + "Installing {download_name}.": "{download_name} インストール中.", + "Port {download_name!r} installed successfully.": "ポート {download_name!r} が正常にインストールされました。", + "Installation failed, removing files...": "インストールに失敗しました。ファイルの削除中...", + "Okay": "了解", + "Cancel": "キャンセル", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "ポート {download_name!r} と {runtime_name!r} が正常にインストールされました。", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "ポート {download_name!r} は正常にインストールされましたが、{runtime_name!r} のインストールに失敗しました!\n\n再インストールして再試行するか、ウィキのヘルプを確認してください。", + "Unable to find a source for {port_name}": "{port_name} のソースが見つかりません", + "Unknown port {port_name}": "不明なポート {port_name}", + "Removing {port_name}": "{port_name} を削除中", + "Successfully uninstalled {port_name}": "{port_name} のアンインストールに成功しました", + "Loading Ports.": "ポートを読み込み中", + "Port {runtime} contains a bad runtime, game may not run correctly.": "ポート {runtime} に不正なランタイムが含まれています。ゲームが正しく動作しない可能性があります。", + "Not Installed": "インストールされていません", + "Update Available": "アップデートが利用できます", + "Verified": "認証済み", + "Unverified": "未確認", + "Broken": "損壊", + "Unable do download a runtime when in offline mode.": "オフラインモードではランタイムをダウンロードできません。", + "Verifying runtime {runtime}": "ランタイム {runtime} を確認しています", + "Verifying": "検証中", + "Unable to find a download for {runtime}.": "{runtime} のダウンロードが見つかりません。", + "Verified {runtime} successfully.": "{runtime} 認証された。", + "Verified {runtime}.": "{runtime} 認証済み", + "Updating {runtime}.": "{runtime} を更新中", + "Runtime {runtime} is broken, reinstalling.": "ランタイム {runtime} が壊れています。再インストールします。", + "Downloading runtime {runtime}.": "ランタイム {runtime} をダウンロードしています。", + "Unable to download {runtime}, game may not run correctly.": "{runtime} をダウンロードできません。ゲームが正しく実行されない可能性があります。", + "Successfully downloaded {runtime}.": "{runtime} のダウンロードに成功しました。", + "Unable to download verification file.": "確認情報が見つかりません。", + "Unable to download non zip files.": "非 zip ファイルをダウンロードできません。", + "Cache out of date.": "キャッシュが古いです。", + "First check.": "最初のチェック", + "Auto Update.": "自動更新", + "Updating": "更新中", + "Up to date already": "既に最新の状態です", + "Fetching latest info": "最新情報を取得中", + "Done.": "完了", + "Fetching info": "情報を取得中", + "Unable to find {port_name}.": "{port_name} が見つかりません。", + "Unable to download file. [{status_code}]": "ファイルをダウンロードできません。 [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "{file_url} - ({total_length_mb}) をダウンロード中", + "Downloading file.": "ファイルをダウンロードしています...", + "Download failed: {err}": "ダウンロードに失敗しました: {err}", + "Passed file validation.": "ファイルの検証が完了しました。", + "Unable to validate download.": "ダウンロードを検証できません。", + "Unknown IP": "不明なIP", + "N/A": "N/A", + "Installing {port_name}": "{port_name} インストール中", + "Uninstalling {port_name}": "{port_name} をアンインストール中", + "Updating all port sources:": "すべてのポートソースを更新しています:", + "Checking {runtime_name}": "{runtime_name} を確認中", + "** NO PORT **": "** ポートなし **", + "Ready to Run": "実行準備完了", + "Requires Files": "必要なファイル", + "Installed": "インストール済み", + "Missing": "欠落", + "Danish": "デンマーク語", + "German": "ドイツ語", + "English": "英語", + "Spanish": "スペイン語", + "Finnish": "フィンランド語", + "French": "フランス語", + "Italian": "イタリア語", + "Japanese": "日本語", + "Korean": "韓国語", + "Dutch": "オランダ語", + "Polish": "ポーランド語", + "Portuguese (Portugal)": "ポルトガル語 (ポルトガル)", + "Portuguese (Brazil)": "ポルトガル語 (ブラジル)", + "Russian": "ロシア語", + "Swedish": "スウェーデン語", + "Ukrainian": "ウクライナ語", + "Chinese Simplified": "中国語 (簡体字)", + "Messages": "メッセージ", + "Are you sure you want to cancel?": "取り消しますか?", + "Featured Ports": "注目のポート", + "Select": "選択", + "Back": "戻る", + "No featured ports found, internet connection is required.": "注目ポートが見つかりません。インターネット接続が必要です。", + "Filters Scene": "フィルターシーン", + "Alphabetical": "アルファベット順", + "Recently Added": "最近の追加", + "Recently Updated": "最近のアップデート", + "Action": "動作", + "Adventure": "冒険", + "Arcade": "アーケード", + "Casino/Card": "カジノ/カード", + "First Person Shooter": "一人称射撃", + "Platformer": "プラットフォーム", + "Puzzle": "パズル", + "Racing": "レース", + "Rhythm": "リズム", + "Role Playing Game": "RPG", + "Simulation": "シミュレーション", + "Sports": "スポーツ", + "Strategy": "戦略", + "Visual Novel": "ビジュアルノベル", + "Other": "その他", + "Broken Ports": "壊れたポート", + "Free game, all files included.": "無料ゲーム、すべてのファイルが含まれています。", + "Demo files included.": "デモファイルが含まれています。", + "Free external assets needed.": "無料の外部アセットが必要です。", + "Paid external assets needed.": "有料の外部アセットが必要です。", + "{runtime_name} Runtime": "{runtime_name} ランタイム", + "Sort:": "ソート:", + "Clear Filters": "フィルターをクリアにする", + "Genres:": "ジャンル:", + "Attributes:": "属性", + "Architecture:": "アーキテクチャ:", + "Availability:": "在庫状況:", + "Porters:": "ポーター:", + "Delete": "削除", + "Upper Case": "大文字", + "Space": "スペース", + "Enter": "Enter", + "Lower Case": "小文字", + "Language Select": "言語を選択してください", + "{lang_name} (Selected)": "{lang_name} (選択済み)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "言語を変更しますか?\n\n影響を受けるには再起動する必要があります。", + "Main Menu": "メインメニュー", + "Hand curated lists of ports": "ポートのハンドキュレーションリスト", + "All Ports": "すべてのポート", + "List all ports available on PortMaster.": "PortMasterで利用可能なすべてのポートを表示します。", + "Ready to Run Ports": "ポートを実行する準備完了", + "List all ports that are ready to play!": "プレーする準備ができた、のポートを表示します", + "Manage Ports": "ポートの管理", + "Update / Uninstall Ports": "ポートの更新/アンインストール", + "Options": "オプション", + "PortMaster Options": "PortMaster オプション", + "Exit": "終了", + "Quit PortMaster": "PortMasterを終了", + "Quit": "終了", + "Secret Mode {secret_mode}": "シークレットモード {secret_mode}", + "Enabled": "有効", + "Disabled": "無効", + "Are you sure you want to exit PortMaster?": "PortMaster を終了しますか?", + "Options Menu": "オプションメニュー", + "Interface": "インターフェース", + "Choose Language": "言語を選択", + "Select the language PortMaster uses.": "PortMaster のインターフェース言語を選択", + "Select Theme": "テーマを選択", + "Select a theme for PortMaster.": "PortMasterのテーマを選択", + "Select Color Scheme": "配色を選択", + "Select a colour scheme for PortMaster": "PortMasterの配色を選択", + "Audio": "音声", + "Music: ": "音楽: ", + "Enable or Disable background music in PortMaster.": "PortMasterでBGMを有効または無効にします。", + "Sound FX: ": "サウンド効果: ", + "Enable or Disable soundfx in PortMaster.": "PortMasterサウンド効果を有効または無効にします。", + "System": "システム", + "Runtime Manager": "ランタイムの管理", + "Manage port runtimes.": "ポートのランタイムを管理", + "Update Ports": "ポートを更新", + "Update PortMaster": "PortMasterを更新", + "Force check for a new PortMaster version.": "新しいPortMasterバージョンを強制的にチェックします。", + "Release Channel: {channel}": "リリースチャンネル: {channel}", + "Change release channel of PortMaster, either beta or stable.": "PortMasterのリリースチャンネルをbetaまたはstableのいずれかに変更します。", + "Controller Mode: {controller_mode}": "コントローラーモード: {controller_mode}", + "Toggle between various controller layouts.": "さまざまなコントローラーレイアウトを切り替えます。", + "Secret Options": "シークレットオプション", + "Delete PortMaster Config": "ポートマスター設定を削除", + "This can break stuff, don't touch unless you know what you are doing.": "これは物事を壊すかもしれない、あなたがしていることを知っていない限り、触れないでください。", + "Delete PortMaster Runtimes": "PortMasterランタイムを削除", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "リリースチャンネルを {current_channel} から {new_channel} に変更してもよろしいですか?\n\nPortMasterはそれに応じてアップグレードまたはダウングレードされます。", + "Deleting Runtimes:": "ランタイムを削除中:", + "Source Manager": "ソースマネージャー", + "Port Info Popup": "ポート情報ポップアップ", + "Hide Info": "情報を非表示にする", + "Reinstall": "再インストール", + "Uninstall": "削除", + "Install": "インストール", + "Port Info": "ポート情報", + "Show Info": "情報を表示", + "Are you sure you want to uninstall {port_name}?": "{port_name} をアンインストールしてもよろしいですか?", + "NO PORTS": "ポートなし", + "** NO PORTS FOUND **": "** 見つかりませんでした **", + "Download ports first.": "ポートをダウンロードしてください。", + "Try removing some filters.": "フィルターを削除してみてください。", + "Ports List": "ポートリスト", + "Filters": "フィルター", + "Download All": "全てダウンロード", + "Used": "使用済み", + "Not Used": "未使用", + "Check": "チェック", + "Are you sure you want to download and verify all runtimes?": "すべてのランタイムをダウンロードして確認しますか?", + "Deleted runtime {runtime}": "ランタイム {runtime} を削除しました", + "{theme_name} (Selected)": "{theme_name} (選択済み)", + "Download": "ダウンロード", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "テーマを変更しますか?\n\n適用するには再起動する必要があります。", + "Select Colour Scheme": "配色を選択", + "{item_name} (Selected)": "{item_name} (選択済み)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "テーマを変更しますか?\n\n影響を受けるには再起動する必要があります。", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel} から {new_release_channel} へ切り替えています。\n\nダウングレードしますか?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster ({portmaster_version})\n\nアップグレードしますか?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "PortMasterを再インストールしますか?\n\n {release_channel} チャンネルから {portmaster_version} に再インストールします。" +} diff --git a/PortMaster/pylibs/locales/ja_JP.themes.json b/PortMaster/pylibs/locales/ja_JP.themes.json new file mode 100644 index 00000000..ac0bdff5 --- /dev/null +++ b/PortMaster/pylibs/locales/ja_JP.themes.json @@ -0,0 +1,16 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "時間: {system.time_24hr}\nバッテリー: {system.battery_level}\nスペース: {system.free_space} フリー", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "ジャンル: {port_info.genres} ダウンロードサイズ: {port_info.download_size}{if:port_info.runtime} ランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}利用可能なポート: {ports_list.filter_ports} / {ports_list.total_ports} \nフィルター: {ports_list.filters}{endif}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PortMaster: {system.portmaster_version}\nHabourMaster: {system.harbourmaster_version}\nスペース: {system.free_space} フリー / {system.total_space} 合計\n時間: {system.time_24hr}\nバッテリー: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "作成者: {theme_info.creator}\n状態: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PortMaster: {system.portmaster_version}\nHarbourMaster: {system.harbourmaster_version}\nスペース: {system.free_space} フリー", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "フィルター: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "ジャンル: {port_info.genres}\nダウンロードサイズ: {port_info.download_size}{if:port_info.runtime}\nランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "時間: {system.time_24hr} バッテリー: {system.battery_level}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}利用可能なポート: {ports_list.filter_ports} / {ports_list.total_ports} \nフィルター: {ports_list.filters}{endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "時間: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nデバイス: {system.device_name}\nバッテリー: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PortMaster: {system.portmaster_version}\nHabourMaster: {system.harbourmaster_version}\nスペース: {system.free_space} フリー / {system.total_space} 合計", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "名前: {runtime_info.name}\nステータス: {runtime_info.status}\nポート: {runtime_info.ports}\nダウンロードサイズ: {runtime_info.download_size}\nインストールサイズ: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/messages.po deleted file mode 100644 index efc17940..00000000 --- a/PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1239 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Japanese\n" -"Language: ja_JP\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ja\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "デンマーク語" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "ドイツ語" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "英語" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "スペイン語" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "フィンランド語" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "フランス語" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "イタリア語" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "日本語" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "韓国語" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "オランダ語" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "ポーランド語" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "ポルトガル語 (ポルトガル)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "ポルトガル語 (ブラジル)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "ロシア語" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "スウェーデン語" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "ウクライナ語" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "中国語 (簡体字)" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "不明なIP" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/A" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** ポートなし **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "実行準備完了" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "必要なファイル" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "インストール済み" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "欠落" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "アップデートが利用できます" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "インストールされていません" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "了解" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "キャンセル" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "" - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "{port_name} インストール中" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "{port_name} をアンインストール中" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "すべてのポートソースを更新しています:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "{runtime_name} を確認中" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "{old_release_channel} から {new_release_channel} へ切り替えています。\n\n" -"ダウングレードしますか?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "PortMaster ({portmaster_version})\n\n" -"アップグレードしますか?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "PortMasterを再インストールしますか?\n\n" -" {release_channel} チャンネルから {portmaster_version} に再インストールします。" - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "読み込み中..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "ダウンロードの検証に失敗しました。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "情報を読み込み中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "最新の注目ポートを取得しています" - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "ソースの読み込み中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "ポートを読み込み中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "テーマ {download_name} のインストール中。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "インストール中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "テーマ {download_name!r} のインストールに成功しました。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "{download_name} インストール中." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "ポート {download_name!r} が正常にインストールされました。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "インストールに失敗しました。ファイルの削除中..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "ポート {download_name} がインストールされませんでした。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "ポート {download_name!r} と {runtime_name!r} が正常にインストールされました。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "ポート {download_name!r} は正常にインストールされましたが、{runtime_name!r} のインストールに失敗しました!\n\n" -"再インストールして再試行するか、ウィキのヘルプを確認してください。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "ポート {runtime} に不正なランタイムが含まれています。ゲームが正しく動作しない可能性があります。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "認証済み" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "未確認" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "損壊" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "オフラインモードではランタイムをダウンロードできません。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "ランタイム {runtime} を確認しています" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "検証中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "{runtime} のダウンロードが見つかりません。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} 認証された。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} 認証済み" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "{runtime} を更新中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "ランタイム {runtime} が壊れています。再インストールします。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "ランタイム {runtime} をダウンロードしています。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "{runtime} をダウンロードできません。ゲームが正しく実行されない可能性があります。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} のダウンロードに成功しました。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "{port_name} のソースが見つかりません" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "不明なポート {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "{port_name} を削除中" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} のアンインストールに成功しました" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "キャッシュが古いです。" - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "最初のチェック" - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "自動更新" - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "更新中" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "既に最新の状態です" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "最新情報を取得中" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "完了" - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "{port_name} が見つかりません。" - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "{port_name} の確認情報が見つかりません。" - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "{port_name} の確認情報が見つかりません。" - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "成功!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "情報を取得中" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: 最新のポートを取得中" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "情報を取得中." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "確認情報が見つかりません。" - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "非 zip ファイルをダウンロードできません。" - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "ファイルをダウンロードできません。 [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "{file_url} - ({total_length_mb}) をダウンロード中" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "ファイルをダウンロードしています..." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "ダウンロードに失敗しました: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "ファイルの検証が完了しました。" - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "ダウンロードを検証できません。" - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "メインメニュー" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "終了" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "PortMaster を終了しますか?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "注目のポート" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "ポートのハンドキュレーションリスト" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "すべてのポート" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "PortMasterで利用可能なすべてのポートを表示します。" - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "ポートを実行する準備完了" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "プレーする準備ができた、のポートを表示します" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "ポートの管理" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "ポートの更新/アンインストール" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "オプション" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "PortMaster オプション" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "終了" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "PortMasterを終了" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Enter" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "シークレットモード {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "有効" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "無効" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "オプションメニュー" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "インターフェース" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "言語を選択" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "PortMaster のインターフェース言語を選択" - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "テーマを選択" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "PortMasterのテーマを選択" - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "配色を選択" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "PortMasterの配色を選択" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "音声" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "音楽: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "PortMasterでBGMを有効または無効にします。" - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "サウンド効果: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "PortMasterサウンド効果を有効または無効にします。" - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "システム" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "ランタイムの管理" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "ポートのランタイムを管理" - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "ポートを更新" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "PortMasterを更新" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "新しいPortMasterバージョンを強制的にチェックします。" - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "リリースチャンネル: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "PortMasterのリリースチャンネルをbetaまたはstableのいずれかに変更します。" - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "コントローラーモード: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "さまざまなコントローラーレイアウトを切り替えます。" - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "シークレットオプション" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "ポートマスター設定を削除" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "これは物事を壊すかもしれない、あなたがしていることを知っていない限り、触れないでください。" - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "PortMasterランタイムを削除" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "戻る" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "リリースチャンネルを {current_channel} から {new_channel} に変更してもよろしいですか?\n\n" -"PortMasterはそれに応じてアップグレードまたはダウングレードされます。" - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "ランタイムを削除中:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "ソースマネージャー" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "全てダウンロード" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "使用済み" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "未使用" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "チェック" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "削除" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "インストール" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "すべてのランタイムをダウンロードして確認しますか?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "ランタイム {runtime} を削除しました" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (選択済み)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "選択" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "ダウンロード" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "テーマを変更しますか?\n\n" -"適用するには再起動する必要があります。" - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "配色を選択" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (選択済み)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "テーマを変更しますか?\n\n" -"影響を受けるには再起動する必要があります。" - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "言語を選択してください" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (選択済み)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "言語を変更しますか?\n\n" -"影響を受けるには再起動する必要があります。" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "削除" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "大文字" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "スペース" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "小文字" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "注目ポートが見つかりません。インターネット接続が必要です。" - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "ポートなし" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** 見つかりませんでした **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "ポートをダウンロードしてください。" - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "フィルターを削除してみてください。" - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "情報を表示" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "ポートリスト" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "フィルター" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "ポート情報ポップアップ" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "情報を非表示にする" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "再インストール" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "ポート情報" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "{port_name} をアンインストールしてもよろしいですか?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "フィルターシーン" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "アルファベット順" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "最近の追加" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "最近のアップデート" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "動作" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "冒険" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "アーケード" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "カジノ/カード" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "一人称射撃" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "プラットフォーム" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "パズル" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "レース" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "リズム" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "RPG" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "シミュレーション" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "スポーツ" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "戦略" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "ビジュアルノベル" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "その他" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "壊れたポート" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "無料ゲーム、すべてのファイルが含まれています。" - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "デモファイルが含まれています。" - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "無料の外部アセットが必要です。" - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "有料の外部アセットが必要です。" - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} ランタイム" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "ソート:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "フィルターをクリアにする" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "ジャンル:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "属性" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "アーキテクチャ:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "在庫状況:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "ポーター:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "メッセージ" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "取り消しますか?" - diff --git a/PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/themes.po deleted file mode 100644 index e2e575e3..00000000 --- a/PortMaster/pylibs/locales/ja_JP/LC_MESSAGES/themes.po +++ /dev/null @@ -1,172 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Japanese\n" -"Language: ja_JP\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ja\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "ジャンル: {port_info.genres} ダウンロードサイズ: {port_info.download_size}{if:port_info.runtime} ランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}利用可能なポート: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"フィルター: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PortMaster: {system.portmaster_version}\n" -"HabourMaster: {system.harbourmaster_version}\n" -"スペース: {system.free_space} フリー / {system.total_space} 合計\n" -"時間: {system.time_24hr}\n" -"バッテリー: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "作成者: {theme_info.creator}\n" -"状態: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "時間: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"デバイス: {system.device_name}\n" -"バッテリー: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PortMaster: {system.portmaster_version}\n" -"HarbourMaster: {system.harbourmaster_version}\n" -"スペース: {system.free_space} フリー" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "フィルター: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "ジャンル: {port_info.genres}\n" -"ダウンロードサイズ: {port_info.download_size}{if:port_info.runtime}\n" -"ランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "時間: {system.time_24hr} バッテリー: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PortMaster: {system.portmaster_version}\n" -"HabourMaster: {system.harbourmaster_version}\n" -"スペース: {system.free_space} フリー / {system.total_space} 合計" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "名前: {runtime_info.name}\n" -"ステータス: {runtime_info.status}\n" -"ポート: {runtime_info.ports}\n" -"ダウンロードサイズ: {runtime_info.download_size}\n" -"インストールサイズ: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "時間: {system.time_24hr}\n" -"バッテリー: {system.battery_level}\n" -"スペース: {system.free_space} フリー" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}利用可能なポート: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"フィルター: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "" - diff --git a/PortMaster/pylibs/locales/ko_KR.json b/PortMaster/pylibs/locales/ko_KR.json new file mode 100644 index 00000000..b61fd572 --- /dev/null +++ b/PortMaster/pylibs/locales/ko_KR.json @@ -0,0 +1,257 @@ +{ + "Loading...": "불러오는 중...", + "Loading Sources.": "소스 불러오는 중", + "Download validation failed.": "다운로드 검증에 실패했습니다.", + "Loading Info.": "정보 불러오는 중", + "Fetching latest featured ports.": "최신 추천 포트를 가져오는 중", + "Installing Theme {download_name}.": "{download_name} 테마를 설치 중", + "Installing": "설치 중", + "Theme {download_name!r} installed successfully.": "{download_name!r} 테마 설치에 성공했습니다.", + "Port {download_name} installed failed.": "{download_name} 포트 설치에 실패했습니다.", + "Installing {download_name}.": "{download_name} 설치 중", + "Port {download_name!r} installed successfully.": "{download_name!r} 포트가 정상적으로 설치되었습니다.", + "Installation failed, removing files...": "설치에 실패했습니다. 파일 삭제 중...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "안녕하세요.\n\n기기와 호환되지 않는 이식본을 수동으로 설치하고 계신 것 같습니다.\n\n문제가 발생하면 Discord에 보고하지 마세요. 조롱을 당할 수 있습니다.\n\n설치를 계속하시겠습니까?", + "Okay": "확인", + "Cancel": "취소", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "{download_name!r} 포트와 {runtime_name!r} 가 정상적으로 설치되었습니다.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} 포트는 정상적으로 설치되었으나, {runtime_name!r} 설치에 실패했습니다!\n\n재시도하려면 다시 설치하거나 위키에서 도움말을 확인하세요.", + "Unable to download in offline mode.": "오프라인 모드에서는 다운로드할 수 없습니다.", + "Unable to find a source for {port_name}": "{port_name} 소스를 찾을 수 없습니다.", + "Unknown port {port_name}": "알 수 없는 포트 {port_name}", + "Removing {port_name}": "{port_name} 제거 중", + "Error uninstalling {port_name}\n\n{error}": "{port_name} 설치 제거 중 오류\n\n{error}", + "Successfully uninstalled {port_name}": "{port_name} 제거 성공", + "Loading Ports.": "포트 불러오는 중", + "Port {runtime} contains a bad runtime, game may not run correctly.": "{runtime} 포트에 비정상적인 런타임이 포함되어 있습니다. 게임이 올바르게 동작하지 않을 수 있습니다.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "{runtime} 포트에 알 수 없는 런타임이 포함되어 있습니다. 게임이 올바르게 동작하지 않을 수 있습니다.", + "Not Installed": "설치되지 않음", + "Update Available": "업데이트 이용 가능", + "Verified": "검증 완료", + "Unverified": "미 검증", + "Broken": "파손", + "Unable do download a runtime when in offline mode.": "오프라인 모드에서는 런타임을 다운로드할 수 없습니다.", + "Verifying runtime {runtime}": "런타임 {runtime}를 검증 중", + "Verifying": "검증 중", + "Unable to find a download for {runtime}.": "{runtime} 다운로드를 찾을 수 없습니다.", + "Unable to download {runtime} in {device_arch}.": "{device_arch}에서 {runtime}을 다운로드 할 수 없습니다.", + "Verified {runtime} successfully.": "{runtime} 검증 성공", + "Verified {runtime}.": "{runtime} 검증 완료", + "Updating {runtime}.": "{runtime} 업데이트 중", + "Runtime {runtime} is broken, reinstalling.": "런타임 {runtime}가 파손되어 있어 다시 설치합니다.", + "Downloading runtime {runtime}.": "런타임 {runtime} 다운로드 중", + "Unable to download {runtime}, game may not run correctly.": "{runtime} 다운로드할 수 없습니다. 게임이 제대로 실행되지 않을 수 있습니다.", + "Successfully downloaded {runtime}.": "{runtime} 다운로드 성공", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\n그렇게 하기 위해:\n메뉴 -> 유틸리티 -> 롬 디렉토리 다시 스캔", + "Unable to download verification file.": "검증 파일을 다운로드할 수 없습니다.", + "Unable to download non zip files.": "Zip 파일이 아니면 다운로드할 수 없습니다.", + "Cache out of date.": "캐시가 오래되었습니다.", + "First check.": "최초 확인", + "Auto Update.": "자동 업데이트", + "Updating": "업데이트 중", + "Up to date already": "이미 최신 상태입니다.", + "Fetching latest info": "최신 정보를 가져오는 중", + "Done.": "완료.", + "Fetching info": "정보 가져오는 중", + "Unable to find {port_name}.": "{port_name} 찾을 수 없습니다.", + "Unable to download file. [{status_code}]": "파일을 다운로드할 수 없습니다.[{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "{file_url} - ({total_length_mb}) 다운로드 중", + "Downloading file.": "파일 다운로드 중", + "Download failed: {err}": "다운로드 실패: {err}", + "Passed file validation.": "파일 검증을 완료했습니다.", + "Unable to validate download.": "다운로드를 검증할 수 없습니다.", + "Unknown IP": "알 수 없는 IP", + "N/A": "N/A", + "Fetching latest gameinfo.xml files and cover images.": "마지막 gameinfo.xml 파일과 표지 이미지를 가져오는 중입니다.", + "Downloading gameinfo.xml for {source_name}": "{source_name}의 gameinfo.xml을 다운로드 중", + "Installing {port_name}": "{port_name} 설치 중", + "Uninstalling {port_name}": "{port_name} 삭제 중", + "Updating all port sources:": "모든 포트 소스 업데이트 중:", + "Checking {runtime_name}": "{runtime_name} 확인 중", + "** NO PORT **": "** 포트 없음 **", + "Ready to Run": "실행 준비 완료", + "Requires Files": "필요한 파일", + "Installed": "설치됨", + "Missing": "누락", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "포트가 추가 또는 제거되었습니다. 게임 목록을 업데이트하여 새로운 게임을 확인하거나 제거된 게임을 삭제하세요.", + "Danish": "덴마크어", + "German": "독일어", + "English": "영어", + "Spanish": "스페인어", + "Finnish": "핀란드어", + "French": "프랑스어", + "Italian": "이탈리아어", + "Japanese": "일본어", + "Korean": "한국어", + "Dutch": "네덜란드어", + "Polish": "폴란드어 (폴란드)", + "Portuguese (Portugal)": "포르투갈어(포르투갈)", + "Portuguese (Brazil)": "포르투갈어 (브라질)", + "Romanian": "루마니아어", + "Russian": "러시아어", + "Swedish": "스웨덴어", + "Ukrainian": "우크라이나어", + "Chinese Simplified": "중국어 간체", + "Messages": "메시지", + "Are you sure you want to cancel?": "정말 취소하시겠습니까?", + "Featured Ports": "추천 포트", + "Select": "선택", + "Back": "뒤로", + "No featured ports found, internet connection is required.": "추천 포트가 없습니다. 인터넷 연결이 필요합니다.", + "Filters Scene": "필터 장면", + "Alphabetical": "알파벳순", + "Recently Added": "최근에 주가된", + "Recently Updated": "최근에 업데이트된", + "Action": "액션", + "Adventure": "어드벤처", + "Arcade": "아케이드", + "Casino/Card": "카지노/카드", + "First Person Shooter": "1인칭 슈팅", + "Platformer": "플랫포머", + "Puzzle": "퍼즐", + "Racing": "레이싱", + "Rhythm": "리듬", + "Role Playing Game": "롤플레잉 게임", + "Simulation": "시뮬레이션", + "Sports": "스포츠", + "Strategy": "전략", + "Visual Novel": "비주얼 노벨", + "Other": "기타", + "Files Required": "필요한 파일", + "Experimental": "실험적인", + "Broken Ports": "깨진 포트", + "Free game, all files included.": "무료 게임, 모든 파일 포함.", + "Demo files included.": "데모 파일이 포함됨.", + "Free external assets needed.": "무료 외부 자료 필요.", + "Paid external assets needed.": "유료 외부 자료 필요.", + "{runtime_name} Runtime": "{runtime_name} 런타임", + "ARM 32bit": "ARM 32비트", + "ARM 64bit": "ARM 64비트", + "x86 64bit": "x86 64비트", + "Sort:": "종류:", + "Clear Filters": "필터 지우기", + "Genres:": "장르:", + "Attributes:": "속성:", + "Architecture:": "아키텍처:", + "Availability:": "유효성:", + "Porters:": "포터:", + "Delete": "삭제", + "Upper Case": "대문자", + "Space": "스페이스", + "Enter": "선택", + "Lower Case": "소문자", + "Language Select": "언어 선택", + "{lang_name} (Selected)": "{lang_name} (선택됨)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "언어를 변경하시겠습니까?\n\n적용하려면 다시 시작해야 합니다.", + "Main Menu": "메인 메뉴", + "Hand curated lists of ports": "엄선한 포트 목록", + "All Ports": "모든 포트", + "List all ports available on PortMaster.": "포트 마스터에서 사용 가능한 모든 포트 목록", + "Ready to Run Ports": "실행 준비가 된 포트", + "List all ports that are ready to play!": "실행할 준비가 된 모든 포트를 나열하세요!", + "Manage Ports": "포트 관리", + "Update / Uninstall Ports": "포트 업데이트/제거", + "Options": "옵션", + "PortMaster Options": "옵션", + "Exit": "나가기", + "Quit PortMaster": "종료", + "Quit": "나가기", + "Secret Mode {secret_mode}": "비밀 방식 {secret_mode}", + "Enabled": "켜기", + "Disabled": "끄기", + "Are you sure you want to exit PortMaster?": "종료하시겠습니까?", + "Options Menu": "옵션 메뉴", + "Interface": "인터페이스", + "Choose Language": "언어 선택", + "Select the language PortMaster uses.": "사용할 언어를 선택하세요.", + "Select Theme": "테마 선택", + "Select a theme for PortMaster.": "사용할 테마를 선택하세요.", + "Select Color Scheme": "색 구성표 선택", + "Select a colour scheme for PortMaster": "색상 구성표를 선택하세요.", + "Audio": "오디오", + "Music: ": "음악:", + "Enable or Disable background music in PortMaster.": "배경 음악을 활성화 또는 비활성화합니다. ", + "Sound FX: ": "효과음: ", + "Enable or Disable soundfx in PortMaster.": "효과음을 활성화 또는 비활성화합니다.", + "Port Metadata": "포트 메타데이터", + "Metadata Update: ": "메타데이터 업데이트:", + "Auto": "자동", + "Manual": "수동", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "포트마스터는 포트가 설치될 때 메타데이터를 업데이트하여 장치에서 포트를 탐색할 때 포트 이미지와 설명을 표시합니다.", + "Metadata Refresh": "메타데이터 새로 고침", + "Manually update port metadata with missing/updated information and artwork.": "누락/업데이트된 정보 및 아트워크를 사용하여 포트 메타데이터를 수동으로 업데이트합니다.", + "Ports Location: ": "포트 위치:", + "Roms section": "롬 섹션", + "Ports tab": "포트 탭", + "Location where ports should be installed to.": "포트를 설치해야 하는 위치입니다.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "시스템", + "Runtime Manager": "런타임 관리자", + "Manage port runtimes.": "포트 런타임을 관리합니다.", + "Experimental Ports: ": "실험 포트:", + "Show or hide experimental ports.": "실험 포트를 표시하거나 숨깁니다.", + "Update Ports": "포트 업데이트", + "Fetch latest ports information.": "최신 포트 정보를 가져옵니다.", + "Update PortMaster": "포트마스터 업데이트", + "Force check for a new PortMaster version.": "새로운 포트마스터 버전을 강제로 확인합니다.", + "Restore PortMaster": "포트마스터 복원", + "This will restore PortMaster to the latest stable version of PortMaster.": "이렇게 하면 포트마스터가 최근 안정적인 버전의 포트마스터로 복원됩니다.", + "Release Channel: {channel}": "업데이트 채널: {channel}", + "Change release channel of PortMaster, either beta or stable.": "업데이트 채널을 베타 또는 안정된 버전으로 변경합니다.", + "Enable Alpha Releases": "알파 업데이트 활성화", + "Change release channel of PortMaster to alpha.": "포트마스터의 업데이트 채널을 알파로 변경합니다.", + "Controller Mode: {controller_mode}": "컨트롤러 방식: {controller_mode}", + "Toggle between various controller layouts.": "다양한 컨트롤러 레이아웃 사이를 전환합니다.", + "Secret Options": "비밀 옵션", + "All Ports: ": "모든 포트: ", + "Show all ports, ignoring requirements.": "요구 사항을 무시하고 모든 포트를 표시합니다.", + "CWTBE Mode: ": "CWTBE 방식:", + "Enable gptokeyb2 by default.": "기본적으로 gptokeyb2를 활성화합니다.", + "Delete PortMaster Config": "구성 삭제", + "This can break stuff, don't touch unless you know what you are doing.": "프로그램이 손상될 수 있으니, 잘 모르는 사람은 만지지 마세요.", + "Delete PortMaster Runtimes": "런타임 삭제", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "복원하시겠습니까?\n\n이렇게 하려면 특별히 제작된 포트를 설치해야 합니다.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "\"Restore PortMaster.sh\" 포트가 설치되었습니다. \n\n이 포트를 실행하여 설치 복원합니다.", + "PortMaster will now quit so you can run the restoration port.": "복원 포트를 실행할 수 있도록 포트마스터가 종료됩니다.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "알파 업데이트 채널로 변경하시겠습니까?\n\n포트마스터는 업그레이드 또는 다운그레이드됩니다.\n\n이것은 프로그램을 손상시킬 수 있습니다. 경고했습니다.", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "업데이트 채널을 {current_channel}에서 {new_channel}로 변경하시겠습니까?\n\n이에 따라 업그레이드 또는 다운그레이드됩니다.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "gamelist.xml을 업데이트하시겠습니까?\n\n이렇게 하면 스크래핑된 아트워크나 포트 정보가 무시되고 50MB 이상의 다운로드가 필요합니다.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "포트를 {from_mode}에서 {to_mode}로 이동하시겠습니까?", + "SDCARD 1": "SD카드 1", + "SDCARD 2": "SD카드 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "{to_loc}에서 포트를 관리하고 설치하시겠습니까?\n\n이미 설치된 포트는 이동하지 않습니다.\n이 기능을 적용하려면 포트마스터가 재시작됩니다.", + "Deleting Runtimes:": "런타임 삭제:", + "Source Manager": "소스 관리자", + "Port Info Popup": "포트 정보 팝업", + "Hide Info": "정보 숨기기", + "Reinstall": "다시 설치", + "Uninstall": "제거", + "Install": "설치", + "Port Info": "포트 정보", + "Show Info": "정보 보기", + "Are you sure you want to uninstall {port_name}?": "{port_name} 제거하시겠습니까?", + "NO PORTS": "포트 없음", + "** NO PORTS FOUND **": "** 포트를 찾을 수 없음 **", + "Download ports first.": "포트를 먼저 다운로드합니다.", + "Try removing some filters.": "일부 필터를 제거해보세요.", + "Ports List": "포트 목록", + "Filters": "필터", + "Download All": "모두 다운로드", + "Used": "사용됨", + "Not Used": "사용되지 않음", + "Check": "확인", + "Are you sure you want to download and verify all runtimes?": "모든 런타임을 다운로드하고 검증하시겠습니까?", + "Deleted runtime {runtime}": "{runtime} 삭제된 런타임", + "Disclaimer": "면책 조항", + "Accept": "수락", + "{theme_name} (Selected)": "{theme_name} (선택됨)", + "Download": "다운로드", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "테마를 변경하시겠습니까?\n\n적용하려면 다시 시작해야 합니다.", + "Select Colour Scheme": "색상 구성표 선택", + "{item_name} (Selected)": "{item_name} (선택됨)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "테마 색 구성표를 변경하시겠습니까?\n\n적용하려면 다시 시작해야 합니다.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel}에서 {new_release_channel}로 전환하고 있습니다.\n\n다운그레이드하시겠습니까?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "새로운 ({portmaster_version}) 버전이 있습니다.\n\n업그레이드하시겠습니까?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "포트마스터를 다시 설치 하겠습니까?\n\n {release_channel} 채널에서 {portmaster_version}로 다시 설치합니다." +} diff --git a/PortMaster/pylibs/locales/ko_KR.themes.json b/PortMaster/pylibs/locales/ko_KR.themes.json new file mode 100644 index 00000000..eab21b1b --- /dev/null +++ b/PortMaster/pylibs/locales/ko_KR.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "시간: {system.time_24hr}\n배터리: {system.battery_level}\n공간: {system.free_space} 여유 공간", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "장르: {port_info.genres} 다운로드 크기: {port_info.download_size}{if:port_info.runtime} 실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}사용 가능한 이식본: {ports_list.filter_ports} / {ports_list.total_ports}\n필터: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "여유 공간: {system.free_space}\n\n포터: {port_info.porter}\n장르: {port_info.genres}\n다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n설명서: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\n공간: {system.free_space} 여유 공간 / {system.total_space} 총 공간\n시간: {system.time_24hr}\n배터리: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "제작자: {theme_info.creator}\n상태: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\n공간: {system.free_space} 여유 공간", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "필터: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "장르: {port_info.genres}\n다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "시간: {system.time_24hr} 배터리: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "제목: {port_info.title}\n포터: {port_info.porter}\n장르: {port_info.genres}\n다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n설명: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}사용 가능한 포트: {ports_list.filter_ports} / {ports_list.total_ports}\n필터: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "장르: {port_info.genres}\n{if:port_info.install_size}설치된 크기: {port_info.install_size}{else}다운로드 크기: {port_info.download_size}{endif}{if:port_info.runtime}\n런타임: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "시간: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\n장치: {system.device_name}\n배터리: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\n공간: {system.free_space} 여유 공간 / {system.total_space} 총 공간", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• 포터: {port_info.porter}\n• 장르: {port_info.genres}\n• 설명: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• 장르: {port_info.genres}\n• 다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n• 실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n• 설명: {port_info.description}\n• 사용 방법:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• 제작자: {theme_info.creator}\n• 상태: {theme_info.status}\n• 설명:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "이름: {runtime_info.name}\n상태: {runtime_info.status}\n포트: {runtime_info.ports}\n다운로드 크기: {runtime_info.download_size}\n설치 크기: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/messages.po deleted file mode 100644 index ca153796..00000000 --- a/PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1252 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Korean\n" -"Language: ko_KR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ko\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "덴마크어" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "독일어" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "영어" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "스페인어" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "핀란드어" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "프랑스어" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "이탈리아어" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "일본어" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "한국어" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "네덜란드어" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "폴란드어 (폴란드)" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "포르투갈어(포르투갈)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "포르투갈어 (브라질)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "루마니아어" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "러시아어" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "스웨덴어" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "우크라이나어" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "중국어 간체" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "알 수 없는 IP" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/A" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** 포트 없음 **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "실행 준비 완료" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "필요한 파일" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "설치됨" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "누락" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "업데이트 이용 가능" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "설치되지 않음" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "확인" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "취소" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "마지막 gameinfo.xml 파일과 표지 이미지를 가져오는 중입니다." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "{source_name}의 gameinfo.xml을 다운로드 중" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "{port_name} 설치 중" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "{port_name} 삭제 중" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "모든 포트 소스 업데이트 중:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "{runtime_name} 확인 중" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "{old_release_channel}에서 {new_release_channel}로 전환하고 있습니다.\n\n" -"다운그레이드하시겠습니까?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "새로운 ({portmaster_version}) 버전이 있습니다.\n\n" -"업그레이드하시겠습니까?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "포트마스터를 다시 설치 하겠습니까?\n\n" -" {release_channel} 채널에서 {portmaster_version}로 다시 설치합니다." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "포트가 추가 또는 제거되었습니다. 게임 목록을 업데이트하여 새로운 게임을 확인하거나 제거된 게임을 삭제하세요." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "불러오는 중..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "다운로드 검증에 실패했습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "정보 불러오는 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "최신 추천 포트를 가져오는 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "소스 불러오는 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "포트 불러오는 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "{download_name} 테마를 설치 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "설치 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "{download_name!r} 테마 설치에 성공했습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "{download_name} 설치 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "{download_name!r} 포트가 정상적으로 설치되었습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "안녕하세요.\n\n" -"기기와 호환되지 않는 이식본을 수동으로 설치하고 계신 것 같습니다.\n\n" -"문제가 발생하면 Discord에 보고하지 마세요. 조롱을 당할 수 있습니다.\n\n" -"설치를 계속하시겠습니까?" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "설치에 실패했습니다. 파일 삭제 중..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "{download_name} 포트 설치에 실패했습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "{download_name!r} 포트와 {runtime_name!r} 가 정상적으로 설치되었습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "{download_name!r} 포트는 정상적으로 설치되었으나, {runtime_name!r} 설치에 실패했습니다!\n\n" -"재시도하려면 다시 설치하거나 위키에서 도움말을 확인하세요." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "{runtime} 포트에 비정상적인 런타임이 포함되어 있습니다. 게임이 올바르게 동작하지 않을 수 있습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "{runtime} 포트에 알 수 없는 런타임이 포함되어 있습니다. 게임이 올바르게 동작하지 않을 수 있습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "검증 완료" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "미 검증" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "파손" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "오프라인 모드에서는 런타임을 다운로드할 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "런타임 {runtime}를 검증 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "검증 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "{runtime} 다운로드를 찾을 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "{device_arch}에서 {runtime}을 다운로드 할 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} 검증 성공" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} 검증 완료" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "{runtime} 업데이트 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "런타임 {runtime}가 파손되어 있어 다시 설치합니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "런타임 {runtime} 다운로드 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "{runtime} 다운로드할 수 없습니다. 게임이 제대로 실행되지 않을 수 있습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} 다운로드 성공" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "오프라인 모드에서는 다운로드할 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "{port_name} 소스를 찾을 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "알 수 없는 포트 {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "{port_name} 제거 중" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "{port_name} 설치 제거 중 오류\n\n" -"{error}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} 제거 성공" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"그렇게 하기 위해:\n" -"메뉴 -> 유틸리티 -> 롬 디렉토리 다시 스캔" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "캐시가 오래되었습니다." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "최초 확인" - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "자동 업데이트" - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "업데이트 중" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "이미 최신 상태입니다." - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "최신 정보를 가져오는 중" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "완료." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "{port_name} 찾을 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "{port_name} 검증 정보를 찾을 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "{port_name} 검증 정보를 다운로드할 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "성공!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "정보 가져오는 중" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: 최신 포트 가져오는 중" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "정보 가져오는 중" - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "검증 파일을 다운로드할 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Zip 파일이 아니면 다운로드할 수 없습니다." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "파일을 다운로드할 수 없습니다.[{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "{file_url} - ({total_length_mb}) 다운로드 중" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "파일 다운로드 중" - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "다운로드 실패: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "파일 검증을 완료했습니다." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "다운로드를 검증할 수 없습니다." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "메인 메뉴" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "면책 조항" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "수락" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "나가기" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "종료하시겠습니까?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "추천 포트" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "엄선한 포트 목록" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "모든 포트" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "포트 마스터에서 사용 가능한 모든 포트 목록" - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "실행 준비가 된 포트" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "실행할 준비가 된 모든 포트를 나열하세요!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "포트 관리" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "포트 업데이트/제거" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "옵션" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "옵션" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "나가기" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "종료" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "선택" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "비밀 방식 {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "켜기" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "끄기" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "옵션 메뉴" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "인터페이스" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "언어 선택" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "사용할 언어를 선택하세요." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "테마 선택" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "사용할 테마를 선택하세요." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "색 구성표 선택" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "색상 구성표를 선택하세요." - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "오디오" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "음악:" - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "배경 음악을 활성화 또는 비활성화합니다. " - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "효과음: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "효과음을 활성화 또는 비활성화합니다." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "포트 메타데이터" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "메타데이터 업데이트:" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "자동" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "수동" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "포트마스터는 포트가 설치될 때 메타데이터를 업데이트하여 장치에서 포트를 탐색할 때 포트 이미지와 설명을 표시합니다." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "메타데이터 새로 고침" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "누락/업데이트된 정보 및 아트워크를 사용하여 포트 메타데이터를 수동으로 업데이트합니다." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "포트 위치:" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "롬 섹션" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "포트 탭" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "포트를 설치해야 하는 위치입니다." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "시스템" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "런타임 관리자" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "포트 런타임을 관리합니다." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "실험 포트:" - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "실험 포트를 표시하거나 숨깁니다." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "포트 업데이트" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "최신 포트 정보를 가져옵니다." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "포트마스터 업데이트" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "새로운 포트마스터 버전을 강제로 확인합니다." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "포트마스터 복원" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "이렇게 하면 포트마스터가 최근 안정적인 버전의 포트마스터로 복원됩니다." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "업데이트 채널: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "업데이트 채널을 베타 또는 안정된 버전으로 변경합니다." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "알파 업데이트 활성화" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "포트마스터의 업데이트 채널을 알파로 변경합니다." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "컨트롤러 방식: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "다양한 컨트롤러 레이아웃 사이를 전환합니다." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "비밀 옵션" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "모든 포트: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "요구 사항을 무시하고 모든 포트를 표시합니다." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "CWTBE 방식:" - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "기본적으로 gptokeyb2를 활성화합니다." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "구성 삭제" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "프로그램이 손상될 수 있으니, 잘 모르는 사람은 만지지 마세요." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "런타임 삭제" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "뒤로" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "복원하시겠습니까?\n\n" -"이렇게 하려면 특별히 제작된 포트를 설치해야 합니다." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "\"Restore PortMaster.sh\" 포트가 설치되었습니다. \n\n" -"이 포트를 실행하여 설치 복원합니다." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "복원 포트를 실행할 수 있도록 포트마스터가 종료됩니다." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "알파 업데이트 채널로 변경하시겠습니까?\n\n" -"포트마스터는 업그레이드 또는 다운그레이드됩니다.\n\n" -"이것은 프로그램을 손상시킬 수 있습니다. 경고했습니다." - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "업데이트 채널을 {current_channel}에서 {new_channel}로 변경하시겠습니까?\n\n" -"이에 따라 업그레이드 또는 다운그레이드됩니다." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "gamelist.xml을 업데이트하시겠습니까?\n\n" -"이렇게 하면 스크래핑된 아트워크나 포트 정보가 무시되고 50MB 이상의 다운로드가 필요합니다." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "포트를 {from_mode}에서 {to_mode}로 이동하시겠습니까?" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SD카드 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SD카드 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "{to_loc}에서 포트를 관리하고 설치하시겠습니까?\n\n" -"이미 설치된 포트는 이동하지 않습니다.\n" -"이 기능을 적용하려면 포트마스터가 재시작됩니다." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "런타임 삭제:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "소스 관리자" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "모두 다운로드" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "사용됨" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "사용되지 않음" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "확인" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "제거" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "설치" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "모든 런타임을 다운로드하고 검증하시겠습니까?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "{runtime} 삭제된 런타임" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (선택됨)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "선택" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "다운로드" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "테마를 변경하시겠습니까?\n\n" -"적용하려면 다시 시작해야 합니다." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "색상 구성표 선택" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (선택됨)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "테마 색 구성표를 변경하시겠습니까?\n\n" -"적용하려면 다시 시작해야 합니다." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "언어 선택" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (선택됨)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "언어를 변경하시겠습니까?\n\n" -"적용하려면 다시 시작해야 합니다." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "삭제" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "대문자" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "스페이스" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "소문자" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "추천 포트가 없습니다. 인터넷 연결이 필요합니다." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "포트 없음" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** 포트를 찾을 수 없음 **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "포트를 먼저 다운로드합니다." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "일부 필터를 제거해보세요." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "정보 보기" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "포트 목록" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "필터" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "포트 정보 팝업" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "정보 숨기기" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "다시 설치" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "포트 정보" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "{port_name} 제거하시겠습니까?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "필터 장면" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "알파벳순" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "최근에 주가된" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "최근에 업데이트된" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "액션" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "어드벤처" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "아케이드" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "카지노/카드" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "1인칭 슈팅" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "플랫포머" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "퍼즐" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "레이싱" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "리듬" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "롤플레잉 게임" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "시뮬레이션" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "스포츠" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "전략" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "비주얼 노벨" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "기타" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "필요한 파일" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "실험적인" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "깨진 포트" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "무료 게임, 모든 파일 포함." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "데모 파일이 포함됨." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "무료 외부 자료 필요." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "유료 외부 자료 필요." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} 런타임" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32비트" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64비트" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64비트" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "종류:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "필터 지우기" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "장르:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "속성:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "아키텍처:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "유효성:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "포터:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "메시지" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "정말 취소하시겠습니까?" - diff --git a/PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/themes.po deleted file mode 100644 index 6cfae773..00000000 --- a/PortMaster/pylibs/locales/ko_KR/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Korean\n" -"Language: ko_KR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ko\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "장르: {port_info.genres} 다운로드 크기: {port_info.download_size}{if:port_info.runtime} 실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}사용 가능한 이식본: {ports_list.filter_ports} / {ports_list.total_ports}\n" -"필터: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "여유 공간: {system.free_space}\n\n" -"포터: {port_info.porter}\n" -"장르: {port_info.genres}\n" -"다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n" -"실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"설명서: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"공간: {system.free_space} 여유 공간 / {system.total_space} 총 공간\n" -"시간: {system.time_24hr}\n" -"배터리: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "제작자: {theme_info.creator}\n" -"상태: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "시간: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"장치: {system.device_name}\n" -"배터리: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"공간: {system.free_space} 여유 공간" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "필터: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "장르: {port_info.genres}\n" -"다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n" -"실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "시간: {system.time_24hr} 배터리: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"공간: {system.free_space} 여유 공간 / {system.total_space} 총 공간" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• 포터: {port_info.porter}\n" -"• 장르: {port_info.genres}\n" -"• 설명: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• 장르: {port_info.genres}\n" -"• 다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n" -"• 실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• 설명: {port_info.description}\n" -"• 사용 방법:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• 제작자: {theme_info.creator}\n" -"• 상태: {theme_info.status}\n" -"• 설명:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "이름: {runtime_info.name}\n" -"상태: {runtime_info.status}\n" -"포트: {runtime_info.ports}\n" -"다운로드 크기: {runtime_info.download_size}\n" -"설치 크기: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "시간: {system.time_24hr}\n" -"배터리: {system.battery_level}\n" -"공간: {system.free_space} 여유 공간" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "제목: {port_info.title}\n" -"포터: {port_info.porter}\n" -"장르: {port_info.genres}\n" -"다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n" -"실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"설명: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}사용 가능한 포트: {ports_list.filter_ports} / {ports_list.total_ports}\n" -"필터: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "장르: {port_info.genres}\n" -"{if:port_info.install_size}설치된 크기: {port_info.install_size}{else}다운로드 크기: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"런타임: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/nl_NL.json b/PortMaster/pylibs/locales/nl_NL.json new file mode 100644 index 00000000..2b97e084 --- /dev/null +++ b/PortMaster/pylibs/locales/nl_NL.json @@ -0,0 +1,254 @@ +{ + "Loading...": "Laden...", + "Loading Sources.": "Bronnen worden geladen.", + "Download validation failed.": "Download validatie mislukt.", + "Loading Info.": "Informatie laden.", + "Fetching latest featured ports.": "Ophalen van de nieuwste ports.", + "Installing Theme {download_name}.": "Thema {download_name} wordt geïnstalleerd.", + "Installing": "Aan het installeren", + "Theme {download_name!r} installed successfully.": "Thema {download_name!r} is succesvol geïnstalleerd.", + "Port {download_name} installed failed.": "Port {download_name} installatie mislukt.", + "Installing {download_name}.": "{download_name} wordt geïnstalleerd.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} is succesvol geïnstalleerd.", + "Installation failed, removing files...": "Installatie mislukt, bestanden verwijderen...", + "Okay": "Oké", + "Cancel": "Annuleren", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} en {runtime_name!r} succesvol geïnstalleerd.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} is succesvol geïnstalleerd, maar {runtime_name!r} installatie is mislukt!!!\n\nHerinstalleer om het opnieuw te proberen, of controleer de wiki voor hulp.", + "Unable to download in offline mode.": "Kan geen port downloaden in offline modus.", + "Unable to find a source for {port_name}": "Kan geen bron vinden voor {port_name}", + "Unknown port {port_name}": "Onbekende port {port_name}", + "Removing {port_name}": "Verwijderen van {port_name}", + "Successfully uninstalled {port_name}": "{port_name} succesvol verwijderd", + "Loading Ports.": "Ports worden geladen.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Poot {runtime} bevat een verkeerde runtime en het spel kan mogelijk niet correct worden uitgevoerd.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Port {runtime} bevat een onbekende runtime, het spel kan mogelijk niet correct worden uitgevoerd.", + "Not Installed": "Niet Geïnstalleerd", + "Update Available": "Update Beschikbaar", + "Verified": "Geverifiëerd", + "Unverified": "Niet geverifiëerd", + "Broken": "Defect", + "Unable do download a runtime when in offline mode.": "Kan geen runtime downloaden in offline modus.", + "Verifying runtime {runtime}": "Runtime {runtime} verifiëren", + "Verifying": "Verifiëren", + "Unable to find a download for {runtime}.": "Kan geen download vinden voor {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Kan {runtime} niet downloaden in {device_arch}.", + "Verified {runtime} successfully.": "{runtime} succesvol geverifieerd.", + "Verified {runtime}.": "{runtime} geverifiëerd.", + "Updating {runtime}.": "{runtime} bijwerken.", + "Runtime {runtime} is broken, reinstalling.": "Runtime {runtime} is defect, herinstalleert.", + "Downloading runtime {runtime}.": "Runtime {runtime} aan het downloaden.", + "Unable to download {runtime}, game may not run correctly.": "Kan {runtime} niet downloaden, het spel kan mogelijk niet correct worden uitgevoerd.", + "Successfully downloaded {runtime}.": "{runtime} succesvol gedownload.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nOm dit te doen:\nMENU → HULPPROGRAMMA'S → Rom-map opnieuw scannen", + "Unable to download verification file.": "Kan verificatiebestand niet downloaden.", + "Unable to download non zip files.": "Kan geen non-zip bestanden downloaden.", + "Cache out of date.": "Cache verouderd.", + "First check.": "Eerste controle.", + "Auto Update.": "Automatisch Bijwerken.", + "Updating": "Bijwerken", + "Up to date already": "Al bijgewerkt", + "Fetching latest info": "Nieuwste informatie ophalen", + "Done.": "Klaar.", + "Fetching info": "Informatie ophalen", + "Unable to find {port_name}.": "Kan {port_name} niet vinden.", + "Unable to download file. [{status_code}]": "Kan bestand niet downloaden. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Downloaden {file_url} - ({total_length_mb})", + "Downloading file.": "Bestand wordt gedownload.", + "Download failed: {err}": "Downloaden mislukt: {err}", + "Passed file validation.": "Bestandsvalidatie goedgekeurd.", + "Unable to validate download.": "Kan download niet valideren.", + "Unknown IP": "Onbekend IP-adres", + "N/A": "N.v.t.", + "Fetching latest gameinfo.xml files and cover images.": "Meest recente gameinfo.xml bestanden en cover afbeeldingen ophalen.", + "Downloading gameinfo.xml for {source_name}": "Downloaden van gameinfo.xml voor {source_name}", + "Installing {port_name}": "Installeert {port_name}", + "Uninstalling {port_name}": "Deïnstalleert {port_name}", + "Updating all port sources:": "Alle port bronnen aan het bijwerken:", + "Checking {runtime_name}": "{runtime_name} controlerenIn order to do so:", + "** NO PORT **": "** GEEN PORT **", + "Ready to Run": "Klaar om te Spelen", + "Requires Files": "Bestanden Vereist", + "Installed": "Geïnstalleerd", + "Missing": "Ontbrekend", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Ports zijn toegevoegd of verwijderd, werk jouw gamelijst bij om de nieuwe games te zien of de verwijderde games uit de lijst te halen.", + "Danish": "Deens", + "German": "Duits", + "English": "Engels", + "Spanish": "Spaans", + "Finnish": "Fins", + "French": "Frans", + "Italian": "Italiaans", + "Japanese": "Japans", + "Korean": "Koreaans", + "Dutch": "Nederlands", + "Polish": "Pools", + "Portuguese (Portugal)": "Portugees (Portugal)", + "Portuguese (Brazil)": "Portugees (Brazilië)", + "Russian": "Russisch", + "Swedish": "Zweeds", + "Ukrainian": "Oekraïens", + "Chinese Simplified": "Chinees (vereenvoudigd)", + "Messages": "Berichten", + "Are you sure you want to cancel?": "Weet u zeker dat u wilt annuleren?", + "Featured Ports": "Aanbevolen Ports", + "Select": "Selecteer", + "Back": "Terug", + "No featured ports found, internet connection is required.": "Geen aanbevolen ports gevonden, internetverbinding is vereist.", + "Filters Scene": "Scène Filters", + "Alphabetical": "Alfabetisch", + "Recently Added": "Recent Toegevoegd", + "Recently Updated": "Recent Bijgewerkt", + "Action": "Actie", + "Adventure": "Avontuur", + "Arcade": "Arcade", + "Casino/Card": "Casino/Kaart", + "First Person Shooter": "First Person Shooter", + "Platformer": "Platformer", + "Puzzle": "Puzzel", + "Racing": "Racing", + "Rhythm": "Ritme", + "Role Playing Game": "Rollenspel", + "Simulation": "Simulatie", + "Sports": "Sporten", + "Strategy": "Strategie", + "Visual Novel": "Visuele Roman", + "Other": "Andere", + "Files Required": "Bestanden Verplicht", + "Experimental": "Experimenteel ", + "Broken Ports": "Gebroken Ports", + "Free game, all files included.": "Gratis spel, alle bestanden inbegrepen.", + "Demo files included.": "Demo bestanden inbegrepen.", + "Free external assets needed.": "Gratis externe bestanden nodig.", + "Paid external assets needed.": "Betaalde externe bestanden nodig.", + "{runtime_name} Runtime": "{runtime_name} Runtime", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Sort:": "Sorteer:", + "Clear Filters": "Filters Wissen", + "Genres:": "Genres:", + "Attributes:": "Attributen:", + "Architecture:": "Architectuur:", + "Availability:": "Beschikbaarheid:", + "Porters:": "Porters:", + "Delete": "Verwijder", + "Upper Case": "Hoofdletters", + "Space": "Spatie", + "Enter": "Ga", + "Lower Case": "Kleine letters", + "Language Select": "Taal selecteren", + "{lang_name} (Selected)": "{lang_name} (Geselecteerd)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Wilt u de taal wijzigen?\n\nU moet opnieuw opstarten voordat het in effect gaat.", + "Main Menu": "Hoofdmenu", + "Hand curated lists of ports": "Hand samengestelde lijst van ports", + "All Ports": "Alle ports", + "List all ports available on PortMaster.": "Toon alle beschikbare ports op PortMaster.", + "Ready to Run Ports": "Klaar om te Spelen Ports", + "List all ports that are ready to play!": "Lijst alle ports die klaar zijn om te spelen!", + "Manage Ports": "Beheer Ports", + "Update / Uninstall Ports": "Update / Verwijder Ports", + "Options": "Opties", + "PortMaster Options": "PortMaster Opties", + "Exit": "Sluiten", + "Quit PortMaster": "Sluit PortMaster", + "Quit": "Sluiten", + "Secret Mode {secret_mode}": "Geheime Modus {secret_mode}", + "Enabled": "Ingeschakeld", + "Disabled": "Uitgeschakeld", + "Are you sure you want to exit PortMaster?": "Weet u zeker dat u PortMaster wilt afsluiten?", + "Options Menu": "Opties Menu", + "Interface": "Interface", + "Choose Language": "Kies Taal", + "Select the language PortMaster uses.": "Selecteer de taal voor PortMaster.", + "Select Theme": "Selecteer Thema", + "Select a theme for PortMaster.": "Selecteer een thema voor PortMaster.", + "Select Color Scheme": "Selecteer Kleurenschema", + "Select a colour scheme for PortMaster": "Selecteer een kleurenschema voor PortMaster", + "Audio": "Audio", + "Music: ": "Muziek: ", + "Enable or Disable background music in PortMaster.": "Achtergrondmuziek voor PortMaster in- of uitschakelen.", + "Sound FX: ": "Geluidseffecten: ", + "Enable or Disable soundfx in PortMaster.": "Geluidseffecten voor PortMaster in- of uitschakelen.", + "Port Metadata": "Port Metadata", + "Metadata Update: ": "Metadata Update: ", + "Auto": "Auto", + "Manual": "Handmatig", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster updates metadata wanneer ports zijn geïnstalleerd zodat deze portafbeeldingen en -beschrijvingen weergeven tijdens het browsen van ports op uw apparaat.", + "Metadata Refresh": "Metadata Verversen", + "Manually update port metadata with missing/updated information and artwork.": "Werk handmatig de poortmetagegevens bij met bijgewerkte informatie en opmaak.", + "Ports Location: ": "Locatie ports: ", + "Roms section": "Roms sectie", + "Ports tab": "Ports tab", + "Location where ports should be installed to.": "Locatie waar ports geïnstalleerd moeten worden.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "Systeem", + "Runtime Manager": "Runtime Beheerder", + "Manage port runtimes.": "Beheer port runtimes.", + "Experimental Ports: ": "Experimentele Ports: ", + "Show or hide experimental ports.": "Toon of verberg experimentele ports.", + "Update Ports": "Ports Bijwerken", + "Fetch latest ports information.": "Haal recente poort informatie op.", + "Update PortMaster": "PortMaster Bijwerken", + "Force check for a new PortMaster version.": "Forceer check voor een nieuwe PortMaster versie.", + "Restore PortMaster": "PortMaster herstellen", + "This will restore PortMaster to the latest stable version of PortMaster.": "Dit zal PortMaster herstellen naar de laatste stabiele versie van PortMaster.", + "Release Channel: {channel}": "Release Kanaal: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Verander het release-kanaal van PortMaster, bèta of stabiel.", + "Enable Alpha Releases": "Activeer Alpha Releases", + "Change release channel of PortMaster to alpha.": "Verander het release-kanaal van PortMaster naar alpha.", + "Controller Mode: {controller_mode}": "Controller Modus: {controller_mode}", + "Toggle between various controller layouts.": "Schakel tussen verschillende controller lay-outs.", + "Secret Options": "Geheime Opties", + "All Ports: ": "Alle Ports: ", + "Show all ports, ignoring requirements.": "Toon alle ports, negeer vereisten.", + "CWTBE Mode: ": "CWTBE Modus: ", + "Enable gptokeyb2 by default.": "Gptokeyb2 als standaard inschakelen.", + "Delete PortMaster Config": "Verwijder PortMaster Configuratie", + "This can break stuff, don't touch unless you know what you are doing.": "Dit kan dingen breken, raak niet aan tenzij je weet wat je doet.", + "Delete PortMaster Runtimes": "Verwijder PortMaster Runtimes", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Weet u zeker dat u PortMaster wilt herstellen?\n\nHiervoor moet u een speciaal gemaakte port uitvoeren die zal worden geïnstalleerd.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Een speciale port is geïnstalleerd genaamd \"Restore PortMaster.sh\"\n\nVoer deze poort uit om uw PortMaster installatie te herstellen.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster wordt nu afgesloten zodat u de herstelport kunt uitvoeren.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Weet u zeker dat u wilt veranderen naar het alpha-release kanaal?\n\nPortMaster zal worden geüpgraded of gedegradeerd.\n\nDIT KAN EN ZAL DINGEN BREKEN, U BEN GEWAARSCHUWD", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Weet u zeker dat u het release-kanaal van {current_channel} naar {new_channel} wilt wijzigen?\n\nPortMaster zal worden geüpgraded of gedegradeerd naar toepassing.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Weet u zeker dat u uw gamelist.xml wilt updaten?\n\nDit overschrijft alle gescrapede bestands- of portinformatie en vereist een download van 50+ MB.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Weet u zeker dat u ports van {from_mode} naar {to_mode} wilt verplaatsen", + "SDCARD 1": "SD KAART 1", + "SDCARD 2": "SD KAART 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Weet je zeker dat je poorten wilt beheren en installeren op {to_loc}?\n\nReeds geïnstalleerde poorten worden niet verplaatst.\nPortMaster zal opnieuw opstarten om dit door te voeren.", + "Deleting Runtimes:": "Runtimes verwijderen:", + "Source Manager": "Bron Beheerder", + "Port Info Popup": "Port Info Popup", + "Hide Info": "Verberg informatie", + "Reinstall": "Herinstalleer", + "Uninstall": "Verwijder", + "Install": "Installeer", + "Port Info": "Port Info", + "Show Info": "Toon info", + "Are you sure you want to uninstall {port_name}?": "Weet u zeker dat u {port_name} wilt verwijderen?", + "NO PORTS": "GEEN PORTS", + "** NO PORTS FOUND **": "** GEEN PORTS GEVONDEN **", + "Download ports first.": "Download eerst ports.", + "Try removing some filters.": "Probeer enkele filters te verwijderen.", + "Ports List": "Ports Lijst", + "Filters": "Filters", + "Download All": "Download Alles", + "Used": "Gebruikt", + "Not Used": "Niet Gebruikt", + "Check": "Controleer", + "Are you sure you want to download and verify all runtimes?": "Weet je zeker dat je alle runtimes wilt downloaden en verifiëren?", + "Deleted runtime {runtime}": "Runtime {runtime} verwijderd", + "Disclaimer": "Disclaimer", + "Accept": "Accepteren", + "{theme_name} (Selected)": "{theme_name} (Geselecteerd)", + "Download": "Download", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Wilt u het thema wijzigen?\n\nU moet opnieuw opstarten voordat het in effect gaat.", + "Select Colour Scheme": "Selecteer Kleurenschema", + "{item_name} (Selected)": "{item_name} (Geselecteerd)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Wilt u het thema's kleurenschema wijzigen?\n\nU moet opnieuw opstarten voordat het in effect gaat.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "U wisselt van {old_release_channel} naar {new_release_channel} versie.\n\nWilt u downgraden?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Er is een nieuwe versie van PortMaster ({portmaster_version})\n\nWilt u upgraden?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Wilt u PortMaster opnieuw installeren?\n\nDit zal opnieuw installeren van het {release_channel} kanaal naar {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/nl_NL.themes.json b/PortMaster/pylibs/locales/nl_NL.themes.json new file mode 100644 index 00000000..07e1de7e --- /dev/null +++ b/PortMaster/pylibs/locales/nl_NL.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Tijd: {system.time_24hr}\nBatterij: {system.battery_level}\nRuimte: {system.free_space} vrij", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres} Download Grootte: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Beschikbare ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Beschikbare ruimte: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Grootte: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\ninstructie: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nRuimte: {system.free_space} vrij / {system.total_space} totaal\nTijd: {system.time_24hr}\nBatterij: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Auteur: {theme_info.creator}\nStatus: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nRuimte: {system.free_space} beschikbaar", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres} Download Grootte: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Tijd: {system.time_24hr} Batterij: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Beschikbare ruimte: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Grootte: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nInstructie: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Beschikbare ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres}\n{if:port_info.install_size}Geïnstalleerde grootte: {port_info.install_size}{else}Downloadgrootte: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Tijd: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nApparaat: {system.device_name}\nBatterij: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nRuimte: {system.free_space} vrij / {system.total_space} totaal", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Beschrijving: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genres: {port_info.genres}\n• Download grootte: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Beschrijving: {port_info.description}\n• Instructies:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Auteur: {theme_info.creator}\n• Status: {theme_info.status}\n• Beschrijving:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Naam: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownloadgrootte: {runtime_info.download_size}\nInstallatiegrootte: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/messages.po deleted file mode 100644 index 3a3d06db..00000000 --- a/PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Dutch\n" -"Language: nl_NL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: nl\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Deens" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Duits" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Engels" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Spaans" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Fins" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Frans" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italiaans" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japans" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Koreaans" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Nederlands" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Pools" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugees (Portugal)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugees (Brazilië)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Russisch" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Zweeds" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Oekraïens" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chinees (vereenvoudigd)" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "Onbekend IP-adres" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N.v.t." - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** GEEN PORT **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Klaar om te Spelen" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Bestanden Vereist" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Geïnstalleerd" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Ontbrekend" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Update Beschikbaar" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Niet Geïnstalleerd" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Oké" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Annuleren" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Meest recente gameinfo.xml bestanden en cover afbeeldingen ophalen." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Downloaden van gameinfo.xml voor {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Installeert {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Deïnstalleert {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Alle port bronnen aan het bijwerken:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "{runtime_name} controlerenIn order to do so:" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "U wisselt van {old_release_channel} naar {new_release_channel} versie.\n\n" -"Wilt u downgraden?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Er is een nieuwe versie van PortMaster ({portmaster_version})\n\n" -"Wilt u upgraden?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Wilt u PortMaster opnieuw installeren?\n\n" -"Dit zal opnieuw installeren van het {release_channel} kanaal naar {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Ports zijn toegevoegd of verwijderd, werk jouw gamelijst bij om de nieuwe games te zien of de verwijderde games uit de lijst te halen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Laden..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Download validatie mislukt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Informatie laden." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Ophalen van de nieuwste ports." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Bronnen worden geladen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Ports worden geladen." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Thema {download_name} wordt geïnstalleerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Aan het installeren" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Thema {download_name!r} is succesvol geïnstalleerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "{download_name} wordt geïnstalleerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Port {download_name!r} is succesvol geïnstalleerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Installatie mislukt, bestanden verwijderen..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Port {download_name} installatie mislukt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Port {download_name!r} en {runtime_name!r} succesvol geïnstalleerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Port {download_name!r} is succesvol geïnstalleerd, maar {runtime_name!r} installatie is mislukt!!!\n\n" -"Herinstalleer om het opnieuw te proberen, of controleer de wiki voor hulp." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Poot {runtime} bevat een verkeerde runtime en het spel kan mogelijk niet correct worden uitgevoerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Port {runtime} bevat een onbekende runtime, het spel kan mogelijk niet correct worden uitgevoerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Geverifiëerd" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Niet geverifiëerd" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Defect" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Kan geen runtime downloaden in offline modus." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Runtime {runtime} verifiëren" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Verifiëren" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Kan geen download vinden voor {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Kan {runtime} niet downloaden in {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} succesvol geverifieerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} geverifiëerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "{runtime} bijwerken." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Runtime {runtime} is defect, herinstalleert." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Runtime {runtime} aan het downloaden." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Kan {runtime} niet downloaden, het spel kan mogelijk niet correct worden uitgevoerd." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} succesvol gedownload." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Kan geen port downloaden in offline modus." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Kan geen bron vinden voor {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Onbekende port {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Verwijderen van {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} succesvol verwijderd" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Om dit te doen:\n" -"MENU → HULPPROGRAMMA'S → Rom-map opnieuw scannen" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache verouderd." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Eerste controle." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Automatisch Bijwerken." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Bijwerken" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Al bijgewerkt" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Nieuwste informatie ophalen" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Klaar." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Kan {port_name} niet vinden." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Kan de verificatie-informatie voor {port_name} niet vinden." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Kan de verificatie-informatie voor {port_name} niet vinden." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Succes!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Informatie ophalen" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Laatste ports ophalen" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Informatie ophalen." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Kan verificatiebestand niet downloaden." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Kan geen non-zip bestanden downloaden." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Kan bestand niet downloaden. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Downloaden {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Bestand wordt gedownload." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Downloaden mislukt: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Bestandsvalidatie goedgekeurd." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Kan download niet valideren." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Hoofdmenu" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Disclaimer" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Accepteren" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Sluiten" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Weet u zeker dat u PortMaster wilt afsluiten?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Aanbevolen Ports" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Hand samengestelde lijst van ports" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Alle ports" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Toon alle beschikbare ports op PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Klaar om te Spelen Ports" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Lijst alle ports die klaar zijn om te spelen!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Beheer Ports" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Update / Verwijder Ports" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Opties" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "PortMaster Opties" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Sluiten" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Sluit PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Ga" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Geheime Modus {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Ingeschakeld" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Uitgeschakeld" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Opties Menu" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interface" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Kies Taal" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Selecteer de taal voor PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Selecteer Thema" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Selecteer een thema voor PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Selecteer Kleurenschema" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Selecteer een kleurenschema voor PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Audio" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Muziek: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Achtergrondmuziek voor PortMaster in- of uitschakelen." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Geluidseffecten: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Geluidseffecten voor PortMaster in- of uitschakelen." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Port Metadata" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Metadata Update: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Auto" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Handmatig" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster updates metadata wanneer ports zijn geïnstalleerd zodat deze portafbeeldingen en -beschrijvingen weergeven tijdens het browsen van ports op uw apparaat." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Metadata Verversen" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Werk handmatig de poortmetagegevens bij met bijgewerkte informatie en opmaak." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Locatie ports: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Roms sectie" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Ports tab" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Locatie waar ports geïnstalleerd moeten worden." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Systeem" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Runtime Beheerder" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Beheer port runtimes." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Experimentele Ports: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Toon of verberg experimentele ports." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Ports Bijwerken" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Haal recente poort informatie op." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "PortMaster Bijwerken" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Forceer check voor een nieuwe PortMaster versie." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "PortMaster herstellen" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Dit zal PortMaster herstellen naar de laatste stabiele versie van PortMaster." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Release Kanaal: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Verander het release-kanaal van PortMaster, bèta of stabiel." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Activeer Alpha Releases" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Verander het release-kanaal van PortMaster naar alpha." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Controller Modus: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Schakel tussen verschillende controller lay-outs." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Geheime Opties" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Alle Ports: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Toon alle ports, negeer vereisten." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "CWTBE Modus: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Gptokeyb2 als standaard inschakelen." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Verwijder PortMaster Configuratie" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Dit kan dingen breken, raak niet aan tenzij je weet wat je doet." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Verwijder PortMaster Runtimes" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Terug" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Weet u zeker dat u PortMaster wilt herstellen?\n\n" -"Hiervoor moet u een speciaal gemaakte port uitvoeren die zal worden geïnstalleerd." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Een speciale port is geïnstalleerd genaamd \"Restore PortMaster.sh\"\n\n" -"Voer deze poort uit om uw PortMaster installatie te herstellen." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster wordt nu afgesloten zodat u de herstelport kunt uitvoeren." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Weet u zeker dat u wilt veranderen naar het alpha-release kanaal?\n\n" -"PortMaster zal worden geüpgraded of gedegradeerd.\n\n" -"DIT KAN EN ZAL DINGEN BREKEN, U BEN GEWAARSCHUWD" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Weet u zeker dat u het release-kanaal van {current_channel} naar {new_channel} wilt wijzigen?\n\n" -"PortMaster zal worden geüpgraded of gedegradeerd naar toepassing." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Weet u zeker dat u uw gamelist.xml wilt updaten?\n\n" -"Dit overschrijft alle gescrapede bestands- of portinformatie en vereist een download van 50+ MB." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Weet u zeker dat u ports van {from_mode} naar {to_mode} wilt verplaatsen" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SD KAART 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SD KAART 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Weet je zeker dat je poorten wilt beheren en installeren op {to_loc}?\n\n" -"Reeds geïnstalleerde poorten worden niet verplaatst.\n" -"PortMaster zal opnieuw opstarten om dit door te voeren." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Runtimes verwijderen:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Bron Beheerder" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Download Alles" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Gebruikt" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Niet Gebruikt" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Controleer" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Verwijder" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Installeer" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Weet je zeker dat je alle runtimes wilt downloaden en verifiëren?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Runtime {runtime} verwijderd" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Geselecteerd)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Selecteer" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Download" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Wilt u het thema wijzigen?\n\n" -"U moet opnieuw opstarten voordat het in effect gaat." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Selecteer Kleurenschema" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Geselecteerd)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Wilt u het thema's kleurenschema wijzigen?\n\n" -"U moet opnieuw opstarten voordat het in effect gaat." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Taal selecteren" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Geselecteerd)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Wilt u de taal wijzigen?\n\n" -"U moet opnieuw opstarten voordat het in effect gaat." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Verwijder" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Hoofdletters" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Spatie" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Kleine letters" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Geen aanbevolen ports gevonden, internetverbinding is vereist." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "GEEN PORTS" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** GEEN PORTS GEVONDEN **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Download eerst ports." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Probeer enkele filters te verwijderen." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Toon info" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Ports Lijst" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filters" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Port Info Popup" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Verberg informatie" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Herinstalleer" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Port Info" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Weet u zeker dat u {port_name} wilt verwijderen?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Scène Filters" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabetisch" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Recent Toegevoegd" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Recent Bijgewerkt" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Actie" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Avontuur" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Casino/Kaart" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "First Person Shooter" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Platformer" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Puzzel" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Racing" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Ritme" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Rollenspel" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulatie" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sporten" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Strategie" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Visuele Roman" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Andere" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Bestanden Verplicht" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Experimenteel " - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Gebroken Ports" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Gratis spel, alle bestanden inbegrepen." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Demo bestanden inbegrepen." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Gratis externe bestanden nodig." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Betaalde externe bestanden nodig." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Runtime" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32bit" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64bit" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64bit" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Sorteer:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Filters Wissen" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Genres:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Attributen:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Architectuur:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Beschikbaarheid:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porters:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Berichten" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Weet u zeker dat u wilt annuleren?" - diff --git a/PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/themes.po deleted file mode 100644 index e7295fa9..00000000 --- a/PortMaster/pylibs/locales/nl_NL/LC_MESSAGES/themes.po +++ /dev/null @@ -1,192 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Dutch\n" -"Language: nl_NL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: nl\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres: {port_info.genres} Download Grootte: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Beschikbare ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Beschikbare ruimte: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Grootte: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"instructie: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Ruimte: {system.free_space} vrij / {system.total_space} totaal\n" -"Tijd: {system.time_24hr}\n" -"Batterij: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Auteur: {theme_info.creator}\n" -"Status: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Tijd: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Apparaat: {system.device_name}\n" -"Batterij: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Ruimte: {system.free_space} beschikbaar" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres: {port_info.genres} Download Grootte: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Tijd: {system.time_24hr} Batterij: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Ruimte: {system.free_space} vrij / {system.total_space} totaal" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Beschrijving: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Genres: {port_info.genres}\n" -"• Download grootte: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Beschrijving: {port_info.description}\n" -"• Instructies:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Auteur: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Beschrijving:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Naam: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Downloadgrootte: {runtime_info.download_size}\n" -"Installatiegrootte: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Tijd: {system.time_24hr}\n" -"Batterij: {system.battery_level}\n" -"Ruimte: {system.free_space} vrij" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Beschikbare ruimte: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Grootte: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Instructie: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Beschikbare ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Geïnstalleerde grootte: {port_info.install_size}{else}Downloadgrootte: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/pl_PL.json b/PortMaster/pylibs/locales/pl_PL.json new file mode 100644 index 00000000..527b3fe3 --- /dev/null +++ b/PortMaster/pylibs/locales/pl_PL.json @@ -0,0 +1,257 @@ +{ + "Loading...": "Ładowanie...", + "Loading Sources.": "Ładowanie źródeł.", + "Download validation failed.": "Sprawdzanie poprawności pliku nie powiodło się.", + "Loading Info.": "Wczytywanie Informacji.", + "Fetching latest featured ports.": "Pobieranie najnowszych polecanych portów.", + "Installing Theme {download_name}.": "Instalowanie motywu {download_name}.", + "Installing": "Instalowanie", + "Theme {download_name!r} installed successfully.": "Motyw {download_name!r} zainstalowany pomyślnie.", + "Port {download_name} installed failed.": "Port {download_name} nie został zainstalowany.", + "Installing {download_name}.": "Instalacja {download_name}.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} zainstalowany pomyślnie.", + "Installation failed, removing files...": "Instalacja nie powiodła się, usuwanie plików...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "CZEŚĆ.\n\nWYGLĄDA NA TO, ŻE RĘCZNIE INSTALUJESZ PORT, KTÓRY JEST NIEZGODNY Z TWOIM URZĄDZENIEM.\n\nJEŚLI MASZ PROBLEMY, NIE ZGŁASZAJ ICH NA DISCORDZIE, GDYŻ NARADZISZ SIĘ.\n\nCZY NA PEWNO CHCESZ WZNOWIĆ INSTALACJĘ?", + "Okay": "W porządku", + "Cancel": "Anuluj", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} i {runtime_name!r} zainstalowany pomyślnie.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} zainstalowany pomyślnie, ale {runtime_name!r} nie udało się zainstalować!\n\nZainstaluj ponownie, aby spróbować ponownie, lub sprawdź wiki po pomoc.", + "Unable to download in offline mode.": "Nie można pobrać w trybie offline.", + "Unable to find a source for {port_name}": "Nie można odnaleźć źródła dla {port_name}", + "Unknown port {port_name}": "Nieznany port {port_name}", + "Removing {port_name}": "Usuwanie {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Błąd odinstalowywania {port_name}\n\n{error}", + "Successfully uninstalled {port_name}": "Pomyślnie pobrano {port_name}", + "Loading Ports.": "Ładowanie portów.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} zawiera złe działanie, gra może nie działać prawidłowo.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Port {runtime} zawiera nieznany runtime, gra może nie działać prawidłowo.", + "Not Installed": "Nie zainstalowano", + "Update Available": "Dostępna aktualizacja", + "Verified": "Zweryfikowane", + "Unverified": "Niezweryfikowane", + "Broken": "Uszkodzone", + "Unable do download a runtime when in offline mode.": "Nie można pobrać runtime w trybie offline.", + "Verifying runtime {runtime}": "Weryfikowanie czasu pracy {runtime}", + "Verifying": "Weryfikowanie", + "Unable to find a download for {runtime}.": "Nie można znaleźć pobrania dla {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Nie można pobrać {runtime} w {device_arch}.", + "Verified {runtime} successfully.": "Zweryfikowano {runtime} pomyślnie.", + "Verified {runtime}.": "Zweryfikowano {runtime}.", + "Updating {runtime}.": "Aktualizowanie {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Runtime {runtime} jest uszkodzony, reinstalacja.", + "Downloading runtime {runtime}.": "Pobieranie runtime {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Nie można pobrać {runtime}, gra może nie działać poprawnie.", + "Successfully downloaded {runtime}.": "Pomyślnie pobrano {runtime}.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nAby to zrobić:\nMENU -> UTILITY -> Przeskanuj katalog Rom", + "Unable to download verification file.": "Nie można pobrać pliku weryfikacyjnego.", + "Unable to download non zip files.": "Nie można pobrać plików bez zip.", + "Cache out of date.": "Pamięć podręczna nieaktualna.", + "First check.": "Pierwsze sprawdzenie.", + "Auto Update.": "Automatyczna aktualizacja.", + "Updating": "Aktualizowanie", + "Up to date already": "Już aktualne", + "Fetching latest info": "Pobieranie najnowszych informacji", + "Done.": "Zrobione.", + "Fetching info": "Pobieram informacje", + "Unable to find {port_name}.": "Nie można znaleźć {port_name}.", + "Unable to download file. [{status_code}]": "Nie można pobrać pliku. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Pobieranie {file_url} - ({total_length_mb})", + "Downloading file.": "Pobieranie pliku.", + "Download failed: {err}": "Pobieranie nie powiodło się: {err}", + "Passed file validation.": "Plik przeszedł pomyślnie walidację.", + "Unable to validate download.": "Nie można zweryfikować pobrania.", + "Unknown IP": "Nieznany adres IP", + "N/A": "Nie dostępne", + "Fetching latest gameinfo.xml files and cover images.": "Pobieranie najnowszych plików gameinfo.xml i okładek.", + "Downloading gameinfo.xml for {source_name}": "Pobieranie gameinfo.xml dla {source_name}", + "Installing {port_name}": "Instalacja {port_name}", + "Uninstalling {port_name}": "Odinstalowywanie {port_name}", + "Updating all port sources:": "Aktualizowanie wszystkich źródeł portów:", + "Checking {runtime_name}": "Sprawdzanie {runtime_name}", + "** NO PORT **": "** BRAK PORTU **", + "Ready to Run": "Gotowe do uruchomienia", + "Requires Files": "Wymagane pliki", + "Installed": "Zainstalowane", + "Missing": "Brakujące", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Porty zostały dodane lub usunięte, zaktualizuj listę gier, aby zobaczyć nowe gry lub usunąć odinstalowane.", + "Danish": "Duński", + "German": "Niemiecki", + "English": "Angielski", + "Spanish": "Hiszpański", + "Finnish": "Fiński", + "French": "Francuski", + "Italian": "Włoski", + "Japanese": "Japoński", + "Korean": "Koreański", + "Dutch": "Holenderski", + "Polish": "Polski", + "Portuguese (Portugal)": "Portugalski (Portugalia)", + "Portuguese (Brazil)": "Portugalski (Brazylia)", + "Romanian": "Rumuński", + "Russian": "Rosyjski", + "Swedish": "Szwedzki", + "Ukrainian": "Ukraiński", + "Chinese Simplified": "Chiński Uproszczony", + "Messages": "Wiadomości", + "Are you sure you want to cancel?": "Czy na pewno chcesz anulować?", + "Featured Ports": "Polecane porty", + "Select": "Wybierz", + "Back": "Powrót", + "No featured ports found, internet connection is required.": "Nie znaleziono polecanych portów, wymagane jest połączenie z Internetem.", + "Filters Scene": "Filtry sceny", + "Alphabetical": "Alfabetycznie", + "Recently Added": "Ostatnio dodane", + "Recently Updated": "Ostatnio zaktualizowane", + "Action": "Akcja", + "Adventure": "Przygodowe", + "Arcade": "Arcade", + "Casino/Card": "Kasino/Karta", + "First Person Shooter": "Pierwszoosobowa strzelanka", + "Platformer": "Platformówka", + "Puzzle": "Puzzle", + "Racing": "Wyścigowe", + "Rhythm": "Rytm", + "Role Playing Game": "Role Playing", + "Simulation": "Symulacja", + "Sports": "Sportowe", + "Strategy": "Strategie", + "Visual Novel": "Wizualna nowela", + "Other": "Inne", + "Files Required": "Wymagane pliki", + "Experimental": "Eksperymentalne", + "Broken Ports": "Uszkodzone porty", + "Free game, all files included.": "Darmowa gra, wszystkie pliki załączone.", + "Demo files included.": "Załączone pliki demo.", + "Free external assets needed.": "Potrzebne są bezpłatne aktywa zewnętrzne.", + "Paid external assets needed.": "Potrzebne płatne aktywa zewnętrzne.", + "{runtime_name} Runtime": "{runtime_name} Czas pracy", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Sort:": "Sortuj:", + "Clear Filters": "Wyczyść filtry", + "Genres:": "Gatunek:", + "Attributes:": "Atrybuty:", + "Architecture:": "Architektura:", + "Availability:": "Dostępność:", + "Porters:": "Porterzy:", + "Delete": "Usuń", + "Upper Case": "Wielkie litery", + "Space": "Spacja", + "Enter": "Wejdź", + "Lower Case": "Małe litery", + "Language Select": "Wybór języka", + "{lang_name} (Selected)": "{lang_name} (Wybrany)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Czy chcesz zmienić język?\n\nBędziesz musiał zrestartować, aby zastosować.", + "Main Menu": "Menu Główne", + "Hand curated lists of ports": "Ręcznie wyselekcjonowane listy portów", + "All Ports": "Wszystkie porty", + "List all ports available on PortMaster.": "Lista wszystkich portów dostępnych na PortMaster.", + "Ready to Run Ports": "Gotowe do uruchomienia porty", + "List all ports that are ready to play!": "Lista wszystkich portów, które są gotowe do gry!", + "Manage Ports": "Zarządzaj portami", + "Update / Uninstall Ports": "Aktualizuj / Odinstaluj porty", + "Options": "Ustawienia", + "PortMaster Options": "Opcje PortMaster", + "Exit": "Wyjście", + "Quit PortMaster": "Wyjdź z PortMaster", + "Quit": "Wyjdź", + "Secret Mode {secret_mode}": "Tryb sekretny {secret_mode}", + "Enabled": "Włączony", + "Disabled": "Wyłączony", + "Are you sure you want to exit PortMaster?": "Czy na pewno chcesz wyjść z PortMastera?", + "Options Menu": "Menu opcji", + "Interface": "Interfejs", + "Choose Language": "Wybierz język", + "Select the language PortMaster uses.": "Wybierz język używany przez PortMaster.", + "Select Theme": "Wybierz motyw", + "Select a theme for PortMaster.": "Wybierz motyw dla PortMaster.", + "Select Color Scheme": "Wybierz schemat kolorów", + "Select a colour scheme for PortMaster": "Wybierz schemat kolorów dla PortMaster", + "Audio": "Dźwięk", + "Music: ": "Muzyka: ", + "Enable or Disable background music in PortMaster.": "Włącz lub wyłącz muzykę w tle w PortMaster.", + "Sound FX: ": "Efekty dźwiękowe: ", + "Enable or Disable soundfx in PortMaster.": "Włącz lub wyłącz efekty dźwiękowe w PortMaster.", + "Port Metadata": "Metadane portu", + "Metadata Update: ": "Aktualizacja metadanych: ", + "Auto": "Auto", + "Manual": "Ręcznie", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster aktualizuje metadane po zainstalowaniu portów, dzięki czemu wyświetla obrazy portów i opisy podczas przeglądania portów na twoim urządzeniu.", + "Metadata Refresh": "Odświeżenie metadanych", + "Manually update port metadata with missing/updated information and artwork.": "Ręcznie aktualizuj metadane portu z brakującymi/zaktualizowanymi informacjami i grafiką.", + "Ports Location: ": "Miejsce portów: ", + "Roms section": "Sekcja Romów", + "Ports tab": "Zakładka portów", + "Location where ports should be installed to.": "Lokalizacja, w której porty powinny być zainstalowane.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "System", + "Runtime Manager": "Menadżer Runtime", + "Manage port runtimes.": "Zarządzaj runtime porta.", + "Experimental Ports: ": "Porty eksperymentalne: ", + "Show or hide experimental ports.": "Pokaż lub ukryj porty eksperymentalne.", + "Update Ports": "Aktualizuj porty", + "Fetch latest ports information.": "Pobierz najnowsze informacje o portach.", + "Update PortMaster": "Aktualizuj PortMaster", + "Force check for a new PortMaster version.": "Wymuś sprawdzenie nowej wersji PortMaster.", + "Restore PortMaster": "Przywróć PostMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "To przywróci PortMaster do najnowszej stabilnej wersji PortMaster.", + "Release Channel: {channel}": "Kanał Wydania: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Zmień kanał wydania PortMaster, beta lub stabilny.", + "Enable Alpha Releases": "Włącz wydania Alfa", + "Change release channel of PortMaster to alpha.": "Zmień kanał wydania PortMaster na Alpha.", + "Controller Mode: {controller_mode}": "Tryb kontrolera: {controller_mode}", + "Toggle between various controller layouts.": "Przełącz między różnymi układami kontrolera.", + "Secret Options": "Sekretne Opcje", + "All Ports: ": "Wszystkie porty: ", + "Show all ports, ignoring requirements.": "Pokaż wszystkie porty, ignorując wymagania.", + "CWTBE Mode: ": "Tryb CWTBE: ", + "Enable gptokeyb2 by default.": "Domyślnie włącz gptokeyb2.", + "Delete PortMaster Config": "Usuń konfigurację PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "To może zniszczyć rzeczy, nie dotykaj, chyba że wiesz co robisz.", + "Delete PortMaster Runtimes": "Usuń runtime PortMastera", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Czy na pewno chcesz przywrócić PortMaster?\n\nTo będzie wymagało uruchomienia specjalnie stworzonego portu, który zostanie zainstalowany.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Zainstalowano specjalny port o nazwie \"Restore PortMaster.sh\"\n\nUruchom ten port aby przywrócić swoją instalację PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster zakończy teraz działanie portu przywracania.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Czy na pewno chcesz przejść na kanał wersji alfa?\n\nPortMaster odpowiednio zaktualizuje lub obniży wersję.\n\nTO MOŻE ZEPSUĆ PEWNE RZECZY, ZOSTAŁEŚ OSTRZEŻONY", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Czy na pewno chcesz zmienić kanał wydania z {current_channel} na {new_channel}?\n\nPortMaster odpowiednio uaktualni lub obniży wersję.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Czy na pewno chcesz zaktualizować swoją listę gamelist.xml?\n\nZastąpi to wszelkie scrapowane informacje o grafice lub porcie.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Czy na pewno chcesz przenieść porty z {from_mode} do {to_mode}", + "SDCARD 1": "SDCARD 1", + "SDCARD 2": "SDCARD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Czy na pewno chcesz zarządzać i instalować porty na {to_loc}?\n\nZainstalowane porty nie zostaną przeniesione.\nPortMaster uruchomi się ponownie, aby to zadziałało.", + "Deleting Runtimes:": "Usuwanie Runtime:", + "Source Manager": "Menedżer źródeł", + "Port Info Popup": "Wyskakujące okno informacji o porcie", + "Hide Info": "Ukryj informacje", + "Reinstall": "Reinstalacja", + "Uninstall": "Odinstaluj", + "Install": "Zainstaluj", + "Port Info": "Informacje o porcie", + "Show Info": "Pokaż info", + "Are you sure you want to uninstall {port_name}?": "Jesteś pewien, że chcesz odinstalować {port_name}?", + "NO PORTS": "BRAK PORTÓW", + "** NO PORTS FOUND **": "** NIE ZNALEZIONO PORTÓW **", + "Download ports first.": "Najpierw pobierz porty.", + "Try removing some filters.": "Spróbuj usunąć kilka filtrów.", + "Ports List": "Listy portów", + "Filters": "Filtry", + "Download All": "Pobierz wszystko", + "Used": "Używane", + "Not Used": "Nieużywane", + "Check": "Sprawdź", + "Are you sure you want to download and verify all runtimes?": "Czy na pewno chcesz pobrać i zweryfikować wszystkie runtimes?", + "Deleted runtime {runtime}": "Usunięto runtime {runtime}", + "Disclaimer": "Uwaga", + "Accept": "Akceptuj", + "{theme_name} (Selected)": "{theme_name} (Wybrany)", + "Download": "Pobierz", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Czy chcesz zmienić motyw?\n\nBędziesz musiał zrestartować, aby go zmienić.", + "Select Colour Scheme": "Wybierz schemat kolorów", + "{item_name} (Selected)": "{item_name} (Wybrany)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Czy chcesz zmienić schemat kolorów motywu?\n\nBędziesz musiał zrestartować, aby go zmienić.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Przechodzisz z {old_release_channel} na wersję {new_release_channel}.\n\nCzy chcesz kontynuować?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Dostępna jest nowa wersja PortMaster ({portmaster_version})\n\nCzy chcesz zaktualizować?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Czy chcesz ponownie zainstalować PortMaster?\n\nZostanie to ponownie zainstalowane z kanału {release_channel} do {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/pl_PL.themes.json b/PortMaster/pylibs/locales/pl_PL.themes.json new file mode 100644 index 00000000..cfb9245c --- /dev/null +++ b/PortMaster/pylibs/locales/pl_PL.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Czas: {system.time_24hr}\nBateria: {system.battery_level}\nMiejsce: {system.free_space} wolnych", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Gatunki: {port_info.genres} Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime} Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Dostępne Porty: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltry: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Wolne Miejsce: {system.free_space}\n\nPorter: {port_info.porter}\nGatunki {port_info.genres}\nRozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\nCzas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstrukcja: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nMiejsce: {system.free_space} wolnych / {system.total_space} łącznie\nCzas: {system.time_24hr}\nBateria: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Twórca: {theme_info.creator}\nStatus: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nPrzestrzeń: {system.free_space} wolnych / łącznie", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtry: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Gatunek: {port_info.genres}\nRozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\nCzas Uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Czas: {system.time_24hr} Bateria: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Tytuł: {port_info.title}\nPorter: {port_info.porter}\nGatunki {port_info.genres}\nRozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\nCzas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\nOpis: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Dostępne Porty: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltry: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Gatunki: {port_info.genres}\n{if:port_info.install_size}Rozmiar po instalacji: {port_info.install_size}{else}Rozmiar pobierania: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Godzina: {system.time_24hr}\nOprogramowanie: {system.cfw_name} ({system.cfw_version})\nUrządzenie: {system.device_name}\nStan Baterii: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nPrzestrzeń: {system.free_space} wolnych / {system.total_space} łącznie", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porter: {port_info.porter}\n• Gatunki: {port_info.genres}\n• Opis: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Gatunki {port_info.genres}\n• Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\n• Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Opis: {port_info.description}\n• Instrukcje:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Twórca: {theme_info.creator}\n• Status: {theme_info.status}\n• Opis:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nazwa: {runtime_info.name}\nStatus: {runtime_info.status}\nPorty: {runtime_info.ports}\nRozmiar pobierania: {runtime_info.download_size}\nRozmiar instalacji: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/messages.po deleted file mode 100644 index 5ef9aa41..00000000 --- a/PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1252 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Polish\n" -"Language: pl_PL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: pl\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Duński" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Niemiecki" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Angielski" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Hiszpański" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Fiński" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Francuski" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Włoski" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japoński" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Koreański" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Holenderski" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polski" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugalski (Portugalia)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugalski (Brazylia)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "Rumuński" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Rosyjski" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Szwedzki" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ukraiński" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chiński Uproszczony" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "Nieznany adres IP" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "Nie dostępne" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** BRAK PORTU **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Gotowe do uruchomienia" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Wymagane pliki" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Zainstalowane" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Brakujące" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Dostępna aktualizacja" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Nie zainstalowano" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "W porządku" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Anuluj" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Pobieranie najnowszych plików gameinfo.xml i okładek." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Pobieranie gameinfo.xml dla {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Instalacja {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Odinstalowywanie {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Aktualizowanie wszystkich źródeł portów:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Sprawdzanie {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Przechodzisz z {old_release_channel} na wersję {new_release_channel}.\n\n" -"Czy chcesz kontynuować?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Dostępna jest nowa wersja PortMaster ({portmaster_version})\n\n" -"Czy chcesz zaktualizować?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Czy chcesz ponownie zainstalować PortMaster?\n\n" -"Zostanie to ponownie zainstalowane z kanału {release_channel} do {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Porty zostały dodane lub usunięte, zaktualizuj listę gier, aby zobaczyć nowe gry lub usunąć odinstalowane." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Ładowanie..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Sprawdzanie poprawności pliku nie powiodło się." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Wczytywanie Informacji." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Pobieranie najnowszych polecanych portów." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Ładowanie źródeł." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Ładowanie portów." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Instalowanie motywu {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Instalowanie" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Motyw {download_name!r} zainstalowany pomyślnie." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Instalacja {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Port {download_name!r} zainstalowany pomyślnie." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "CZEŚĆ.\n\n" -"WYGLĄDA NA TO, ŻE RĘCZNIE INSTALUJESZ PORT, KTÓRY JEST NIEZGODNY Z TWOIM URZĄDZENIEM.\n\n" -"JEŚLI MASZ PROBLEMY, NIE ZGŁASZAJ ICH NA DISCORDZIE, GDYŻ NARADZISZ SIĘ.\n\n" -"CZY NA PEWNO CHCESZ WZNOWIĆ INSTALACJĘ?" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Instalacja nie powiodła się, usuwanie plików..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Port {download_name} nie został zainstalowany." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Port {download_name!r} i {runtime_name!r} zainstalowany pomyślnie." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Port {download_name!r} zainstalowany pomyślnie, ale {runtime_name!r} nie udało się zainstalować!\n\n" -"Zainstaluj ponownie, aby spróbować ponownie, lub sprawdź wiki po pomoc." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Port {runtime} zawiera złe działanie, gra może nie działać prawidłowo." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Port {runtime} zawiera nieznany runtime, gra może nie działać prawidłowo." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Zweryfikowane" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Niezweryfikowane" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Uszkodzone" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Nie można pobrać runtime w trybie offline." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Weryfikowanie czasu pracy {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Weryfikowanie" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Nie można znaleźć pobrania dla {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Nie można pobrać {runtime} w {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "Zweryfikowano {runtime} pomyślnie." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "Zweryfikowano {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Aktualizowanie {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Runtime {runtime} jest uszkodzony, reinstalacja." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Pobieranie runtime {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Nie można pobrać {runtime}, gra może nie działać poprawnie." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "Pomyślnie pobrano {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Nie można pobrać w trybie offline." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Nie można odnaleźć źródła dla {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Nieznany port {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Usuwanie {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "Błąd odinstalowywania {port_name}\n\n" -"{error}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "Pomyślnie pobrano {port_name}" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Aby to zrobić:\n" -"MENU -> UTILITY -> Przeskanuj katalog Rom" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Pamięć podręczna nieaktualna." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Pierwsze sprawdzenie." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Automatyczna aktualizacja." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Aktualizowanie" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Już aktualne" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Pobieranie najnowszych informacji" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Zrobione." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Nie można znaleźć {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Nie można znaleźć informacji weryfikacyjnych dla {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Nie można pobrać informacji weryfikacyjnych dla {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Udało się!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Pobieram informacje" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Pobieranie najnowszych portów" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Pobieranie informacji." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Nie można pobrać pliku weryfikacyjnego." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Nie można pobrać plików bez zip." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Nie można pobrać pliku. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Pobieranie {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Pobieranie pliku." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Pobieranie nie powiodło się: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Plik przeszedł pomyślnie walidację." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Nie można zweryfikować pobrania." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Menu Główne" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Uwaga" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Akceptuj" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Wyjdź" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Czy na pewno chcesz wyjść z PortMastera?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Polecane porty" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Ręcznie wyselekcjonowane listy portów" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Wszystkie porty" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Lista wszystkich portów dostępnych na PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Gotowe do uruchomienia porty" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Lista wszystkich portów, które są gotowe do gry!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Zarządzaj portami" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Aktualizuj / Odinstaluj porty" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Ustawienia" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Opcje PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Wyjście" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Wyjdź z PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Wejdź" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Tryb sekretny {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Włączony" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Wyłączony" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Menu opcji" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interfejs" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Wybierz język" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Wybierz język używany przez PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Wybierz motyw" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Wybierz motyw dla PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Wybierz schemat kolorów" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Wybierz schemat kolorów dla PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Dźwięk" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Muzyka: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Włącz lub wyłącz muzykę w tle w PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Efekty dźwiękowe: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Włącz lub wyłącz efekty dźwiękowe w PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Metadane portu" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Aktualizacja metadanych: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Auto" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Ręcznie" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster aktualizuje metadane po zainstalowaniu portów, dzięki czemu wyświetla obrazy portów i opisy podczas przeglądania portów na twoim urządzeniu." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Odświeżenie metadanych" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Ręcznie aktualizuj metadane portu z brakującymi/zaktualizowanymi informacjami i grafiką." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Miejsce portów: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Sekcja Romów" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Zakładka portów" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Lokalizacja, w której porty powinny być zainstalowane." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "System" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Menadżer Runtime" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Zarządzaj runtime porta." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Porty eksperymentalne: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Pokaż lub ukryj porty eksperymentalne." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Aktualizuj porty" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Pobierz najnowsze informacje o portach." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Aktualizuj PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Wymuś sprawdzenie nowej wersji PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Przywróć PostMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "To przywróci PortMaster do najnowszej stabilnej wersji PortMaster." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Kanał Wydania: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Zmień kanał wydania PortMaster, beta lub stabilny." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Włącz wydania Alfa" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Zmień kanał wydania PortMaster na Alpha." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Tryb kontrolera: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Przełącz między różnymi układami kontrolera." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Sekretne Opcje" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Wszystkie porty: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Pokaż wszystkie porty, ignorując wymagania." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Tryb CWTBE: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Domyślnie włącz gptokeyb2." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Usuń konfigurację PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "To może zniszczyć rzeczy, nie dotykaj, chyba że wiesz co robisz." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Usuń runtime PortMastera" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Powrót" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Czy na pewno chcesz przywrócić PortMaster?\n\n" -"To będzie wymagało uruchomienia specjalnie stworzonego portu, który zostanie zainstalowany." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Zainstalowano specjalny port o nazwie \"Restore PortMaster.sh\"\n\n" -"Uruchom ten port aby przywrócić swoją instalację PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster zakończy teraz działanie portu przywracania." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Czy na pewno chcesz przejść na kanał wersji alfa?\n\n" -"PortMaster odpowiednio zaktualizuje lub obniży wersję.\n\n" -"TO MOŻE ZEPSUĆ PEWNE RZECZY, ZOSTAŁEŚ OSTRZEŻONY" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Czy na pewno chcesz zmienić kanał wydania z {current_channel} na {new_channel}?\n\n" -"PortMaster odpowiednio uaktualni lub obniży wersję." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Czy na pewno chcesz zaktualizować swoją listę gamelist.xml?\n\n" -"Zastąpi to wszelkie scrapowane informacje o grafice lub porcie." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Czy na pewno chcesz przenieść porty z {from_mode} do {to_mode}" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SDCARD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SDCARD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Czy na pewno chcesz zarządzać i instalować porty na {to_loc}?\n\n" -"Zainstalowane porty nie zostaną przeniesione.\n" -"PortMaster uruchomi się ponownie, aby to zadziałało." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Usuwanie Runtime:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Menedżer źródeł" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Pobierz wszystko" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Używane" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Nieużywane" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Sprawdź" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Odinstaluj" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Zainstaluj" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Czy na pewno chcesz pobrać i zweryfikować wszystkie runtimes?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Usunięto runtime {runtime}" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Wybrany)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Wybierz" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Pobierz" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Czy chcesz zmienić motyw?\n\n" -"Będziesz musiał zrestartować, aby go zmienić." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Wybierz schemat kolorów" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Wybrany)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Czy chcesz zmienić schemat kolorów motywu?\n\n" -"Będziesz musiał zrestartować, aby go zmienić." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Wybór języka" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Wybrany)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Czy chcesz zmienić język?\n\n" -"Będziesz musiał zrestartować, aby zastosować." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Usuń" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Wielkie litery" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Spacja" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Małe litery" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Nie znaleziono polecanych portów, wymagane jest połączenie z Internetem." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "BRAK PORTÓW" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** NIE ZNALEZIONO PORTÓW **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Najpierw pobierz porty." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Spróbuj usunąć kilka filtrów." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Pokaż info" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Listy portów" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filtry" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Wyskakujące okno informacji o porcie" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Ukryj informacje" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Reinstalacja" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Informacje o porcie" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Jesteś pewien, że chcesz odinstalować {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Filtry sceny" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabetycznie" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Ostatnio dodane" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Ostatnio zaktualizowane" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Akcja" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Przygodowe" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Kasino/Karta" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Pierwszoosobowa strzelanka" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Platformówka" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Puzzle" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Wyścigowe" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Rytm" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Role Playing" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Symulacja" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sportowe" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Strategie" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Wizualna nowela" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Inne" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Wymagane pliki" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Eksperymentalne" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Uszkodzone porty" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Darmowa gra, wszystkie pliki załączone." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Załączone pliki demo." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Potrzebne są bezpłatne aktywa zewnętrzne." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Potrzebne płatne aktywa zewnętrzne." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Czas pracy" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32bit" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64bit" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64bit" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Sortuj:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Wyczyść filtry" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Gatunek:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Atrybuty:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Architektura:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Dostępność:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porterzy:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Wiadomości" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Czy na pewno chcesz anulować?" - diff --git a/PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/themes.po deleted file mode 100644 index a8a87d84..00000000 --- a/PortMaster/pylibs/locales/pl_PL/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Polish\n" -"Language: pl_PL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: pl\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Gatunki: {port_info.genres} Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime} Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Dostępne Porty: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtry: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Wolne Miejsce: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Gatunki {port_info.genres}\n" -"Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\n" -"Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instrukcja: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Miejsce: {system.free_space} wolnych / {system.total_space} łącznie\n" -"Czas: {system.time_24hr}\n" -"Bateria: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Twórca: {theme_info.creator}\n" -"Status: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Godzina: {system.time_24hr}\n" -"Oprogramowanie: {system.cfw_name} ({system.cfw_version})\n" -"Urządzenie: {system.device_name}\n" -"Stan Baterii: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Przestrzeń: {system.free_space} wolnych / łącznie" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filtry: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Gatunek: {port_info.genres}\n" -"Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\n" -"Czas Uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Czas: {system.time_24hr} Bateria: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Przestrzeń: {system.free_space} wolnych / {system.total_space} łącznie" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porter: {port_info.porter}\n" -"• Gatunki: {port_info.genres}\n" -"• Opis: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Gatunki {port_info.genres}\n" -"• Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\n" -"• Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Opis: {port_info.description}\n" -"• Instrukcje:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Twórca: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Opis:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Nazwa: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Porty: {runtime_info.ports}\n" -"Rozmiar pobierania: {runtime_info.download_size}\n" -"Rozmiar instalacji: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Czas: {system.time_24hr}\n" -"Bateria: {system.battery_level}\n" -"Miejsce: {system.free_space} wolnych" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Tytuł: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Gatunki {port_info.genres}\n" -"Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\n" -"Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Opis: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Dostępne Porty: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtry: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Gatunki: {port_info.genres}\n" -"{if:port_info.install_size}Rozmiar po instalacji: {port_info.install_size}{else}Rozmiar pobierania: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/pt_BR.json b/PortMaster/pylibs/locales/pt_BR.json new file mode 100644 index 00000000..f1d39da5 --- /dev/null +++ b/PortMaster/pylibs/locales/pt_BR.json @@ -0,0 +1,257 @@ +{ + "Loading...": "Carregando...", + "Loading Sources.": "Carregando fontes.", + "Download validation failed.": "Validação do download falhou.", + "Loading Info.": "Carregando informação.", + "Fetching latest featured ports.": "Buscando jogos em destaque mais recentes.", + "Installing Theme {download_name}.": "Instalando tema {download_name}.", + "Installing": "Instalando", + "Theme {download_name!r} installed successfully.": "Tema {download_name!r} instalado com sucesso.", + "Port {download_name} installed failed.": "Jogo {download_name} falhou na instalação.", + "Installing {download_name}.": "Instalando {download_name}.", + "Port {download_name!r} installed successfully.": "Jogo {download_name!r} instalado com sucesso.", + "Installation failed, removing files...": "Instalação falhou, removendo arquivos...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "OLÁ.\n\nPARECE QUE VOCÊ ESTÁ INSTALANDO MANUALMENTE UM JOGO QUE NÃO É COMPATÍVEL COM SEU APARELHO.\n\nSE VOCÊ TIVER PROBLEMAS, NÃO REPORTE ELES NO DISCORD, VOCÊ ESTARÁ SUJEITO A SER RIDICULARIZADO.\n\nVOCÊ TEM CERTEZA QUE QUER CONTINUAR INSTALANDO?", + "Okay": "Ok", + "Cancel": "Cancelar", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Jogo {download_name!r} e {runtime_name!r} instalados com sucesso.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Jogo {download_name!r} instalado com sucesso, mas {runtime_name!r} falhou em instalar!\n\nReinstale para tentar novamente ou cheque o site para ajuda.", + "Unable to download in offline mode.": "Impossível baixar no modo offline.", + "Unable to find a source for {port_name}": "Impossível encontrar uma fonte para {port_name}", + "Unknown port {port_name}": "Jogo desconhecido {port_name}", + "Removing {port_name}": "Removendo {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Erro ao desinstalar {port_name}\n\n{error}", + "Successfully uninstalled {port_name}": "{port_name} desinstalado com sucesso", + "Loading Ports.": "Carregando jogos.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Jogo {runtime} contém um tempo de execução incompatível, jogo pode não executar corretamente.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Jogo {runtime} contém um tempo de execução desconhecido, jogo talvez não rode corretamente.", + "Not Installed": "Não instalado", + "Update Available": "Atualização disponível", + "Verified": "Verificado", + "Unverified": "Não verificado", + "Broken": "Corrompido", + "Unable do download a runtime when in offline mode.": "Não foi possível baixar o ambiente de execução no modo desconectado.", + "Verifying runtime {runtime}": "Verificando tempo de execução {runtime}", + "Verifying": "Verificando", + "Unable to find a download for {runtime}.": "Impossivel encontrar um download para {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Impossível baixar {runtime} em {device_arch}.", + "Verified {runtime} successfully.": "Verificado {runtime} com sucesso.", + "Verified {runtime}.": "Verificado {runtime}.", + "Updating {runtime}.": "Atualizando {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Tempo de execução {runtime} está quebrado, reinstalando.", + "Downloading runtime {runtime}.": "Baixando tempo de execução {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Impossível baixar {runtime}, jogo pode não funcionar corretamente.", + "Successfully downloaded {runtime}.": "{runtime} baixado com sucesso.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nPara fazer isso:\nMENU -> UTILIDADES -> Reescanear Diretório de Roms", + "Unable to download verification file.": "Impossível baixar arquivo de verificação.", + "Unable to download non zip files.": "Incapaz de baixar arquivos que não sejam zip.", + "Cache out of date.": "Cache desatualizado.", + "First check.": "Primeira verificação.", + "Auto Update.": "Atualização automática.", + "Updating": "Atualizando", + "Up to date already": "Já está atualizado", + "Fetching latest info": "Buscando informação mais recente", + "Done.": "Concluído.", + "Fetching info": "Buscando informação", + "Unable to find {port_name}.": "Impossível encontrar {port_name}.", + "Unable to download file. [{status_code}]": "Incapaz de baixar arquivo. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Baixando {file_url} - ({total_length_mb})", + "Downloading file.": "Baixando arquivo.", + "Download failed: {err}": "Download falhou: {err}", + "Passed file validation.": "Validação de arquivo passou.", + "Unable to validate download.": "Incapaz de validar download.", + "Unknown IP": "IP desconhecido", + "N/A": "N/A", + "Fetching latest gameinfo.xml files and cover images.": "Buscando os arquivos mais recentes de gameinfo.xml e imagens de capa.", + "Downloading gameinfo.xml for {source_name}": "Baixando gameinfo.xml para {source_name}", + "Installing {port_name}": "Instalando {port_name}", + "Uninstalling {port_name}": "Desinstalando {port_name}", + "Updating all port sources:": "Atualizando todas as fontes de jogos:", + "Checking {runtime_name}": "Verificando {runtime_name}", + "** NO PORT **": "** SEM JOGO **", + "Ready to Run": "Pronto para executar", + "Requires Files": "Arquivos necessários", + "Installed": "Instalado", + "Missing": "Faltando", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Jogos foram adicionados ou removidos, atualize sua lista para ver os novos jogos ou remover os que não estão mais instalados.", + "Danish": "Dinamarquês", + "German": "Alemão", + "English": "Inglês", + "Spanish": "Espanhol", + "Finnish": "Finlandês", + "French": "Francês", + "Italian": "Italiano", + "Japanese": "Japonês", + "Korean": "Coreano", + "Dutch": "Holandês", + "Polish": "Polonês", + "Portuguese (Portugal)": "Português (Portugal)", + "Portuguese (Brazil)": "Português (Brasil)", + "Romanian": "Romeno", + "Russian": "Russo", + "Swedish": "Sueco", + "Ukrainian": "Ucraniano", + "Chinese Simplified": "Chines Simplificado", + "Messages": "Mensagens", + "Are you sure you want to cancel?": "Tem certeza de que quer cancelar?", + "Featured Ports": "Jogos em Destaque", + "Select": "Selecionar", + "Back": "Voltar", + "No featured ports found, internet connection is required.": "Nenhum jogo em destaque encontrado, conexão com a internet é necessária.", + "Filters Scene": "Cena de Filtros", + "Alphabetical": "Alfabético", + "Recently Added": "Adicionado Recentemente", + "Recently Updated": "Atualizado Recentemente", + "Action": "Ação", + "Adventure": "Aventura", + "Arcade": "Arcade", + "Casino/Card": "Cassino/Cartas", + "First Person Shooter": "Tiro em primeira pessoa", + "Platformer": "Plataforma", + "Puzzle": "Quebra-cabeça", + "Racing": "Corrida", + "Rhythm": "Ritmo", + "Role Playing Game": "RPG", + "Simulation": "Simulação", + "Sports": "Esportes", + "Strategy": "Estratégia", + "Visual Novel": "Visual Novel", + "Other": "Outros", + "Files Required": "Arquivos necessários", + "Experimental": "Experimental", + "Broken Ports": "Jogos corrompidos", + "Free game, all files included.": "Jogo grátis, todos os arquivos incluídos.", + "Demo files included.": "Arquivos de demo incluídos.", + "Free external assets needed.": "Recursos externos gratuitos necessários.", + "Paid external assets needed.": "Recursos externos pagos necessários.", + "{runtime_name} Runtime": "Tempo de execução {runtime_name}", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Sort:": "Organizar:", + "Clear Filters": "Remover Filtros", + "Genres:": "Gêneros:", + "Attributes:": "Atributos:", + "Architecture:": "Arquitetura:", + "Availability:": "Disponibilidade:", + "Porters:": "Porters:", + "Delete": "Apagar", + "Upper Case": "Letra maiúscula", + "Space": "Espaço", + "Enter": "Entrar", + "Lower Case": "Letra minúscula", + "Language Select": "Seleção de Idioma", + "{lang_name} (Selected)": "{lang_name} (Selecionado)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Você quer trocar de idioma?\n\nVocê terá que reiniciar para trocar o idioma.", + "Main Menu": "Menu Principal", + "Hand curated lists of ports": "Listas de jogos selecionados", + "All Ports": "Todos os Jogos", + "List all ports available on PortMaster.": "Listar todos os jogos disponíveis em PortMaster.", + "Ready to Run Ports": "Jogos prontos para rodar", + "List all ports that are ready to play!": "Listar todos os jogos que estão prontos para jogar!", + "Manage Ports": "Gerenciar jogos", + "Update / Uninstall Ports": "Atualizar / desinstalar Jogos", + "Options": "Opções", + "PortMaster Options": "Opções do PortMaster", + "Exit": "Sair", + "Quit PortMaster": "Sair do PortMaster", + "Quit": "Sair", + "Secret Mode {secret_mode}": "Modo Secreto {secret_mode}", + "Enabled": "Ativado", + "Disabled": "Desativado", + "Are you sure you want to exit PortMaster?": "Você tem certeza que quer sair de PortMaster?", + "Options Menu": "Menu de Opções", + "Interface": "Interface", + "Choose Language": "Escolher Idioma", + "Select the language PortMaster uses.": "Selecione o idioma que PortMaster usa.", + "Select Theme": "Selecionar Tema", + "Select a theme for PortMaster.": "Selecione um tema para PortMaster.", + "Select Color Scheme": "Selecionar Esquema de Cores", + "Select a colour scheme for PortMaster": "Selecionar um esquema de cores para PortMaster", + "Audio": "Áudio", + "Music: ": "Música: ", + "Enable or Disable background music in PortMaster.": "Ativar ou Desativar música de fundo em PortMaster.", + "Sound FX: ": "Efeitos Sonoros: ", + "Enable or Disable soundfx in PortMaster.": "Ativar ou Desativar efeitos sonoros em PortMaster.", + "Port Metadata": "Metadados de Port", + "Metadata Update: ": "Atualização de metadados: ", + "Auto": "Auto", + "Manual": "Manual", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "O PortMaster atualiza metadados quando os port estão instalados para que ele mostre imagens e descrições do port quando estiver navegando pelos ports no seu dispositivo.", + "Metadata Refresh": "Atualização de metadados", + "Manually update port metadata with missing/updated information and artwork.": "Atualiza manualmente os metadados de ports com informações e artes ausentes/atualizadas.", + "Ports Location: ": "Localização dos Jogos: ", + "Roms section": "Seção de roms", + "Ports tab": "Aba de jogos", + "Location where ports should be installed to.": "Localização onde os jogos devem ser instalados.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "Sistema", + "Runtime Manager": "Gerenciador de Tempos de Execução", + "Manage port runtimes.": "Gerenciar tempos de execução dos jogos.", + "Experimental Ports: ": "Jogos Experimentais: ", + "Show or hide experimental ports.": "Exibir ou ocultar jogos experimentais.", + "Update Ports": "Atualizar Jogos", + "Fetch latest ports information.": "Buscar informação de jogos mais recentes.", + "Update PortMaster": "Atualizar PortMaster", + "Force check for a new PortMaster version.": "Forçar verificação para uma nova versão de PortMaster.", + "Restore PortMaster": "Restaurar Portmaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Isso irá restaurar PortMaster para a última versão estável de Portmaster.", + "Release Channel: {channel}": "Canal de lançamento: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Alterar o canal de lançamento de Portmaster, beta ou estável.", + "Enable Alpha Releases": "Habilitar lançamentos alpha", + "Change release channel of PortMaster to alpha.": "Trocar canal de atualização de Portmaster para alpha.", + "Controller Mode: {controller_mode}": "Modo de controle: {controller_mode}", + "Toggle between various controller layouts.": "Alternar entre diferentes disposições de controle.", + "Secret Options": "Opções Secretas", + "All Ports: ": "Todos os Jogos: ", + "Show all ports, ignoring requirements.": "Exibir todos os jogos, ignorando requisitos.", + "CWTBE Mode: ": "Modo CWTBE: ", + "Enable gptokeyb2 by default.": "Habilitar gptokeyb2 por padrão.", + "Delete PortMaster Config": "Deletar Configuração de PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "Isso pode causar problemas, não mexa a menos que saiba o que está fazendo.", + "Delete PortMaster Runtimes": "Deletar Tempos de Execução de PortMaster", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Você tem certeza que quer restaurar PortMaster?\n\nIsso exigirá que você execute um comando especial que será instalado.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Um \"jogo\" especial foi instalado chamado \"Restore PortMaster.sh\"\n\nAbra esse \"jogo\" para restaurar sua instalação do PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster fechará agora para você poder executar o \"jogo\" de restauração.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Você tem certeza que quer trocar para o canal alpha de lançamento?\n\nPortmaster vai atualizar ou reverter de acordo com ele.\n\nISSO PODE E VAI QUEBRAR COISAS, VOCÊ FOI AVISADO", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Você tem certeza que quer alterar o canal de lançamento de {current_channel} para {new_channel}?\n\nPortMaster irá atualizar ou regredir de acordo.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Você tem certeza que quer atualizar seu gamelist.xml?\n\nIsso vai substituir qualquer arte ou informação baixada e necessitará um download de 50+ MB.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Tem certeza que quer mover jogos de {from_mode} para {to_mode}", + "SDCARD 1": "Cartão SD 1", + "SDCARD 2": "Cartão SD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Você tem certeza que quer gerenciar e instalar jogos em {to_loc}?\n\nJogos já instalados não serão movidos.\nPortMaster irá reiniciar para isso ter efeito.", + "Deleting Runtimes:": "Deletando Tempos de Execução:", + "Source Manager": "Gerenciador de Fontes", + "Port Info Popup": "Popup de Informação de Jogo", + "Hide Info": "Ocultar Informação", + "Reinstall": "Reinstalar", + "Uninstall": "Desinstalar", + "Install": "Instalar", + "Port Info": "Informações de Jogo", + "Show Info": "Mostrar informação", + "Are you sure you want to uninstall {port_name}?": "Você tem certeza que quer desinstalar {port_name}?", + "NO PORTS": "SEM JOGOS", + "** NO PORTS FOUND **": "** NENHUM JOGO ENCONTRADO **", + "Download ports first.": "Baixe jogos primeiro.", + "Try removing some filters.": "Tente remover alguns filtros.", + "Ports List": "Lista de Jogos", + "Filters": "Filtros", + "Download All": "Baixar Tudo", + "Used": "Usado", + "Not Used": "Não usado", + "Check": "Verificar", + "Are you sure you want to download and verify all runtimes?": "Você tem certeza que quer baixar e verificar todos os tempos de execução?", + "Deleted runtime {runtime}": "Tempo de execução {runtime} deletado", + "Disclaimer": "Aviso", + "Accept": "Aceitar", + "{theme_name} (Selected)": "{theme_name} (Selecionado)", + "Download": "Baixar", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Você quer trocar de tema?\n\nVocê terá que reiniciar para trocar de tema.", + "Select Colour Scheme": "Selecionar Esquema de Cores", + "{item_name} (Selected)": "{item_name} (Selecionado)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Você quer trocar o esquema de cores do tema?\n\nVocê terá que reiniciar para trocar as cores.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Você está trocando da versão {old_release_channel} para a versão {new_release_channel}.\n\nVocê deseja retroceder a versão?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Há uma nova versão do Portmaster ({portmaster_version})\n\nDeseja atualizar?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Você quer reinstalar PortMaster?\n\nIsso irá reinstalar do canal {release_channel} para {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/pt_BR.themes.json b/PortMaster/pylibs/locales/pt_BR.themes.json new file mode 100644 index 00000000..f190dcc7 --- /dev/null +++ b/PortMaster/pylibs/locales/pt_BR.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Hora: {system.time_24hr}\nBateria: {system.battery_level}\nEspaço: {system.free_space} livre", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Gêneros: {port_info.genres} Tamanho do download: {port_info.download_size}{if:port_info.runtime} Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Jogos disponíveis: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltros: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Espaço livre: {system.free_space}\n\nPorter: {port_info.porter}\nGêneros: {port_info.genres}\nTamanho do download: {port_info.download_size}{if:port_info.runtime}\nTempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstrução: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nEspaço: {system.free_space} livre / {system.total_space} total\nHora: {system.time_24hr}\nBateria: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Criador: {theme_info.creator}\nEstado: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nEspaço: {system.free_space} livre", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtros: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Gêneros: {port_info.genres}\nTamanho do download: {port_info.download_size}{if:port_info.runtime}\nTempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Hora: {system.time_24hr} Bateria: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Título: {port_info.title}\nPorter: {port_info.porter}\nGêneros: {port_info.genres}\nTamanho do download: {port_info.download_size}{if:port_info.runtime}\nTempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescrição: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Jogos disponíveis: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltros: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Gêneros: {port_info.genres}\n{if:port_info.install_size}Tamanho instalado: {port_info.install_size}{else}Tamanho do arquivo: {port_info.download_size}{endif}{if:port_info.runtime}\nTempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Hora: {system.time_24hr}\nSistema: {system.cfw_name} ({system.cfw_version})\nAparelho: {system.device_name}\nBateria: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nEspaço: {system.free_space} livre / {system.total_space} total", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porter: {port_info.porter}\n• Gêneros: {port_info.genres}\n• Descrição: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Gêneros: {port_info.genres}\n• Tamanho de download: {port_info.download_size}{if:port_info.runtime}\n• Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descrição: {port_info.description}\n• Instruções:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Criador: {theme_info.creator}\n• Estado: {theme_info.status}\n• Descrição:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nome: {runtime_info.name}\nEstado: {runtime_info.status}\nJogos: {runtime_info.ports}\nTamanho do Download: {runtime_info.download_size}\nTamanho para Instalar: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/messages.po deleted file mode 100644 index ebceca44..00000000 --- a/PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1252 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Portuguese, Brazilian\n" -"Language: pt_BR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: pt-BR\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Dinamarquês" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Alemão" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Inglês" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Espanhol" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finlandês" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Francês" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italiano" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japonês" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Coreano" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Holandês" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polonês" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Português (Portugal)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Português (Brasil)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "Romeno" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Russo" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Sueco" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ucraniano" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chines Simplificado" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "IP desconhecido" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/A" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** SEM JOGO **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Pronto para executar" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Arquivos necessários" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Instalado" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Faltando" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Atualização disponível" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Não instalado" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Ok" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Cancelar" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Buscando os arquivos mais recentes de gameinfo.xml e imagens de capa." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Baixando gameinfo.xml para {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Instalando {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Desinstalando {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Atualizando todas as fontes de jogos:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Verificando {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Você está trocando da versão {old_release_channel} para a versão {new_release_channel}.\n\n" -"Você deseja retroceder a versão?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Há uma nova versão do Portmaster ({portmaster_version})\n\n" -"Deseja atualizar?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Você quer reinstalar PortMaster?\n\n" -"Isso irá reinstalar do canal {release_channel} para {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Jogos foram adicionados ou removidos, atualize sua lista para ver os novos jogos ou remover os que não estão mais instalados." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Carregando..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Validação do download falhou." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Carregando informação." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Buscando jogos em destaque mais recentes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Carregando fontes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Carregando jogos." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Instalando tema {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Instalando" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Tema {download_name!r} instalado com sucesso." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Instalando {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Jogo {download_name!r} instalado com sucesso." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "OLÁ.\n\n" -"PARECE QUE VOCÊ ESTÁ INSTALANDO MANUALMENTE UM JOGO QUE NÃO É COMPATÍVEL COM SEU APARELHO.\n\n" -"SE VOCÊ TIVER PROBLEMAS, NÃO REPORTE ELES NO DISCORD, VOCÊ ESTARÁ SUJEITO A SER RIDICULARIZADO.\n\n" -"VOCÊ TEM CERTEZA QUE QUER CONTINUAR INSTALANDO?" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Instalação falhou, removendo arquivos..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Jogo {download_name} falhou na instalação." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Jogo {download_name!r} e {runtime_name!r} instalados com sucesso." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Jogo {download_name!r} instalado com sucesso, mas {runtime_name!r} falhou em instalar!\n\n" -"Reinstale para tentar novamente ou cheque o site para ajuda." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Jogo {runtime} contém um tempo de execução incompatível, jogo pode não executar corretamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Jogo {runtime} contém um tempo de execução desconhecido, jogo talvez não rode corretamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Verificado" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Não verificado" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Corrompido" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Não foi possível baixar o ambiente de execução no modo desconectado." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Verificando tempo de execução {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Verificando" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Impossivel encontrar um download para {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Impossível baixar {runtime} em {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "Verificado {runtime} com sucesso." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "Verificado {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Atualizando {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Tempo de execução {runtime} está quebrado, reinstalando." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Baixando tempo de execução {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Impossível baixar {runtime}, jogo pode não funcionar corretamente." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} baixado com sucesso." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Impossível baixar no modo offline." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Impossível encontrar uma fonte para {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Jogo desconhecido {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Removendo {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "Erro ao desinstalar {port_name}\n\n" -"{error}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} desinstalado com sucesso" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Para fazer isso:\n" -"MENU -> UTILIDADES -> Reescanear Diretório de Roms" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache desatualizado." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Primeira verificação." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Atualização automática." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Atualizando" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Já está atualizado" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Buscando informação mais recente" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Concluído." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Impossível encontrar {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Impossível encontrar informação de verificação para {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Impossível baixar informação de verificação para {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Sucesso!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Buscando informação" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Buscando jogos mais recentes" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Buscando informação." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Impossível baixar arquivo de verificação." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Incapaz de baixar arquivos que não sejam zip." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Incapaz de baixar arquivo. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Baixando {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Baixando arquivo." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Download falhou: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Validação de arquivo passou." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Incapaz de validar download." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Menu Principal" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Aviso" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Aceitar" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Sair" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Você tem certeza que quer sair de PortMaster?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Jogos em Destaque" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Listas de jogos selecionados" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Todos os Jogos" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Listar todos os jogos disponíveis em PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Jogos prontos para rodar" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Listar todos os jogos que estão prontos para jogar!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Gerenciar jogos" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Atualizar / desinstalar Jogos" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Opções" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Opções do PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Sair" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Sair do PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Entrar" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Modo Secreto {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Ativado" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Desativado" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Menu de Opções" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interface" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Escolher Idioma" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Selecione o idioma que PortMaster usa." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Selecionar Tema" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Selecione um tema para PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Selecionar Esquema de Cores" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Selecionar um esquema de cores para PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Áudio" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Música: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Ativar ou Desativar música de fundo em PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Efeitos Sonoros: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Ativar ou Desativar efeitos sonoros em PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Metadados de Port" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Atualização de metadados: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Auto" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manual" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "O PortMaster atualiza metadados quando os port estão instalados para que ele mostre imagens e descrições do port quando estiver navegando pelos ports no seu dispositivo." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Atualização de metadados" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Atualiza manualmente os metadados de ports com informações e artes ausentes/atualizadas." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Localização dos Jogos: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Seção de roms" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Aba de jogos" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Localização onde os jogos devem ser instalados." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Sistema" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Gerenciador de Tempos de Execução" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Gerenciar tempos de execução dos jogos." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Jogos Experimentais: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Exibir ou ocultar jogos experimentais." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Atualizar Jogos" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Buscar informação de jogos mais recentes." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Atualizar PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Forçar verificação para uma nova versão de PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Restaurar Portmaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Isso irá restaurar PortMaster para a última versão estável de Portmaster." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Canal de lançamento: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Alterar o canal de lançamento de Portmaster, beta ou estável." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Habilitar lançamentos alpha" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Trocar canal de atualização de Portmaster para alpha." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Modo de controle: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Alternar entre diferentes disposições de controle." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Opções Secretas" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Todos os Jogos: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Exibir todos os jogos, ignorando requisitos." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Modo CWTBE: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Habilitar gptokeyb2 por padrão." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Deletar Configuração de PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Isso pode causar problemas, não mexa a menos que saiba o que está fazendo." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Deletar Tempos de Execução de PortMaster" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Voltar" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Você tem certeza que quer restaurar PortMaster?\n\n" -"Isso exigirá que você execute um comando especial que será instalado." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Um \"jogo\" especial foi instalado chamado \"Restore PortMaster.sh\"\n\n" -"Abra esse \"jogo\" para restaurar sua instalação do PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster fechará agora para você poder executar o \"jogo\" de restauração." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Você tem certeza que quer trocar para o canal alpha de lançamento?\n\n" -"Portmaster vai atualizar ou reverter de acordo com ele.\n\n" -"ISSO PODE E VAI QUEBRAR COISAS, VOCÊ FOI AVISADO" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Você tem certeza que quer alterar o canal de lançamento de {current_channel} para {new_channel}?\n\n" -"PortMaster irá atualizar ou regredir de acordo." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Você tem certeza que quer atualizar seu gamelist.xml?\n\n" -"Isso vai substituir qualquer arte ou informação baixada e necessitará um download de 50+ MB." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Tem certeza que quer mover jogos de {from_mode} para {to_mode}" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "Cartão SD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "Cartão SD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Você tem certeza que quer gerenciar e instalar jogos em {to_loc}?\n\n" -"Jogos já instalados não serão movidos.\n" -"PortMaster irá reiniciar para isso ter efeito." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Deletando Tempos de Execução:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Gerenciador de Fontes" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Baixar Tudo" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Usado" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Não usado" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Verificar" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Desinstalar" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Instalar" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Você tem certeza que quer baixar e verificar todos os tempos de execução?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Tempo de execução {runtime} deletado" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Selecionado)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Selecionar" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Baixar" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Você quer trocar de tema?\n\n" -"Você terá que reiniciar para trocar de tema." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Selecionar Esquema de Cores" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Selecionado)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Você quer trocar o esquema de cores do tema?\n\n" -"Você terá que reiniciar para trocar as cores." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Seleção de Idioma" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Selecionado)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Você quer trocar de idioma?\n\n" -"Você terá que reiniciar para trocar o idioma." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Apagar" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Letra maiúscula" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Espaço" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Letra minúscula" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Nenhum jogo em destaque encontrado, conexão com a internet é necessária." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "SEM JOGOS" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** NENHUM JOGO ENCONTRADO **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Baixe jogos primeiro." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Tente remover alguns filtros." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Mostrar informação" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Lista de Jogos" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filtros" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Popup de Informação de Jogo" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Ocultar Informação" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Reinstalar" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Informações de Jogo" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Você tem certeza que quer desinstalar {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Cena de Filtros" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabético" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Adicionado Recentemente" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Atualizado Recentemente" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Ação" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Aventura" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arcade" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Cassino/Cartas" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Tiro em primeira pessoa" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Plataforma" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Quebra-cabeça" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Corrida" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Ritmo" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "RPG" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulação" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Esportes" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Estratégia" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Visual Novel" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Outros" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Arquivos necessários" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Experimental" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Jogos corrompidos" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Jogo grátis, todos os arquivos incluídos." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Arquivos de demo incluídos." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Recursos externos gratuitos necessários." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Recursos externos pagos necessários." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "Tempo de execução {runtime_name}" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32bit" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64bit" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64bit" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Organizar:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Remover Filtros" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Gêneros:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Atributos:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Arquitetura:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Disponibilidade:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porters:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Mensagens" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Tem certeza de que quer cancelar?" - diff --git a/PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/themes.po deleted file mode 100644 index db06f83b..00000000 --- a/PortMaster/pylibs/locales/pt_BR/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Portuguese, Brazilian\n" -"Language: pt_BR\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: pt-BR\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Gêneros: {port_info.genres} Tamanho do download: {port_info.download_size}{if:port_info.runtime} Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Jogos disponíveis: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtros: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Espaço livre: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Gêneros: {port_info.genres}\n" -"Tamanho do download: {port_info.download_size}{if:port_info.runtime}\n" -"Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instrução: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Espaço: {system.free_space} livre / {system.total_space} total\n" -"Hora: {system.time_24hr}\n" -"Bateria: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Criador: {theme_info.creator}\n" -"Estado: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Hora: {system.time_24hr}\n" -"Sistema: {system.cfw_name} ({system.cfw_version})\n" -"Aparelho: {system.device_name}\n" -"Bateria: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Espaço: {system.free_space} livre" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filtros: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Gêneros: {port_info.genres}\n" -"Tamanho do download: {port_info.download_size}{if:port_info.runtime}\n" -"Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Hora: {system.time_24hr} Bateria: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Espaço: {system.free_space} livre / {system.total_space} total" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porter: {port_info.porter}\n" -"• Gêneros: {port_info.genres}\n" -"• Descrição: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Gêneros: {port_info.genres}\n" -"• Tamanho de download: {port_info.download_size}{if:port_info.runtime}\n" -"• Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Descrição: {port_info.description}\n" -"• Instruções:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Criador: {theme_info.creator}\n" -"• Estado: {theme_info.status}\n" -"• Descrição:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Nome: {runtime_info.name}\n" -"Estado: {runtime_info.status}\n" -"Jogos: {runtime_info.ports}\n" -"Tamanho do Download: {runtime_info.download_size}\n" -"Tamanho para Instalar: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Hora: {system.time_24hr}\n" -"Bateria: {system.battery_level}\n" -"Espaço: {system.free_space} livre" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Título: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Gêneros: {port_info.genres}\n" -"Tamanho do download: {port_info.download_size}{if:port_info.runtime}\n" -"Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Descrição: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Jogos disponíveis: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtros: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Gêneros: {port_info.genres}\n" -"{if:port_info.install_size}Tamanho instalado: {port_info.install_size}{else}Tamanho do arquivo: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/ro_RO.json b/PortMaster/pylibs/locales/ro_RO.json new file mode 100644 index 00000000..f07b4307 --- /dev/null +++ b/PortMaster/pylibs/locales/ro_RO.json @@ -0,0 +1,247 @@ +{ + "Loading...": "Se încarcă...", + "Loading Sources.": "Se încarcă sursele.", + "Download validation failed.": "Validarea descărcării a eșuat.", + "Loading Info.": "Se încarcă informațiile.", + "Fetching latest featured ports.": "Se preiau cele mai recente porturi recomandate.", + "Installing Theme {download_name}.": "Se instalează tema {download_name}.", + "Installing": "Se instalează", + "Theme {download_name!r} installed successfully.": "Tema {download_name!r} a fost instalată cu succes.", + "Port {download_name} installed failed.": "Instalarea portului {download_name} a eșuat.", + "Installing {download_name}.": "Se instalează {download_name}.", + "Port {download_name!r} installed successfully.": "Portul {download_name!r} a fost instalat cu succes.", + "Installation failed, removing files...": "Instalarea a eșuat, se elimină fișierele...", + "Okay": "OK", + "Cancel": "Anulează", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Portul {download_name!r} și {runtime_name!r} au fost instalate cu succes.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Portul {download_name!r} a fost instalat cu succes, dar instalarea pentru {runtime_name!r} a eșuat!!\n\nPoți reîncerca instalarea sau verifica wiki-ul pentru ajutor.", + "Unable to download in offline mode.": "Imposibil de descărcat în modul offline.", + "Unable to find a source for {port_name}": "Nu s-a găsit o sursă pentru {port_name}.", + "Unknown port {port_name}": "Port necunoscut {port_name}.", + "Removing {port_name}": "Se elimină {port_name}", + "Successfully uninstalled {port_name}": "{port_name} a fost dezinstalat cu succes.", + "Loading Ports.": "Se încarcă porturile.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Portul {runtime} conține un runtime defect, jocul ar putea să nu ruleze corect.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Portul {runtime} conține un runtime necunoscut, jocul ar putea să nu ruleze corect.", + "Not Installed": "Neinstalat", + "Update Available": "Actualizare Disponibilă", + "Verified": "Verificat", + "Unverified": "Neverificat", + "Broken": "Defect", + "Unable do download a runtime when in offline mode.": "Imposibil de descărcat un runtime în modul offline.", + "Verifying runtime {runtime}": "Se verifică runtime-ul {runtime}", + "Verifying": "Se verifică", + "Unable to find a download for {runtime}.": "Nu s-a găsit un fișier de descărcare pentru {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Nu s-a putut descărca {runtime} pentru arhitectura {device_arch}.", + "Verified {runtime} successfully.": "{runtime} a fost verificat cu succes.", + "Verified {runtime}.": "{runtime} verificat.", + "Updating {runtime}.": "Se actualizează {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Runtime-ul {runtime} este defect, se reinstalează.", + "Downloading runtime {runtime}.": "Se descarcă runtime-ul {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Nu s-a putut descărca {runtime}, jocul ar putea să nu ruleze corect.", + "Successfully downloaded {runtime}.": "{runtime} a fost descărcat cu succes.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nPentru a face acest lucru:\nMENU -> UTILITARE -> Rescanare Director ROM", + "Unable to download verification file.": "Nu s-a putut descărca fișierul de verificare.", + "Unable to download non zip files.": "Nu s-au putut descărca fișierele non zip.", + "Cache out of date.": "Cache-ul este expirat.", + "First check.": "Prima verificare.", + "Auto Update.": "Actualizare Automată.", + "Updating": "Se actualizează", + "Up to date already": "Deja la zi", + "Fetching latest info": "Se preiau cele mai recente informații", + "Done.": "Gata.", + "Fetching info": "Se preiau informațiile", + "Unable to find {port_name}.": "Nu s-a putut găsi {port_name}.", + "Unable to download file. [{status_code}]": "Nu s-a putut descărca fișierul. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Se descarcă {file_url} - ({total_length_mb} MB)", + "Downloading file.": "Se descarcă fișierul.", + "Download failed: {err}": "Descărcarea a eșuat: {err}", + "Passed file validation.": "Fișierul a trecut validarea.", + "Unable to validate download.": "Nu s-a putut valida descărcarea.", + "Unknown IP": "IP Necunoscut", + "Fetching latest gameinfo.xml files and cover images.": "Se preiau cele mai recente fișiere gameinfo.xml și imagini de copertă.", + "Downloading gameinfo.xml for {source_name}": "Se descarcă gameinfo.xml pentru {source_name}", + "Installing {port_name}": "Se instalează {port_name}", + "Uninstalling {port_name}": "Se dezinstalează {port_name}", + "Updating all port sources:": "Se actualizează toate sursele porturilor:", + "Checking {runtime_name}": "Se verifică {runtime_name}", + "** NO PORT **": "** FĂRĂ PORT **", + "Ready to Run": "Gata de Rulare", + "Requires Files": "Necesită Fișiere", + "Installed": "Instalat", + "Missing": "Lipsă", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Porturile au fost adăugate sau eliminate, actualizează lista de jocuri pentru a vedea noile jocuri sau pentru a elimina cele dezinstalate.", + "Danish": "Daneză", + "German": "Germană", + "English": "Engleză", + "Spanish": "Spaniolă", + "Finnish": "Finlandeză", + "French": "Franceză", + "Italian": "Italiană", + "Japanese": "Japoneză", + "Korean": "Coreeană", + "Dutch": "Olandeză", + "Polish": "Poloneză", + "Portuguese (Portugal)": "Portugheză (Portugalia)", + "Portuguese (Brazil)": "Portugheză (Brazilia)", + "Russian": "Rusă", + "Swedish": "Suedeză", + "Ukrainian": "Ucraineană", + "Chinese Simplified": "Chineză Simplificată", + "Messages": "Mesaje", + "Are you sure you want to cancel?": "Ești sigur că vrei să anulezi?", + "Featured Ports": "Porturi Recomandate", + "Select": "Selectează", + "Back": "Înapoi", + "No featured ports found, internet connection is required.": "Nu s-au găsit porturi recomandate, este necesară o conexiune la internet.", + "Filters Scene": "Scena Filtrelor", + "Alphabetical": "Alfabetic", + "Recently Added": "Adăugate Recent", + "Recently Updated": "Actualizate Recent", + "Action": "Acțiune", + "Adventure": "Aventură", + "Casino/Card": "Casino/Cărți", + "First Person Shooter": "Shooter la persoana întâi", + "Platformer": "Platformer", + "Racing": "Curse", + "Rhythm": "Ritm", + "Role Playing Game": "Joc de rol (RPG)", + "Simulation": "Simulare", + "Sports": "Sporturi", + "Strategy": "Strategie", + "Visual Novel": "Roman Vizual", + "Other": "Altele", + "Files Required": "Fișiere Necesare", + "Broken Ports": "Porturi Defecte", + "Free game, all files included.": "Joc gratuit, toate fișierele incluse.", + "Demo files included.": "Fișiere demo incluse.", + "Free external assets needed.": "Necesită resurse externe gratuite.", + "Paid external assets needed.": "Necesită resurse externe plătite.", + "{runtime_name} Runtime": "Runtime {runtime_name}", + "ARM 32bit": "ARM 32biți", + "ARM 64bit": "ARM 64biți", + "x86 64bit": "x86 64biți", + "Sort:": "Sortare:", + "Clear Filters": "Șterge Filtrele", + "Genres:": "Genuri:", + "Attributes:": "Atribute:", + "Architecture:": "Arhitectură:", + "Availability:": "Disponibilitate:", + "Porters:": "Porteri:", + "Delete": "Șterge", + "Upper Case": "Majuscule", + "Space": "Spațiu", + "Enter": "Intră", + "Lower Case": "Minuscule", + "Language Select": "Selectare Limbă", + "{lang_name} (Selected)": "{lang_name} (Selectat)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Vrei să schimbi limba?\n\nVa trebui să repornești pentru ca modificarea să aibă efect.", + "Main Menu": "Meniu Principal", + "Hand curated lists of ports": "Liste de porturi selectate manual", + "All Ports": "Toate Porturile", + "List all ports available on PortMaster.": "Listează toate porturile disponibile pe PortMaster.", + "Ready to Run Ports": "Porturi gata de rulare", + "List all ports that are ready to play!": "Listează toate porturile gata de jucat!", + "Manage Ports": "Gestionează Porturile", + "Update / Uninstall Ports": "Actualizare / Dezinstalare Porturi", + "Options": "Opțiuni", + "PortMaster Options": "Opțiuni PortMaster", + "Exit": "Ieșire", + "Quit PortMaster": "Ieși din PortMaster", + "Quit": "Ieșire", + "Secret Mode {secret_mode}": "Mod Secret {secret_mode}", + "Enabled": "Activat", + "Disabled": "Dezactivat", + "Are you sure you want to exit PortMaster?": "Ești sigur că vrei să ieși din PortMaster?", + "Options Menu": "Meniu Opțiuni", + "Interface": "Interfață", + "Choose Language": "Alege limba", + "Select the language PortMaster uses.": "Selectează limba utilizată de PortMaster.", + "Select Theme": "Selectează Tema", + "Select a theme for PortMaster.": "Selectează o temă pentru PortMaster.", + "Select Color Scheme": "Selectează Schema de Culori", + "Select a colour scheme for PortMaster": "Selectează o schemă de culori pentru PortMaster", + "Music: ": "Muzică: ", + "Enable or Disable background music in PortMaster.": "Activează sau dezactivează muzica de fundal în PortMaster.", + "Sound FX: ": "Efecte Sonore: ", + "Enable or Disable soundfx in PortMaster.": "Activează sau dezactivează efectele sonore în PortMaster.", + "Port Metadata": "Metadate Port", + "Metadata Update: ": "Actualizare Metadate: ", + "Auto": "Automată", + "Manual": "Manuală", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster actualizează metadatele atunci când porturile sunt instalate, astfel încât să afișeze imagini și descrieri ale porturilor când navighezi pe dispozitivul tău.", + "Metadata Refresh": "Reîmprospătare Metadate", + "Manually update port metadata with missing/updated information and artwork.": "Actualizează manual metadatele porturilor cu informații și ilustrații lipsă sau actualizate.", + "Ports Location: ": "Locația Porturilor: ", + "Roms section": "Secțiunea ROM-uri", + "Ports tab": "Fila porturi", + "Location where ports should be installed to.": "Locația unde ar trebui instalate porturile.", + "System": "Sistem", + "Runtime Manager": "Manager Runtime", + "Manage port runtimes.": "Gestionează runtime-urile porturilor.", + "Experimental Ports: ": "Porturi experimentale: ", + "Show or hide experimental ports.": "Afișează sau ascunde porturile experimentale.", + "Update Ports": "Actualizare Porturi", + "Fetch latest ports information.": "Preia cele mai recente informații despre porturi.", + "Update PortMaster": "Actualizare PortMaster", + "Force check for a new PortMaster version.": "Forțează verificarea unei noi versiuni a PortMaster.", + "Restore PortMaster": "Restaurare PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Aceasta va restaura PortMaster la cea mai recentă versiune stabilă.", + "Release Channel: {channel}": "Canal de distribuție: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Schimbă canalul de distribuție al PortMaster, fie beta, fie stabil.", + "Enable Alpha Releases": "Activează Versiunile Alpha", + "Change release channel of PortMaster to alpha.": "Schimbă canalul de distribuție al PortMaster la alpha.", + "Controller Mode: {controller_mode}": "Mod Controler: {controller_mode}", + "Toggle between various controller layouts.": "Comută între diferite configurații ale controlerului.", + "Secret Options": "Opțiuni Secrete", + "All Ports: ": "Toate Porturile: ", + "Show all ports, ignoring requirements.": "Afișează toate porturile, ignorând cerințele.", + "CWTBE Mode: ": "Mod CWTBE: ", + "Enable gptokeyb2 by default.": "Activează gptokeyb2 în mod implicit.", + "Delete PortMaster Config": "Șterge configurația PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "Aceasta poate cauza probleme, nu modifica dacă nu știi ce faci.", + "Delete PortMaster Runtimes": "Șterge Runtime-urile PortMaster", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Ești sigur că vrei să restaurezi PortMaster?\n\nAceasta va necesita rularea unui port special conceput care va fi instalat.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Un port special a fost instalat, numit \"Restore PortMaster.sh\"\n\nRulează acest port pentru a restaura instalarea PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster se va închide acum pentru a putea rula portul de restaurare.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Ești sigur că vrei să schimbi canalul de distribuție la versiunea alpha?\n\nPortMaster va face upgrade sau downgrade în consecință.\n\nACEASTA POATE ȘI VA PROVOCA PROBLEME, AI FOST AVERTIZAT", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Ești sigur că vrei să schimbi canalul de distribuție de la {current_channel} la {new_channel}?\n\nPortMaster va face upgrade sau downgrade în consecință.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Ești sigur că vrei să actualizezi gamelist.xml?\n\nAceasta va suprascrie orice imagine sau informație despre port colectată automat și va necesita o descărcare de peste 50 MB.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Ești sigur că vrei să muți porturile din {from_mode} în {to_mode}?", + "SDCARD 1": "Card SD 1", + "SDCARD 2": "Card SD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Ești sigur că vrei să gestionezi și să instalezi porturi pe {to_loc}?\n\nPorturile deja instalate nu vor fi mutate.\nPortMaster va reporni pentru ca această modificare să aibă efect.", + "Deleting Runtimes:": "Se șterg runtime-urile:", + "Source Manager": "Manager Surse", + "Port Info Popup": "Fereastră de Informații Port", + "Hide Info": "Ascunde Informațiile", + "Reinstall": "Reinstalează", + "Uninstall": "Dezinstalează", + "Install": "Instalează", + "Port Info": "Informații Port", + "Show Info": "Afișează Informații", + "Are you sure you want to uninstall {port_name}?": "Ești sigur că vrei să dezinstalezi {port_name}?", + "NO PORTS": "NICIUN PORT", + "** NO PORTS FOUND **": "** NU S-AU GĂSIT PORTURI **", + "Download ports first.": "Descarcă mai întâi porturile.", + "Try removing some filters.": "Încearcă să elimini unele filtre.", + "Ports List": "Lista Porturilor", + "Filters": "Filtre", + "Download All": "Descarcă Tot", + "Used": "Utilizat", + "Not Used": "Neutilizat", + "Check": "Verifică", + "Are you sure you want to download and verify all runtimes?": "Ești sigur că vrei să descarci și să verifici toate runtime-urile?", + "Deleted runtime {runtime}": "Runtime-ul {runtime} a fost șters", + "Disclaimer": "Declinarea responsabilității", + "Accept": "Acceptă", + "{theme_name} (Selected)": "{theme_name} (Selectat)", + "Download": "Descarcă", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Vrei să schimbi tema?\n\nVa trebui să repornești pentru ca modificarea să aibă efect.", + "Select Colour Scheme": "Selectează Schema de Culori", + "{item_name} (Selected)": "{item_name} (Selectat)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Vrei să schimbi schema de culori a temei?\n\nVa trebui să repornești pentru ca modificarea să aibă efect.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Schimbi de la versiunea {old_release_channel} la versiunea {new_release_channel}.\n\nDorești să faci un downgrade?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Există o nouă versiune a PortMaster ({portmaster_version})\n\nDorești să faci un upgrade?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Dorești să reinstalezi PortMaster?\n\nAceasta va reinstala din canalul {release_channel} la versiunea {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/ro_RO.themes.json b/PortMaster/pylibs/locales/ro_RO.themes.json new file mode 100644 index 00000000..93ff45cc --- /dev/null +++ b/PortMaster/pylibs/locales/ro_RO.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Timp: {system.time_24hr}\nBaterie: {system.battery_level}\nSpațiu: {system.free_space} liber", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genuri: {port_info.genres}\nDimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Porturi Disponibile: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltre: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Spațiu Liber: {system.free_space}\n\nPorter: {port_info.porter}\nGenuri: {port_info.genres}\nDimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstrucțiuni: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpațiu: {system.free_space} liber / {system.total_space} total\nTimp: {system.time_24hr}\nBaterie: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Creator: {theme_info.creator}\nStare: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpațiu: {system.free_space} liber", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtre: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genuri: {port_info.genres}\nDimensiune descărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Timp: {system.time_24hr} Baterie: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Titlu: {port_info.title}\nPorter: {port_info.porter}\nGenuri: {port_info.genres}\nDimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescriere: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Porturi Disponibile: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltre: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genuri: {port_info.genres}\n{if:port_info.install_size}Dimensiune Instalată: {port_info.install_size}{else}Dimensiune descărcare: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Timp: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDispozitiv: {system.device_name}\nBaterie: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpațiu: {system.free_space} liber / {system.total_space} total", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porter: {port_info.porter}\n• Genuri: {port_info.genres}\n• Descriere: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genuri: {port_info.genres}\n• Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descriere: {port_info.description}\n• Instrucțiuni:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Descriere:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nume: {runtime_info.name}\nStatus: {runtime_info.status}\nPorturi: {runtime_info.ports}\nDimensiune Descărcare: {runtime_info.download_size}\nDimensiune Instalare: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/messages.po deleted file mode 100644 index a16ef3af..00000000 --- a/PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Romanian\n" -"Language: ro_RO\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ro\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Daneză" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Germană" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Engleză" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Spaniolă" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finlandeză" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Franceză" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italiană" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japoneză" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Coreeană" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Olandeză" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Poloneză" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugheză (Portugalia)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugheză (Brazilia)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Rusă" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Suedeză" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ucraineană" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Chineză Simplificată" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "IP Necunoscut" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** FĂRĂ PORT **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Gata de Rulare" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Necesită Fișiere" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Instalat" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Lipsă" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Actualizare Disponibilă" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Neinstalat" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "OK" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Anulează" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Se preiau cele mai recente fișiere gameinfo.xml și imagini de copertă." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Se descarcă gameinfo.xml pentru {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Se instalează {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Se dezinstalează {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Se actualizează toate sursele porturilor:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Se verifică {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Schimbi de la versiunea {old_release_channel} la versiunea {new_release_channel}.\n\n" -"Dorești să faci un downgrade?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Există o nouă versiune a PortMaster ({portmaster_version})\n\n" -"Dorești să faci un upgrade?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Dorești să reinstalezi PortMaster?\n\n" -"Aceasta va reinstala din canalul {release_channel} la versiunea {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Porturile au fost adăugate sau eliminate, actualizează lista de jocuri pentru a vedea noile jocuri sau pentru a elimina cele dezinstalate." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Se încarcă..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Validarea descărcării a eșuat." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Se încarcă informațiile." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Se preiau cele mai recente porturi recomandate." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Se încarcă sursele." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Se încarcă porturile." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Se instalează tema {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Se instalează" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Tema {download_name!r} a fost instalată cu succes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Se instalează {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Portul {download_name!r} a fost instalat cu succes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Instalarea a eșuat, se elimină fișierele..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Instalarea portului {download_name} a eșuat." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Portul {download_name!r} și {runtime_name!r} au fost instalate cu succes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Portul {download_name!r} a fost instalat cu succes, dar instalarea pentru {runtime_name!r} a eșuat!!\n\n" -"Poți reîncerca instalarea sau verifica wiki-ul pentru ajutor." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Portul {runtime} conține un runtime defect, jocul ar putea să nu ruleze corect." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Portul {runtime} conține un runtime necunoscut, jocul ar putea să nu ruleze corect." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Verificat" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Neverificat" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Defect" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Imposibil de descărcat un runtime în modul offline." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Se verifică runtime-ul {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Se verifică" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Nu s-a găsit un fișier de descărcare pentru {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Nu s-a putut descărca {runtime} pentru arhitectura {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} a fost verificat cu succes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "{runtime} verificat." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Se actualizează {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Runtime-ul {runtime} este defect, se reinstalează." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Se descarcă runtime-ul {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Nu s-a putut descărca {runtime}, jocul ar putea să nu ruleze corect." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} a fost descărcat cu succes." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Imposibil de descărcat în modul offline." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Nu s-a găsit o sursă pentru {port_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Port necunoscut {port_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Se elimină {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} a fost dezinstalat cu succes." - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Pentru a face acest lucru:\n" -"MENU -> UTILITARE -> Rescanare Director ROM" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache-ul este expirat." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Prima verificare." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Actualizare Automată." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Se actualizează" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Deja la zi" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Se preiau cele mai recente informații" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Gata." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Nu s-a putut găsi {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Nu s-au găsit informații de verificare pentru {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Nu s-au putut descărca informațiile de verificare pentru {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Succes!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Se preiau informațiile" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Se preiau cele mai recente porturi" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Se preiau informațiile." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Nu s-a putut descărca fișierul de verificare." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Nu s-au putut descărca fișierele non zip." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Nu s-a putut descărca fișierul. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Se descarcă {file_url} - ({total_length_mb} MB)" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Se descarcă fișierul." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Descărcarea a eșuat: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Fișierul a trecut validarea." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Nu s-a putut valida descărcarea." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Meniu Principal" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Declinarea responsabilității" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Acceptă" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Ieșire" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Ești sigur că vrei să ieși din PortMaster?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Porturi Recomandate" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Liste de porturi selectate manual" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Toate Porturile" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Listează toate porturile disponibile pe PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Porturi gata de rulare" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Listează toate porturile gata de jucat!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Gestionează Porturile" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Actualizare / Dezinstalare Porturi" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Opțiuni" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Opțiuni PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Ieșire" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Ieși din PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Intră" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Mod Secret {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Activat" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Dezactivat" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Meniu Opțiuni" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Interfață" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Alege limba" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Selectează limba utilizată de PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Selectează Tema" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Selectează o temă pentru PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Selectează Schema de Culori" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Selectează o schemă de culori pentru PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Muzică: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Activează sau dezactivează muzica de fundal în PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Efecte Sonore: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Activează sau dezactivează efectele sonore în PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Metadate Port" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Actualizare Metadate: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Automată" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manuală" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster actualizează metadatele atunci când porturile sunt instalate, astfel încât să afișeze imagini și descrieri ale porturilor când navighezi pe dispozitivul tău." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Reîmprospătare Metadate" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Actualizează manual metadatele porturilor cu informații și ilustrații lipsă sau actualizate." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Locația Porturilor: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Secțiunea ROM-uri" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Fila porturi" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Locația unde ar trebui instalate porturile." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Sistem" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Manager Runtime" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Gestionează runtime-urile porturilor." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Porturi experimentale: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Afișează sau ascunde porturile experimentale." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Actualizare Porturi" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Preia cele mai recente informații despre porturi." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Actualizare PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Forțează verificarea unei noi versiuni a PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Restaurare PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Aceasta va restaura PortMaster la cea mai recentă versiune stabilă." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Canal de distribuție: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Schimbă canalul de distribuție al PortMaster, fie beta, fie stabil." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Activează Versiunile Alpha" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Schimbă canalul de distribuție al PortMaster la alpha." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Mod Controler: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Comută între diferite configurații ale controlerului." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Opțiuni Secrete" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Toate Porturile: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Afișează toate porturile, ignorând cerințele." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Mod CWTBE: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Activează gptokeyb2 în mod implicit." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Șterge configurația PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Aceasta poate cauza probleme, nu modifica dacă nu știi ce faci." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Șterge Runtime-urile PortMaster" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Înapoi" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Ești sigur că vrei să restaurezi PortMaster?\n\n" -"Aceasta va necesita rularea unui port special conceput care va fi instalat." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Un port special a fost instalat, numit \"Restore PortMaster.sh\"\n\n" -"Rulează acest port pentru a restaura instalarea PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster se va închide acum pentru a putea rula portul de restaurare." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Ești sigur că vrei să schimbi canalul de distribuție la versiunea alpha?\n\n" -"PortMaster va face upgrade sau downgrade în consecință.\n\n" -"ACEASTA POATE ȘI VA PROVOCA PROBLEME, AI FOST AVERTIZAT" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Ești sigur că vrei să schimbi canalul de distribuție de la {current_channel} la {new_channel}?\n\n" -"PortMaster va face upgrade sau downgrade în consecință." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Ești sigur că vrei să actualizezi gamelist.xml?\n\n" -"Aceasta va suprascrie orice imagine sau informație despre port colectată automat și va necesita o descărcare de peste 50 MB." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Ești sigur că vrei să muți porturile din {from_mode} în {to_mode}?" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "Card SD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "Card SD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Ești sigur că vrei să gestionezi și să instalezi porturi pe {to_loc}?\n\n" -"Porturile deja instalate nu vor fi mutate.\n" -"PortMaster va reporni pentru ca această modificare să aibă efect." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Se șterg runtime-urile:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Manager Surse" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Descarcă Tot" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Utilizat" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Neutilizat" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Verifică" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Dezinstalează" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Instalează" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Ești sigur că vrei să descarci și să verifici toate runtime-urile?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Runtime-ul {runtime} a fost șters" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Selectat)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Selectează" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Descarcă" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Vrei să schimbi tema?\n\n" -"Va trebui să repornești pentru ca modificarea să aibă efect." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Selectează Schema de Culori" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Selectat)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Vrei să schimbi schema de culori a temei?\n\n" -"Va trebui să repornești pentru ca modificarea să aibă efect." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Selectare Limbă" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Selectat)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Vrei să schimbi limba?\n\n" -"Va trebui să repornești pentru ca modificarea să aibă efect." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Șterge" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Majuscule" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Spațiu" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Minuscule" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Nu s-au găsit porturi recomandate, este necesară o conexiune la internet." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "NICIUN PORT" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** NU S-AU GĂSIT PORTURI **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Descarcă mai întâi porturile." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Încearcă să elimini unele filtre." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Afișează Informații" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Lista Porturilor" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filtre" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Fereastră de Informații Port" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Ascunde Informațiile" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Reinstalează" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Informații Port" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Ești sigur că vrei să dezinstalezi {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Scena Filtrelor" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabetic" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Adăugate Recent" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Actualizate Recent" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Acțiune" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Aventură" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Casino/Cărți" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Shooter la persoana întâi" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Platformer" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Curse" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Ritm" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Joc de rol (RPG)" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulare" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sporturi" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Strategie" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Roman Vizual" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Altele" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Fișiere Necesare" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Porturi Defecte" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Joc gratuit, toate fișierele incluse." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Fișiere demo incluse." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Necesită resurse externe gratuite." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Necesită resurse externe plătite." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "Runtime {runtime_name}" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32biți" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64biți" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64biți" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Sortare:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Șterge Filtrele" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Genuri:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Atribute:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Arhitectură:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Disponibilitate:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Porteri:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Mesaje" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Ești sigur că vrei să anulezi?" - diff --git a/PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/themes.po deleted file mode 100644 index 95844041..00000000 --- a/PortMaster/pylibs/locales/ro_RO/LC_MESSAGES/themes.po +++ /dev/null @@ -1,196 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Romanian\n" -"Language: ro_RO\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : (n==0 || (n%100>0 && n%100<20)) ? 1 : 2);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ro\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genuri: {port_info.genres}\n" -"Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Porturi Disponibile: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtre: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Spațiu Liber: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genuri: {port_info.genres}\n" -"Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instrucțiuni: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Spațiu: {system.free_space} liber / {system.total_space} total\n" -"Timp: {system.time_24hr}\n" -"Baterie: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Creator: {theme_info.creator}\n" -"Stare: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Timp: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Dispozitiv: {system.device_name}\n" -"Baterie: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Spațiu: {system.free_space} liber" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filtre: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genuri: {port_info.genres}\n" -"Dimensiune descărcare: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Timp: {system.time_24hr} Baterie: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Spațiu: {system.free_space} liber / {system.total_space} total" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Porter: {port_info.porter}\n" -"• Genuri: {port_info.genres}\n" -"• Descriere: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Genuri: {port_info.genres}\n" -"• Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Descriere: {port_info.description}\n" -"• Instrucțiuni:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Descriere:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Nume: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Porturi: {runtime_info.ports}\n" -"Dimensiune Descărcare: {runtime_info.download_size}\n" -"Dimensiune Instalare: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Timp: {system.time_24hr}\n" -"Baterie: {system.battery_level}\n" -"Spațiu: {system.free_space} liber" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Titlu: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genuri: {port_info.genres}\n" -"Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Descriere: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Porturi Disponibile: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filtre: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genuri: {port_info.genres}\n" -"{if:port_info.install_size}Dimensiune Instalată: {port_info.install_size}{else}Dimensiune descărcare: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/ru_RU.json b/PortMaster/pylibs/locales/ru_RU.json new file mode 100644 index 00000000..106db2f8 --- /dev/null +++ b/PortMaster/pylibs/locales/ru_RU.json @@ -0,0 +1,257 @@ +{ + "Loading...": "Загрузка...", + "Loading Sources.": "Загрузка источников.", + "Download validation failed.": "Ошибка проверки загрузки.", + "Loading Info.": "Загрузка информации.", + "Fetching latest featured ports.": "Получение последних портов.", + "Installing Theme {download_name}.": "Установка темы {download_name}.", + "Installing": "Установка", + "Theme {download_name!r} installed successfully.": "Тема {download_name!r} успешно установлена.", + "Port {download_name} installed failed.": "Не удалось установить порт {download_name}.", + "Installing {download_name}.": "Установка {download_name}.", + "Port {download_name!r} installed successfully.": "Порт {download_name!r} успешно установлен.", + "Installation failed, removing files...": "Установка не удалась, удаление файлов...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "ПРИВЕТ.\n\nКАЖЕТСЯ, ВЫ ВРУЧНУЮ УСТАНАВЛИВАЕТЕ ПОРТ, НЕСОВМЕСТИМЫЙ С ВАШИМ УСТРОЙСТВОМ.\n\nЕСЛИ У ВАС ВОЗНИКЛИ ПРОБЛЕМЫ, НЕ СООБЩАЙТЕ О НИХ В DISCORD, ВЫ СТАНЕТЕ ОБЪЕКТОМ НАСМЕШЕК.\n\nВЫ УВЕРЕНЫ, ЧТО ХОТИТЕ ПРОДОЛЖИТЬ УСТАНОВКУ?", + "Okay": "Ок", + "Cancel": "Отменить", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Порт {download_name!r} и {runtime_name!r} успешно установлены.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успешно установлен, но {runtime_name!r} не удалось установить!\n\nПопробуйте переустановить или обратитесь за помощью на вики.", + "Unable to download in offline mode.": "Невозможно загрузить в автономном режиме.", + "Unable to find a source for {port_name}": "Для {port_name} не удается найти источник", + "Unknown port {port_name}": "Неизвестный порт {port_name}", + "Removing {port_name}": "Удаление {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Ошибка удаления {port_name}\n\n{error}", + "Successfully uninstalled {port_name}": "{port_name} Успешно удален", + "Loading Ports.": "Загрузка портов.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Порт {runtime} содержит неподходящую среду исполнения, игра может работать некорректно.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Порт {runtime} содержит неизвестную среду исполнения, игра может работать некорректно.", + "Not Installed": "Не установлен", + "Update Available": "Доступно обновление", + "Verified": "Проверенный", + "Unverified": "Непроверенный", + "Broken": "Не рабочий", + "Unable do download a runtime when in offline mode.": "Невозможно загрузить среду исполнения в автономном режиме.", + "Verifying runtime {runtime}": "Проверка среды исполнения {runtime}", + "Verifying": "Проверка", + "Unable to find a download for {runtime}.": "Не удалось найти загрузку для {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Невозможно загрузить {runtime} в {device_arch}.", + "Verified {runtime} successfully.": "{runtime} Успешно проверена.", + "Verified {runtime}.": "Проверено {runtime}.", + "Updating {runtime}.": "Обновление {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Среда исполнения {runtime} повреждена, переустановка.", + "Downloading runtime {runtime}.": "Установка среды исполнения {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Не удается загрузить {runtime}, игра может работать некорректно.", + "Successfully downloaded {runtime}.": "{runtime} Успешно загружено.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nЧтобы сделать это:\nМЕНЮ -> ПРОГРАММЫ -> Повторить поиск каталога", + "Unable to download verification file.": "Не удалось загрузить проверочный файл.", + "Unable to download non zip files.": "Невозможно загрузить не zip-файлы.", + "Cache out of date.": "Кэш устарел.", + "First check.": "Первая проверка.", + "Auto Update.": "Автоматическое обновление.", + "Updating": "Обновление", + "Up to date already": "Актуальная версия уже существует", + "Fetching latest info": "Получение последней информации", + "Done.": "Готово.", + "Fetching info": "Получение информации", + "Unable to find {port_name}.": "Не удается найти {port_name}.", + "Unable to download file. [{status_code}]": "Невозможно загрузить файл. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Загрузка {file_url} - ({total_length_mb})", + "Downloading file.": "Загрузка файла.", + "Download failed: {err}": "Ошибка загрузки: {err}", + "Passed file validation.": "Проверка файла пройдена.", + "Unable to validate download.": "Не удалось проверить загрузку.", + "Unknown IP": "Неизвестный IP", + "N/A": "Н/Д", + "Fetching latest gameinfo.xml files and cover images.": "Получение последних файлов gameinfo.xml и обложек.", + "Downloading gameinfo.xml for {source_name}": "Скачивание gameinfo.xml для\n{source_name}", + "Installing {port_name}": "Установка {port_name}", + "Uninstalling {port_name}": "Удаление {port_name}", + "Updating all port sources:": "Обновление всех источников портов:", + "Checking {runtime_name}": "Проверяем {runtime_name}", + "** NO PORT **": "** НЕТ ПОРТА **", + "Ready to Run": "Готов к запуску", + "Requires Files": "Требуются файлы", + "Installed": "Установлено", + "Missing": "Отсутствует", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Порты были добавлены или удалены, обновите список игр, чтобы увидеть новые игры или удалить неустановленные.", + "Danish": "Датский", + "German": "Немецкий", + "English": "Английский", + "Spanish": "Испанский", + "Finnish": "Финский", + "French": "Французский", + "Italian": "Итальянский", + "Japanese": "Японский", + "Korean": "Корейский", + "Dutch": "Нидерландский", + "Polish": "Польский", + "Portuguese (Portugal)": "Португальский (Португалия)", + "Portuguese (Brazil)": "Португальский (Бразилия)", + "Romanian": "Румынский", + "Russian": "Русский", + "Swedish": "Шведский", + "Ukrainian": "Украинский", + "Chinese Simplified": "Китайский упрощенный", + "Messages": "Сообщения", + "Are you sure you want to cancel?": "Прервать установку?", + "Featured Ports": "Рекомендуемые порты", + "Select": "Выбрать", + "Back": "Назад", + "No featured ports found, internet connection is required.": "Порты не найдены, необходимо подключение к Интернету.", + "Filters Scene": "Вкладка фильтров", + "Alphabetical": "По алфавиту", + "Recently Added": "Недавно добавленные", + "Recently Updated": "Недавно обновленные", + "Action": "Экшен", + "Adventure": "Приключение", + "Arcade": "Аркада", + "Casino/Card": "Казино/Карточная", + "First Person Shooter": "Шутер от первого лица", + "Platformer": "Платформер", + "Puzzle": "Головоломка", + "Racing": "Гонки", + "Rhythm": "Ритм", + "Role Playing Game": "Ролевая игра", + "Simulation": "Симулятор", + "Sports": "Спорт", + "Strategy": "Стратегия", + "Visual Novel": "Визуальная новелла", + "Other": "Прочие", + "Files Required": "Необходимые файлы", + "Experimental": "Экспериментально", + "Broken Ports": "Поврежденные порты", + "Free game, all files included.": "Бесплатная игра, все файлы включены.", + "Demo files included.": "Демо файлы включены.", + "Free external assets needed.": "Необходимы бесплатные внешние ресурсы.", + "Paid external assets needed.": "Необходимы платные внешние ресурсы.", + "{runtime_name} Runtime": "{runtime_name} Среда выполнения", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Sort:": "Сортировка:", + "Clear Filters": "Очистить фильтры", + "Genres:": "Жанры:", + "Attributes:": "Атрибуты:", + "Architecture:": "Архитектура:", + "Availability:": "Доступность:", + "Porters:": "Портеры:", + "Delete": "Удалить", + "Upper Case": "Верхний регистр", + "Space": "Пробел", + "Enter": "Выбрать", + "Lower Case": "Нижний регистр", + "Language Select": "Выберите язык", + "{lang_name} (Selected)": "{lang_name} (Выбранный)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Вы хотите сменить язык?\n\nВам придется перезагрузиться, чтобы изменения вступили в силу.", + "Main Menu": "Главное меню", + "Hand curated lists of ports": "Создать вручную списки портов", + "All Ports": "Все порты", + "List all ports available on PortMaster.": "Список всех доступных портов на PortMaster.", + "Ready to Run Ports": "Готовые к запуску", + "List all ports that are ready to play!": "Список всех портов, готовых к игре!", + "Manage Ports": "Управление портами", + "Update / Uninstall Ports": "Обновить / удалить порт", + "Options": "Настройки", + "PortMaster Options": "Настройки PortMaster", + "Exit": "Выход", + "Quit PortMaster": "Покинуть PortMaster", + "Quit": "Выйти", + "Secret Mode {secret_mode}": "Секретный режим {secret_mode}", + "Enabled": "Включено", + "Disabled": "Выключено", + "Are you sure you want to exit PortMaster?": "Вы хотите выйти из PortMaster?", + "Options Menu": "Настройки", + "Interface": "Интерфейс", + "Choose Language": "Язык", + "Select the language PortMaster uses.": "Изменить используемый язык PortMaster.", + "Select Theme": "Выбрать тему", + "Select a theme for PortMaster.": "Изменить тему PortMaster.", + "Select Color Scheme": "Выбрать цветовую схему", + "Select a colour scheme for PortMaster": "Изменить цветовую схему PortMaster", + "Audio": "Звук", + "Music: ": "Музыка: ", + "Enable or Disable background music in PortMaster.": "Включить или отключить фоновую музыку в PortMaster.", + "Sound FX: ": "Звуковые эффекты: ", + "Enable or Disable soundfx in PortMaster.": "Включить или выключить звуковые эффекты в PortMaster.", + "Port Metadata": "Метаданные порта", + "Metadata Update: ": "Обновление метаданных: ", + "Auto": "Авто", + "Manual": "Ручной режим", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster обновляет метаданные при установке портов так, чтобы при просмотре портов на вашем устройстве отображались изображения и описания портов.", + "Metadata Refresh": "Обновить метаданные", + "Manually update port metadata with missing/updated information and artwork.": "Ручное обновление метаданных порта с пропущенной/обновленной информацией и обложкой.", + "Ports Location: ": "Местоположение портов: ", + "Roms section": "Roms раздел", + "Ports tab": "Закладка портов", + "Location where ports should be installed to.": "Местоположение, в котором должны быть установлены порты.", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "Система", + "Runtime Manager": "Менеджер среды исполнения", + "Manage port runtimes.": "Управление средами выполнения.", + "Experimental Ports: ": "Экспериментальные порты: ", + "Show or hide experimental ports.": "Показать или скрыть экспериментальные порты.", + "Update Ports": "Обновить порты", + "Fetch latest ports information.": "Получить последнюю информацию о портах.", + "Update PortMaster": "Обновить PortMaster", + "Force check for a new PortMaster version.": "Проверить новую версию PortMaster.", + "Restore PortMaster": "Восстановить PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Это восстановит PortMaster до последней стабильной версии PortMaster.", + "Release Channel: {channel}": "Канал выпуска: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Изменить канал релизов PortMaster, бета или стабильный.", + "Enable Alpha Releases": "Включить альфа релизы", + "Change release channel of PortMaster to alpha.": "Изменить канал релизов PortMaster на альфа.", + "Controller Mode: {controller_mode}": "Режим контроллера: {controller_mode}", + "Toggle between various controller layouts.": "Переключение между различными раскладками контроллеров.", + "Secret Options": "Секретные параметры", + "All Ports: ": "Все порты: ", + "Show all ports, ignoring requirements.": "Показать все порты, игнорировать требования.", + "CWTBE Mode: ": "CWTBE режим: ", + "Enable gptokeyb2 by default.": "По умолчанию включить gptokeyb2.", + "Delete PortMaster Config": "Удалить конфигурацию PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "Это может сломать некоторые функции, не трогайте, если вы не знаете, что делаете.", + "Delete PortMaster Runtimes": "Удалить среды выполнения PortMaster", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Вы уверены, что хотите восстановить ПортМастер?\n\nДля этого вам потребуется запустить специально созданный порт, который будет установлен.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Был установлен специальный порт под названием \"Restore PortMaster.sh\"\n\nЗапустите этот порт для восстановления вашей установки PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster будет закрыт, так что вы можете запустить порт восстановления.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Вы уверены, что хотите изменить канал выпуска на альфа?\n\nPortMaster обновится или понизится соответствующим образом.\n\nЭТО МОЖЕТ СЛОМАТЬ НЕКОТОРЫЕ ФУНКЦИИ, ВАС ПРЕДУПРЕЖДАЛИ", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Вы уверены, что хотите изменить канал выпуска с {current_channel} на {new_channel}?\n\nPortMaster обновится или понизится соответствующим образом.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Вы уверены, что хотите обновить файл gamelist.xml?\n\nЭто переопределит любые изображения или информацию о порте, и потребуется загрузка объемом более 50 МБ.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Вы уверены, что хотите переместить порты из {from_mode} в {to_mode}", + "SDCARD 1": "SDCARD 1", + "SDCARD 2": "SDCARD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Вы уверены, что хотите управлять и устанавливать порты на {to_loc}?\n\nУже установленные порты не будут перемещены.\nPortMaster перезагрузится чтобы изменения вступили в силу.", + "Deleting Runtimes:": "Удаление сред выполнения:", + "Source Manager": "Менеджер источника", + "Port Info Popup": "Всплывающая информация о порте", + "Hide Info": "Скрыть информацию", + "Reinstall": "Переустановить", + "Uninstall": "Удалить", + "Install": "Установить", + "Port Info": "Информация о порте", + "Show Info": "Показать информацию", + "Are you sure you want to uninstall {port_name}?": "Вы уверены, что хотите удалить {port_name}?", + "NO PORTS": "НЕТ ПОРТОВ", + "** NO PORTS FOUND **": "** ПОРТОВ НЕ НАЙДЕНО **", + "Download ports first.": "Сначала загрузите порты.", + "Try removing some filters.": "Попробуйте удалить некоторые фильтры.", + "Ports List": "Списки портов", + "Filters": "Фильтры", + "Download All": "Скачать все", + "Used": "Используется", + "Not Used": "Не используется", + "Check": "Проверить", + "Are you sure you want to download and verify all runtimes?": "Вы уверены, что хотите загрузить и проверить все среды выполнения?", + "Deleted runtime {runtime}": "Удаление сред выполнения {runtime}", + "Disclaimer": "Отказ от ответственности", + "Accept": "Принять", + "{theme_name} (Selected)": "{theme_name} (Выбрана)", + "Download": "Скачать", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Вы хотите сменить тему?\n\nВам придется перезагрузиться, чтобы изменения вступили в силу.", + "Select Colour Scheme": "Выбрать цветовую схему", + "{item_name} (Selected)": "{item_name} (Выбранная)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Хотите изменить цветовую схему темы?\n\nВам придется перезагрузиться, чтобы изменения вступили в силу.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Вы переключаетесь с версии {old_release_channel} на версию {new_release_channel}.\n\nВы желаете сменить версию?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна новая версия PortMaster ({portmaster_version})\n\nВы хотите обновиться?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Вы хотите переустановить PortMaster?\n\nЭто переустановит канал обновления {release_channel} на {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/ru_RU.themes.json b/PortMaster/pylibs/locales/ru_RU.themes.json new file mode 100644 index 00000000..3e33c017 --- /dev/null +++ b/PortMaster/pylibs/locales/ru_RU.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Время: {system.time_24hr}\nБатарея: {system.battery_level}\nНакопитель: {system.free_space} свободно", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанры: {port_info.genres} Размер загрузки: {port_info.download_size}{if:port_info.runtime} Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Доступные порты: {ports_list.filter_ports} / {ports_list.total_ports} \nФильтры: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Свободное место: {system.free_space}\n\nСоздатель: {port_info.porter}\nЖанры: {port_info.genres}\nРазмер скачивания: {port_info.download_size}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nИнструкция: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nНакопитель: {system.free_space} свободно / {system.total_space} всего\nВремя: {system.time_24hr}\nБатарея: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Автор: {theme_info.creator}\nСтатус: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nНакопитель: {system.free_space} свободно", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Фильтры: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанры: {port_info.genres}\nРазмер загрузки: {port_info.download_size}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Время: {system.time_24hr} Батарея: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Заголовок: {port_info.title}\nПортер: {port_info.porter}\nЖанры: {port_info.genres}\nРазмер скачивания: {port_info.download_size}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\nИнформация о порте: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Доступные порты: {ports_list.filter_ports} / {ports_list.total_ports} \nФильтры: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанры: {port_info.genres}\n{if:port_info.install_size}Установленный размер: {port_info.install_size}{else}Размер загрузки: {port_info.download_size}{endif}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Время: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nУстройство: {system.device_name}\nБатарея: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}Накопитель: {system.free_space} свободно / {system.total_space} всего", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Создатель: {port_info.porter}\n• Жанры: {port_info.genres}\n• Описание: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Жанры: {port_info.genres}\n• Скачать Size: {port_info.download_size}{if:port_info.runtime}\n• Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Описание: {port_info.description}\n• Инструкции:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Создатель: {theme_info.creator}\n• Статус: {theme_info.status}\n• Описание:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Имя: {runtime_info.name}\nСтатус: {runtime_info.status}\nПорты: {runtime_info.ports}\nРазмер загрузки: {runtime_info.download_size}\nРазмер установки: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/messages.po deleted file mode 100644 index f7bc9c6f..00000000 --- a/PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1253 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Russian\n" -"Language: ru_RU\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ru\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Датский" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Немецкий" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Английский" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Испанский" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Финский" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Французский" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Итальянский" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Японский" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Корейский" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Нидерландский" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Польский" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Португальский (Португалия)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Португальский (Бразилия)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "Румынский" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Русский" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Шведский" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Украинский" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Китайский упрощенный" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "Неизвестный IP" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "Н/Д" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** НЕТ ПОРТА **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Готов к запуску" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Требуются файлы" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Установлено" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Отсутствует" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Доступно обновление" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Не установлен" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Ок" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Отменить" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Получение последних файлов gameinfo.xml и обложек." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Скачивание gameinfo.xml для\n" -"{source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Установка {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Удаление {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Обновление всех источников портов:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Проверяем {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Вы переключаетесь с версии {old_release_channel} на версию {new_release_channel}.\n\n" -"Вы желаете сменить версию?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Доступна новая версия PortMaster ({portmaster_version})\n\n" -"Вы хотите обновиться?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Вы хотите переустановить PortMaster?\n\n" -"Это переустановит канал обновления {release_channel} на {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Порты были добавлены или удалены, обновите список игр, чтобы увидеть новые игры или удалить неустановленные." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Загрузка..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Ошибка проверки загрузки." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Загрузка информации." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Получение последних портов." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Загрузка источников." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Загрузка портов." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Установка темы {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Установка" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Тема {download_name!r} успешно установлена." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Установка {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Порт {download_name!r} успешно установлен." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "ПРИВЕТ.\n\n" -"КАЖЕТСЯ, ВЫ ВРУЧНУЮ УСТАНАВЛИВАЕТЕ ПОРТ, НЕСОВМЕСТИМЫЙ С ВАШИМ УСТРОЙСТВОМ.\n\n" -"ЕСЛИ У ВАС ВОЗНИКЛИ ПРОБЛЕМЫ, НЕ СООБЩАЙТЕ О НИХ В DISCORD, ВЫ СТАНЕТЕ ОБЪЕКТОМ НАСМЕШЕК.\n\n" -"ВЫ УВЕРЕНЫ, ЧТО ХОТИТЕ ПРОДОЛЖИТЬ УСТАНОВКУ?" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Установка не удалась, удаление файлов..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Не удалось установить порт {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Порт {download_name!r} и {runtime_name!r} успешно установлены." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Порт {download_name!r} успешно установлен, но {runtime_name!r} не удалось установить!\n\n" -"Попробуйте переустановить или обратитесь за помощью на вики." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Порт {runtime} содержит неподходящую среду исполнения, игра может работать некорректно." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Порт {runtime} содержит неизвестную среду исполнения, игра может работать некорректно." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Проверенный" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Непроверенный" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Не рабочий" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Невозможно загрузить среду исполнения в автономном режиме." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Проверка среды исполнения {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Проверка" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Не удалось найти загрузку для {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Невозможно загрузить {runtime} в {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} Успешно проверена." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "Проверено {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Обновление {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Среда исполнения {runtime} повреждена, переустановка." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Установка среды исполнения {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Не удается загрузить {runtime}, игра может работать некорректно." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "{runtime} Успешно загружено." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Невозможно загрузить в автономном режиме." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Для {port_name} не удается найти источник" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Неизвестный порт {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Удаление {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "Ошибка удаления {port_name}\n\n" -"{error}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "{port_name} Успешно удален" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Чтобы сделать это:\n" -"МЕНЮ -> ПРОГРАММЫ -> Повторить поиск каталога" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Кэш устарел." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Первая проверка." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Автоматическое обновление." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Обновление" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Актуальная версия уже существует" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Получение последней информации" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Готово." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Не удается найти {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Не удается найти информацию о верификации для {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Не удается найти информацию о верификации для {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Успешно!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Получение информации" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Получение последних портов" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Получение информации." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Не удалось загрузить проверочный файл." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Невозможно загрузить не zip-файлы." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Невозможно загрузить файл. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Загрузка {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Загрузка файла." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Ошибка загрузки: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Проверка файла пройдена." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Не удалось проверить загрузку." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Главное меню" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Отказ от ответственности" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Принять" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Выйти" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Вы хотите выйти из PortMaster?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Рекомендуемые порты" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Создать вручную списки портов" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Все порты" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Список всех доступных портов на PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Готовые к запуску" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Список всех портов, готовых к игре!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Управление портами" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Обновить / удалить порт" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Настройки" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Настройки PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Выход" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Покинуть PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Выбрать" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Секретный режим {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Включено" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Выключено" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Настройки" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Интерфейс" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Язык" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Изменить используемый язык PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Выбрать тему" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Изменить тему PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Выбрать цветовую схему" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Изменить цветовую схему PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Звук" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Музыка: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Включить или отключить фоновую музыку в PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Звуковые эффекты: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Включить или выключить звуковые эффекты в PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Метаданные порта" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Обновление метаданных: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Авто" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Ручной режим" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster обновляет метаданные при установке портов так, чтобы при просмотре портов на вашем устройстве отображались изображения и описания портов." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Обновить метаданные" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Ручное обновление метаданных порта с пропущенной/обновленной информацией и обложкой." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Местоположение портов: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Roms раздел" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Закладка портов" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Местоположение, в котором должны быть установлены порты." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Система" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Менеджер среды исполнения" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Управление средами выполнения." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Экспериментальные порты: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Показать или скрыть экспериментальные порты." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Обновить порты" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Получить последнюю информацию о портах." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Обновить PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Проверить новую версию PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Восстановить PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Это восстановит PortMaster до последней стабильной версии PortMaster." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Канал выпуска: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Изменить канал релизов PortMaster, бета или стабильный." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Включить альфа релизы" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Изменить канал релизов PortMaster на альфа." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Режим контроллера: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Переключение между различными раскладками контроллеров." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Секретные параметры" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Все порты: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Показать все порты, игнорировать требования." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "CWTBE режим: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "По умолчанию включить gptokeyb2." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Удалить конфигурацию PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Это может сломать некоторые функции, не трогайте, если вы не знаете, что делаете." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Удалить среды выполнения PortMaster" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Назад" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Вы уверены, что хотите восстановить ПортМастер?\n\n" -"Для этого вам потребуется запустить специально созданный порт, который будет установлен." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Был установлен специальный порт под названием \"Restore PortMaster.sh\"\n\n" -"Запустите этот порт для восстановления вашей установки PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster будет закрыт, так что вы можете запустить порт восстановления." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Вы уверены, что хотите изменить канал выпуска на альфа?\n\n" -"PortMaster обновится или понизится соответствующим образом.\n\n" -"ЭТО МОЖЕТ СЛОМАТЬ НЕКОТОРЫЕ ФУНКЦИИ, ВАС ПРЕДУПРЕЖДАЛИ" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Вы уверены, что хотите изменить канал выпуска с {current_channel} на {new_channel}?\n\n" -"PortMaster обновится или понизится соответствующим образом." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Вы уверены, что хотите обновить файл gamelist.xml?\n\n" -"Это переопределит любые изображения или информацию о порте, и потребуется загрузка объемом более 50 МБ." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Вы уверены, что хотите переместить порты из {from_mode} в {to_mode}" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SDCARD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SDCARD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Вы уверены, что хотите управлять и устанавливать порты на {to_loc}?\n\n" -"Уже установленные порты не будут перемещены.\n" -"PortMaster перезагрузится чтобы изменения вступили в силу." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Удаление сред выполнения:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Менеджер источника" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Скачать все" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Используется" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Не используется" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Проверить" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Удалить" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Установить" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Вы уверены, что хотите загрузить и проверить все среды выполнения?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Удаление сред выполнения {runtime}" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Выбрана)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Выбрать" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Скачать" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Вы хотите сменить тему?\n\n" -"Вам придется перезагрузиться, чтобы изменения вступили в силу." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Выбрать цветовую схему" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Выбранная)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Хотите изменить цветовую схему темы?\n\n" -"Вам придется перезагрузиться, чтобы изменения вступили в силу." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Выберите язык" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Выбранный)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Вы хотите сменить язык?\n\n" -"Вам придется перезагрузиться, чтобы изменения вступили в силу." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Удалить" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Верхний регистр" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Пробел" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Нижний регистр" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Порты не найдены, необходимо подключение к Интернету." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "НЕТ ПОРТОВ" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** ПОРТОВ НЕ НАЙДЕНО **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Сначала загрузите порты." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Попробуйте удалить некоторые фильтры." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Показать информацию" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Списки портов" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Фильтры" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Всплывающая информация о порте" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Скрыть информацию" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Переустановить" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Информация о порте" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Вы уверены, что хотите удалить {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Вкладка фильтров" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "По алфавиту" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Недавно добавленные" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Недавно обновленные" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Экшен" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Приключение" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Аркада" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Казино/Карточная" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Шутер от первого лица" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Платформер" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Головоломка" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Гонки" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Ритм" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Ролевая игра" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Симулятор" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Спорт" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Стратегия" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Визуальная новелла" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Прочие" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Необходимые файлы" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Экспериментально" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Поврежденные порты" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Бесплатная игра, все файлы включены." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Демо файлы включены." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Необходимы бесплатные внешние ресурсы." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Необходимы платные внешние ресурсы." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Среда выполнения" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32bit" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64bit" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64bit" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Сортировка:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Очистить фильтры" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Жанры:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Атрибуты:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Архитектура:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Доступность:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Портеры:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Сообщения" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Прервать установку?" - diff --git a/PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/themes.po deleted file mode 100644 index 385cb5e9..00000000 --- a/PortMaster/pylibs/locales/ru_RU/LC_MESSAGES/themes.po +++ /dev/null @@ -1,193 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Russian\n" -"Language: ru_RU\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: ru\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Жанры: {port_info.genres} Размер загрузки: {port_info.download_size}{if:port_info.runtime} Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Доступные порты: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Фильтры: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Свободное место: {system.free_space}\n\n" -"Создатель: {port_info.porter}\n" -"Жанры: {port_info.genres}\n" -"Размер скачивания: {port_info.download_size}{if:port_info.runtime}\n" -"Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Инструкция: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Накопитель: {system.free_space} свободно / {system.total_space} всего\n" -"Время: {system.time_24hr}\n" -"Батарея: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Автор: {theme_info.creator}\n" -"Статус: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Время: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Устройство: {system.device_name}\n" -"Батарея: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Накопитель: {system.free_space} свободно" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Фильтры: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Жанры: {port_info.genres}\n" -"Размер загрузки: {port_info.download_size}{if:port_info.runtime}\n" -"Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Время: {system.time_24hr} Батарея: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}Накопитель: {system.free_space} свободно / {system.total_space} всего" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Создатель: {port_info.porter}\n" -"• Жанры: {port_info.genres}\n" -"• Описание: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Жанры: {port_info.genres}\n" -"• Скачать Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Описание: {port_info.description}\n" -"• Инструкции:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Создатель: {theme_info.creator}\n" -"• Статус: {theme_info.status}\n" -"• Описание:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Имя: {runtime_info.name}\n" -"Статус: {runtime_info.status}\n" -"Порты: {runtime_info.ports}\n" -"Размер загрузки: {runtime_info.download_size}\n" -"Размер установки: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Время: {system.time_24hr}\n" -"Батарея: {system.battery_level}\n" -"Накопитель: {system.free_space} свободно" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Заголовок: {port_info.title}\n" -"Портер: {port_info.porter}\n" -"Жанры: {port_info.genres}\n" -"Размер скачивания: {port_info.download_size}{if:port_info.runtime}\n" -"Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Информация о порте: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Доступные порты: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Фильтры: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Жанры: {port_info.genres}\n" -"{if:port_info.install_size}Установленный размер: {port_info.install_size}{else}Размер загрузки: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json new file mode 100644 index 00000000..d6145555 --- /dev/null +++ b/PortMaster/pylibs/locales/strings.json @@ -0,0 +1,1035 @@ +{ + "This is not a valid zip file.": [ + "pylibs/harbourmaster/captain.py:47", + "pylibs/pugwash/autoinstall.py:112" + ], + "illegal file {file_name}": [ + "pylibs/harbourmaster/captain.py:54", + "pylibs/harbourmaster/captain.py:59", + "pylibs/harbourmaster/captain.py:64" + ], + "the port has no directory": [ + "pylibs/harbourmaster/captain.py:116" + ], + "the port has no launch script": [ + "pylibs/harbourmaster/captain.py:120" + ], + "unable to load {file_name}": [ + "pylibs/harbourmaster/captain.py:127", + "pylibs/harbourmaster/captain.py:132" + ], + "Loading...": [ + "pylibs/harbourmaster/harbour.py:126" + ], + "Loading Sources.": [ + "pylibs/harbourmaster/harbour.py:210" + ], + "Download validation failed.": [ + "pylibs/harbourmaster/hm_info.py:71", + "pylibs/harbourmaster/hm_info.py:91", + "pylibs/harbourmaster/util/net.py:141" + ], + "Unable to download file.": [ + "pylibs/harbourmaster/hm_info.py:86" + ], + "Loading Info.": [ + "pylibs/harbourmaster/hm_info.py:101" + ], + "Fetching latest ports info data.": [ + "pylibs/harbourmaster/hm_info.py:120" + ], + "Fetching latest porters info.": [ + "pylibs/harbourmaster/hm_info.py:124" + ], + "Fetching latest port stats.": [ + "pylibs/harbourmaster/hm_info.py:128" + ], + "Fetching latest featured ports.": [ + "pylibs/harbourmaster/hm_info.py:132" + ], + "Fetching latest featured port images zip.": [ + "pylibs/harbourmaster/hm_info.py:136" + ], + "Fetching featured port image {file_name}": [ + "pylibs/harbourmaster/hm_info.py:281" + ], + "Installing Theme {download_name}.": [ + "pylibs/harbourmaster/hm_install.py:57" + ], + "Installing": [ + "pylibs/harbourmaster/hm_install.py:66", + "pylibs/harbourmaster/hm_install.py:133", + "pylibs/harbourmaster/hm_install.py:267", + "pylibs/pugwash/autoinstall.py:131" + ], + "Theme {download_name!r} installed successfully.": [ + "pylibs/harbourmaster/hm_install.py:79" + ], + "PortMaster.zip is damaged, download it again.": [ + "pylibs/harbourmaster/hm_install.py:105" + ], + "Port {download_name} installed failed.": [ + "pylibs/harbourmaster/hm_install.py:107", + "pylibs/harbourmaster/hm_install.py:397" + ], + "Installing {download_name}.": [ + "pylibs/harbourmaster/hm_install.py:124", + "pylibs/harbourmaster/hm_install.py:251", + "pylibs/pugwash/autoinstall.py:126" + ], + "Port {download_name!r} installed successfully.": [ + "pylibs/harbourmaster/hm_install.py:168", + "pylibs/harbourmaster/hm_install.py:421" + ], + "Installation failed, removing files...": [ + "pylibs/harbourmaster/hm_install.py:186" + ], + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": [ + "pylibs/harbourmaster/hm_install.py:230" + ], + "Okay": [ + "pylibs/harbourmaster/hm_install.py:231", + "pylibs/pugwash/app_callbacks.py:179", + "pylibs/pugwash/app_fifo.py:79", + "pylibs/pugwash/scenes/dialogs.py:64", + "pylibs/pugwash/scenes/dialogs.py:111" + ], + "Cancel": [ + "pylibs/harbourmaster/hm_install.py:231", + "pylibs/pugwash/app_callbacks.py:182", + "pylibs/pugwash/app_fifo.py:80", + "pylibs/pugwash/scenes/dialogs.py:33", + "pylibs/pugwash/scenes/dialogs.py:67", + "pylibs/pugwash/scenes/dialogs.py:112", + "pylibs/pugwash/scenes/keyboard.py:33", + "pylibs/pugwash/scenes/keyboard.py:41" + ], + "Not compatible with this device.": [ + "pylibs/harbourmaster/hm_install.py:233" + ], + "Installation failed: {reason}": [ + "pylibs/harbourmaster/hm_install.py:394" + ], + "Port {download_name!r} and {runtime_name!r} installed successfully.": [ + "pylibs/harbourmaster/hm_install.py:411" + ], + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": [ + "pylibs/harbourmaster/hm_install.py:416" + ], + "Unable to download in offline mode.": [ + "pylibs/harbourmaster/hm_install.py:432", + "pylibs/harbourmaster/hm_install.py:512" + ], + "File not found.": [ + "pylibs/harbourmaster/hm_install.py:455" + ], + "Unable to find a source for {port_name}": [ + "pylibs/harbourmaster/hm_install.py:530" + ], + "Unknown port {port_name}": [ + "pylibs/harbourmaster/hm_install.py:544" + ], + "Removing {port_name}": [ + "pylibs/harbourmaster/hm_install.py:579" + ], + "Error uninstalling {port_name}\n\n{error}": [ + "pylibs/harbourmaster/hm_install.py:623" + ], + "Successfully uninstalled {port_name}": [ + "pylibs/harbourmaster/hm_install.py:627" + ], + "Loading Ports.": [ + "pylibs/harbourmaster/hm_ports.py:187" + ], + "Port {runtime} contains a bad runtime, game may not run correctly.": [ + "pylibs/harbourmaster/hm_runtimes.py:90" + ], + "Port {runtime} contains an unknown runtime, game may not run correctly.": [ + "pylibs/harbourmaster/hm_runtimes.py:104" + ], + "Not Installed": [ + "pylibs/harbourmaster/hm_runtimes.py:121", + "pylibs/pugwash/app_ports.py:93", + "pylibs/pugwash/scenes/filters.py:69", + "pylibs/pugwash/scenes/runtimes.py:91" + ], + "Update Available": [ + "pylibs/harbourmaster/hm_runtimes.py:122", + "pylibs/pugwash/app_ports.py:92", + "pylibs/pugwash/scenes/filters.py:70" + ], + "Verified": [ + "pylibs/harbourmaster/hm_runtimes.py:123" + ], + "Unverified": [ + "pylibs/harbourmaster/hm_runtimes.py:124" + ], + "Broken": [ + "pylibs/harbourmaster/hm_runtimes.py:125" + ], + "Unable do download a runtime when in offline mode.": [ + "pylibs/harbourmaster/hm_runtimes.py:136" + ], + "Verifying runtime {runtime}": [ + "pylibs/harbourmaster/hm_runtimes.py:142" + ], + "Verifying": [ + "pylibs/harbourmaster/hm_runtimes.py:151", + "pylibs/harbourmaster/hm_runtimes.py:154" + ], + "Unable to find a download for {runtime}.": [ + "pylibs/harbourmaster/hm_runtimes.py:179" + ], + "Unable to download {runtime} in {device_arch}.": [ + "pylibs/harbourmaster/hm_runtimes.py:185" + ], + "Verified {runtime} successfully.": [ + "pylibs/harbourmaster/hm_runtimes.py:207" + ], + "Verified {runtime}.": [ + "pylibs/harbourmaster/hm_runtimes.py:210" + ], + "Updating {runtime}.": [ + "pylibs/harbourmaster/hm_runtimes.py:216" + ], + "Runtime {runtime} is broken, reinstalling.": [ + "pylibs/harbourmaster/hm_runtimes.py:220" + ], + "Downloading runtime {runtime}.": [ + "pylibs/harbourmaster/hm_runtimes.py:224" + ], + "Unable to download {runtime}, game may not run correctly.": [ + "pylibs/harbourmaster/hm_runtimes.py:253", + "pylibs/harbourmaster/hm_runtimes.py:263" + ], + "Successfully downloaded {runtime}.": [ + "pylibs/harbourmaster/hm_runtimes.py:272" + ], + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": [ + "pylibs/harbourmaster/platform/retrodeck.py:18" + ], + "Unable to download verification file.": [ + "pylibs/harbourmaster/source/__init__.py:35" + ], + "Unable to download non zip files.": [ + "pylibs/harbourmaster/source/__init__.py:46" + ], + "Cache out of date.": [ + "pylibs/harbourmaster/source/base.py:34" + ], + "First check.": [ + "pylibs/harbourmaster/source/base.py:39" + ], + "Auto Update.": [ + "pylibs/harbourmaster/source/base.py:42" + ], + "Updating": [ + "pylibs/harbourmaster/source/portmaster.py:42", + "pylibs/pugwash/theme.py:441" + ], + "Up to date already": [ + "pylibs/harbourmaster/source/portmaster.py:53", + "pylibs/pugwash/theme.py:448" + ], + "Fetching latest info": [ + "pylibs/harbourmaster/source/portmaster.py:57", + "pylibs/pugwash/theme.py:451" + ], + "Done.": [ + "pylibs/harbourmaster/source/portmaster.py:136", + "pylibs/pugwash/theme.py:474" + ], + "Fetching info": [ + "pylibs/harbourmaster/source/portmaster.py:168", + "pylibs/pugwash/theme.py:480" + ], + "Unable to find {port_name}.": [ + "pylibs/harbourmaster/source/portmaster.py:389" + ], + "Unable to download file. [{status_code}]": [ + "pylibs/harbourmaster/util/net.py:70" + ], + "Downloading {file_url} - ({total_length_mb})": [ + "pylibs/harbourmaster/util/net.py:86" + ], + "Downloading file.": [ + "pylibs/harbourmaster/util/net.py:98", + "pylibs/harbourmaster/util/net.py:112" + ], + "Download failed: {err}": [ + "pylibs/harbourmaster/util/net.py:129" + ], + "Passed file validation.": [ + "pylibs/harbourmaster/util/net.py:147" + ], + "Unable to validate download.": [ + "pylibs/harbourmaster/util/net.py:152" + ], + "Unknown IP": [ + "pylibs/pugwash/app.py:229" + ], + "N/A": [ + "pylibs/pugwash/app.py:382", + "pylibs/pugwash/app_ports.py:70" + ], + "Fetching latest gameinfo.xml files and cover images.": [ + "pylibs/pugwash/app_commands.py:47" + ], + "Downloading gameinfo.xml for {source_name}": [ + "pylibs/pugwash/app_commands.py:68" + ], + "Installing {port_name}": [ + "pylibs/pugwash/app_commands.py:101" + ], + "Uninstalling {port_name}": [ + "pylibs/pugwash/app_commands.py:112" + ], + "Updating all port sources:": [ + "pylibs/pugwash/app_commands.py:125" + ], + "Checking {runtime_name}": [ + "pylibs/pugwash/app_commands.py:137" + ], + "Auto-installation": [ + "pylibs/pugwash/app_fifo.py:356", + "pylibs/pugwash/autoinstall.py:168" + ], + "** NO PORT **": [ + "pylibs/pugwash/app_ports.py:33" + ], + "Ready to Run": [ + "pylibs/pugwash/app_ports.py:52", + "pylibs/pugwash/scenes/filters.py:66", + "pylibs/pugwash/scenes/main_menu.py:96" + ], + "Requires Files": [ + "pylibs/pugwash/app_ports.py:52" + ], + "Installed": [ + "pylibs/pugwash/app_ports.py:64", + "pylibs/pugwash/app_ports.py:91", + "pylibs/pugwash/scenes/runtimes.py:91" + ], + "Missing": [ + "pylibs/pugwash/app_ports.py:66" + ], + "Installation failed, see log.txt for details.": [ + "pylibs/pugwash/autoinstall.py:101" + ], + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": [ + "pylibs/pugwash/autoinstall.py:123" + ], + "Installed {download_name}.": [ + "pylibs/pugwash/autoinstall.py:154" + ], + "- SUCCESS: {name}": [ + "pylibs/pugwash/autoinstall.py:192" + ], + "- FAILURE: {name}": [ + "pylibs/pugwash/autoinstall.py:194" + ], + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": [ + "pylibs/pugwash/autoinstall.py:204" + ], + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": [ + "pylibs/pugwash/autoinstall.py:219" + ], + "Everything installed successfully.": [ + "pylibs/pugwash/autoinstall.py:221" + ], + "{installed} installed, {failed} failed": [ + "pylibs/pugwash/autoinstall.py:227" + ], + "OK: {name}": [ + "pylibs/pugwash/autoinstall.py:233" + ], + "{name} installed successfully.": [ + "pylibs/pugwash/autoinstall.py:234" + ], + "FAILED: {name}": [ + "pylibs/pugwash/autoinstall.py:236" + ], + "{name} failed to install:\n\n{reason}": [ + "pylibs/pugwash/autoinstall.py:237" + ], + "Danish": [ + "pylibs/pugwash/lang.py:22" + ], + "German": [ + "pylibs/pugwash/lang.py:23" + ], + "English": [ + "pylibs/pugwash/lang.py:24" + ], + "Spanish": [ + "pylibs/pugwash/lang.py:25" + ], + "Esperanto": [ + "pylibs/pugwash/lang.py:26" + ], + "Finnish": [ + "pylibs/pugwash/lang.py:27" + ], + "French": [ + "pylibs/pugwash/lang.py:28" + ], + "Italian": [ + "pylibs/pugwash/lang.py:29" + ], + "Japanese": [ + "pylibs/pugwash/lang.py:30" + ], + "Korean": [ + "pylibs/pugwash/lang.py:31" + ], + "Dutch": [ + "pylibs/pugwash/lang.py:32" + ], + "Polish": [ + "pylibs/pugwash/lang.py:33" + ], + "Portuguese (Portugal)": [ + "pylibs/pugwash/lang.py:34" + ], + "Portuguese (Brazil)": [ + "pylibs/pugwash/lang.py:35" + ], + "Romanian": [ + "pylibs/pugwash/lang.py:36" + ], + "Russian": [ + "pylibs/pugwash/lang.py:37" + ], + "Swedish": [ + "pylibs/pugwash/lang.py:38" + ], + "Ukrainian": [ + "pylibs/pugwash/lang.py:39" + ], + "Chinese Simplified": [ + "pylibs/pugwash/lang.py:40" + ], + "Messages": [ + "pylibs/pugwash/scenes/dialogs.py:17" + ], + "Are you sure you want to cancel?": [ + "pylibs/pugwash/scenes/dialogs.py:52" + ], + "Featured Ports": [ + "pylibs/pugwash/scenes/featured.py:17", + "pylibs/pugwash/scenes/featured.py:19", + "pylibs/pugwash/scenes/main_menu.py:30" + ], + "Select": [ + "pylibs/pugwash/scenes/featured.py:30", + "pylibs/pugwash/scenes/keyboard.py:33", + "pylibs/pugwash/scenes/keyboard.py:41", + "pylibs/pugwash/scenes/language.py:30", + "pylibs/pugwash/scenes/themes.py:45", + "pylibs/pugwash/scenes/themes.py:130" + ], + "Back": [ + "pylibs/pugwash/scenes/featured.py:30", + "pylibs/pugwash/scenes/language.py:29", + "pylibs/pugwash/scenes/language.py:30", + "pylibs/pugwash/scenes/options.py:158", + "pylibs/pugwash/scenes/port_info.py:36", + "pylibs/pugwash/scenes/port_info.py:39", + "pylibs/pugwash/scenes/port_info.py:129", + "pylibs/pugwash/scenes/port_info.py:131", + "pylibs/pugwash/scenes/ports.py:241", + "pylibs/pugwash/scenes/ports.py:267", + "pylibs/pugwash/scenes/ports.py:269", + "pylibs/pugwash/scenes/runtimes.py:107", + "pylibs/pugwash/scenes/runtimes.py:110", + "pylibs/pugwash/scenes/themes.py:47", + "pylibs/pugwash/scenes/themes.py:129", + "pylibs/pugwash/scenes/themes.py:130" + ], + "No featured ports found, internet connection is required.": [ + "pylibs/pugwash/scenes/featured.py:47" + ], + "Filters Scene": [ + "pylibs/pugwash/scenes/filters.py:14" + ], + "Alphabetical": [ + "pylibs/pugwash/scenes/filters.py:43" + ], + "Recently Added": [ + "pylibs/pugwash/scenes/filters.py:44" + ], + "Recently Updated": [ + "pylibs/pugwash/scenes/filters.py:45" + ], + "Total Downloads": [ + "pylibs/pugwash/scenes/filters.py:46" + ], + "Action": [ + "pylibs/pugwash/scenes/filters.py:49" + ], + "Adventure": [ + "pylibs/pugwash/scenes/filters.py:50" + ], + "Arcade": [ + "pylibs/pugwash/scenes/filters.py:51" + ], + "Casino/Card": [ + "pylibs/pugwash/scenes/filters.py:52" + ], + "First Person Shooter": [ + "pylibs/pugwash/scenes/filters.py:53" + ], + "Platformer": [ + "pylibs/pugwash/scenes/filters.py:54" + ], + "Puzzle": [ + "pylibs/pugwash/scenes/filters.py:55" + ], + "Racing": [ + "pylibs/pugwash/scenes/filters.py:56" + ], + "Rhythm": [ + "pylibs/pugwash/scenes/filters.py:57" + ], + "Role Playing Game": [ + "pylibs/pugwash/scenes/filters.py:58" + ], + "Simulation": [ + "pylibs/pugwash/scenes/filters.py:59" + ], + "Sports": [ + "pylibs/pugwash/scenes/filters.py:60" + ], + "Strategy": [ + "pylibs/pugwash/scenes/filters.py:61" + ], + "Visual Novel": [ + "pylibs/pugwash/scenes/filters.py:62" + ], + "Other": [ + "pylibs/pugwash/scenes/filters.py:63" + ], + "Files Required": [ + "pylibs/pugwash/scenes/filters.py:67" + ], + "Experimental": [ + "pylibs/pugwash/scenes/filters.py:68" + ], + "Broken Ports": [ + "pylibs/pugwash/scenes/filters.py:71" + ], + "Free game, all files included.": [ + "pylibs/pugwash/scenes/filters.py:74" + ], + "Demo files included.": [ + "pylibs/pugwash/scenes/filters.py:75" + ], + "Free external assets needed.": [ + "pylibs/pugwash/scenes/filters.py:76" + ], + "Paid external assets needed.": [ + "pylibs/pugwash/scenes/filters.py:77" + ], + "{runtime_name} Runtime": [ + "pylibs/pugwash/scenes/filters.py:80", + "pylibs/pugwash/scenes/filters.py:81", + "pylibs/pugwash/scenes/filters.py:82", + "pylibs/pugwash/scenes/filters.py:83", + "pylibs/pugwash/scenes/filters.py:84", + "pylibs/pugwash/scenes/filters.py:85", + "pylibs/pugwash/scenes/filters.py:86" + ], + "ARM 32bit": [ + "pylibs/pugwash/scenes/filters.py:89" + ], + "ARM 64bit": [ + "pylibs/pugwash/scenes/filters.py:90" + ], + "x86 64bit": [ + "pylibs/pugwash/scenes/filters.py:91" + ], + "Ascending": [ + "pylibs/pugwash/scenes/filters.py:128" + ], + "Descending": [ + "pylibs/pugwash/scenes/filters.py:130" + ], + "Sort:": [ + "pylibs/pugwash/scenes/filters.py:138" + ], + "Clear Filters": [ + "pylibs/pugwash/scenes/filters.py:153" + ], + "Genres:": [ + "pylibs/pugwash/scenes/filters.py:177" + ], + "Attributes:": [ + "pylibs/pugwash/scenes/filters.py:207" + ], + "Architecture:": [ + "pylibs/pugwash/scenes/filters.py:236" + ], + "Availability:": [ + "pylibs/pugwash/scenes/filters.py:265" + ], + "Porters:": [ + "pylibs/pugwash/scenes/filters.py:294" + ], + "Delete": [ + "pylibs/pugwash/scenes/keyboard.py:33", + "pylibs/pugwash/scenes/keyboard.py:41" + ], + "Upper Case": [ + "pylibs/pugwash/scenes/keyboard.py:33" + ], + "Space": [ + "pylibs/pugwash/scenes/keyboard.py:33", + "pylibs/pugwash/scenes/keyboard.py:41" + ], + "Enter": [ + "pylibs/pugwash/scenes/keyboard.py:33", + "pylibs/pugwash/scenes/keyboard.py:41", + "pylibs/pugwash/scenes/main_menu.py:55", + "pylibs/pugwash/scenes/options.py:158" + ], + "Lower Case": [ + "pylibs/pugwash/scenes/keyboard.py:41" + ], + "Language Select": [ + "pylibs/pugwash/scenes/language.py:14" + ], + "{lang_name} (Selected)": [ + "pylibs/pugwash/scenes/language.py:24" + ], + "Do you want to change language?\n\nYou will have to restart for it to take affect.": [ + "pylibs/pugwash/scenes/language.py:47" + ], + "Main Menu": [ + "pylibs/pugwash/scenes/main_menu.py:17", + "pylibs/pugwash/scenes/startup.py:65" + ], + "Hand curated lists of ports": [ + "pylibs/pugwash/scenes/main_menu.py:31" + ], + "All Ports": [ + "pylibs/pugwash/scenes/main_menu.py:34", + "pylibs/pugwash/scenes/main_menu.py:95" + ], + "List all ports available on PortMaster.": [ + "pylibs/pugwash/scenes/main_menu.py:35" + ], + "Ready to Run Ports": [ + "pylibs/pugwash/scenes/main_menu.py:38" + ], + "List all ports that are ready to play!": [ + "pylibs/pugwash/scenes/main_menu.py:39" + ], + "Manage Ports": [ + "pylibs/pugwash/scenes/main_menu.py:42", + "pylibs/pugwash/scenes/main_menu.py:97" + ], + "Update / Uninstall Ports": [ + "pylibs/pugwash/scenes/main_menu.py:43" + ], + "Options": [ + "pylibs/pugwash/scenes/main_menu.py:48" + ], + "PortMaster Options": [ + "pylibs/pugwash/scenes/main_menu.py:49" + ], + "Exit": [ + "pylibs/pugwash/scenes/main_menu.py:52" + ], + "Quit PortMaster": [ + "pylibs/pugwash/scenes/main_menu.py:53" + ], + "Quit": [ + "pylibs/pugwash/scenes/main_menu.py:55", + "pylibs/pugwash/scenes/startup.py:129", + "pylibs/pugwash/scenes/startup.py:212" + ], + "Secret Mode {secret_mode}": [ + "pylibs/pugwash/scenes/main_menu.py:75" + ], + "Enabled": [ + "pylibs/pugwash/scenes/main_menu.py:76", + "pylibs/pugwash/scenes/options.py:49", + "pylibs/pugwash/scenes/options.py:53", + "pylibs/pugwash/scenes/options.py:93", + "pylibs/pugwash/scenes/options.py:136", + "pylibs/pugwash/scenes/options.py:140", + "pylibs/pugwash/scenes/options.py:144", + "pylibs/pugwash/scenes/options.py:200", + "pylibs/pugwash/scenes/options.py:212", + "pylibs/pugwash/scenes/options.py:226", + "pylibs/pugwash/scenes/options.py:238", + "pylibs/pugwash/scenes/options.py:314", + "pylibs/pugwash/scenes/options.py:323" + ], + "Disabled": [ + "pylibs/pugwash/scenes/main_menu.py:76", + "pylibs/pugwash/scenes/options.py:49", + "pylibs/pugwash/scenes/options.py:53", + "pylibs/pugwash/scenes/options.py:93", + "pylibs/pugwash/scenes/options.py:136", + "pylibs/pugwash/scenes/options.py:140", + "pylibs/pugwash/scenes/options.py:144", + "pylibs/pugwash/scenes/options.py:200", + "pylibs/pugwash/scenes/options.py:212", + "pylibs/pugwash/scenes/options.py:226", + "pylibs/pugwash/scenes/options.py:238", + "pylibs/pugwash/scenes/options.py:314", + "pylibs/pugwash/scenes/options.py:323" + ], + "Are you sure you want to exit PortMaster?": [ + "pylibs/pugwash/scenes/main_menu.py:124", + "pylibs/pugwash/scenes/startup.py:166", + "pylibs/pugwash/scenes/startup.py:242" + ], + "Options Menu": [ + "pylibs/pugwash/scenes/options.py:21" + ], + "Interface": [ + "pylibs/pugwash/scenes/options.py:27" + ], + "Choose Language": [ + "pylibs/pugwash/scenes/options.py:31" + ], + "Select the language PortMaster uses.": [ + "pylibs/pugwash/scenes/options.py:32" + ], + "Select Theme": [ + "pylibs/pugwash/scenes/options.py:35", + "pylibs/pugwash/scenes/themes.py:13" + ], + "Select a theme for PortMaster.": [ + "pylibs/pugwash/scenes/options.py:36" + ], + "Select Color Scheme": [ + "pylibs/pugwash/scenes/options.py:42" + ], + "Select a colour scheme for PortMaster": [ + "pylibs/pugwash/scenes/options.py:43" + ], + "Audio": [ + "pylibs/pugwash/scenes/options.py:45" + ], + "Music: ": [ + "pylibs/pugwash/scenes/options.py:49", + "pylibs/pugwash/scenes/options.py:314" + ], + "Enable or Disable background music in PortMaster.": [ + "pylibs/pugwash/scenes/options.py:50" + ], + "Sound FX: ": [ + "pylibs/pugwash/scenes/options.py:53", + "pylibs/pugwash/scenes/options.py:323" + ], + "Enable or Disable soundfx in PortMaster.": [ + "pylibs/pugwash/scenes/options.py:54" + ], + "Port Metadata": [ + "pylibs/pugwash/scenes/options.py:57" + ], + "Metadata Update: ": [ + "pylibs/pugwash/scenes/options.py:61", + "pylibs/pugwash/scenes/options.py:351" + ], + "Auto": [ + "pylibs/pugwash/scenes/options.py:61", + "pylibs/pugwash/scenes/options.py:351" + ], + "Manual": [ + "pylibs/pugwash/scenes/options.py:61", + "pylibs/pugwash/scenes/options.py:351" + ], + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": [ + "pylibs/pugwash/scenes/options.py:62" + ], + "Metadata Refresh": [ + "pylibs/pugwash/scenes/options.py:66" + ], + "Manually update port metadata with missing/updated information and artwork.": [ + "pylibs/pugwash/scenes/options.py:67" + ], + "Ports Location: ": [ + "pylibs/pugwash/scenes/options.py:72", + "pylibs/pugwash/scenes/options.py:81", + "pylibs/pugwash/scenes/options.py:388" + ], + "Roms section": [ + "pylibs/pugwash/scenes/options.py:72", + "pylibs/pugwash/scenes/options.py:366" + ], + "Ports tab": [ + "pylibs/pugwash/scenes/options.py:72", + "pylibs/pugwash/scenes/options.py:367" + ], + "Location where ports should be installed to.": [ + "pylibs/pugwash/scenes/options.py:73", + "pylibs/pugwash/scenes/options.py:82" + ], + "SD 1": [ + "pylibs/pugwash/scenes/options.py:81" + ], + "SD 2": [ + "pylibs/pugwash/scenes/options.py:81" + ], + "System": [ + "pylibs/pugwash/scenes/options.py:84" + ], + "Runtime Manager": [ + "pylibs/pugwash/scenes/options.py:88", + "pylibs/pugwash/scenes/runtimes.py:13" + ], + "Manage port runtimes.": [ + "pylibs/pugwash/scenes/options.py:89" + ], + "Experimental Ports: ": [ + "pylibs/pugwash/scenes/options.py:93", + "pylibs/pugwash/scenes/options.py:212" + ], + "Show or hide experimental ports.": [ + "pylibs/pugwash/scenes/options.py:94" + ], + "Update Ports": [ + "pylibs/pugwash/scenes/options.py:98" + ], + "Fetch latest ports information.": [ + "pylibs/pugwash/scenes/options.py:99" + ], + "Update PortMaster": [ + "pylibs/pugwash/scenes/options.py:103" + ], + "Force check for a new PortMaster version.": [ + "pylibs/pugwash/scenes/options.py:104" + ], + "Restore PortMaster": [ + "pylibs/pugwash/scenes/options.py:109" + ], + "This will restore PortMaster to the latest stable version of PortMaster.": [ + "pylibs/pugwash/scenes/options.py:110" + ], + "Release Channel: {channel}": [ + "pylibs/pugwash/scenes/options.py:114" + ], + "Change release channel of PortMaster, either beta or stable.": [ + "pylibs/pugwash/scenes/options.py:116" + ], + "Enable Alpha Releases": [ + "pylibs/pugwash/scenes/options.py:122" + ], + "Change release channel of PortMaster to alpha.": [ + "pylibs/pugwash/scenes/options.py:123" + ], + "Controller Mode: {controller_mode}": [ + "pylibs/pugwash/scenes/options.py:129", + "pylibs/pugwash/scenes/options.py:341" + ], + "Toggle between various controller layouts.": [ + "pylibs/pugwash/scenes/options.py:130" + ], + "Secret Options": [ + "pylibs/pugwash/scenes/options.py:133" + ], + "All Ports: ": [ + "pylibs/pugwash/scenes/options.py:136", + "pylibs/pugwash/scenes/options.py:200" + ], + "Show all ports, ignoring requirements.": [ + "pylibs/pugwash/scenes/options.py:137" + ], + "CWTBE Mode: ": [ + "pylibs/pugwash/scenes/options.py:140", + "pylibs/pugwash/scenes/options.py:226" + ], + "Enable gptokeyb2 by default.": [ + "pylibs/pugwash/scenes/options.py:141" + ], + "Debug Mode: ": [ + "pylibs/pugwash/scenes/options.py:144", + "pylibs/pugwash/scenes/options.py:238" + ], + "Enable debug logging all the time.": [ + "pylibs/pugwash/scenes/options.py:145" + ], + "Delete PortMaster Config": [ + "pylibs/pugwash/scenes/options.py:148" + ], + "This can break stuff, don't touch unless you know what you are doing.": [ + "pylibs/pugwash/scenes/options.py:149", + "pylibs/pugwash/scenes/options.py:153" + ], + "Delete PortMaster Runtimes": [ + "pylibs/pugwash/scenes/options.py:152" + ], + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": [ + "pylibs/pugwash/scenes/options.py:242" + ], + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": [ + "pylibs/pugwash/scenes/options.py:252" + ], + "PortMaster will now quit so you can run the restoration port.": [ + "pylibs/pugwash/scenes/options.py:255" + ], + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": [ + "pylibs/pugwash/scenes/options.py:262" + ], + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": [ + "pylibs/pugwash/scenes/options.py:289" + ], + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": [ + "pylibs/pugwash/scenes/options.py:357" + ], + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": [ + "pylibs/pugwash/scenes/options.py:378" + ], + "SDCARD 1": [ + "pylibs/pugwash/scenes/options.py:397" + ], + "SDCARD 2": [ + "pylibs/pugwash/scenes/options.py:398" + ], + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": [ + "pylibs/pugwash/scenes/options.py:402" + ], + "Deleting Runtimes:": [ + "pylibs/pugwash/scenes/options.py:466" + ], + "Source Manager": [ + "pylibs/pugwash/scenes/options.py:491" + ], + "Port Info Popup": [ + "pylibs/pugwash/scenes/port_info.py:13" + ], + "Hide Info": [ + "pylibs/pugwash/scenes/port_info.py:24", + "pylibs/pugwash/scenes/port_info.py:27" + ], + "Reinstall": [ + "pylibs/pugwash/scenes/port_info.py:36", + "pylibs/pugwash/scenes/port_info.py:129" + ], + "Uninstall": [ + "pylibs/pugwash/scenes/port_info.py:36", + "pylibs/pugwash/scenes/port_info.py:129", + "pylibs/pugwash/scenes/runtimes.py:107" + ], + "Install": [ + "pylibs/pugwash/scenes/port_info.py:39", + "pylibs/pugwash/scenes/port_info.py:131", + "pylibs/pugwash/scenes/runtimes.py:110" + ], + "Port Info": [ + "pylibs/pugwash/scenes/port_info.py:63" + ], + "Show Info": [ + "pylibs/pugwash/scenes/port_info.py:96", + "pylibs/pugwash/scenes/port_info.py:101", + "pylibs/pugwash/scenes/ports.py:241", + "pylibs/pugwash/scenes/ports.py:267", + "pylibs/pugwash/scenes/ports.py:269" + ], + "Are you sure you want to uninstall {port_name}?": [ + "pylibs/pugwash/scenes/port_info.py:151" + ], + "NO PORTS": [ + "pylibs/pugwash/scenes/ports.py:34", + "pylibs/pugwash/scenes/ports.py:65" + ], + "** NO PORTS FOUND **": [ + "pylibs/pugwash/scenes/ports.py:38", + "pylibs/pugwash/scenes/ports.py:72" + ], + "Download ports first.": [ + "pylibs/pugwash/scenes/ports.py:40", + "pylibs/pugwash/scenes/ports.py:75" + ], + "Try removing some filters.": [ + "pylibs/pugwash/scenes/ports.py:77" + ], + "Ports List": [ + "pylibs/pugwash/scenes/ports.py:246" + ], + "Filters": [ + "pylibs/pugwash/scenes/ports.py:267" + ], + "Download All": [ + "pylibs/pugwash/scenes/runtimes.py:47" + ], + "Used": [ + "pylibs/pugwash/scenes/runtimes.py:92" + ], + "Not Used": [ + "pylibs/pugwash/scenes/runtimes.py:92" + ], + "Check": [ + "pylibs/pugwash/scenes/runtimes.py:107" + ], + "Are you sure you want to download and verify all runtimes?": [ + "pylibs/pugwash/scenes/runtimes.py:125" + ], + "Deleted runtime {runtime}": [ + "pylibs/pugwash/scenes/runtimes.py:167" + ], + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": [ + "pylibs/pugwash/scenes/startup.py:20" + ], + "SpruceOS is not supported by PortMaster.": [ + "pylibs/pugwash/scenes/startup.py:24" + ], + "CrossMix-OS is not supported by PortMaster.": [ + "pylibs/pugwash/scenes/startup.py:28" + ], + "NextUI is not supported by PortMaster.": [ + "pylibs/pugwash/scenes/startup.py:34" + ], + "Your OS is not supported by PortMaster.": [ + "pylibs/pugwash/scenes/startup.py:42" + ], + "Your device has less than 1GB of RAM.": [ + "pylibs/pugwash/scenes/startup.py:46" + ], + "Disclaimer": [ + "pylibs/pugwash/scenes/startup.py:103" + ], + "Accept": [ + "pylibs/pugwash/scenes/startup.py:129" + ], + "Warning": [ + "pylibs/pugwash/scenes/startup.py:177" + ], + "Many ports will not work.": [ + "pylibs/pugwash/scenes/startup.py:192" + ], + "I Understand": [ + "pylibs/pugwash/scenes/startup.py:212" + ], + "{theme_name} (Selected)": [ + "pylibs/pugwash/scenes/themes.py:31" + ], + "Download": [ + "pylibs/pugwash/scenes/themes.py:50" + ], + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": [ + "pylibs/pugwash/scenes/themes.py:69" + ], + "Select Colour Scheme": [ + "pylibs/pugwash/scenes/themes.py:110" + ], + "{item_name} (Selected)": [ + "pylibs/pugwash/scenes/themes.py:124" + ], + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": [ + "pylibs/pugwash/scenes/themes.py:147" + ], + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": [ + "pylibs/pugwash/update.py:79" + ], + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": [ + "pylibs/pugwash/update.py:85" + ], + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": [ + "pylibs/pugwash/update.py:90" + ] +} diff --git a/PortMaster/pylibs/locales/strings.themes.json b/PortMaster/pylibs/locales/strings.themes.json new file mode 100644 index 00000000..851afbdb --- /dev/null +++ b/PortMaster/pylibs/locales/strings.themes.json @@ -0,0 +1,127 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": [ + "Doom/theme.json:586" + ], + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": [ + "Fallout/theme.json:550", + "Doom/theme.json:337", + "ff_VII/theme.json:257", + "Stardew/theme.json:374" + ], + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": [ + "Fallout/theme.json:611", + "Doom/theme.json:444", + "ff_VII/theme.json:363", + "Stardew/theme.json:475" + ], + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": [ + "Fallout/theme.json:684", + "Doom/theme.json:522", + "ff_VII/theme.json:435", + "Zelda/theme.json:485", + "Stardew/theme.json:541", + "default_theme/theme.json:418" + ], + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": [ + "Fallout/theme.json:726" + ], + "IP: {system.ip_address}": [ + "Fallout/theme.json:762", + "Doom/theme.json:644", + "ff_VII/theme.json:523", + "Zelda/theme.json:551", + "Stardew/theme.json:651", + "default_theme/theme.json:504", + "default_theme/theme.json:504" + ], + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": [ + "Fallout/theme.json:831", + "Doom/theme.json:741", + "ff_VII/theme.json:612", + "Zelda/theme.json:633", + "Stardew/theme.json:739", + "default_theme/theme.json:555" + ], + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": [ + "Stardew/theme.json:613" + ], + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": [ + "Zelda/theme.json:326" + ], + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": [ + "Zelda/theme.json:376" + ], + "Time: {system.time_24hr} Battery: {system.battery_level}": [ + "Zelda/theme.json:522" + ], + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": [ + "basic_theme/theme.json:77" + ], + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": [ + "default_theme/theme.json:319" + ], + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": [ + "default_theme/theme.json:347", + "default_theme/theme.json:347" + ], + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": [ + "ff_VII/theme.json:480", + "Stardew/theme.json:605", + "default_theme/theme.json:445" + ], + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": [ + "ff_VII/theme.json:488", + "default_theme/theme.json:453" + ], + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": [ + "hiviz/theme.json:267" + ], + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": [ + "hiviz/theme.json:327" + ], + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": [ + "hiviz/theme.json:375" + ], + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": [ + "hiviz/theme.json:463" + ], + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": [ + "hiviz/theme.json:526", + "default_theme/theme.json:621" + ], + "{ports_list.filter_ports} games": [ + "jimmyhat999_grid_dark/theme.json:239", + "jimmyhat999_grid_wii/theme.json:239" + ], + "{if:ports_list.position}{ports_list.position} / {endif}{ports_list.filter_ports}": [ + "jimmyhat999_grid_dark/theme.json:248", + "jimmyhat999_grid_wii/theme.json:248" + ], + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": [ + "jimmyhat999_grid_dark/theme.json:291", + "jimmyhat999_grid_wii/theme.json:291" + ], + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": [ + "jimmyhat999_grid_dark/theme.json:341", + "jimmyhat999_grid_wii/theme.json:341" + ], + "{system.free_space} free": [ + "jimmyhat999_grid_dark/theme.json:396", + "jimmyhat999_grid_wii/theme.json:396" + ], + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": [ + "jimmyhat999_grid_dark/theme.json:453", + "jimmyhat999_grid_wii/theme.json:453" + ], + "{if:runtime_info.name}{runtime_info.name}{endif}": [ + "jimmyhat999_grid_dark/theme.json:487", + "jimmyhat999_grid_wii/theme.json:487" + ], + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": [ + "jimmyhat999_grid_dark/theme.json:492", + "jimmyhat999_grid_wii/theme.json:492" + ], + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: \nPlace file x in folder y then dance until tomorrow": [ + "unknown" + ] +} diff --git a/PortMaster/pylibs/locales/sv_SE.json b/PortMaster/pylibs/locales/sv_SE.json new file mode 100644 index 00000000..363d7e33 --- /dev/null +++ b/PortMaster/pylibs/locales/sv_SE.json @@ -0,0 +1,232 @@ +{ + "Loading...": "Laddar...", + "Loading Sources.": "Laddar Källor.", + "Download validation failed.": "Validering av nedladdning misslyckades.", + "Loading Info.": "Laddar Info.", + "Fetching latest featured ports.": "Hämtar senaste utvalda portar.", + "Installing Theme {download_name}.": "Installerar Tema {download_name}.", + "Installing": "Installerar", + "Theme {download_name!r} installed successfully.": "Tema {download_name!r} har installerats.", + "Port {download_name} installed failed.": "Installation av {download_name} misslyckades.", + "Installing {download_name}.": "Installerar {download_name}.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} installerades.", + "Installation failed, removing files...": "Installationen misslyckades, raderar filer...", + "Okay": "Okej", + "Cancel": "Avbryt", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} och {runtime_name!r} har installerats.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} installerades, men {runtime_name!r} kunde inte installeras!!\n\nAntingen installera om för att försöka igen, eller kolla wikin för hjälp.", + "Unable to find a source for {port_name}": "Kunde inte hitta en nedladdning för {port_name}", + "Unknown port {port_name}": "Okänd port {port_name}", + "Removing {port_name}": "Raderar {port_name}", + "Successfully uninstalled {port_name}": "Avinstallerat {port_name}", + "Loading Ports.": "Laddar Portar.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} innehåller en dålig körtid, spelet kanske inte körs korrekt.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Port {runtime} innehåller en okänd körtid, spelet kanske inte körs korrekt.", + "Not Installed": "Ej installerad", + "Update Available": "Uppdatering tillgänglig", + "Verified": "Verifierad", + "Unverified": "Overifierad", + "Broken": "Trasig", + "Unable do download a runtime when in offline mode.": "Kan inte ladda ner en körtid i offlineläge.", + "Verifying runtime {runtime}": "Verifierar körtid {runtime}", + "Verifying": "Verifierar", + "Unable to find a download for {runtime}.": "Kunde inte hitta en nedladdning för {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Kunde inte ladda ner {runtime} för {device_arch}.", + "Verified {runtime} successfully.": "Verifiering av {runtime} lyckades.", + "Verified {runtime}.": "Verifierat {runtime}.", + "Updating {runtime}.": "Uppdaterar {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Körtid {runtime} är trasig, ominstalleras.", + "Downloading runtime {runtime}.": "Hämtar körtid {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Kunde inte ladda ner {runtime}, spelet kanske inte körs korrekt.", + "Successfully downloaded {runtime}.": "Hämtat {runtime}.", + "Unable to download verification file.": "Kunde inte hämta verifieringsfilen.", + "Unable to download non zip files.": "Kan inte hämta icke-zip-filer.", + "Cache out of date.": "Cache är föråldrad.", + "First check.": "Första kontroll.", + "Auto Update.": "Automatisk uppdatering.", + "Updating": "Uppdaterar", + "Up to date already": "Redan uppdaterad", + "Fetching latest info": "Hämtar senaste information", + "Done.": "Färdig.", + "Fetching info": "Hämtar information", + "Unable to find {port_name}.": "Kunde inte hitta {port_name}.", + "Unable to download file. [{status_code}]": "Kunde inte hämta filen. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Hämtar {file_url} - ({total_length_mb})", + "Downloading file.": "Hämtar fil.", + "Download failed: {err}": "Nedladdning misslyckades: {err}", + "Passed file validation.": "Godkänd filvalidering.", + "Unable to validate download.": "Kunde inte validera nedladdning.", + "Unknown IP": "Okänt IP", + "N/A": "N/A", + "Fetching latest gameinfo.xml files and cover images.": "Hämtar senaste gameinfo.xml-filer och omslagsbilder.", + "Downloading gameinfo.xml for {source_name}": "Hämtar gameinfo.xml för {source_name}", + "Installing {port_name}": "Installerar {port_name}", + "Uninstalling {port_name}": "Avinstallerar {port_name}", + "Updating all port sources:": "Uppdaterar alla portkällor:", + "Checking {runtime_name}": "Kontrollerar {runtime_name}", + "** NO PORT **": "** INGEN PORT **", + "Ready to Run": "Redo att köras", + "Requires Files": "Kräver filer", + "Installed": "Installerad", + "Missing": "Saknas", + "Danish": "Danska", + "German": "Tyska", + "English": "Engelska", + "Spanish": "Spanska", + "Finnish": "Finska", + "French": "Franska", + "Italian": "Italienska", + "Japanese": "Japanska", + "Korean": "Koreanska", + "Dutch": "Holländska", + "Polish": "Polska", + "Portuguese (Portugal)": "Portugisiska (Portugal)", + "Portuguese (Brazil)": "Portugisiska (Brasilien)", + "Russian": "Ryska", + "Ukrainian": "Ukrainska", + "Chinese Simplified": "Kinesiska, förenklad", + "Messages": "Meddelanden", + "Are you sure you want to cancel?": "Vill du verkligen avbryta?", + "Featured Ports": "Utvalda portar", + "Select": "Välj", + "Back": "Tillbaka", + "No featured ports found, internet connection is required.": "Inga utvalda portar hittades, internetanslutning krävs.", + "Alphabetical": "Alfabetisk", + "Recently Added": "Nyligen tillagda", + "Recently Updated": "Senast uppdaterade", + "Action": "Action", + "Adventure": "Äventyr", + "Arcade": "Arkad", + "Casino/Card": "Kasino/Kortspel", + "Platformer": "Platformer", + "Puzzle": "Pussel", + "Racing": "Racing", + "Rhythm": "Rytm", + "Role Playing Game": "Rollspel", + "Simulation": "Simulering", + "Sports": "Sport", + "Strategy": "Strategi", + "Visual Novel": "Visuell roman", + "Other": "Övrigt", + "Broken Ports": "Trasiga Portar", + "Free game, all files included.": "Gratis spel, alla filer ingår.", + "Demo files included.": "Demofiler ingår.", + "Free external assets needed.": "Gratis externa tillgångar krävs.", + "Paid external assets needed.": "Betalda externa tillgångar krävs.", + "{runtime_name} Runtime": "{runtime_name}", + "Sort:": "Sortera:", + "Clear Filters": "Rensa Filter", + "Genres:": "Genrer:", + "Attributes:": "Attribut:", + "Availability:": "Tillgänglighet:", + "Porters:": "Portutvecklare:", + "Delete": "Radera", + "Upper Case": "Versaler", + "Space": "Mellanslag", + "Enter": "Öppna", + "Lower Case": "Gemener", + "Language Select": "Välj Språk", + "{lang_name} (Selected)": "{lang_name} (Vald)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Vill du ändra språk?\n\nOmstart krävs för att visa ändringar.", + "Main Menu": "Huvudmeny", + "Hand curated lists of ports": "Listor av handplockade portar", + "All Ports": "Alla portar", + "List all ports available on PortMaster.": "Visa alla portar tillgängliga på PortMaster.", + "Ready to Run Ports": "Redo att köras", + "List all ports that are ready to play!": "Visa alla portar som är redo att köras!", + "Manage Ports": "Hantera Portar", + "Update / Uninstall Ports": "Uppdatera / Avinstallera portar", + "Options": "Inställningar", + "PortMaster Options": "PortMaster-inställningar", + "Exit": "Stäng", + "Quit PortMaster": "Avsluta PortMaster", + "Quit": "Avsluta", + "Secret Mode {secret_mode}": "Hemligt Läge {secret_mode}", + "Enabled": "På", + "Disabled": "Av", + "Are you sure you want to exit PortMaster?": "Är du säker på att du vill lämna PortMaster?", + "Options Menu": "Alternativ", + "Interface": "Gränssnitt", + "Choose Language": "Välj språk", + "Select the language PortMaster uses.": "Välj det språk PortMaster använder.", + "Select Theme": "Välj tema", + "Select a theme for PortMaster.": "Välj ett tema för PortMaster.", + "Select Color Scheme": "Välj färgschema", + "Select a colour scheme for PortMaster": "Välj färgschema för PortMaster", + "Audio": "Ljud", + "Music: ": "Musik: ", + "Enable or Disable background music in PortMaster.": "Aktivera eller inaktivera bakgrundsmusik i PortMaster.", + "Sound FX: ": "Ljudeffekter: ", + "Enable or Disable soundfx in PortMaster.": "Aktivera eller inaktivera ljudeffekter i PortMaster.", + "Port Metadata": "Port-metadata", + "Metadata Update: ": "Uppdatera metadata: ", + "Auto": "Automatisk", + "Manual": "Manuell", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster uppdaterar metadata när portar är installeras så att portbilder och beskrivningar visas när du surfar på din enhet.", + "Metadata Refresh": "Uppdatera metadata", + "Manually update port metadata with missing/updated information and artwork.": "Uppdatera portmetadata manuellt med saknad/uppdaterad information och illustrationer.", + "Ports Location: ": "Lagringsplats för Portar: ", + "Ports tab": "Portfliken", + "Location where ports should be installed to.": "Plats där portar ska installeras till.", + "System": "System", + "Runtime Manager": "Körtidshanterare", + "Manage port runtimes.": "Hantera portkörtider.", + "Experimental Ports: ": "Experimentella portar: ", + "Show or hide experimental ports.": "Visa eller dölj experimentella portar.", + "Update Ports": "Uppdatera Portar", + "Update PortMaster": "Uppdatera PortMaster", + "Force check for a new PortMaster version.": "Tvinga sökning av en ny PortMaster-version.", + "Restore PortMaster": "Återställ PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Detta kommer återställa PortMaster till den senaste stabila versionen.", + "Release Channel: {channel}": "Släppkanal: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Ändra släppkanal för PortMaster, antingen beta eller stabil.", + "Enable Alpha Releases": "Aktivera alfaversioner", + "Change release channel of PortMaster to alpha.": "Ändra släppkanalen för PortMaster till alpha.", + "Controller Mode: {controller_mode}": "Styrläge: {controller_mode}", + "Toggle between various controller layouts.": "Växla mellan olika kontrollayouter.", + "Secret Options": "Hemliga alternativ", + "All Ports: ": "Alla portar: ", + "Show all ports, ignoring requirements.": "Visa alla portar, ignorera krav.", + "CWTBE Mode: ": "CWTBE Läge: ", + "Enable gptokeyb2 by default.": "Aktivera gptokeyb2 som standard.", + "Delete PortMaster Config": "Radera PortMaster-konfiguration", + "This can break stuff, don't touch unless you know what you are doing.": "Detta kan sabba saker, rör ej om inte du vet vad du gör.", + "Delete PortMaster Runtimes": "Radera PortMaster-körtider", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Är du säker på att du vill återställa PortMaster?\n\nDetta kräver att du kör en speciellt skapad port som kommer att installeras.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "En speciell port vid namn \"Restore PortMaster.sh\" har installerats.\n\nKör denna port för att återställa din PortMaster-installation.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster kommer nu avslutas så att du kan köra återställningsporten.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Är du säker på att du vill ändra till alfa-kanalen?\n\nPortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta.\n\nDETTA KAN OCH KOMMER ATT SABBA SAKER, DU HAR BLIVIT VARNAD", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Är du säker på att du vill ändra releasekanalen från {current_channel} till {new_channel}?\n\nPortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Är du säker på att du vill uppdatera din spellista.xml?\n\nDetta kommer att åsidosätta alla skrapade konstverk eller portinformation och kräver en nedladdning på 50+ MB.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Är du säker på att du vill flytta portar från {from_mode} till {to_mode}", + "Deleting Runtimes:": "Raderar körtider:", + "Source Manager": "Källhanterare", + "Hide Info": "Dölj info", + "Reinstall": "Ominstallera", + "Uninstall": "Avinstallera", + "Install": "Installera", + "Port Info": "Portinformation", + "Show Info": "Visa info", + "Are you sure you want to uninstall {port_name}?": "Är du säker på att du vill avinstallera {port_name}?", + "NO PORTS": "INGA PORTAR", + "** NO PORTS FOUND **": "** INGA PORTER HITTADES **", + "Download ports first.": "Ladda ner portar först.", + "Try removing some filters.": "Prova att ta bort några filter.", + "Ports List": "Portlista", + "Filters": "Filter", + "Download All": "Hämta Alla", + "Used": "Används", + "Not Used": "Används ej", + "Check": "Kontrollera", + "Are you sure you want to download and verify all runtimes?": "Är du säker på att du vill ladda ner och verifiera alla körtider?", + "Deleted runtime {runtime}": "Raderat körtid {runtime}", + "{theme_name} (Selected)": "{theme_name} (Vald)", + "Download": "Hämta", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Vill du ändra tema?\n\nOmstart krävs för att visa ändringar.", + "Select Colour Scheme": "Välj färgschema", + "{item_name} (Selected)": "{item_name} (Vald)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Vill du ändra temats färgschema?\n\nOmstart krävs för att visa ändringar.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Du kommer byta från {old_release_channel} till {new_release_channel}.\n\nVill du nedgradera?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Det finns en ny version av PortMaster ({portmaster_version})\n\nVill du uppgradera?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Vill du ominstallera PortMaster?\n\nDetta kommer ominstallera {portmaster_version} från {release_channel}-kanalen." +} diff --git a/PortMaster/pylibs/locales/sv_SE.themes.json b/PortMaster/pylibs/locales/sv_SE.themes.json new file mode 100644 index 00000000..96af26a6 --- /dev/null +++ b/PortMaster/pylibs/locales/sv_SE.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Tid: {system.time_24hr}\nBatteri: {system.battery_level}\nUtrymme: {system.free_space} ledigt", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genrer: {port_info.genres} Storlek: {port_info.download_size}{if:port_info.runtime} Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Tillgängliga portar: {ports_list.filter_ports} / {ports_list.total_ports} \nFilter: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Ledigt utrymme: {system.free_space}\n\nPortutvecklare: {port_info.porter}\nGenrer: {port_info.genres}\nStorlek: {port_info.download_size}{if:port_info.runtime}\nKörtid: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruktion: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nUtrymme: {system.free_space} ledigt / {system.total_space} totalt\nTid: {system.time_24hr}\nBatteri: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Skapare: {theme_info.creator}\nStatus: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nUtrymme: {system.free_space} ledigt", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filter: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genrer: {port_info.genres}\nHämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\nKörtid: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Tid: {system.time_24hr} Batteri: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Titel: {port_info.title}\nPortutvecklare: {port_info.porter}\nGenrer: {port_info.genres}\nHämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\nKörtid: {port_info.runtime} ({port_info.runtime_status}){endif}\nBeskrivning: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Tillgängliga portar: {ports_list.filter_ports} / {ports_list.total_ports} \nFilter: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genrer: {port_info.genres}\n{if:port_info.install_size}Storlek (installerad): {port_info.install_size}{else}Hämtningsstorlek: {port_info.download_size}{endif}{if:port_info.runtime}\nKörtid: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Tid: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nEnhet: {system.device_name}\nBatteri: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nUtrymme: {system.free_space} ledigt / {system.total_space} totalt", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Portutvecklare: {port_info.porter}\n• Genrer: {port_info.genres}\n• Beskrivning: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genrer: {port_info.genres}\n• Hämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\n• Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Beskrivning: {port_info.description}\n• Instruktioner:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Skapare: {theme_info.creator}\n• Status: {theme_info.status}\n• Beskrivning:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Namn: {runtime_info.name}\nStatus: {runtime_info.status}\nPortar: {runtime_info.ports}\nHämtningsstorlek: {runtime_info.download_size}\nInstalleringsstorlek: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/messages.po deleted file mode 100644 index 1aa425db..00000000 --- a/PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1244 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Swedish\n" -"Language: sv_SE\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: sv-SE\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Danska" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Tyska" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Engelska" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Spanska" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Finska" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Franska" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Italienska" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Japanska" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Koreanska" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Holländska" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Polska" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Portugisiska (Portugal)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Portugisiska (Brasilien)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "Ryska" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Ukrainska" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Kinesiska, förenklad" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "Okänt IP" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "N/A" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** INGEN PORT **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Redo att köras" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Kräver filer" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Installerad" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Saknas" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Uppdatering tillgänglig" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Ej installerad" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Okej" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Avbryt" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Hämtar senaste gameinfo.xml-filer och omslagsbilder." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Hämtar gameinfo.xml för {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Installerar {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Avinstallerar {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Uppdaterar alla portkällor:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Kontrollerar {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Du kommer byta från {old_release_channel} till {new_release_channel}.\n\n" -"Vill du nedgradera?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Det finns en ny version av PortMaster ({portmaster_version})\n\n" -"Vill du uppgradera?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Vill du ominstallera PortMaster?\n\n" -"Detta kommer ominstallera {portmaster_version} från {release_channel}-kanalen." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Laddar..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Validering av nedladdning misslyckades." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Laddar Info." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Hämtar senaste utvalda portar." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Laddar Källor." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Laddar Portar." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Installerar Tema {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Installerar" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Tema {download_name!r} har installerats." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Installerar {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Port {download_name!r} installerades." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Installationen misslyckades, raderar filer..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Installation av {download_name} misslyckades." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Port {download_name!r} och {runtime_name!r} har installerats." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Port {download_name!r} installerades, men {runtime_name!r} kunde inte installeras!!\n\n" -"Antingen installera om för att försöka igen, eller kolla wikin för hjälp." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Port {runtime} innehåller en dålig körtid, spelet kanske inte körs korrekt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Port {runtime} innehåller en okänd körtid, spelet kanske inte körs korrekt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Verifierad" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Overifierad" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Trasig" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Kan inte ladda ner en körtid i offlineläge." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Verifierar körtid {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Verifierar" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Kunde inte hitta en nedladdning för {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Kunde inte ladda ner {runtime} för {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "Verifiering av {runtime} lyckades." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "Verifierat {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Uppdaterar {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Körtid {runtime} är trasig, ominstalleras." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Hämtar körtid {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Kunde inte ladda ner {runtime}, spelet kanske inte körs korrekt." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "Hämtat {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Kunde inte hitta en nedladdning för {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Okänd port {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Raderar {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "Avinstallerat {port_name}" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Cache är föråldrad." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Första kontroll." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Automatisk uppdatering." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Uppdaterar" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Redan uppdaterad" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Hämtar senaste information" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Färdig." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Kunde inte hitta {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Kunde inte hitta verifieringsinfo för {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Kunde inte hämta verifieringsinfo för {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Klart!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Hämtar information" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Hämtar senaste portar" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Hämtar information." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Kunde inte hämta verifieringsfilen." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Kan inte hämta icke-zip-filer." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Kunde inte hämta filen. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Hämtar {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Hämtar fil." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Nedladdning misslyckades: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Godkänd filvalidering." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Kunde inte validera nedladdning." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Huvudmeny" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Avsluta" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Är du säker på att du vill lämna PortMaster?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Utvalda portar" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Listor av handplockade portar" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Alla portar" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Visa alla portar tillgängliga på PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Redo att köras" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Visa alla portar som är redo att köras!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Hantera Portar" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Uppdatera / Avinstallera portar" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Inställningar" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "PortMaster-inställningar" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Stäng" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Avsluta PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Öppna" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Hemligt Läge {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "På" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Av" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Alternativ" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Gränssnitt" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Välj språk" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Välj det språk PortMaster använder." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Välj tema" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Välj ett tema för PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Välj färgschema" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Välj färgschema för PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Ljud" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Musik: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Aktivera eller inaktivera bakgrundsmusik i PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Ljudeffekter: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Aktivera eller inaktivera ljudeffekter i PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Port-metadata" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Uppdatera metadata: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Automatisk" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Manuell" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster uppdaterar metadata när portar är installeras så att portbilder och beskrivningar visas när du surfar på din enhet." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Uppdatera metadata" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Uppdatera portmetadata manuellt med saknad/uppdaterad information och illustrationer." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Lagringsplats för Portar: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Portfliken" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Plats där portar ska installeras till." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "System" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Körtidshanterare" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Hantera portkörtider." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Experimentella portar: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Visa eller dölj experimentella portar." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Uppdatera Portar" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Uppdatera PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Tvinga sökning av en ny PortMaster-version." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Återställ PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Detta kommer återställa PortMaster till den senaste stabila versionen." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Släppkanal: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Ändra släppkanal för PortMaster, antingen beta eller stabil." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Aktivera alfaversioner" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Ändra släppkanalen för PortMaster till alpha." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Styrläge: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Växla mellan olika kontrollayouter." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Hemliga alternativ" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Alla portar: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Visa alla portar, ignorera krav." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "CWTBE Läge: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Aktivera gptokeyb2 som standard." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Radera PortMaster-konfiguration" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Detta kan sabba saker, rör ej om inte du vet vad du gör." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Radera PortMaster-körtider" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Tillbaka" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Är du säker på att du vill återställa PortMaster?\n\n" -"Detta kräver att du kör en speciellt skapad port som kommer att installeras." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "En speciell port vid namn \"Restore PortMaster.sh\" har installerats.\n\n" -"Kör denna port för att återställa din PortMaster-installation." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster kommer nu avslutas så att du kan köra återställningsporten." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Är du säker på att du vill ändra till alfa-kanalen?\n\n" -"PortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta.\n\n" -"DETTA KAN OCH KOMMER ATT SABBA SAKER, DU HAR BLIVIT VARNAD" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Är du säker på att du vill ändra releasekanalen från {current_channel} till {new_channel}?\n\n" -"PortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Är du säker på att du vill uppdatera din spellista.xml?\n\n" -"Detta kommer att åsidosätta alla skrapade konstverk eller portinformation och kräver en nedladdning på 50+ MB." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Är du säker på att du vill flytta portar från {from_mode} till {to_mode}" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Raderar körtider:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Källhanterare" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Hämta Alla" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Används" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Används ej" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Kontrollera" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Avinstallera" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Installera" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Är du säker på att du vill ladda ner och verifiera alla körtider?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Raderat körtid {runtime}" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Vald)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Välj" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Hämta" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Vill du ändra tema?\n\n" -"Omstart krävs för att visa ändringar." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Välj färgschema" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Vald)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Vill du ändra temats färgschema?\n\n" -"Omstart krävs för att visa ändringar." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Välj Språk" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Vald)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Vill du ändra språk?\n\n" -"Omstart krävs för att visa ändringar." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Radera" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Versaler" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Mellanslag" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Gemener" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Inga utvalda portar hittades, internetanslutning krävs." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "INGA PORTAR" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** INGA PORTER HITTADES **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Ladda ner portar först." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Prova att ta bort några filter." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Visa info" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Portlista" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Filter" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Dölj info" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Ominstallera" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Portinformation" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Är du säker på att du vill avinstallera {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "Alfabetisk" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Nyligen tillagda" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Senast uppdaterade" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Action" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Äventyr" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Arkad" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Kasino/Kortspel" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Platformer" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Pussel" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Racing" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Rytm" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Rollspel" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Simulering" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Sport" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Strategi" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Visuell roman" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Övrigt" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Trasiga Portar" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Gratis spel, alla filer ingår." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Demofiler ingår." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Gratis externa tillgångar krävs." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Betalda externa tillgångar krävs." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name}" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Sortera:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Rensa Filter" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Genrer:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Attribut:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Tillgänglighet:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Portutvecklare:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Meddelanden" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Vill du verkligen avbryta?" - diff --git a/PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/themes.po deleted file mode 100644 index 733ec141..00000000 --- a/PortMaster/pylibs/locales/sv_SE/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Swedish\n" -"Language: sv_SE\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: sv-SE\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genrer: {port_info.genres} Storlek: {port_info.download_size}{if:port_info.runtime} Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Tillgängliga portar: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filter: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Ledigt utrymme: {system.free_space}\n\n" -"Portutvecklare: {port_info.porter}\n" -"Genrer: {port_info.genres}\n" -"Storlek: {port_info.download_size}{if:port_info.runtime}\n" -"Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruktion: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Utrymme: {system.free_space} ledigt / {system.total_space} totalt\n" -"Tid: {system.time_24hr}\n" -"Batteri: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Skapare: {theme_info.creator}\n" -"Status: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Tid: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Enhet: {system.device_name}\n" -"Batteri: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Utrymme: {system.free_space} ledigt" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Filter: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genrer: {port_info.genres}\n" -"Hämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\n" -"Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Tid: {system.time_24hr} Batteri: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Utrymme: {system.free_space} ledigt / {system.total_space} totalt" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Portutvecklare: {port_info.porter}\n" -"• Genrer: {port_info.genres}\n" -"• Beskrivning: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Genrer: {port_info.genres}\n" -"• Hämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\n" -"• Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Beskrivning: {port_info.description}\n" -"• Instruktioner:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Skapare: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Beskrivning:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Namn: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Portar: {runtime_info.ports}\n" -"Hämtningsstorlek: {runtime_info.download_size}\n" -"Installeringsstorlek: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Tid: {system.time_24hr}\n" -"Batteri: {system.battery_level}\n" -"Utrymme: {system.free_space} ledigt" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Titel: {port_info.title}\n" -"Portutvecklare: {port_info.porter}\n" -"Genrer: {port_info.genres}\n" -"Hämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\n" -"Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Beskrivning: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Tillgängliga portar: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filter: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Genrer: {port_info.genres}\n" -"{if:port_info.install_size}Storlek (installerad): {port_info.install_size}{else}Hämtningsstorlek: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/uk_UA.json b/PortMaster/pylibs/locales/uk_UA.json new file mode 100644 index 00000000..0747d498 --- /dev/null +++ b/PortMaster/pylibs/locales/uk_UA.json @@ -0,0 +1,254 @@ +{ + "Loading...": "Завантаження...", + "Loading Sources.": "Завантаження джерел.", + "Download validation failed.": "Помилка при перевірці завантаження.", + "Loading Info.": "Завантаження інформації.", + "Fetching latest featured ports.": "Отримання останніх рекомендованих портів.", + "Installing Theme {download_name}.": "Встановлення теми {download_name}.", + "Installing": "Встановлення", + "Theme {download_name!r} installed successfully.": "Тема {download_name!r} успішно встановлена.", + "Port {download_name} installed failed.": "Не вдалося встановити порт {download_name}.", + "Installing {download_name}.": "Встановлення {download_name}.", + "Port {download_name!r} installed successfully.": "Порт {download_name!r} успішно встановлено.", + "Installation failed, removing files...": "Помилка встановлення, видалення файлів...", + "Okay": "Гаразд", + "Cancel": "Скасувати", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "Порт {download_name!r} та {runtime_name!r} успішно встановлено.", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успішно встановлено, але встановити {runtime_name!r} не вдалося!\n\nСпробуйте перевстановити, або зверніться до вікі за допомогою.", + "Unable to download in offline mode.": "Не вдається завантажити в офлайн режимі.", + "Unable to find a source for {port_name}": "Не вдалося знайти джерело для {port_name}", + "Unknown port {port_name}": "Невідомий порт {port_name}", + "Removing {port_name}": "Видалення {port_name}", + "Successfully uninstalled {port_name}": "Успішно видалено {port_name}", + "Loading Ports.": "Завантаження портів.", + "Port {runtime} contains a bad runtime, game may not run correctly.": "Порт {runtime} містить неправильний Runtime, гра може працювати некоректно.", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Порт {runtime} містить невідомий Runtime, гра може працювати некоректно.", + "Not Installed": "Не встановлено", + "Update Available": "Доступне оновлення", + "Verified": "Підтверджено", + "Unverified": "Не підтверджено", + "Broken": "Не працює", + "Unable do download a runtime when in offline mode.": "Неможливо завантажити Runtime в офлайн режимі.", + "Verifying runtime {runtime}": "Verifying runtime {runtime}", + "Verifying": "Підтвердження", + "Unable to find a download for {runtime}.": "Не вдалося знайти завантаження для {runtime}.", + "Unable to download {runtime} in {device_arch}.": "Не вдалося завантажити {runtime} з {device_arch}.", + "Verified {runtime} successfully.": "{runtime} успішно підтверджено.", + "Verified {runtime}.": "Підтверджено {runtime}.", + "Updating {runtime}.": "Оновлення {runtime}.", + "Runtime {runtime} is broken, reinstalling.": "Runtime {runtime} пошкоджено, виконується перевстановлення.", + "Downloading runtime {runtime}.": "Завантаження Runtime {runtime}.", + "Unable to download {runtime}, game may not run correctly.": "Не вдалося завантажити {runtime}, гра може працювати некоректно.", + "Successfully downloaded {runtime}.": "Успішно завантажено {runtime}.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nЩоб це зробити: \nМЕНЮ -> УТИЛІТИ -> Повторно сканувати папку з ROM'ами", + "Unable to download verification file.": "Не вдалося завантажити інформацію для перевірки.", + "Unable to download non zip files.": "Не вдалося завантажити не zip-файли.", + "Cache out of date.": "Кеш застарілий.", + "First check.": "Перша перевірка.", + "Auto Update.": "Автоматичне оновлення.", + "Updating": "Оновлення", + "Up to date already": "Вже оновлено", + "Fetching latest info": "Отримання останньої інформації", + "Done.": "Готово.", + "Fetching info": "Отримання інформації", + "Unable to find {port_name}.": "Не вдалося знайти {port_name}.", + "Unable to download file. [{status_code}]": "Не вдалося завантажити файл. [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "Завантаження {file_url} - ({total_length_mb})", + "Downloading file.": "Завантаження файлу.", + "Download failed: {err}": "Не вдалося завантажити: {err}", + "Passed file validation.": "Файл перевірено.", + "Unable to validate download.": "Не вдалося перевірити завантаження.", + "Unknown IP": "Невідома IP-адреса", + "N/A": "Н/Д", + "Fetching latest gameinfo.xml files and cover images.": "Отримання останніх файлів gameinfo.xml і обкладинок.", + "Downloading gameinfo.xml for {source_name}": "Завантаження gameinfo.xml для {source_name}", + "Installing {port_name}": "Встановлення {port_name}", + "Uninstalling {port_name}": "Видалення {port_name}", + "Updating all port sources:": "Оновлення всіх джерел порту:", + "Checking {runtime_name}": "Перевірка {runtime_name}", + "** NO PORT **": "** НЕМАЄ ПОРТУ **", + "Ready to Run": "Готовий до запуску", + "Requires Files": "Необхідні файли", + "Installed": "Встановлено", + "Missing": "Відсутній", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Порти було додано або видалено, оновіть список ігор, щоб побачити нові ігри або видалити деінстальовані.", + "Danish": "Данська", + "German": "Німецька", + "English": "Англійська", + "Spanish": "Іспанська", + "Finnish": "Фінська", + "French": "Французька", + "Italian": "Італійська", + "Japanese": "Японська", + "Korean": "Корейська", + "Dutch": "Нідерландська", + "Polish": "Польська", + "Portuguese (Portugal)": "Португальська (Португалія)", + "Portuguese (Brazil)": "Португальська (Бразилія)", + "Russian": "російська", + "Swedish": "Шведська", + "Ukrainian": "Українська", + "Chinese Simplified": "Китайська спрощена", + "Messages": "Сповіщення", + "Are you sure you want to cancel?": "Ви справді бажаєте скасувати?", + "Featured Ports": "Рекомендовані порти", + "Select": "Обрати", + "Back": "Назад", + "No featured ports found, internet connection is required.": "Рекомендовані порти не знайдено, потрібно підключення до інтернету.", + "Filters Scene": "Вкладка фільтрів", + "Alphabetical": "За абеткою", + "Recently Added": "Нещодавно додані", + "Recently Updated": "Нещодавно оновлені", + "Action": "Бойовик", + "Adventure": "Пригоди", + "Arcade": "Аркада", + "Casino/Card": "Казино/карткова гра", + "First Person Shooter": "Стрілянка від першої особи", + "Platformer": "Платформер", + "Puzzle": "Головоломка", + "Racing": "Перегони", + "Rhythm": "Ритм-гра", + "Role Playing Game": "Рольова гра", + "Simulation": "Симулятор", + "Sports": "Спорт", + "Strategy": "Стратегія", + "Visual Novel": "Візуальна новела", + "Other": "Інше", + "Files Required": "Необхідні файли", + "Experimental": "Експериментальний", + "Broken Ports": "Пошкоджені порти", + "Free game, all files included.": "Безкоштовна гра, усі файли включені.", + "Demo files included.": "Демо-файли включені.", + "Free external assets needed.": "Потрібне завантаження безплатних зовнішніх ресурсів.", + "Paid external assets needed.": "Потрібне завантаження платних зовнішніх ресурсів.", + "{runtime_name} Runtime": "{runtime_name} Runtime", + "ARM 32bit": "ARM 32-біт", + "ARM 64bit": "ARM 64-біт", + "x86 64bit": "x86 64-біт", + "Sort:": "Сортувати:", + "Clear Filters": "Скинути фільтри", + "Genres:": "Жанри:", + "Attributes:": "Атрибути:", + "Architecture:": "Архітектура:", + "Availability:": "Наявність:", + "Porters:": "Портувальники:", + "Delete": "Видалити", + "Upper Case": "Верхній регістр", + "Space": "Пробіл", + "Enter": "Підтвердити", + "Lower Case": "Нижній регістр", + "Language Select": "Вибір мови", + "{lang_name} (Selected)": "{lang_name} (Обрано)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Ви хочете змінити мову?\n\nВам доведеться перезапустити застосунок, щоб вона набула чинності.", + "Main Menu": "Головне меню", + "Hand curated lists of ports": "Кураторський список портів", + "All Ports": "Всі порти", + "List all ports available on PortMaster.": "Список усіх портів доступних на PortMaster.", + "Ready to Run Ports": "Порти готові до запуску", + "List all ports that are ready to play!": "Показати всі порти, готові до гри!", + "Manage Ports": "Керування портами", + "Update / Uninstall Ports": "Оновити / видалити порти", + "Options": "Налаштування", + "PortMaster Options": "Налаштування PortMaster", + "Exit": "Вихід", + "Quit PortMaster": "Вийти з PortMaster", + "Quit": "Вийти", + "Secret Mode {secret_mode}": "Таємний режим {secret_mode}", + "Enabled": "Увімкнено", + "Disabled": "Вимкнено", + "Are you sure you want to exit PortMaster?": "Ви впевнені, що хочете вийти з PortMaster?", + "Options Menu": "Меню налаштувань", + "Interface": "Інтерфейс", + "Choose Language": "Обрати мову", + "Select the language PortMaster uses.": "Оберіть мову, яку використовує PortMaster.", + "Select Theme": "Оберіть тему", + "Select a theme for PortMaster.": "Оберіть тему для PortMaster.", + "Select Color Scheme": "Оберіть кольорову палітру", + "Select a colour scheme for PortMaster": "Оберіть кольорову палітру для PortMaster", + "Audio": "Аудіо", + "Music: ": "Музика: ", + "Enable or Disable background music in PortMaster.": "Увімкнення чи вимкнення фонової музики в PortMaster.", + "Sound FX: ": "Звукові ефекти: ", + "Enable or Disable soundfx in PortMaster.": "Увімкнення чи вимкнення звукових ефектів в PortMaster.", + "Port Metadata": "Метадані портів", + "Metadata Update: ": "Оновлення метаданих: ", + "Auto": "Автоматично", + "Manual": "Вручну", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster оновлює метадані під час встановлення портів, щоб показувати обкладинки та опис портів під час перегляду портів на Вашому пристрої.", + "Metadata Refresh": "Оновити метадані", + "Manually update port metadata with missing/updated information and artwork.": "Оновити метадані портів вручну, з відсутніми/оновленими інформацією та обкладинками.", + "Ports Location: ": "Розташування портів: ", + "Roms section": "Секція ROM-файлів", + "Ports tab": "Вкладка портів", + "Location where ports should be installed to.": "Місце встановлення портів.", + "SD 1": "SD-карта 1", + "SD 2": "SD-карта 2", + "System": "Система", + "Runtime Manager": "Керування Runtime", + "Manage port runtimes.": "Керувати Runtime портів.", + "Experimental Ports: ": "Експериментальні порти: ", + "Show or hide experimental ports.": "Показати або приховати експериментальні порти.", + "Update Ports": "Оновити порти", + "Fetch latest ports information.": "Отримати останню інформацію про порти.", + "Update PortMaster": "Оновити PortMaster", + "Force check for a new PortMaster version.": "Примусовий пошук нової версії PortMaster.", + "Restore PortMaster": "Відновлення PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Це відновить PortMaster до останньої стабільної версії.", + "Release Channel: {channel}": "Канал релізів: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Змінити канал релізів PortMaster — або бета, або стабільний.", + "Enable Alpha Releases": "Увімкнути альфа релізи", + "Change release channel of PortMaster to alpha.": "Змінити канал релізів PortMaster на альфа.", + "Controller Mode: {controller_mode}": "Режим контролеру: {controller_mode}", + "Toggle between various controller layouts.": "Перемикання між різними розкладками контролеру.", + "Secret Options": "Таємні налаштування", + "All Ports: ": "Всі порти: ", + "Show all ports, ignoring requirements.": "Показати всі порти, ігноруючи вимоги.", + "CWTBE Mode: ": "Режим CWTBE: ", + "Enable gptokeyb2 by default.": "Увімкнути gptokeyb2 за замовчуванням.", + "Delete PortMaster Config": "Видалити конфігурацію PortMaster", + "This can break stuff, don't touch unless you know what you are doing.": "Це може щось зламати, не чіпайте, якщо ви не знаєте, що робите.", + "Delete PortMaster Runtimes": "Видалити Runtime PortMaster", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Ви дійсно бажаєте відновити PortMaster?\n\nДля цього знадобиться запустити спеціально створений порт, який буде встановлено.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "Встановлено спеціальний порт під назвою \"Restore PortMaster.sh\"\n\nЗапустіть цей порт для відновлення інсталяції PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster зараз закриється, щоб запустити відновлювальний порт.", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Ви впевнені, що хочете перейти на канал альфа-релізів?\n\nВерсію PortMaster буде відповідно оновлено або знижено.\n\nЦЕ МОЖЕ І ПОШКОДИТЬ ФУНКЦІОНАЛ, ВИ БУЛИ ПОПЕРЕДЖЕНІ", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Ви впевнені, що хочете змінити канал релізу з {current_channel} на {new_channel}?\n\nВерсію PortMaster буде відповідно оновлено або знижено.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Ви дійсно бажаєте оновити свій gamelist.xml?\n\nЦе перезапише всі завантажені обкладинки або дані порту, і потребуватиме завантаження понад 50 МБ.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Ви впевнені, що хочете перемістити порти з {from_mode} до {to_mode}", + "SDCARD 1": "SDCARD 1", + "SDCARD 2": "SDCARD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Ви впевнені, що хочете керувати портами та встановлювати їх у розташування {to_loc}?\n\nВже встановлені порти не будуть переміщені.\nPortMaster буде перезапущено, щоб зміни набули чинності.", + "Deleting Runtimes:": "Видалення Runtime:", + "Source Manager": "Менеджер джерел", + "Port Info Popup": "Спливаюче вікно відомостей про порт", + "Hide Info": "Сховати відомості", + "Reinstall": "Перевстановити", + "Uninstall": "Видалити", + "Install": "Встановити", + "Port Info": "Відомості про порт", + "Show Info": "Показати відомості", + "Are you sure you want to uninstall {port_name}?": "Ви впевнені, що бажаєте видалити {port_name}?", + "NO PORTS": "НЕМАЄ ПОРТІВ", + "** NO PORTS FOUND **": "** ПОРТИ НЕ ЗНАЙДЕНО **", + "Download ports first.": "Спершу звантажте порти.", + "Try removing some filters.": "Спробуйте видалити деякі фільтри.", + "Ports List": "Список портів", + "Filters": "Фільтри", + "Download All": "Завантажити все", + "Used": "Використовується", + "Not Used": "Не використовується", + "Check": "Перевірити", + "Are you sure you want to download and verify all runtimes?": "Ви впевнені, що хочете завантажити та перевірити всі Runtime?", + "Deleted runtime {runtime}": "Видалено Runtime {runtime}", + "Disclaimer": "Відмова від відповідальності", + "Accept": "Прийняти", + "{theme_name} (Selected)": "{theme_name} (Обрано)", + "Download": "Завантажити", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Ви хочете змінити тему?\n\nВам доведеться перезапустити застосунок, щоб вона набула чинності.", + "Select Colour Scheme": "Оберіть кольорову палітру", + "{item_name} (Selected)": "{item_name} (Обрано)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Ви хочете змінити кольорову палітру?\n\nВам доведеться перезапустити застосунок, щоб вона набула чинності.", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Ви переходите з {old_release_channel} на {new_release_channel} версію.\n\nХочете знизити версію?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна нова версія PortMaster ({portmaster_version})\n\nХочете оновити?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Хочете перевстановити PortMaster?\n\nЦе перевстановить застосунок з {release_channel} каналу до {portmaster_version}." +} diff --git a/PortMaster/pylibs/locales/uk_UA.themes.json b/PortMaster/pylibs/locales/uk_UA.themes.json new file mode 100644 index 00000000..747e567e --- /dev/null +++ b/PortMaster/pylibs/locales/uk_UA.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Час: {system.time_24hr}\nБатарея: {system.battery_level}\nМісце: {system.free_space} доступно", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанри: {port_info.genres} Розмір завантаження: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Доступні порти: {ports_list.filter_ports} / {ports_list.total_ports} \nФільтри: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Доступне місце: {system.free_space}\n\nПортувальник: {port_info.porter}\nЖанри: {port_info.genres}\nРозмір завантаження: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nІнструкції: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nМісце: {system.free_space} доступно / {system.total_space} загалом\nЧас: {system.time_24hr}\nБатарея: {system.battery_level}", + "IP: {system.ip_address}": "IP: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Автор: {theme_info.creator}\nСтатус: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nМісце: {system.free_space} доступно", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Фільтри: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанри: {port_info.genres}\nРозмір завантаження: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "Час: {system.time_24hr} Батарея: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Назва: {port_info.title}\nПортувальник: {port_info.porter}\nЖанри: {port_info.genres}\nРозмір завантаження: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nОпис: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Доступні порти: {ports_list.filter_ports} / {ports_list.total_ports} \nФільтри: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанри: {port_info.genres}\n{if:port_info.install_size}Розмір інсталяції: {port_info.install_size}{else}Розмір завантаження: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Час: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nПристрій: {system.device_name}\nБатарея: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nМісце: {system.free_space} доступно / {system.total_space} загалом", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Портувальник: {port_info.porter}\n• Жанри: {port_info.genres}\n• Опис: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Жанри: {port_info.genres}\n• Розмір завантаження: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Опис: {port_info.description}\n• Інструкції:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Автор: {theme_info.creator}\n• Статус: {theme_info.status}\n• Опис:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Назва: {runtime_info.name}\nСтатус: {runtime_info.status}\nПорти: {runtime_info.ports}\nРозмір завантаження: {runtime_info.download_size}\nРозмір інсталяції: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/messages.po deleted file mode 100644 index 159e12ba..00000000 --- a/PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Ukrainian\n" -"Language: uk_UA\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: uk\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "Данська" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "Німецька" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "Англійська" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "Іспанська" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "Фінська" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "Французька" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "Італійська" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "Японська" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "Корейська" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "Нідерландська" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "Польська" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "Португальська (Португалія)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "Португальська (Бразилія)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "російська" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "Шведська" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "Українська" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "Китайська спрощена" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "Невідома IP-адреса" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "Н/Д" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** НЕМАЄ ПОРТУ **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "Готовий до запуску" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "Необхідні файли" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "Встановлено" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "Відсутній" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "Доступне оновлення" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "Не встановлено" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "Гаразд" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "Скасувати" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "Отримання останніх файлів gameinfo.xml і обкладинок." - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "Завантаження gameinfo.xml для {source_name}" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "Встановлення {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "Видалення {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "Оновлення всіх джерел порту:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "Перевірка {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "Ви переходите з {old_release_channel} на {new_release_channel} версію.\n\n" -"Хочете знизити версію?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "Доступна нова версія PortMaster ({portmaster_version})\n\n" -"Хочете оновити?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "Хочете перевстановити PortMaster?\n\n" -"Це перевстановить застосунок з {release_channel} каналу до {portmaster_version}." - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "Порти було додано або видалено, оновіть список ігор, щоб побачити нові ігри або видалити деінстальовані." - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "Завантаження..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "Помилка при перевірці завантаження." - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "Завантаження інформації." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "Отримання останніх рекомендованих портів." - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "Завантаження джерел." - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "Завантаження портів." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "Встановлення теми {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "Встановлення" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "Тема {download_name!r} успішно встановлена." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "Встановлення {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "Порт {download_name!r} успішно встановлено." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "Помилка встановлення, видалення файлів..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "Не вдалося встановити порт {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "Порт {download_name!r} та {runtime_name!r} успішно встановлено." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "Порт {download_name!r} успішно встановлено, але встановити {runtime_name!r} не вдалося!\n\n" -"Спробуйте перевстановити, або зверніться до вікі за допомогою." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "Порт {runtime} містить неправильний Runtime, гра може працювати некоректно." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "Порт {runtime} містить невідомий Runtime, гра може працювати некоректно." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "Підтверджено" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "Не підтверджено" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "Не працює" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "Неможливо завантажити Runtime в офлайн режимі." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "Verifying runtime {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "Підтвердження" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "Не вдалося знайти завантаження для {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "Не вдалося завантажити {runtime} з {device_arch}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} успішно підтверджено." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "Підтверджено {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "Оновлення {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "Runtime {runtime} пошкоджено, виконується перевстановлення." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "Завантаження Runtime {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "Не вдалося завантажити {runtime}, гра може працювати некоректно." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "Успішно завантажено {runtime}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "Не вдається завантажити в офлайн режимі." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "Не вдалося знайти джерело для {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "Невідомий порт {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "Видалення {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "Успішно видалено {port_name}" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "\n\n" -"Щоб це зробити: \n" -"МЕНЮ -> УТИЛІТИ -> Повторно сканувати папку з ROM'ами" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "Кеш застарілий." - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "Перша перевірка." - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "Автоматичне оновлення." - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "Оновлення" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "Вже оновлено" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "Отримання останньої інформації" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "Готово." - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "Не вдалося знайти {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "Не вдалося знайти інформацію для перевірки {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "Не вдалося завантажити інформацію для перевірки для {port_name}." - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "Успіх!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "Отримання інформації" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}: Отримання останніх портів" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "Отримання інформації." - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "Не вдалося завантажити інформацію для перевірки." - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "Не вдалося завантажити не zip-файли." - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "Не вдалося завантажити файл. [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "Завантаження {file_url} - ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "Завантаження файлу." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "Не вдалося завантажити: {err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "Файл перевірено." - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "Не вдалося перевірити завантаження." - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "Головне меню" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "Відмова від відповідальності" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "Прийняти" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "Вийти" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "Ви впевнені, що хочете вийти з PortMaster?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "Рекомендовані порти" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "Кураторський список портів" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "Всі порти" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "Список усіх портів доступних на PortMaster." - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "Порти готові до запуску" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "Показати всі порти, готові до гри!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "Керування портами" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "Оновити / видалити порти" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "Налаштування" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Налаштування PortMaster" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "Вихід" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "Вийти з PortMaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "Підтвердити" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "Таємний режим {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "Увімкнено" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "Вимкнено" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "Меню налаштувань" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "Інтерфейс" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "Обрати мову" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "Оберіть мову, яку використовує PortMaster." - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "Оберіть тему" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "Оберіть тему для PortMaster." - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "Оберіть кольорову палітру" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "Оберіть кольорову палітру для PortMaster" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "Аудіо" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "Музика: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "Увімкнення чи вимкнення фонової музики в PortMaster." - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "Звукові ефекти: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "Увімкнення чи вимкнення звукових ефектів в PortMaster." - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "Метадані портів" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "Оновлення метаданих: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "Автоматично" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "Вручну" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster оновлює метадані під час встановлення портів, щоб показувати обкладинки та опис портів під час перегляду портів на Вашому пристрої." - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "Оновити метадані" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "Оновити метадані портів вручну, з відсутніми/оновленими інформацією та обкладинками." - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "Розташування портів: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "Секція ROM-файлів" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "Вкладка портів" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "Місце встановлення портів." - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD-карта 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD-карта 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "Система" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "Керування Runtime" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "Керувати Runtime портів." - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "Експериментальні порти: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "Показати або приховати експериментальні порти." - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "Оновити порти" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "Отримати останню інформацію про порти." - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "Оновити PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "Примусовий пошук нової версії PortMaster." - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "Відновлення PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "Це відновить PortMaster до останньої стабільної версії." - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "Канал релізів: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "Змінити канал релізів PortMaster — або бета, або стабільний." - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "Увімкнути альфа релізи" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "Змінити канал релізів PortMaster на альфа." - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "Режим контролеру: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "Перемикання між різними розкладками контролеру." - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "Таємні налаштування" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "Всі порти: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "Показати всі порти, ігноруючи вимоги." - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "Режим CWTBE: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "Увімкнути gptokeyb2 за замовчуванням." - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "Видалити конфігурацію PortMaster" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "Це може щось зламати, не чіпайте, якщо ви не знаєте, що робите." - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "Видалити Runtime PortMaster" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "Назад" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "Ви дійсно бажаєте відновити PortMaster?\n\n" -"Для цього знадобиться запустити спеціально створений порт, який буде встановлено." - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "Встановлено спеціальний порт під назвою \"Restore PortMaster.sh\"\n\n" -"Запустіть цей порт для відновлення інсталяції PortMaster." - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster зараз закриється, щоб запустити відновлювальний порт." - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "Ви впевнені, що хочете перейти на канал альфа-релізів?\n\n" -"Версію PortMaster буде відповідно оновлено або знижено.\n\n" -"ЦЕ МОЖЕ І ПОШКОДИТЬ ФУНКЦІОНАЛ, ВИ БУЛИ ПОПЕРЕДЖЕНІ" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "Ви впевнені, що хочете змінити канал релізу з {current_channel} на {new_channel}?\n\n" -"Версію PortMaster буде відповідно оновлено або знижено." - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "Ви дійсно бажаєте оновити свій gamelist.xml?\n\n" -"Це перезапише всі завантажені обкладинки або дані порту, і потребуватиме завантаження понад 50 МБ." - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "Ви впевнені, що хочете перемістити порти з {from_mode} до {to_mode}" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SDCARD 1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SDCARD 2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "Ви впевнені, що хочете керувати портами та встановлювати їх у розташування {to_loc}?\n\n" -"Вже встановлені порти не будуть переміщені.\n" -"PortMaster буде перезапущено, щоб зміни набули чинності." - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "Видалення Runtime:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "Менеджер джерел" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "Завантажити все" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "Використовується" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "Не використовується" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "Перевірити" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "Видалити" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "Встановити" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "Ви впевнені, що хочете завантажити та перевірити всі Runtime?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "Видалено Runtime {runtime}" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (Обрано)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "Обрати" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "Завантажити" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "Ви хочете змінити тему?\n\n" -"Вам доведеться перезапустити застосунок, щоб вона набула чинності." - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "Оберіть кольорову палітру" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (Обрано)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "Ви хочете змінити кольорову палітру?\n\n" -"Вам доведеться перезапустити застосунок, щоб вона набула чинності." - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "Вибір мови" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (Обрано)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "Ви хочете змінити мову?\n\n" -"Вам доведеться перезапустити застосунок, щоб вона набула чинності." - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "Видалити" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "Верхній регістр" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "Пробіл" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "Нижній регістр" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "Рекомендовані порти не знайдено, потрібно підключення до інтернету." - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "НЕМАЄ ПОРТІВ" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** ПОРТИ НЕ ЗНАЙДЕНО **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "Спершу звантажте порти." - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "Спробуйте видалити деякі фільтри." - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "Показати відомості" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "Список портів" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "Фільтри" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "Спливаюче вікно відомостей про порт" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "Сховати відомості" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "Перевстановити" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "Відомості про порт" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "Ви впевнені, що бажаєте видалити {port_name}?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "Вкладка фільтрів" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "За абеткою" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "Нещодавно додані" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "Нещодавно оновлені" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "Бойовик" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "Пригоди" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "Аркада" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "Казино/карткова гра" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "Стрілянка від першої особи" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "Платформер" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "Головоломка" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "Перегони" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "Ритм-гра" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "Рольова гра" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "Симулятор" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "Спорт" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "Стратегія" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "Візуальна новела" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "Інше" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "Необхідні файли" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "Експериментальний" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "Пошкоджені порти" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "Безкоштовна гра, усі файли включені." - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "Демо-файли включені." - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "Потрібне завантаження безплатних зовнішніх ресурсів." - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "Потрібне завантаження платних зовнішніх ресурсів." - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "{runtime_name} Runtime" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32-біт" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64-біт" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64-біт" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "Сортувати:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "Скинути фільтри" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "Жанри:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "Атрибути:" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "Архітектура:" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "Наявність:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "Портувальники:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "Сповіщення" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "Ви справді бажаєте скасувати?" - diff --git a/PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/themes.po deleted file mode 100644 index 660b31b4..00000000 --- a/PortMaster/pylibs/locales/uk_UA/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Ukrainian\n" -"Language: uk_UA\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: uk\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Жанри: {port_info.genres} Розмір завантаження: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Доступні порти: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Фільтри: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "Доступне місце: {system.free_space}\n\n" -"Портувальник: {port_info.porter}\n" -"Жанри: {port_info.genres}\n" -"Розмір завантаження: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Інструкції: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Місце: {system.free_space} доступно / {system.total_space} загалом\n" -"Час: {system.time_24hr}\n" -"Батарея: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "Автор: {theme_info.creator}\n" -"Статус: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "Час: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Пристрій: {system.device_name}\n" -"Батарея: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Місце: {system.free_space} доступно" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "Фільтри: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Жанри: {port_info.genres}\n" -"Розмір завантаження: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "Час: {system.time_24hr} Батарея: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Місце: {system.free_space} доступно / {system.total_space} загалом" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• Портувальник: {port_info.porter}\n" -"• Жанри: {port_info.genres}\n" -"• Опис: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• Жанри: {port_info.genres}\n" -"• Розмір завантаження: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Опис: {port_info.description}\n" -"• Інструкції:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• Автор: {theme_info.creator}\n" -"• Статус: {theme_info.status}\n" -"• Опис:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "Назва: {runtime_info.name}\n" -"Статус: {runtime_info.status}\n" -"Порти: {runtime_info.ports}\n" -"Розмір завантаження: {runtime_info.download_size}\n" -"Розмір інсталяції: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "Час: {system.time_24hr}\n" -"Батарея: {system.battery_level}\n" -"Місце: {system.free_space} доступно" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "Назва: {port_info.title}\n" -"Портувальник: {port_info.porter}\n" -"Жанри: {port_info.genres}\n" -"Розмір завантаження: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Опис: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports}Доступні порти: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Фільтри: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "Жанри: {port_info.genres}\n" -"{if:port_info.install_size}Розмір інсталяції: {port_info.install_size}{else}Розмір завантаження: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/locales/zh_CN.json b/PortMaster/pylibs/locales/zh_CN.json new file mode 100644 index 00000000..4aa04f7d --- /dev/null +++ b/PortMaster/pylibs/locales/zh_CN.json @@ -0,0 +1,253 @@ +{ + "Loading...": "加载中…", + "Loading Sources.": "正在加载资源。", + "Download validation failed.": "下载验证失败。", + "Loading Info.": "正在载入信息...", + "Fetching latest featured ports.": "正在获取最新的优秀移植作品。", + "Installing Theme {download_name}.": "正在安装 {download_name} 主题。", + "Installing": "正在安装", + "Theme {download_name!r} installed successfully.": "主题 {download_name!r} 安装成功。", + "Port {download_name} installed failed.": "移植作品 {download_name} 安装失败。", + "Installing {download_name}.": "正在安装 {download_name}.", + "Port {download_name!r} installed successfully.": "已成功安装了移植作品 {download_name!r} 。", + "Installation failed, removing files...": "安装失败,正在移除文件...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "你好!\n\n你正在人工下载一个与此设备不适配的移植软件!\n\n如果你遇到了问题,请勿将其报告到Discord,你会被狠狠笑话!!\n\n你确定要继续安装吗?", + "Okay": "确定", + "Cancel": "取消", + "Port {download_name!r} and {runtime_name!r} installed successfully.": "移植作品 {download_name!r} 和 {runtime_name!r} 安装成功。", + "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "移植作品 {download_name!r} 安装成功,但 {runtime_name!r} 运行时安装失败!\n\n请尝试重新安装,或者查阅wiki以获得帮助。", + "Unable to download in offline mode.": "无法在离线模式下下载。", + "Unable to find a source for {port_name}": "无法找到{port_name} 的下载资源。", + "Unknown port {port_name}": "未知移植作品 {port_name}", + "Removing {port_name}": "正在移除{port_name}", + "Error uninstalling {port_name}\n\n{error}": "卸载时出现问题{port_name}\n\n{error}", + "Successfully uninstalled {port_name}": "成功卸载 {port_name}", + "Loading Ports.": "正在加载移植作品。", + "Port {runtime} contains a bad runtime, game may not run correctly.": "移植作品 {runtime} 包含了一个不适用的运行时,游戏可能无法正常运行。", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "移植作品 {runtime} 包含了一个未知的运行时,游戏可能无法正常运行。", + "Not Installed": "未安装", + "Update Available": "有可用更新", + "Verified": "已验证", + "Unverified": "未验证", + "Broken": "损坏", + "Unable do download a runtime when in offline mode.": "在离线模式下无法下载运行时。", + "Verifying runtime {runtime}": "正在检查运行时 {runtime}", + "Verifying": "正在验证", + "Unable to find a download for {runtime}.": "无法找到 {runtime} 的下载链接。", + "Unable to download {runtime} in {device_arch}.": "无法下载适用于 {device_arch} 的运行时 {runtime}", + "Verified {runtime} successfully.": "{runtime} 验证通过。", + "Verified {runtime}.": "已验证 {runtime}。", + "Updating {runtime}.": "正在更新{runtime}...", + "Runtime {runtime} is broken, reinstalling.": "运行时{runtime} 损坏,正在重装。", + "Downloading runtime {runtime}.": "正在下载运行时{runtime}", + "Unable to download {runtime}, game may not run correctly.": "无法下载 {runtime},游戏可能无法正常运行。", + "Successfully downloaded {runtime}.": "成功下载 {runtime}。", + "Unable to download verification file.": "无法下载验证信息。", + "Unable to download non zip files.": "无法下载非zip文件。", + "Cache out of date.": "缓存过期。", + "First check.": "首次检查", + "Auto Update.": "自动更新", + "Updating": "正在更新", + "Up to date already": "已经更新", + "Fetching latest info": "正在获取最新信息。", + "Done.": "完成。", + "Fetching info": "正在获取信息", + "Unable to find {port_name}.": "无法找到 {port_name}。", + "Unable to download file. [{status_code}]": "无法下载文件。错误代码 [{status_code}]", + "Downloading {file_url} - ({total_length_mb})": "正在从 {file_url} 下载 ({total_length_mb})", + "Downloading file.": "下载文件中...", + "Download failed: {err}": "下载失败,{err}", + "Passed file validation.": "已通过文件验证。", + "Unable to validate download.": "无法验证下载。", + "Unknown IP": "未知IP地址", + "N/A": "不适用", + "Fetching latest gameinfo.xml files and cover images.": "正在获取最新的gameinfo.xml文件与封面。", + "Downloading gameinfo.xml for {source_name}": "正在下载{source_name}的gameinfo.xml文件。", + "Installing {port_name}": "正在安装 {port_name}", + "Uninstalling {port_name}": "正在卸载 {port_name}", + "Updating all port sources:": "正在更新所有移植作品源:", + "Checking {runtime_name}": "正在检查 {runtime_name}", + "** NO PORT **": "** 无移植作品 **", + "Ready to Run": "可直接运行", + "Requires Files": "需要用户提供文件", + "Installed": "已安装", + "Missing": "缺失", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "有移植作品被增加或移除了,请刷新您的游戏列表以查看新增的游戏或移除已删除的游戏。", + "Danish": "丹麦语", + "German": "德语", + "English": "英语", + "Spanish": "西班牙语", + "Finnish": "芬兰语", + "French": "法语", + "Italian": "意大利语", + "Japanese": "日语", + "Korean": "韩语", + "Dutch": "荷兰语", + "Polish": "波兰语", + "Portuguese (Portugal)": "葡萄牙语(葡萄牙)", + "Portuguese (Brazil)": "葡萄牙语 (巴西)", + "Romanian": "罗马尼亚语", + "Russian": "俄语", + "Swedish": "瑞典语", + "Ukrainian": "乌克兰语", + "Chinese Simplified": "简体中文", + "Messages": "消息", + "Are you sure you want to cancel?": "您确定要取消吗?", + "Featured Ports": "精选移植作品", + "Select": "选择", + "Back": "返回", + "No featured ports found, internet connection is required.": "未找到精选移植作品,需要网络连接。", + "Filters Scene": "筛选场景", + "Alphabetical": "按字母顺序", + "Recently Added": "最近添加", + "Recently Updated": "最近更新", + "Action": "动作", + "Adventure": "冒险", + "Arcade": "街机", + "Casino/Card": "赌博/卡牌", + "First Person Shooter": "FPS", + "Platformer": "平台跳跃", + "Puzzle": "解谜", + "Racing": "竞速", + "Rhythm": "节奏", + "Role Playing Game": "RPG", + "Simulation": "模拟", + "Sports": "运动", + "Strategy": "策略", + "Visual Novel": "视觉小说", + "Other": "其他", + "Files Required": "需要文件", + "Experimental": "实验性", + "Broken Ports": "损坏的移植作品", + "Free game, all files included.": "免费游戏,包含所有文件。", + "Demo files included.": "包含试玩游戏文件。", + "Free external assets needed.": "需要可免费下载的外部资源。", + "Paid external assets needed.": "需要付费下载的外部资源。", + "{runtime_name} Runtime": "运行时 {runtime_name}", + "ARM 32bit": "ARM 32位", + "ARM 64bit": "ARM 64位", + "x86 64bit": "x86 64位", + "Sort:": "排序:", + "Clear Filters": "重置筛选", + "Genres:": "类型:", + "Attributes:": "属性", + "Availability:": "可用性:", + "Porters:": "移植人员:", + "Delete": "删除", + "Upper Case": "大写", + "Space": "空格", + "Enter": "进入", + "Lower Case": "小写", + "Language Select": "选择语言", + "{lang_name} (Selected)": "{lang_name} (已选)", + "Do you want to change language?\n\nYou will have to restart for it to take affect.": "您想要更改语言吗?\n\n需要重启程序以适用更改。", + "Main Menu": "主菜单", + "Hand curated lists of ports": "社区推荐的移植作品", + "All Ports": "所有移植作品", + "List all ports available on PortMaster.": "列出PortMaster所有可用的移植作品。", + "Ready to Run Ports": "可直接运行移植作品", + "List all ports that are ready to play!": "列出所有可以直接运行的移植作品!", + "Manage Ports": "管理移植作品", + "Update / Uninstall Ports": "更新 / 卸载移植作品", + "Options": "设置", + "PortMaster Options": "Portmaster 选项", + "Exit": "退出", + "Quit PortMaster": "退出 Portmaster", + "Quit": "退出", + "Secret Mode {secret_mode}": "秘密模式 {secret_mode}", + "Enabled": "已启用", + "Disabled": "已禁用", + "Are you sure you want to exit PortMaster?": "您确定要退出PortMaster吗?", + "Options Menu": "选项菜单", + "Interface": "用户界面", + "Choose Language": "选择语言", + "Select the language PortMaster uses.": "选择 PortMaster 语言。", + "Select Theme": "选择主题", + "Select a theme for PortMaster.": "选择一个PortMaster主题。", + "Select Color Scheme": "选择配色方案", + "Select a colour scheme for PortMaster": "选择一个PortMaster配色方案", + "Audio": "音频:", + "Music: ": "音乐: ", + "Enable or Disable background music in PortMaster.": "在 PortMaster中启用或禁用背景音乐", + "Sound FX: ": "音效: ", + "Enable or Disable soundfx in PortMaster.": "在 PortMaster中启用或禁用音效", + "Port Metadata": "移植作品元数据", + "Metadata Update: ": "元数据更新: ", + "Auto": "自动", + "Manual": "手动", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster在安装移植作品时会更新元数据,以便您浏览移植作品时可见图片和描述。", + "Metadata Refresh": "元数据重置", + "Manually update port metadata with missing/updated information and artwork.": "手动更新缺失或需要更新的移植作品元数据", + "Ports Location: ": "移植作品位置: ", + "Location where ports should be installed to.": "移植作品将安装的位置目录。", + "SD 1": "SD 1", + "SD 2": "SD 2", + "System": "系统", + "Runtime Manager": "运行时管理器", + "Manage port runtimes.": "管理移植作品的运行时。", + "Experimental Ports: ": "实验性移植作品: ", + "Show or hide experimental ports.": "是否显示实验性移植作品", + "Update Ports": "更新移植作品", + "Fetch latest ports information.": "查找移植软件最新信息。", + "Update PortMaster": "更新PortMaster", + "Force check for a new PortMaster version.": "强制检查新的 PortMaster 版本。", + "Restore PortMaster": "恢复PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "将恢复PortMaster为最新稳定版本。", + "Release Channel: {channel}": "发布通道: {channel}", + "Change release channel of PortMaster, either beta or stable.": "更改PortMaster的发布通道,可选beta(测试)或者stable(稳定)。", + "Enable Alpha Releases": "启用 Alpha 版本", + "Change release channel of PortMaster to alpha.": "更改PortMaster的发行频道为Alpha", + "Controller Mode: {controller_mode}": "控制器模式: {controller_mode}", + "Toggle between various controller layouts.": "切换不同控制器布局。", + "Secret Options": "秘密选项", + "All Ports: ": "所有移植作品: ", + "Show all ports, ignoring requirements.": "显示所有移植作品,忽略系统需求。", + "CWTBE Mode: ": "CWTBE 模式: ", + "Enable gptokeyb2 by default.": "默认启用gptokeyb2。", + "Delete PortMaster Config": "删除 PortMaster 配置", + "This can break stuff, don't touch unless you know what you are doing.": "如果您不知道此选择的后果,请勿继续,损失自负!", + "Delete PortMaster Runtimes": "删除 PortMaster 运行时", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "您确定需要还原PortMaster吗?\n\n该操作需要安装一个特殊配置的“移植作品”文件。", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "特殊“移植作品”已安装,名为“Restore PortMaster.sh”\n\n运行该文件以还原PortMaster。", + "PortMaster will now quit so you can run the restoration port.": "PortMaster将退出以运行还原程序。", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "您确定要切换到Alpha通道吗?\nPortMaster会按需升级或降级。\n【现有设置会损坏,后果自负!!】", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "您确定要把发布通道从 {current_channel} 改为 {new_channel}?\n\nPortMaster会按需升级或者降级。", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "您确定希望更新gamelist.xml文件吗?\n将覆盖现有所有移植作品的封面和其他信息,且需要下载50MB以上的数据。", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "您确定希望将移植作品从{from_mode} 移动到{to_mode} ?", + "SDCARD 1": "SD卡1", + "SDCARD 2": "SD卡2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "您确定希望在{to_loc} 目录管理安装移植作品吗?\n已经安装的移植作品不会被移动。\nPortMaster需要重启以适用更改。", + "Deleting Runtimes:": "删除运行时:", + "Source Manager": "源管理器", + "Port Info Popup": "移植作品信息弹出", + "Hide Info": "隐藏信息", + "Reinstall": "重新安装", + "Uninstall": "卸载", + "Install": "安装", + "Port Info": "移植作品信息", + "Show Info": "显示详情", + "Are you sure you want to uninstall {port_name}?": "您确定要卸载 {port_name} 吗?", + "NO PORTS": "无移植作品", + "** NO PORTS FOUND **": "** 没有发现移植作品 **", + "Download ports first.": "请先下载移植作品。", + "Try removing some filters.": "试试去除一些筛选条件。", + "Ports List": "移植作品列表", + "Filters": "筛选条件", + "Download All": "下载全部", + "Used": "已用", + "Not Used": "未使用", + "Check": "检查", + "Are you sure you want to download and verify all runtimes?": "您确定要下载并验证所有运行时吗?", + "Deleted runtime {runtime}": "删除运行时 {runtime}", + "Disclaimer": "声明", + "Accept": "同意", + "{theme_name} (Selected)": "{theme_name} (已选)", + "Download": "下载", + "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "您确定想更换主题吗?\n\n需要重启程序以适用更改。", + "Select Colour Scheme": "选择配色方案", + "{item_name} (Selected)": "{item_name} (已选)", + "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "您确定想更改主题颜色方案吗?\n\n需要重启程序以适用更改。", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "正在从 {old_release_channel} 切换到 {new_release_channel} 版本。希望退回吗?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster有新版本 ({portmaster_version}) 可用,希望升级吗?", + "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "您希望重装PortMaster吗?\n\n这会安装来自 {release_channel} 的 {portmaster_version} 版本。" +} diff --git a/PortMaster/pylibs/locales/zh_CN.themes.json b/PortMaster/pylibs/locales/zh_CN.themes.json new file mode 100644 index 00000000..62187b35 --- /dev/null +++ b/PortMaster/pylibs/locales/zh_CN.themes.json @@ -0,0 +1,23 @@ +{ + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "时间: {system.time_24hr}\n电量: {system.battery_level}\n存储空间剩余: {system.free_space}", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "类别: {port_info.genres} 下载大小: {port_info.download_size}{if:port_info.runtime} 运行时: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports} 可用移植作品数: {ports_list.filter_ports} / {ports_list.total_ports} \n筛选条件: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "存储空间剩余: {system.free_space}\n\n移植人员: {port_info.porter}\n类型: {port_info.genres}\n下载大小: {port_info.download_size}{if:port_info.runtime}\n运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n安装说明: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM版本: {system.portmaster_version}\nHM版本: {system.harbourmaster_version}\n存储空间剩余: {system.free_space} ,总容量 {system.total_space} \n时间: {system.time_24hr}\n电量: {system.battery_level}", + "IP: {system.ip_address}": "IP地址: {system.ip_address}", + "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "作者: {theme_info.creator}\n状态: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM版本: {system.portmaster_version}\nHM版本: {system.harbourmaster_version}\n存储空间剩余: {system.free_space}", + "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "筛选条件: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "类型: {port_info.genres}\n下载大小: {port_info.download_size}{if:port_info.runtime}\n运行时: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr} Battery: {system.battery_level}": "时间: {system.time_24hr} 电量: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "标题: {port_info.title}\n移植人员: {port_info.porter}\n类型: {port_info.genres}\n下载大小: {port_info.download_size}{if:port_info.runtime}\n运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n描述: {port_info.description}", + "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports} 可用移植作品数: {ports_list.filter_ports} / {ports_list.total_ports} \n筛选条件: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "类型: {port_info.genres}\n{if:port_info.install_size} 安装大小: {port_info.install_size}{else} 下载大小:{port_info.download_size}{endif}{if:port_info.runtime}\n运行时: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "时间: {system.time_24hr}\n系统: {system.cfw_name} ({system.cfw_version})\n设备: {system.device_name}\n电量: {system.battery_level}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM版本: {system.portmaster_version}\nHM版本: {system.harbourmaster_version}\n存储空间剩余: {system.free_space} ,总容量{system.total_space}", + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• 移植人员: {port_info.porter}\n• 作品类型: {port_info.genres}\n• 描述: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• 作品类型: {port_info.genres}\n• 下载大小: {port_info.download_size}{if:port_info.runtime}\n• 运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n• 描述: {port_info.description}\n• 安装说明:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM版本: {system.portmaster_version}, HM版本: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• 创作者: {theme_info.creator}\n• 状态: {theme_info.status}\n• 描述:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "运行时名称: {runtime_info.name}\n状态: {runtime_info.status}\n移植作品: {runtime_info.ports}\n下载大小: {runtime_info.download_size}\n安装大小: {runtime_info.disk_size}" +} diff --git a/PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/messages.po b/PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/messages.po deleted file mode 100644 index f0b5635d..00000000 --- a/PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/messages.po +++ /dev/null @@ -1,1248 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-01-14 19:52+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Chinese Simplified\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: zh-CN\n" -"X-Crowdin-File: messages.pot\n" -"X-Crowdin-File-ID: 1\n" - -#: PortMaster/pugwash:134 -msgid "Danish" -msgstr "丹麦语" - -#: PortMaster/pugwash:135 -msgid "German" -msgstr "德语" - -#: PortMaster/pugwash:136 -msgid "English" -msgstr "英语" - -#: PortMaster/pugwash:137 -msgid "Spanish" -msgstr "西班牙语" - -#: PortMaster/pugwash:138 -msgid "Finnish" -msgstr "芬兰语" - -#: PortMaster/pugwash:139 -msgid "French" -msgstr "法语" - -#: PortMaster/pugwash:140 -msgid "Italian" -msgstr "意大利语" - -#: PortMaster/pugwash:141 -msgid "Japanese" -msgstr "日语" - -#: PortMaster/pugwash:142 -msgid "Korean" -msgstr "韩语" - -#: PortMaster/pugwash:143 -msgid "Dutch" -msgstr "荷兰语" - -#: PortMaster/pugwash:144 -msgid "Polish" -msgstr "波兰语" - -#: PortMaster/pugwash:145 -msgid "Portuguese (Portugal)" -msgstr "葡萄牙语(葡萄牙)" - -#: PortMaster/pugwash:146 -msgid "Portuguese (Brazil)" -msgstr "葡萄牙语 (巴西)" - -#: PortMaster/pugwash:147 -msgid "Romanian" -msgstr "罗马尼亚语" - -#: PortMaster/pugwash:148 -msgid "Russian" -msgstr "俄语" - -#: PortMaster/pugwash:149 -msgid "Swedish" -msgstr "瑞典语" - -#: PortMaster/pugwash:150 -msgid "Ukrainian" -msgstr "乌克兰语" - -#: PortMaster/pugwash:151 -msgid "Chinese Simplified" -msgstr "简体中文" - -#: PortMaster/pugwash:705 -msgid "Unknown IP" -msgstr "未知IP地址" - -#: PortMaster/pugwash:838 PortMaster/pugwash:987 -msgid "N/A" -msgstr "不适用" - -#: PortMaster/pugwash:950 -msgid "** NO PORT **" -msgstr "** 无移植作品 **" - -#: PortMaster/pugwash:969 PortMaster/pylibs/pugscene.py:2066 -msgid "Ready to Run" -msgstr "可直接运行" - -#: PortMaster/pugwash:969 -msgid "Requires Files" -msgstr "需要用户提供文件" - -#: PortMaster/pugwash:981 PortMaster/pugwash:1008 -#: PortMaster/pylibs/pugscene.py:1145 -msgid "Installed" -msgstr "已安装" - -#: PortMaster/pugwash:983 -msgid "Missing" -msgstr "缺失" - -#: PortMaster/pugwash:1009 PortMaster/pylibs/harbourmaster/harbour.py:2081 -#: PortMaster/pylibs/pugscene.py:2070 -msgid "Update Available" -msgstr "有可用更新" - -#: PortMaster/pugwash:1010 PortMaster/pylibs/harbourmaster/harbour.py:2080 -#: PortMaster/pylibs/pugscene.py:1145 PortMaster/pylibs/pugscene.py:2069 -msgid "Not Installed" -msgstr "未安装" - -#: PortMaster/pugwash:1186 PortMaster/pugwash:1574 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:2410 PortMaster/pylibs/pugscene.py:2458 -#: PortMaster/pylibs/pugscene.py:2460 -msgid "Okay" -msgstr "确定" - -#: PortMaster/pugwash:1189 PortMaster/pugwash:1575 -#: PortMaster/pylibs/harbourmaster/harbour.py:1851 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -#: PortMaster/pylibs/pugscene.py:2379 PortMaster/pylibs/pugscene.py:2413 -#: PortMaster/pylibs/pugscene.py:2458 -msgid "Cancel" -msgstr "取消" - -#: PortMaster/pugwash:1426 -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "正在获取最新的gameinfo.xml文件与封面。" - -#: PortMaster/pugwash:1447 -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "正在下载{source_name}的gameinfo.xml文件。" - -#: PortMaster/pugwash:1480 -#, python-brace-format -msgid "Installing {port_name}" -msgstr "正在安装 {port_name}" - -#: PortMaster/pugwash:1489 -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "正在卸载 {port_name}" - -#: PortMaster/pugwash:1500 -msgid "Updating all port sources:" -msgstr "正在更新所有移植作品源:" - -#: PortMaster/pugwash:1510 -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "正在检查 {runtime_name}" - -#: PortMaster/pugwash:1939 -#, python-brace-format -msgid "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\n" -"Do you want to downgrade?" -msgstr "正在从 {old_release_channel} 切换到 {new_release_channel} 版本。希望退回吗?" - -#: PortMaster/pugwash:1945 -#, python-brace-format -msgid "There is a new version of PortMaster ({portmaster_version})\n\n" -"Do you want to upgrade?" -msgstr "PortMaster有新版本 ({portmaster_version}) 可用,希望升级吗?" - -#: PortMaster/pugwash:1950 -#, python-brace-format -msgid "Do you want to reinstall PortMaster?\n\n" -"This will reinstall from the {release_channel} channel to {portmaster_version}." -msgstr "您希望重装PortMaster吗?\n\n" -"这会安装来自 {release_channel} 的 {portmaster_version} 版本。" - -#: PortMaster/pugwash:2092 -msgid "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones." -msgstr "有移植作品被增加或移除了,请刷新您的游戏列表以查看新增的游戏或移除已删除的游戏。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:129 -msgid "Loading..." -msgstr "加载中…" - -#: PortMaster/pylibs/harbourmaster/harbour.py:262 -#: PortMaster/pylibs/harbourmaster/harbour.py:282 -#: PortMaster/pylibs/harbourmaster/util.py:519 -msgid "Download validation failed." -msgstr "下载验证失败。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:277 -msgid "Unable to download file." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:292 -msgid "Loading Info." -msgstr "正在载入信息..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:311 -msgid "Fetching latest ports info data." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:315 -msgid "Fetching latest porters info." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:319 -msgid "Fetching latest port stats." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:323 -msgid "Fetching latest featured ports." -msgstr "正在获取最新的优秀移植作品。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:327 -msgid "Fetching latest featured port images zip." -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:472 -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/harbour.py:494 -msgid "Loading Sources." -msgstr "正在加载资源。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:712 -msgid "Loading Ports." -msgstr "正在加载移植作品。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1730 -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "正在安装 {download_name} 主题。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1737 -#: PortMaster/pylibs/harbourmaster/harbour.py:1784 -#: PortMaster/pylibs/harbourmaster/harbour.py:1887 -msgid "Installing" -msgstr "正在安装" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1750 -msgid "Theme {download_name!r} installed successfully." -msgstr "主题 {download_name!r} 安装成功。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1775 -#: PortMaster/pylibs/harbourmaster/harbour.py:1871 -#, python-brace-format -msgid "Installing {download_name}." -msgstr "正在安装 {download_name}." - -#: PortMaster/pylibs/harbourmaster/harbour.py:1811 -#: PortMaster/pylibs/harbourmaster/harbour.py:2039 -msgid "Port {download_name!r} installed successfully." -msgstr "已成功安装了移植作品 {download_name!r} 。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:1850 -msgid "HELLO.\n\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "你好!\n\n" -"你正在人工下载一个与此设备不适配的移植软件!\n\n" -"如果你遇到了问题,请勿将其报告到Discord,你会被狠狠笑话!!\n\n" -"你确定要继续安装吗?" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2001 -msgid "Installation failed, removing files..." -msgstr "安装失败,正在移除文件..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2013 -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "移植作品 {download_name} 安装失败。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2029 -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "移植作品 {download_name!r} 和 {runtime_name!r} 安装成功。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2034 -msgid "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "移植作品 {download_name!r} 安装成功,但 {runtime_name!r} 运行时安装失败!\n\n" -"请尝试重新安装,或者查阅wiki以获得帮助。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2049 -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "移植作品 {runtime} 包含了一个不适用的运行时,游戏可能无法正常运行。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2063 -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "移植作品 {runtime} 包含了一个未知的运行时,游戏可能无法正常运行。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2082 -msgid "Verified" -msgstr "已验证" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2083 -msgid "Unverified" -msgstr "未验证" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2084 -msgid "Broken" -msgstr "损坏" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2091 -msgid "Unable do download a runtime when in offline mode." -msgstr "在离线模式下无法下载运行时。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2097 -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "正在检查运行时 {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2106 -#: PortMaster/pylibs/harbourmaster/harbour.py:2109 -msgid "Verifying" -msgstr "正在验证" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2134 -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "无法找到 {runtime} 的下载链接。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2140 -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "无法下载适用于 {device_arch} 的运行时 {runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2162 -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "{runtime} 验证通过。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2165 -#, python-brace-format -msgid "Verified {runtime}." -msgstr "已验证 {runtime}。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2171 -#, python-brace-format -msgid "Updating {runtime}." -msgstr "正在更新{runtime}..." - -#: PortMaster/pylibs/harbourmaster/harbour.py:2175 -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "运行时{runtime} 损坏,正在重装。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2179 -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "正在下载运行时{runtime}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2208 -#: PortMaster/pylibs/harbourmaster/harbour.py:2218 -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "无法下载 {runtime},游戏可能无法正常运行。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2227 -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "成功下载 {runtime}。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2236 -#: PortMaster/pylibs/harbourmaster/harbour.py:2315 -msgid "Unable to download in offline mode." -msgstr "无法在离线模式下下载。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2333 -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "无法找到{port_name} 的下载资源。" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2347 -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "未知移植作品 {port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2382 -#, python-brace-format -msgid "Removing {port_name}" -msgstr "正在移除{port_name}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2425 -#, python-brace-format -msgid "Error uninstalling {port_name}\n\n" -"{error}" -msgstr "卸载时出现问题{port_name}\n\n" -"{error}" - -#: PortMaster/pylibs/harbourmaster/harbour.py:2430 -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "成功卸载 {port_name}" - -#: PortMaster/pylibs/harbourmaster/platform.py:613 -msgid "\n\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "" - -#: PortMaster/pylibs/harbourmaster/source.py:48 -msgid "Cache out of date." -msgstr "缓存过期。" - -#: PortMaster/pylibs/harbourmaster/source.py:53 -msgid "First check." -msgstr "首次检查" - -#: PortMaster/pylibs/harbourmaster/source.py:56 -msgid "Auto Update." -msgstr "自动更新" - -#: PortMaster/pylibs/harbourmaster/source.py:151 -#: PortMaster/pylibs/harbourmaster/source.py:956 -msgid "Updating" -msgstr "正在更新" - -#: PortMaster/pylibs/harbourmaster/source.py:162 -#: PortMaster/pylibs/harbourmaster/source.py:968 -msgid "Up to date already" -msgstr "已经更新" - -#: PortMaster/pylibs/harbourmaster/source.py:167 -#: PortMaster/pylibs/harbourmaster/source.py:973 -msgid "Fetching latest info" -msgstr "正在获取最新信息。" - -#: PortMaster/pylibs/harbourmaster/source.py:201 -#: PortMaster/pylibs/harbourmaster/source.py:1053 -msgid "Done." -msgstr "完成。" - -#: PortMaster/pylibs/harbourmaster/source.py:209 -#: PortMaster/pylibs/harbourmaster/source.py:542 -#: PortMaster/pylibs/harbourmaster/source.py:1061 -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "无法找到 {port_name}。" - -#: PortMaster/pylibs/harbourmaster/source.py:220 -#, python-brace-format -msgid "Unable to find verification info for {port_name}." -msgstr "无法找到{port_name} 的验证信息。" - -#: PortMaster/pylibs/harbourmaster/source.py:227 -#, python-brace-format -msgid "Unable to download verification info for {port_name}." -msgstr "无法下载 {port_name} 的验证信息。" - -#: PortMaster/pylibs/harbourmaster/source.py:237 -msgid "Success!" -msgstr "成功!" - -#: PortMaster/pylibs/harbourmaster/source.py:291 -#: PortMaster/pylibs/harbourmaster/source.py:455 -#: PortMaster/pylibs/harbourmaster/source.py:860 -#: PortMaster/pylibs/pugtheme.py:428 -msgid "Fetching info" -msgstr "正在获取信息" - -#: PortMaster/pylibs/harbourmaster/source.py:603 -#, python-brace-format -msgid "{source_name}: Fetching latest ports" -msgstr "{source_name}:正在获取最新的移植作品" - -#: PortMaster/pylibs/harbourmaster/source.py:642 -msgid "Fetching info." -msgstr "正在获取信息。" - -#: PortMaster/pylibs/harbourmaster/source.py:1152 -msgid "Unable to download verification file." -msgstr "无法下载验证信息。" - -#: PortMaster/pylibs/harbourmaster/source.py:1163 -msgid "Unable to download non zip files." -msgstr "无法下载非zip文件。" - -#: PortMaster/pylibs/harbourmaster/util.py:448 -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "无法下载文件。错误代码 [{status_code}]" - -#: PortMaster/pylibs/harbourmaster/util.py:464 -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "正在从 {file_url} 下载 ({total_length_mb})" - -#: PortMaster/pylibs/harbourmaster/util.py:476 -#: PortMaster/pylibs/harbourmaster/util.py:490 -msgid "Downloading file." -msgstr "下载文件中..." - -#: PortMaster/pylibs/harbourmaster/util.py:507 -#, python-brace-format -msgid "Download failed: {err}" -msgstr "下载失败,{err}" - -#: PortMaster/pylibs/harbourmaster/util.py:525 -msgid "Passed file validation." -msgstr "已通过文件验证。" - -#: PortMaster/pylibs/harbourmaster/util.py:530 -msgid "Unable to validate download." -msgstr "无法验证下载。" - -#: PortMaster/pylibs/pugscene.py:381 PortMaster/pylibs/pugscene.py:477 -msgid "Main Menu" -msgstr "主菜单" - -#: PortMaster/pylibs/pugscene.py:409 -msgid "Disclaimer" -msgstr "声明" - -#: PortMaster/pylibs/pugscene.py:435 -msgid "Accept" -msgstr "同意" - -#: PortMaster/pylibs/pugscene.py:435 PortMaster/pylibs/pugscene.py:515 -msgid "Quit" -msgstr "退出" - -#: PortMaster/pylibs/pugscene.py:465 PortMaster/pylibs/pugscene.py:579 -msgid "Are you sure you want to exit PortMaster?" -msgstr "您确定要退出PortMaster吗?" - -#: PortMaster/pylibs/pugscene.py:490 PortMaster/pylibs/pugscene.py:1529 -#: PortMaster/pylibs/pugscene.py:1531 -msgid "Featured Ports" -msgstr "精选移植作品" - -#: PortMaster/pylibs/pugscene.py:491 -msgid "Hand curated lists of ports" -msgstr "社区推荐的移植作品" - -#: PortMaster/pylibs/pugscene.py:494 -msgid "All Ports" -msgstr "所有移植作品" - -#: PortMaster/pylibs/pugscene.py:495 -msgid "List all ports available on PortMaster." -msgstr "列出PortMaster所有可用的移植作品。" - -#: PortMaster/pylibs/pugscene.py:498 -msgid "Ready to Run Ports" -msgstr "可直接运行移植作品" - -#: PortMaster/pylibs/pugscene.py:499 -msgid "List all ports that are ready to play!" -msgstr "列出所有可以直接运行的移植作品!" - -#: PortMaster/pylibs/pugscene.py:502 -msgid "Manage Ports" -msgstr "管理移植作品" - -#: PortMaster/pylibs/pugscene.py:503 -msgid "Update / Uninstall Ports" -msgstr "更新 / 卸载移植作品" - -#: PortMaster/pylibs/pugscene.py:508 -msgid "Options" -msgstr "设置" - -#: PortMaster/pylibs/pugscene.py:509 -msgid "PortMaster Options" -msgstr "Portmaster 选项" - -#: PortMaster/pylibs/pugscene.py:512 -msgid "Exit" -msgstr "退出" - -#: PortMaster/pylibs/pugscene.py:513 -msgid "Quit PortMaster" -msgstr "退出 Portmaster" - -#: PortMaster/pylibs/pugscene.py:515 PortMaster/pylibs/pugscene.py:726 -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Enter" -msgstr "进入" - -#: PortMaster/pylibs/pugscene.py:535 -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "秘密模式 {secret_mode}" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Enabled" -msgstr "已启用" - -#: PortMaster/pylibs/pugscene.py:536 PortMaster/pylibs/pugscene.py:617 -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:661 -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:708 -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:768 -#: PortMaster/pylibs/pugscene.py:780 PortMaster/pylibs/pugscene.py:794 -#: PortMaster/pylibs/pugscene.py:806 PortMaster/pylibs/pugscene.py:882 -#: PortMaster/pylibs/pugscene.py:891 -msgid "Disabled" -msgstr "已禁用" - -#: PortMaster/pylibs/pugscene.py:589 -msgid "Options Menu" -msgstr "选项菜单" - -#: PortMaster/pylibs/pugscene.py:595 -msgid "Interface" -msgstr "用户界面" - -#: PortMaster/pylibs/pugscene.py:599 -msgid "Choose Language" -msgstr "选择语言" - -#: PortMaster/pylibs/pugscene.py:600 -msgid "Select the language PortMaster uses." -msgstr "选择 PortMaster 语言。" - -#: PortMaster/pylibs/pugscene.py:603 PortMaster/pylibs/pugscene.py:1238 -msgid "Select Theme" -msgstr "选择主题" - -#: PortMaster/pylibs/pugscene.py:604 -msgid "Select a theme for PortMaster." -msgstr "选择一个PortMaster主题。" - -#: PortMaster/pylibs/pugscene.py:610 -msgid "Select Color Scheme" -msgstr "选择配色方案" - -#: PortMaster/pylibs/pugscene.py:611 -msgid "Select a colour scheme for PortMaster" -msgstr "选择一个PortMaster配色方案" - -#: PortMaster/pylibs/pugscene.py:613 -msgid "Audio" -msgstr "音频:" - -#: PortMaster/pylibs/pugscene.py:617 PortMaster/pylibs/pugscene.py:882 -msgid "Music: " -msgstr "音乐: " - -#: PortMaster/pylibs/pugscene.py:618 -msgid "Enable or Disable background music in PortMaster." -msgstr "在 PortMaster中启用或禁用背景音乐" - -#: PortMaster/pylibs/pugscene.py:621 PortMaster/pylibs/pugscene.py:891 -msgid "Sound FX: " -msgstr "音效: " - -#: PortMaster/pylibs/pugscene.py:622 -msgid "Enable or Disable soundfx in PortMaster." -msgstr "在 PortMaster中启用或禁用音效" - -#: PortMaster/pylibs/pugscene.py:625 -msgid "Port Metadata" -msgstr "移植作品元数据" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Metadata Update: " -msgstr "元数据更新: " - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Auto" -msgstr "自动" - -#: PortMaster/pylibs/pugscene.py:629 PortMaster/pylibs/pugscene.py:919 -msgid "Manual" -msgstr "手动" - -#: PortMaster/pylibs/pugscene.py:630 -msgid "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device." -msgstr "PortMaster在安装移植作品时会更新元数据,以便您浏览移植作品时可见图片和描述。" - -#: PortMaster/pylibs/pugscene.py:634 -msgid "Metadata Refresh" -msgstr "元数据重置" - -#: PortMaster/pylibs/pugscene.py:635 -msgid "Manually update port metadata with missing/updated information and artwork." -msgstr "手动更新缺失或需要更新的移植作品元数据" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:649 -#: PortMaster/pylibs/pugscene.py:956 -msgid "Ports Location: " -msgstr "移植作品位置: " - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:934 -msgid "Roms section" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:640 PortMaster/pylibs/pugscene.py:935 -msgid "Ports tab" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:641 PortMaster/pylibs/pugscene.py:650 -msgid "Location where ports should be installed to." -msgstr "移植作品将安装的位置目录。" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 1" -msgstr "SD 1" - -#: PortMaster/pylibs/pugscene.py:649 -msgid "SD 2" -msgstr "SD 2" - -#: PortMaster/pylibs/pugscene.py:652 -msgid "System" -msgstr "系统" - -#: PortMaster/pylibs/pugscene.py:656 PortMaster/pylibs/pugscene.py:1067 -msgid "Runtime Manager" -msgstr "运行时管理器" - -#: PortMaster/pylibs/pugscene.py:657 -msgid "Manage port runtimes." -msgstr "管理移植作品的运行时。" - -#: PortMaster/pylibs/pugscene.py:661 PortMaster/pylibs/pugscene.py:780 -msgid "Experimental Ports: " -msgstr "实验性移植作品: " - -#: PortMaster/pylibs/pugscene.py:662 -msgid "Show or hide experimental ports." -msgstr "是否显示实验性移植作品" - -#: PortMaster/pylibs/pugscene.py:666 -msgid "Update Ports" -msgstr "更新移植作品" - -#: PortMaster/pylibs/pugscene.py:667 -msgid "Fetch latest ports information." -msgstr "查找移植软件最新信息。" - -#: PortMaster/pylibs/pugscene.py:671 -msgid "Update PortMaster" -msgstr "更新PortMaster" - -#: PortMaster/pylibs/pugscene.py:672 -msgid "Force check for a new PortMaster version." -msgstr "强制检查新的 PortMaster 版本。" - -#: PortMaster/pylibs/pugscene.py:677 -msgid "Restore PortMaster" -msgstr "恢复PortMaster" - -#: PortMaster/pylibs/pugscene.py:678 -msgid "This will restore PortMaster to the latest stable version of PortMaster." -msgstr "将恢复PortMaster为最新稳定版本。" - -#: PortMaster/pylibs/pugscene.py:682 -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "发布通道: {channel}" - -#: PortMaster/pylibs/pugscene.py:684 -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "更改PortMaster的发布通道,可选beta(测试)或者stable(稳定)。" - -#: PortMaster/pylibs/pugscene.py:690 -msgid "Enable Alpha Releases" -msgstr "启用 Alpha 版本" - -#: PortMaster/pylibs/pugscene.py:691 -msgid "Change release channel of PortMaster to alpha." -msgstr "更改PortMaster的发行频道为Alpha" - -#: PortMaster/pylibs/pugscene.py:697 PortMaster/pylibs/pugscene.py:909 -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "控制器模式: {controller_mode}" - -#: PortMaster/pylibs/pugscene.py:698 -msgid "Toggle between various controller layouts." -msgstr "切换不同控制器布局。" - -#: PortMaster/pylibs/pugscene.py:701 -msgid "Secret Options" -msgstr "秘密选项" - -#: PortMaster/pylibs/pugscene.py:704 PortMaster/pylibs/pugscene.py:768 -msgid "All Ports: " -msgstr "所有移植作品: " - -#: PortMaster/pylibs/pugscene.py:705 -msgid "Show all ports, ignoring requirements." -msgstr "显示所有移植作品,忽略系统需求。" - -#: PortMaster/pylibs/pugscene.py:708 PortMaster/pylibs/pugscene.py:794 -msgid "CWTBE Mode: " -msgstr "CWTBE 模式: " - -#: PortMaster/pylibs/pugscene.py:709 -msgid "Enable gptokeyb2 by default." -msgstr "默认启用gptokeyb2。" - -#: PortMaster/pylibs/pugscene.py:712 PortMaster/pylibs/pugscene.py:806 -msgid "Debug Mode: " -msgstr "" - -#: PortMaster/pylibs/pugscene.py:713 -msgid "Enable debug logging all the time." -msgstr "" - -#: PortMaster/pylibs/pugscene.py:716 -msgid "Delete PortMaster Config" -msgstr "删除 PortMaster 配置" - -#: PortMaster/pylibs/pugscene.py:717 PortMaster/pylibs/pugscene.py:721 -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "如果您不知道此选择的后果,请勿继续,损失自负!" - -#: PortMaster/pylibs/pugscene.py:720 -msgid "Delete PortMaster Runtimes" -msgstr "删除 PortMaster 运行时" - -#: PortMaster/pylibs/pugscene.py:726 PortMaster/pylibs/pugscene.py:1161 -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1272 -#: PortMaster/pylibs/pugscene.py:1354 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1408 PortMaster/pylibs/pugscene.py:1409 -#: PortMaster/pylibs/pugscene.py:1542 PortMaster/pylibs/pugscene.py:1800 -#: PortMaster/pylibs/pugscene.py:1824 PortMaster/pylibs/pugscene.py:1826 -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1948 PortMaster/pylibs/pugscene.py:1950 -msgid "Back" -msgstr "返回" - -#: PortMaster/pylibs/pugscene.py:810 -msgid "Are you sure you want to restore PortMaster?\n\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "您确定需要还原PortMaster吗?\n\n" -"该操作需要安装一个特殊配置的“移植作品”文件。" - -#: PortMaster/pylibs/pugscene.py:820 -msgid "A special port has been installed called \"Restore PortMaster.sh\"\n\n" -"Run this port to restore your PortMaster installation." -msgstr "特殊“移植作品”已安装,名为“Restore PortMaster.sh”\n\n" -"运行该文件以还原PortMaster。" - -#: PortMaster/pylibs/pugscene.py:823 -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "PortMaster将退出以运行还原程序。" - -#: PortMaster/pylibs/pugscene.py:830 -msgid "Are you sure you want to change to the alpha release channel?\n\n" -"PortMaster will upgrade or downgrade accordingly.\n\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "您确定要切换到Alpha通道吗?\n" -"PortMaster会按需升级或降级。\n" -"【现有设置会损坏,后果自负!!】" - -#: PortMaster/pylibs/pugscene.py:857 -#, python-brace-format -msgid "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "您确定要把发布通道从 {current_channel} 改为 {new_channel}?\n\n" -"PortMaster会按需升级或者降级。" - -#: PortMaster/pylibs/pugscene.py:925 -msgid "Are you sure you want to update your gamelist.xml?\n\n" -"This will override any scraped artwork or port information and will require a 50+ MB download." -msgstr "您确定希望更新gamelist.xml文件吗?\n" -"将覆盖现有所有移植作品的封面和其他信息,且需要下载50MB以上的数据。" - -#: PortMaster/pylibs/pugscene.py:946 -#, python-brace-format -msgid "Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "您确定希望将移植作品从{from_mode} 移动到{to_mode} ?" - -#: PortMaster/pylibs/pugscene.py:965 -msgid "SDCARD 1" -msgstr "SD卡1" - -#: PortMaster/pylibs/pugscene.py:966 -msgid "SDCARD 2" -msgstr "SD卡2" - -#: PortMaster/pylibs/pugscene.py:970 -#, python-brace-format -msgid "Are you sure you want to manage and install ports on {to_loc}?\n\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "您确定希望在{to_loc} 目录管理安装移植作品吗?\n" -"已经安装的移植作品不会被移动。\n" -"PortMaster需要重启以适用更改。" - -#: PortMaster/pylibs/pugscene.py:1034 -msgid "Deleting Runtimes:" -msgstr "删除运行时:" - -#: PortMaster/pylibs/pugscene.py:1059 -msgid "Source Manager" -msgstr "源管理器" - -#: PortMaster/pylibs/pugscene.py:1101 -msgid "Download All" -msgstr "下载全部" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Used" -msgstr "已用" - -#: PortMaster/pylibs/pugscene.py:1146 -msgid "Not Used" -msgstr "未使用" - -#: PortMaster/pylibs/pugscene.py:1161 -msgid "Check" -msgstr "检查" - -#: PortMaster/pylibs/pugscene.py:1161 PortMaster/pylibs/pugscene.py:1855 -#: PortMaster/pylibs/pugscene.py:1948 -msgid "Uninstall" -msgstr "卸载" - -#: PortMaster/pylibs/pugscene.py:1164 PortMaster/pylibs/pugscene.py:1858 -#: PortMaster/pylibs/pugscene.py:1950 -msgid "Install" -msgstr "安装" - -#: PortMaster/pylibs/pugscene.py:1179 -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "您确定要下载并验证所有运行时吗?" - -#: PortMaster/pylibs/pugscene.py:1221 -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "删除运行时 {runtime}" - -#: PortMaster/pylibs/pugscene.py:1256 -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "{theme_name} (已选)" - -#: PortMaster/pylibs/pugscene.py:1270 PortMaster/pylibs/pugscene.py:1355 -#: PortMaster/pylibs/pugscene.py:1409 PortMaster/pylibs/pugscene.py:1473 -#: PortMaster/pylibs/pugscene.py:1481 PortMaster/pylibs/pugscene.py:1542 -msgid "Select" -msgstr "选择" - -#: PortMaster/pylibs/pugscene.py:1275 -msgid "Download" -msgstr "下载" - -#: PortMaster/pylibs/pugscene.py:1294 -msgid "Do you want to change theme?\n\n" -"You will have to restart for it to take effect." -msgstr "您确定想更换主题吗?\n\n" -"需要重启程序以适用更改。" - -#: PortMaster/pylibs/pugscene.py:1335 -msgid "Select Colour Scheme" -msgstr "选择配色方案" - -#: PortMaster/pylibs/pugscene.py:1349 -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "{item_name} (已选)" - -#: PortMaster/pylibs/pugscene.py:1372 -msgid "Do you want to change the themes color scheme?\n\n" -"You will have to restart for it to take affect." -msgstr "您确定想更改主题颜色方案吗?\n\n" -"需要重启程序以适用更改。" - -#: PortMaster/pylibs/pugscene.py:1393 -msgid "Language Select" -msgstr "选择语言" - -#: PortMaster/pylibs/pugscene.py:1403 -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "{lang_name} (已选)" - -#: PortMaster/pylibs/pugscene.py:1426 -msgid "Do you want to change language?\n\n" -"You will have to restart for it to take affect." -msgstr "您想要更改语言吗?\n\n" -"需要重启程序以适用更改。" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Delete" -msgstr "删除" - -#: PortMaster/pylibs/pugscene.py:1473 -msgid "Upper Case" -msgstr "大写" - -#: PortMaster/pylibs/pugscene.py:1473 PortMaster/pylibs/pugscene.py:1481 -msgid "Space" -msgstr "空格" - -#: PortMaster/pylibs/pugscene.py:1481 -msgid "Lower Case" -msgstr "小写" - -#: PortMaster/pylibs/pugscene.py:1559 -msgid "No featured ports found, internet connection is required." -msgstr "未找到精选移植作品,需要网络连接。" - -#: PortMaster/pylibs/pugscene.py:1603 PortMaster/pylibs/pugscene.py:1633 -msgid "NO PORTS" -msgstr "无移植作品" - -#: PortMaster/pylibs/pugscene.py:1607 PortMaster/pylibs/pugscene.py:1640 -msgid "** NO PORTS FOUND **" -msgstr "** 没有发现移植作品 **" - -#: PortMaster/pylibs/pugscene.py:1609 PortMaster/pylibs/pugscene.py:1643 -msgid "Download ports first." -msgstr "请先下载移植作品。" - -#: PortMaster/pylibs/pugscene.py:1645 -msgid "Try removing some filters." -msgstr "试试去除一些筛选条件。" - -#: PortMaster/pylibs/pugscene.py:1800 PortMaster/pylibs/pugscene.py:1824 -#: PortMaster/pylibs/pugscene.py:1826 PortMaster/pylibs/pugscene.py:1915 -#: PortMaster/pylibs/pugscene.py:1920 -msgid "Show Info" -msgstr "显示详情" - -#: PortMaster/pylibs/pugscene.py:1805 -msgid "Ports List" -msgstr "移植作品列表" - -#: PortMaster/pylibs/pugscene.py:1824 -msgid "Filters" -msgstr "筛选条件" - -#: PortMaster/pylibs/pugscene.py:1832 -msgid "Port Info Popup" -msgstr "移植作品信息弹出" - -#: PortMaster/pylibs/pugscene.py:1843 PortMaster/pylibs/pugscene.py:1846 -msgid "Hide Info" -msgstr "隐藏信息" - -#: PortMaster/pylibs/pugscene.py:1855 PortMaster/pylibs/pugscene.py:1948 -msgid "Reinstall" -msgstr "重新安装" - -#: PortMaster/pylibs/pugscene.py:1882 -msgid "Port Info" -msgstr "移植作品信息" - -#: PortMaster/pylibs/pugscene.py:1970 -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "您确定要卸载 {port_name} 吗?" - -#: PortMaster/pylibs/pugscene.py:2014 -msgid "Filters Scene" -msgstr "筛选场景" - -#: PortMaster/pylibs/pugscene.py:2043 -msgid "Alphabetical" -msgstr "按字母顺序" - -#: PortMaster/pylibs/pugscene.py:2044 -msgid "Recently Added" -msgstr "最近添加" - -#: PortMaster/pylibs/pugscene.py:2045 -msgid "Recently Updated" -msgstr "最近更新" - -#: PortMaster/pylibs/pugscene.py:2046 -msgid "Total Downloads" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2049 -msgid "Action" -msgstr "动作" - -#: PortMaster/pylibs/pugscene.py:2050 -msgid "Adventure" -msgstr "冒险" - -#: PortMaster/pylibs/pugscene.py:2051 -msgid "Arcade" -msgstr "街机" - -#: PortMaster/pylibs/pugscene.py:2052 -msgid "Casino/Card" -msgstr "赌博/卡牌" - -#: PortMaster/pylibs/pugscene.py:2053 -msgid "First Person Shooter" -msgstr "FPS" - -#: PortMaster/pylibs/pugscene.py:2054 -msgid "Platformer" -msgstr "平台跳跃" - -#: PortMaster/pylibs/pugscene.py:2055 -msgid "Puzzle" -msgstr "解谜" - -#: PortMaster/pylibs/pugscene.py:2056 -msgid "Racing" -msgstr "竞速" - -#: PortMaster/pylibs/pugscene.py:2057 -msgid "Rhythm" -msgstr "节奏" - -#: PortMaster/pylibs/pugscene.py:2058 -msgid "Role Playing Game" -msgstr "RPG" - -#: PortMaster/pylibs/pugscene.py:2059 -msgid "Simulation" -msgstr "模拟" - -#: PortMaster/pylibs/pugscene.py:2060 -msgid "Sports" -msgstr "运动" - -#: PortMaster/pylibs/pugscene.py:2061 -msgid "Strategy" -msgstr "策略" - -#: PortMaster/pylibs/pugscene.py:2062 -msgid "Visual Novel" -msgstr "视觉小说" - -#: PortMaster/pylibs/pugscene.py:2063 -msgid "Other" -msgstr "其他" - -#: PortMaster/pylibs/pugscene.py:2067 -msgid "Files Required" -msgstr "需要文件" - -#: PortMaster/pylibs/pugscene.py:2068 -msgid "Experimental" -msgstr "实验性" - -#: PortMaster/pylibs/pugscene.py:2071 -msgid "Broken Ports" -msgstr "损坏的移植作品" - -#: PortMaster/pylibs/pugscene.py:2074 -msgid "Free game, all files included." -msgstr "免费游戏,包含所有文件。" - -#: PortMaster/pylibs/pugscene.py:2075 -msgid "Demo files included." -msgstr "包含试玩游戏文件。" - -#: PortMaster/pylibs/pugscene.py:2076 -msgid "Free external assets needed." -msgstr "需要可免费下载的外部资源。" - -#: PortMaster/pylibs/pugscene.py:2077 -msgid "Paid external assets needed." -msgstr "需要付费下载的外部资源。" - -#: PortMaster/pylibs/pugscene.py:2080 PortMaster/pylibs/pugscene.py:2081 -#: PortMaster/pylibs/pugscene.py:2082 PortMaster/pylibs/pugscene.py:2083 -#: PortMaster/pylibs/pugscene.py:2084 PortMaster/pylibs/pugscene.py:2085 -#: PortMaster/pylibs/pugscene.py:2086 -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "运行时 {runtime_name}" - -#: PortMaster/pylibs/pugscene.py:2089 -msgid "ARM 32bit" -msgstr "ARM 32位" - -#: PortMaster/pylibs/pugscene.py:2090 -msgid "ARM 64bit" -msgstr "ARM 64位" - -#: PortMaster/pylibs/pugscene.py:2091 -msgid "x86 64bit" -msgstr "x86 64位" - -#: PortMaster/pylibs/pugscene.py:2128 -msgid "Ascending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2130 -msgid "Descending" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2138 -msgid "Sort:" -msgstr "排序:" - -#: PortMaster/pylibs/pugscene.py:2153 -msgid "Clear Filters" -msgstr "重置筛选" - -#: PortMaster/pylibs/pugscene.py:2177 -msgid "Genres:" -msgstr "类型:" - -#: PortMaster/pylibs/pugscene.py:2207 -msgid "Attributes:" -msgstr "属性" - -#: PortMaster/pylibs/pugscene.py:2236 -msgid "Architecture:" -msgstr "" - -#: PortMaster/pylibs/pugscene.py:2265 -msgid "Availability:" -msgstr "可用性:" - -#: PortMaster/pylibs/pugscene.py:2294 -msgid "Porters:" -msgstr "移植人员:" - -#: PortMaster/pylibs/pugscene.py:2363 -msgid "Messages" -msgstr "消息" - -#: PortMaster/pylibs/pugscene.py:2398 -msgid "Are you sure you want to cancel?" -msgstr "您确定要取消吗?" - diff --git a/PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/themes.po b/PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/themes.po deleted file mode 100644 index b5b4530d..00000000 --- a/PortMaster/pylibs/locales/zh_CN/LC_MESSAGES/themes.po +++ /dev/null @@ -1,194 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: portmaster\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: 2026-03-02 06:58\n" -"Last-Translator: \n" -"Language-Team: Chinese Simplified\n" -"Language: zh_CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" -"X-Crowdin-Project: portmaster\n" -"X-Crowdin-Project-ID: 610497\n" -"X-Crowdin-Language: zh-CN\n" -"X-Crowdin-File: themes.pot\n" -"X-Crowdin-File-ID: 3\n" - -#: Fallout.theme.zip/theme.json:550, Stardew.theme.zip/theme.json:374, ff_VII.theme.zip/theme.json:257, Doom.theme.zip/theme.json:337 -msgid "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "类别: {port_info.genres} 下载大小: {port_info.download_size}{if:port_info.runtime} 运行时: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Fallout.theme.zip/theme.json:611, Stardew.theme.zip/theme.json:475, ff_VII.theme.zip/theme.json:363, Doom.theme.zip/theme.json:444 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports} 可用移植作品数: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"筛选条件: {ports_list.filters}{endif}" - -#: Fallout.theme.zip/theme.json:684, Stardew.theme.zip/theme.json:541, Zelda.theme.zip/theme.json:485, ff_VII.theme.zip/theme.json:435, Doom.theme.zip/theme.json:522 -#: default_theme/theme.json:418 -msgid "Free Space: {system.free_space}\n\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"Instruction: {port_info.instructions}" -msgstr "存储空间剩余: {system.free_space}\n\n" -"移植人员: {port_info.porter}\n" -"类型: {port_info.genres}\n" -"下载大小: {port_info.download_size}{if:port_info.runtime}\n" -"运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n" -"安装说明: {port_info.instructions}" - -#: Fallout.theme.zip/theme.json:726 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total\n" -"Time: {system.time_24hr}\n" -"Battery: {system.battery_level}" -msgstr "PM版本: {system.portmaster_version}\n" -"HM版本: {system.harbourmaster_version}\n" -"存储空间剩余: {system.free_space} ,总容量 {system.total_space} \n" -"时间: {system.time_24hr}\n" -"电量: {system.battery_level}" - -#: Fallout.theme.zip/theme.json:762, Stardew.theme.zip/theme.json:651, Zelda.theme.zip/theme.json:551, ff_VII.theme.zip/theme.json:523, Doom.theme.zip/theme.json:644 -#: default_theme/theme.json:504, default_theme/theme.json:630 -msgid "IP: {system.ip_address}" -msgstr "IP地址: {system.ip_address}" - -#: Fallout.theme.zip/theme.json:831, Stardew.theme.zip/theme.json:739, Zelda.theme.zip/theme.json:633, ff_VII.theme.zip/theme.json:612, Doom.theme.zip/theme.json:741 -#: default_theme/theme.json:555 -msgid "Creator: {theme_info.creator}\n" -"Status: {theme_info.status}" -msgstr "作者: {theme_info.creator}\n" -"状态: {theme_info.status}" - -#: Stardew.theme.zip/theme.json:605, ff_VII.theme.zip/theme.json:480, default_theme/theme.json:445 -msgid "Time: {system.time_24hr}\n" -"CFW: {system.cfw_name} ({system.cfw_version})\n" -"Device: {system.device_name}\n" -"Battery: {system.battery_level}" -msgstr "时间: {system.time_24hr}\n" -"系统: {system.cfw_name} ({system.cfw_version})\n" -"设备: {system.device_name}\n" -"电量: {system.battery_level}" - -#: Stardew.theme.zip/theme.json:613 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free" -msgstr "PM版本: {system.portmaster_version}\n" -"HM版本: {system.harbourmaster_version}\n" -"存储空间剩余: {system.free_space}" - -#: Zelda.theme.zip/theme.json:326 -msgid "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" -msgstr "筛选条件: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}" - -#: Zelda.theme.zip/theme.json:376 -msgid "Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "类型: {port_info.genres}\n" -"下载大小: {port_info.download_size}{if:port_info.runtime}\n" -"运行时: {port_info.runtime} ({port_info.runtime_status}){endif}" - -#: Zelda.theme.zip/theme.json:522 -msgid "Time: {system.time_24hr} Battery: {system.battery_level}" -msgstr "时间: {system.time_24hr} 电量: {system.battery_level}" - -#: ff_VII.theme.zip/theme.json:488, default_theme/theme.json:453 -msgid "PM: {system.portmaster_version}\n" -"HM: {system.harbourmaster_version}\n" -"Space: {system.free_space} free / {system.total_space} total" -msgstr "PM版本: {system.portmaster_version}\n" -"HM版本: {system.harbourmaster_version}\n" -"存储空间剩余: {system.free_space} ,总容量{system.total_space}" - -#: hiviz.theme.zip/theme.json:267 -msgid "• Porter: {port_info.porter}\n" -"• Genres: {port_info.genres}\n" -"• Description: {port_info.description}" -msgstr "• 移植人员: {port_info.porter}\n" -"• 作品类型: {port_info.genres}\n" -"• 描述: {port_info.description}" - -#: hiviz.theme.zip/theme.json:327 -msgid "• Genres: {port_info.genres}\n" -"• Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• Description: {port_info.description}\n" -"• Instructions:\n" -"{port_info.instructions}" -msgstr "• 作品类型: {port_info.genres}\n" -"• 下载大小: {port_info.download_size}{if:port_info.runtime}\n" -"• 运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"• 描述: {port_info.description}\n" -"• 安装说明:\n" -"{port_info.instructions}" - -#: hiviz.theme.zip/theme.json:375 -msgid "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}" -msgstr "PM版本: {system.portmaster_version}, HM版本: {system.harbourmaster_version}" - -#: hiviz.theme.zip/theme.json:463 -msgid "• Creator: {theme_info.creator}\n" -"• Status: {theme_info.status}\n" -"• Description:\n" -"{theme_info.description}" -msgstr "• 创作者: {theme_info.creator}\n" -"• 状态: {theme_info.status}\n" -"• 描述:\n" -"{theme_info.description}" - -#: hiviz.theme.zip/theme.json:526, default_theme/theme.json:621 -msgid "Name: {runtime_info.name}\n" -"Status: {runtime_info.status}\n" -"Ports: {runtime_info.ports}\n" -"Download Size: {runtime_info.download_size}\n" -"Install Size: {runtime_info.disk_size}" -msgstr "运行时名称: {runtime_info.name}\n" -"状态: {runtime_info.status}\n" -"移植作品: {runtime_info.ports}\n" -"下载大小: {runtime_info.download_size}\n" -"安装大小: {runtime_info.disk_size}" - -#: Doom.theme.zip/theme.json:586 -msgid "Time: {system.time_24hr}\n" -"Battery: {system.battery_level}\n" -"Space: {system.free_space} free" -msgstr "时间: {system.time_24hr}\n" -"电量: {system.battery_level}\n" -"存储空间剩余: {system.free_space}" - -#: basic_theme.theme.zip/theme.json:77 -msgid "Title: {port_info.title}\n" -"Porter: {port_info.porter}\n" -"Genres: {port_info.genres}\n" -"Download Size: {port_info.download_size}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"Description: {port_info.description}" -msgstr "标题: {port_info.title}\n" -"移植人员: {port_info.porter}\n" -"类型: {port_info.genres}\n" -"下载大小: {port_info.download_size}{if:port_info.runtime}\n" -"运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n" -"描述: {port_info.description}" - -#: default_theme/theme.json:319 -msgid "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"Filters: {ports_list.filters}{endif}" -msgstr "{if:!ports_list.total_ports::ports_list.filter_ports} 可用移植作品数: {ports_list.filter_ports} / {ports_list.total_ports} \n" -"筛选条件: {ports_list.filters}{endif}" - -#: default_theme/theme.json:347, default_theme/theme.json:846 -msgid "Genres: {port_info.genres}\n" -"{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\n" -"Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}" -msgstr "类型: {port_info.genres}\n" -"{if:port_info.install_size} 安装大小: {port_info.install_size}{else} 下载大小:{port_info.download_size}{endif}{if:port_info.runtime}\n" -"运行时: {port_info.runtime} ({port_info.runtime_status}){endif}" - diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index dcb49729..8b7a5afd 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -19,7 +19,7 @@ from loguru import logger from pugwash.theme import ThemeEngine from pugwash.scenes import StringFormatter, TempMenuScene -from gettext import gettext as _ +from harbourmaster.i18n import _ from pugwash import PYLIB_PATH, PORTMASTER_VERSION, PORTMASTER_DEBUG from pugwash.lang import lang_list from pugwash.util import battery_level, get_ip_address, is_process_running diff --git a/PortMaster/pylibs/pugwash/app_callbacks.py b/PortMaster/pylibs/pugwash/app_callbacks.py index 274e62a9..e34030cc 100644 --- a/PortMaster/pylibs/pugwash/app_callbacks.py +++ b/PortMaster/pylibs/pugwash/app_callbacks.py @@ -8,7 +8,7 @@ from pugwash import tasks from loguru import logger from pugwash.scenes import MessageBoxScene, MessageWindowScene -from gettext import gettext as _ +from harbourmaster.i18n import _ from pugwash.util import format_progress diff --git a/PortMaster/pylibs/pugwash/app_commands.py b/PortMaster/pylibs/pugwash/app_commands.py index 6672a054..4696fce8 100644 --- a/PortMaster/pylibs/pugwash/app_commands.py +++ b/PortMaster/pylibs/pugwash/app_commands.py @@ -6,7 +6,7 @@ import zipfile import harbourmaster from loguru import logger -from gettext import gettext as _ +from harbourmaster.i18n import _ class CommandsMixin: diff --git a/PortMaster/pylibs/pugwash/app_fifo.py b/PortMaster/pylibs/pugwash/app_fifo.py index 2a55bbb6..5f73fc1a 100644 --- a/PortMaster/pylibs/pugwash/app_fifo.py +++ b/PortMaster/pylibs/pugwash/app_fifo.py @@ -8,7 +8,7 @@ from pugwash import autoinstall, tasks from loguru import logger from pugwash.scenes import DialogSelectionList -from gettext import gettext as _ +from harbourmaster.i18n import _ class FifoControlMixin: diff --git a/PortMaster/pylibs/pugwash/app_ports.py b/PortMaster/pylibs/pugwash/app_ports.py index a4405369..efe673c9 100644 --- a/PortMaster/pylibs/pugwash/app_ports.py +++ b/PortMaster/pylibs/pugwash/app_ports.py @@ -4,7 +4,7 @@ import os import harbourmaster -from gettext import gettext as _ +from harbourmaster.i18n import _ class PortInfoMixin: diff --git a/PortMaster/pylibs/pugwash/autoinstall.py b/PortMaster/pylibs/pugwash/autoinstall.py index 199eef31..827f2baa 100644 --- a/PortMaster/pylibs/pugwash/autoinstall.py +++ b/PortMaster/pylibs/pugwash/autoinstall.py @@ -11,7 +11,7 @@ import zipfile from pathlib import Path from loguru import logger -from gettext import gettext as _ +from harbourmaster.i18n import _ class AutoinstallResult: diff --git a/PortMaster/pylibs/pugwash/lang.py b/PortMaster/pylibs/pugwash/lang.py index ed640422..cd439908 100644 --- a/PortMaster/pylibs/pugwash/lang.py +++ b/PortMaster/pylibs/pugwash/lang.py @@ -1,11 +1,11 @@ # SPDX-License-Identifier: MIT # -# Translations: picks the language and sets up gettext when imported. +# Translations: picks the language and loads it when imported (harbourmaster.i18n). -import gettext import os import harbourmaster -from gettext import gettext as _ +from harbourmaster import i18n +from harbourmaster.i18n import _ from pugwash import PYLIB_PATH @@ -43,7 +43,7 @@ def check_lang(lang): """ - Check if `LANG_DIR / lang / "LC_MESSAGES"` exists + The language PortMaster has a translation file for (`LANG_DIR / ".json"`), or None. We simplify the language by removing any encoding eg: `en_AU.utf8` to `en_AU` We then simplify the language by removing any sub-language: `en_AU` to `en` @@ -53,23 +53,20 @@ def check_lang(lang): if lang is None: return None - temp = LANG_DIR / lang / "LC_MESSAGES" - if temp.is_dir(): + if i18n.language_file(LANG_DIR, lang).is_file(): return lang if '.' in lang: lang = lang.rsplit('.', 1)[0] - temp = LANG_DIR / lang / "LC_MESSAGES" - if temp.is_dir(): + if i18n.language_file(LANG_DIR, lang).is_file(): return lang if '_' in lang: lang = lang.split('_', 1)[0] for lang_opt in ALL_LANGUAGES: - temp = LANG_DIR / lang_opt / "LC_MESSAGES" - if temp.is_dir() and lang_opt.startswith(lang): + if i18n.language_file(LANG_DIR, lang_opt).is_file() and lang_opt.startswith(lang): return lang_opt return None @@ -130,12 +127,10 @@ def lang_list(): languages = { lang_id: lang_name for lang_id, lang_name in sorted(ALL_LANGUAGES.items(), key=lambda item: (item[1])) - if (LANG_DIR / lang_id).is_dir()} + if i18n.language_file(LANG_DIR, lang_id).is_file()} return languages load_lang() -gettext.bindtextdomain('messages', str(LANG_DIR)) -gettext.bindtextdomain('themes', str(LANG_DIR)) -gettext.textdomain('messages') +i18n.load_language(LANG_DIR, CURRENT_LANG) diff --git a/PortMaster/pylibs/pugwash/main.py b/PortMaster/pylibs/pugwash/main.py index 89b291d7..06e1b1dc 100644 --- a/PortMaster/pylibs/pugwash/main.py +++ b/PortMaster/pylibs/pugwash/main.py @@ -8,7 +8,7 @@ from loguru import logger from pugwash.scenes import BlankScene from harbourmaster import HarbourMaster, make_temp_directory -from gettext import gettext as _ +from harbourmaster.i18n import _ from pugwash import PORTMASTER_DEBUG from pugwash.app import PortMasterGUI from pugwash.autoinstall import games_list_refresh_text diff --git a/PortMaster/pylibs/pugwash/scenes/base.py b/PortMaster/pylibs/pugwash/scenes/base.py index e3eeed4b..3489529a 100644 --- a/PortMaster/pylibs/pugwash/scenes/base.py +++ b/PortMaster/pylibs/pugwash/scenes/base.py @@ -3,7 +3,7 @@ # StringFormatter (the {tag} template text) and BaseScene, which loads a theme section into regions. import functools -import gettext +from harbourmaster.i18n import theme_text from pugwash import sdl from loguru import logger @@ -83,7 +83,7 @@ def format_string(self, text, keys_used=None): stack = [True] # TRANSLATIONS :D - text = gettext.dgettext('themes', text) + text = theme_text(text) for before, key in self.parse_text(text): if stack[-1] and before != '': diff --git a/PortMaster/pylibs/pugwash/scenes/dialogs.py b/PortMaster/pylibs/pugwash/scenes/dialogs.py index 099fec33..99ff2aad 100644 --- a/PortMaster/pylibs/pugwash/scenes/dialogs.py +++ b/PortMaster/pylibs/pugwash/scenes/dialogs.py @@ -2,7 +2,7 @@ # # Message window, message box and selection list dialogs. -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/scenes/featured.py b/PortMaster/pylibs/pugwash/scenes/featured.py index 6f946b7d..836bc980 100644 --- a/PortMaster/pylibs/pugwash/scenes/featured.py +++ b/PortMaster/pylibs/pugwash/scenes/featured.py @@ -2,7 +2,7 @@ # # Featured ports navigation. -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene from .ports import FeaturedPortsScene diff --git a/PortMaster/pylibs/pugwash/scenes/filters.py b/PortMaster/pylibs/pugwash/scenes/filters.py index d9870c84..b7934268 100644 --- a/PortMaster/pylibs/pugwash/scenes/filters.py +++ b/PortMaster/pylibs/pugwash/scenes/filters.py @@ -4,7 +4,7 @@ import harbourmaster from loguru import logger -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/scenes/keyboard.py b/PortMaster/pylibs/pugwash/scenes/keyboard.py index f3c170f4..2738dd60 100644 --- a/PortMaster/pylibs/pugwash/scenes/keyboard.py +++ b/PortMaster/pylibs/pugwash/scenes/keyboard.py @@ -2,7 +2,7 @@ # # The on screen keyboard. -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/scenes/language.py b/PortMaster/pylibs/pugwash/scenes/language.py index 6dc678ad..caac92cb 100644 --- a/PortMaster/pylibs/pugwash/scenes/language.py +++ b/PortMaster/pylibs/pugwash/scenes/language.py @@ -4,7 +4,7 @@ import os import harbourmaster -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/scenes/main_menu.py b/PortMaster/pylibs/pugwash/scenes/main_menu.py index 60c627b3..2a760721 100644 --- a/PortMaster/pylibs/pugwash/scenes/main_menu.py +++ b/PortMaster/pylibs/pugwash/scenes/main_menu.py @@ -2,7 +2,7 @@ # # The main menu. -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene from .featured import FeaturedPortsNavigationScene from .options import OptionScene diff --git a/PortMaster/pylibs/pugwash/scenes/options.py b/PortMaster/pylibs/pugwash/scenes/options.py index 47c7ac95..cc319dd8 100644 --- a/PortMaster/pylibs/pugwash/scenes/options.py +++ b/PortMaster/pylibs/pugwash/scenes/options.py @@ -7,7 +7,7 @@ import harbourmaster from pathlib import Path from loguru import logger -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene from .keyboard import OnScreenKeyboard from .language import LanguageScene diff --git a/PortMaster/pylibs/pugwash/scenes/port_info.py b/PortMaster/pylibs/pugwash/scenes/port_info.py index f6240436..28e98468 100644 --- a/PortMaster/pylibs/pugwash/scenes/port_info.py +++ b/PortMaster/pylibs/pugwash/scenes/port_info.py @@ -3,7 +3,7 @@ # Port information screens. from loguru import logger -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/scenes/ports.py b/PortMaster/pylibs/pugwash/scenes/ports.py index 8392a7c2..d6b952f7 100644 --- a/PortMaster/pylibs/pugwash/scenes/ports.py +++ b/PortMaster/pylibs/pugwash/scenes/ports.py @@ -3,7 +3,7 @@ # Ports lists. from loguru import logger -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene from .filters import FiltersScene from .port_info import PortInfoScene diff --git a/PortMaster/pylibs/pugwash/scenes/runtimes.py b/PortMaster/pylibs/pugwash/scenes/runtimes.py index aed66e30..93677f3c 100644 --- a/PortMaster/pylibs/pugwash/scenes/runtimes.py +++ b/PortMaster/pylibs/pugwash/scenes/runtimes.py @@ -3,7 +3,7 @@ # The runtime manager. import harbourmaster -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/scenes/startup.py b/PortMaster/pylibs/pugwash/scenes/startup.py index 1291cde8..63a8fcce 100644 --- a/PortMaster/pylibs/pugwash/scenes/startup.py +++ b/PortMaster/pylibs/pugwash/scenes/startup.py @@ -4,7 +4,7 @@ import harbourmaster from pathlib import Path -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene from .main_menu import MainMenuScene diff --git a/PortMaster/pylibs/pugwash/scenes/themes.py b/PortMaster/pylibs/pugwash/scenes/themes.py index a69ca53d..98d11185 100644 --- a/PortMaster/pylibs/pugwash/scenes/themes.py +++ b/PortMaster/pylibs/pugwash/scenes/themes.py @@ -3,7 +3,7 @@ # Theme and colour scheme selection. import harbourmaster -from gettext import gettext as _ +from harbourmaster.i18n import _ from .base import BaseScene diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 4c8aff52..0cd4c859 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -4,7 +4,6 @@ import copy import datetime import functools -import gettext import json import zipfile @@ -13,6 +12,8 @@ import harbourmaster import harbourmaster.source +from harbourmaster import i18n +from harbourmaster.i18n import _ from harbourmaster.util import net from pugwash import sdl @@ -21,9 +22,6 @@ from pugwash import PYLIB_PATH -_ = gettext.gettext - - ## TODO: make this all a class, and maybe make it less janky, maybe... def extract_requirements(text, strict=False): @@ -142,6 +140,9 @@ def theme_load(gui, theme_file, color_scheme=None): capabilities = harbourmaster.device_info()["capabilities"] + ## The theme's own translations of its text ({lang: {text: translation}}), if it has any. + i18n.set_theme_translations(theme_data.get("#translations", {})) + base_data = {} elements = {} sections = {} diff --git a/PortMaster/pylibs/pugwash/update.py b/PortMaster/pylibs/pugwash/update.py index 0d8d0acc..a2fa48b4 100644 --- a/PortMaster/pylibs/pugwash/update.py +++ b/PortMaster/pylibs/pugwash/update.py @@ -7,7 +7,7 @@ import harbourmaster from loguru import logger from harbourmaster import HarbourMaster -from gettext import gettext as _ +from harbourmaster.i18n import _ from pugwash import PORTMASTER_VERSION, PORTMASTER_RELEASE_CHANNEL, PORTMASTER_MIN_VERSION, PORTMASTER_RELEASE_URL, PORTMASTER_RELEASE_VALUES, PORTMASTER_UPDATE_FREQUENCY diff --git a/do_i18n.sh b/do_i18n.sh deleted file mode 100755 index be3b2b13..00000000 --- a/do_i18n.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -# -# SPDX-License-Identifier: MIT -# - -# Directory where .pot file is located -POT_DIR="PortMaster/pylibs/locales" -POT_FILES=("messages" "themes") -# Languages registered but not yet translated -NOT_WORKING=("da_DK" "fi_FI" "eo_UY") - -for POT_FILE in "${POT_FILES[@]}"; do - if [[ "$POT_FILE" == "messages" ]]; then - echo "Extracting strings ${POT_FILE}" - xgettext -v -o "${POT_DIR}/${POT_FILE}.pot" -L Python PortMaster/pugwash $(find PortMaster/pylibs/harbourmaster PortMaster/pylibs/pugwash -name '*.py' | sort) - - elif [[ "$POT_FILE" == "themes" ]]; then - echo "Extracting strings ${POT_FILE}" - python3 theme_msgfmt.py - # wget -O "${POT_DIR}/${POT_FILE}.pot" "https://github.com/PortsMaster/PortMaster-Themes/releases/latest/download/themes.pot" - fi -done - -crowdin upload - -for POT_FILE in "${POT_FILES[@]}"; do - # pygettext.py -d libharbourmaster -o pylibs/locales/harbourmaster.pot pylibs/harbourmaster - # pygettext.py -d harbourmaster -o pylibs/locales/harbourmaster.pot harbourmaster - - # Iterate over subdirectories (languages) in the LC_MESSAGES folder - for lang_dir in $POT_DIR/* ; do - LANG_CODE=$(basename "$lang_dir") - if [[ "${LANG_CODE}" != "." ]] && [[ "${LANG_CODE}" != ".." ]] && [ -d "$lang_dir" ]; then - - PO_FILE="$lang_dir/LC_MESSAGES/${POT_FILE}.pot" - MO_FILE="$lang_dir/LC_MESSAGES/${POT_FILE}.mo" - - # Check if the .po file exists - if [ -f "$PO_FILE" ]; then - mkdir -p "$lang_dir/LC_MESSAGES" - cp -v "${POT_DIR}/${POT_FILE}.pot" "${PO_FILE}" - fi - fi - done -done - -crowdin download - -for BROKEN in "${NOT_WORKING[@]}"; do - rm -fR "$POT_DIR/$BROKEN" -done - -for lang_dir in $POT_DIR/* ; do - LANG_CODE=$(basename "$lang_dir") - if [[ "${LANG_CODE}" != "." ]] && [[ "${LANG_CODE}" != ".." ]] && [ -d "$lang_dir" ]; then - echo "${LANG_CODE}:" - - for POT_FILE in "${POT_FILES[@]}"; do - - LANG_POT_FILE="$lang_dir/LC_MESSAGES/${POT_FILE}.pot" - LANG_PO_FILE="$lang_dir/LC_MESSAGES/${POT_FILE}.po" - LANG_MO_FILE="$lang_dir/LC_MESSAGES/${POT_FILE}.mo" - - if [ -f "$LANG_POT_FILE" ]; then - mv -fv "$LANG_POT_FILE" "$LANG_PO_FILE" - fi - - # Compile .po file into .mo file - printf "msgfmt: " - msgfmt -v -o "${LANG_MO_FILE}" "${LANG_PO_FILE}" - - echo "" - done - fi -done diff --git a/do_release.sh b/do_release.sh index 8b07c3dc..1813110a 100755 --- a/do_release.sh +++ b/do_release.sh @@ -95,7 +95,7 @@ if [ -n "$IS_TEST" ]; then STAMP_CHANNEL="${PM_TEST_CHANNEL:-beta}" fi -for tool in zip msgfmt python3; do +for tool in zip python3; do if ! command -v "$tool" > /dev/null; then echo "do_release.sh needs $tool." exit 1 @@ -109,14 +109,13 @@ trap 'rm -rf "$BUILD_DIR"' EXIT ## -- A clean copy of the sources ------------------------------------------------- ## From a checkout: what git knows about (tracked files as they are on disk, and new -## files that aren't ignored), plus Crowdin's .pot files. Logs, config/, __pycache__, +## files that aren't ignored). Logs, config/, __pycache__, ## ports installed while testing, ... never get in. Without git (the release ## workflow deletes .git): the tree as it is. cd "$REPO_DIR" if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then { git ls-files -z --cached --others --exclude-standard -- PortMaster tools tests pytest.ini - find PortMaster/pylibs/locales -name '*.pot' -print0 } | while IFS= read -r -d '' file_name; do ## (tracked files deleted on disk are still listed) [ -f "$file_name" ] && printf '%s\0' "$file_name" @@ -131,22 +130,6 @@ cd "$BUILD_DIR" python3 tools/pm_release.py "$STAMP_CHANNEL" "$VERSION" echo "$VERSION" > PortMaster/version -## -- Translations ------------------------------------------------------------------ -for lang_dir in PortMaster/pylibs/locales/*/; do - for pot_file in messages themes; do - messages_dir="${lang_dir}LC_MESSAGES" - - ## Crowdin downloads them as .pot - if [ -f "$messages_dir/$pot_file.pot" ]; then - mv -f "$messages_dir/$pot_file.pot" "$messages_dir/$pot_file.po" - fi - - if [ -f "$messages_dir/$pot_file.po" ]; then - msgfmt -o "$messages_dir/$pot_file.mo" "$messages_dir/$pot_file.po" - fi - done -done - ## -- The zips ---------------------------------------------------------------------- echo "Creating pylibs.zip" ( diff --git a/tests/data/msgids.txt b/tests/data/msgids.txt deleted file mode 100644 index 2cae1ac0..00000000 --- a/tests/data/msgids.txt +++ /dev/null @@ -1,1028 +0,0 @@ -msgid "" -"\n" -"\n" -"In order to do so:\n" -"MENU -> UTILITIES -> Rescan Rom Directory" -msgstr "" - -msgid "** NO PORT **" -msgstr "" - -msgid "** NO PORTS FOUND **" -msgstr "" - -#, python-brace-format -msgid "- FAILURE: {name}" -msgstr "" - -#, python-brace-format -msgid "- SUCCESS: {name}" -msgstr "" - -msgid "" -"A special port has been installed called \"Restore PortMaster.sh\"\n" -"\n" -"Run this port to restore your PortMaster installation." -msgstr "" - -msgid "ARM 32bit" -msgstr "" - -msgid "ARM 64bit" -msgstr "" - -msgid "Accept" -msgstr "" - -msgid "Action" -msgstr "" - -msgid "Adventure" -msgstr "" - -msgid "All Ports" -msgstr "" - -msgid "All Ports: " -msgstr "" - -msgid "Alphabetical" -msgstr "" - -msgid "Arcade" -msgstr "" - -msgid "Architecture:" -msgstr "" - -msgid "Are you sure you want to cancel?" -msgstr "" - -#, python-brace-format -msgid "" -"Are you sure you want to change the release channel from {current_channel} " -"to {new_channel}?\n" -"\n" -"PortMaster will upgrade or downgrade accordingly." -msgstr "" - -msgid "" -"Are you sure you want to change to the alpha release channel?\n" -"\n" -"PortMaster will upgrade or downgrade accordingly.\n" -"\n" -"THIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED" -msgstr "" - -msgid "Are you sure you want to download and verify all runtimes?" -msgstr "" - -msgid "Are you sure you want to exit PortMaster?" -msgstr "" - -#, python-brace-format -msgid "" -"Are you sure you want to manage and install ports on {to_loc}?\n" -"\n" -"Already installed ports will not be moved.\n" -"PortMaster will restart for this to take effect." -msgstr "" - -#, python-brace-format -msgid "" -"Are you sure you want to move ports from the {from_mode} to the {to_mode}" -msgstr "" - -msgid "" -"Are you sure you want to restore PortMaster?\n" -"\n" -"This will require you to run a specially crafted port that will be installed." -msgstr "" - -#, python-brace-format -msgid "Are you sure you want to uninstall {port_name}?" -msgstr "" - -msgid "" -"Are you sure you want to update your gamelist.xml?\n" -"\n" -"This will override any scraped artwork or port information and will require " -"a 50+ MB download." -msgstr "" - -msgid "Ascending" -msgstr "" - -msgid "Attributes:" -msgstr "" - -msgid "Audio" -msgstr "" - -msgid "Auto" -msgstr "" - -msgid "Auto Update." -msgstr "" - -msgid "Auto-installation" -msgstr "" - -msgid "Availability:" -msgstr "" - -msgid "Back" -msgstr "" - -msgid "Broken" -msgstr "" - -msgid "Broken Ports" -msgstr "" - -msgid "CWTBE Mode: " -msgstr "" - -msgid "Cache out of date." -msgstr "" - -msgid "Cancel" -msgstr "" - -msgid "Casino/Card" -msgstr "" - -msgid "Change release channel of PortMaster to alpha." -msgstr "" - -msgid "Change release channel of PortMaster, either beta or stable." -msgstr "" - -msgid "Check" -msgstr "" - -#, python-brace-format -msgid "Checking {runtime_name}" -msgstr "" - -msgid "Chinese Simplified" -msgstr "" - -msgid "Choose Language" -msgstr "" - -msgid "Clear Filters" -msgstr "" - -#, python-brace-format -msgid "Controller Mode: {controller_mode}" -msgstr "" - -msgid "CrossMix-OS is not supported by PortMaster." -msgstr "" - -msgid "Danish" -msgstr "" - -msgid "Debug Mode: " -msgstr "" - -msgid "Delete" -msgstr "" - -msgid "Delete PortMaster Config" -msgstr "" - -msgid "Delete PortMaster Runtimes" -msgstr "" - -#, python-brace-format -msgid "Deleted runtime {runtime}" -msgstr "" - -msgid "Deleting Runtimes:" -msgstr "" - -msgid "Demo files included." -msgstr "" - -msgid "Descending" -msgstr "" - -msgid "Disabled" -msgstr "" - -msgid "Disclaimer" -msgstr "" - -msgid "" -"Do you want to change language?\n" -"\n" -"You will have to restart for it to take affect." -msgstr "" - -msgid "" -"Do you want to change the themes color scheme?\n" -"\n" -"You will have to restart for it to take affect." -msgstr "" - -msgid "" -"Do you want to change theme?\n" -"\n" -"You will have to restart for it to take effect." -msgstr "" - -#, python-brace-format -msgid "" -"Do you want to reinstall PortMaster?\n" -"\n" -"This will reinstall from the {release_channel} channel to " -"{portmaster_version}." -msgstr "" - -msgid "Done." -msgstr "" - -msgid "Download" -msgstr "" - -msgid "Download All" -msgstr "" - -#, python-brace-format -msgid "Download failed: {err}" -msgstr "" - -msgid "Download ports first." -msgstr "" - -msgid "Download validation failed." -msgstr "" - -msgid "Downloading file." -msgstr "" - -#, python-brace-format -msgid "Downloading gameinfo.xml for {source_name}" -msgstr "" - -#, python-brace-format -msgid "Downloading runtime {runtime}." -msgstr "" - -#, python-brace-format -msgid "Downloading {file_url} - ({total_length_mb})" -msgstr "" - -msgid "Dutch" -msgstr "" - -msgid "Enable Alpha Releases" -msgstr "" - -msgid "Enable debug logging all the time." -msgstr "" - -msgid "Enable gptokeyb2 by default." -msgstr "" - -msgid "Enable or Disable background music in PortMaster." -msgstr "" - -msgid "Enable or Disable soundfx in PortMaster." -msgstr "" - -msgid "Enabled" -msgstr "" - -msgid "English" -msgstr "" - -msgid "Enter" -msgstr "" - -#, python-brace-format -msgid "" -"Error uninstalling {port_name}\n" -"\n" -"{error}" -msgstr "" - -msgid "Esperanto" -msgstr "" - -msgid "Everything installed successfully." -msgstr "" - -msgid "Exit" -msgstr "" - -msgid "Experimental" -msgstr "" - -msgid "Experimental Ports: " -msgstr "" - -#, python-brace-format -msgid "FAILED: {name}" -msgstr "" - -msgid "Featured Ports" -msgstr "" - -msgid "Fetch latest ports information." -msgstr "" - -#, python-brace-format -msgid "Fetching featured port image {file_name}" -msgstr "" - -msgid "Fetching info" -msgstr "" - -msgid "Fetching latest featured port images zip." -msgstr "" - -msgid "Fetching latest featured ports." -msgstr "" - -msgid "Fetching latest gameinfo.xml files and cover images." -msgstr "" - -msgid "Fetching latest info" -msgstr "" - -msgid "Fetching latest port stats." -msgstr "" - -msgid "Fetching latest porters info." -msgstr "" - -msgid "Fetching latest ports info data." -msgstr "" - -msgid "File not found." -msgstr "" - -msgid "Files Required" -msgstr "" - -msgid "Filters" -msgstr "" - -msgid "Filters Scene" -msgstr "" - -msgid "Finnish" -msgstr "" - -msgid "First Person Shooter" -msgstr "" - -msgid "First check." -msgstr "" - -msgid "" -"For the best experience, switch to a supported firmware such as muOS or " -"KNULLI." -msgstr "" - -msgid "Force check for a new PortMaster version." -msgstr "" - -msgid "Free external assets needed." -msgstr "" - -msgid "Free game, all files included." -msgstr "" - -msgid "French" -msgstr "" - -msgid "Genres:" -msgstr "" - -msgid "German" -msgstr "" - -msgid "" -"HELLO.\n" -"\n" -"IT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH " -"YOUR DEVICE.\n" -"\n" -"IF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO " -"RIDICULE.\n" -"\n" -"ARE YOU SURE YOU WANT TO RESUME INSTALLING?" -msgstr "" - -msgid "Hand curated lists of ports" -msgstr "" - -msgid "Hide Info" -msgstr "" - -msgid "I Understand" -msgstr "" - -msgid "Install" -msgstr "" - -msgid "Installation failed, removing files..." -msgstr "" - -msgid "Installation failed, see log.txt for details." -msgstr "" - -#, python-brace-format -msgid "Installation failed: {reason}" -msgstr "" - -msgid "Installed" -msgstr "" - -#, python-brace-format -msgid "Installed {download_name}." -msgstr "" - -msgid "Installing" -msgstr "" - -#, python-brace-format -msgid "Installing Theme {download_name}." -msgstr "" - -#, python-brace-format -msgid "Installing {download_name}." -msgstr "" - -#, python-brace-format -msgid "Installing {port_name}" -msgstr "" - -msgid "Interface" -msgstr "" - -#, python-brace-format -msgid "" -"It is for the wrong architecture ({zip_arch}, this device is {device_arch}), " -"it was deleted." -msgstr "" - -msgid "Italian" -msgstr "" - -msgid "Japanese" -msgstr "" - -msgid "Korean" -msgstr "" - -msgid "Language Select" -msgstr "" - -msgid "List all ports available on PortMaster." -msgstr "" - -msgid "List all ports that are ready to play!" -msgstr "" - -msgid "Loading Info." -msgstr "" - -msgid "Loading Ports." -msgstr "" - -msgid "Loading Sources." -msgstr "" - -msgid "Loading..." -msgstr "" - -msgid "Location where ports should be installed to." -msgstr "" - -msgid "Lower Case" -msgstr "" - -msgid "Main Menu" -msgstr "" - -msgid "Manage Ports" -msgstr "" - -msgid "Manage port runtimes." -msgstr "" - -msgid "Manual" -msgstr "" - -msgid "" -"Manually update port metadata with missing/updated information and artwork." -msgstr "" - -msgid "Many ports will not work." -msgstr "" - -msgid "Messages" -msgstr "" - -msgid "Metadata Refresh" -msgstr "" - -msgid "Metadata Update: " -msgstr "" - -msgid "Missing" -msgstr "" - -msgid "Music: " -msgstr "" - -msgid "N/A" -msgstr "" - -msgid "NO PORTS" -msgstr "" - -msgid "NextUI is not supported by PortMaster." -msgstr "" - -msgid "No featured ports found, internet connection is required." -msgstr "" - -msgid "Not Installed" -msgstr "" - -msgid "Not Used" -msgstr "" - -msgid "Not compatible with this device." -msgstr "" - -#, python-brace-format -msgid "OK: {name}" -msgstr "" - -msgid "Okay" -msgstr "" - -msgid "Options" -msgstr "" - -msgid "Options Menu" -msgstr "" - -msgid "Other" -msgstr "" - -msgid "Paid external assets needed." -msgstr "" - -msgid "Passed file validation." -msgstr "" - -msgid "Platformer" -msgstr "" - -msgid "Polish" -msgstr "" - -msgid "Port Info" -msgstr "" - -msgid "Port Info Popup" -msgstr "" - -msgid "Port Metadata" -msgstr "" - -msgid "Port {download_name!r} and {runtime_name!r} installed successfully." -msgstr "" - -msgid "Port {download_name!r} installed successfully." -msgstr "" - -msgid "" -"Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to " -"install!!\n" -"\n" -"Either reinstall to try again, or check the wiki for help." -msgstr "" - -#, python-brace-format -msgid "Port {download_name} installed failed." -msgstr "" - -#, python-brace-format -msgid "Port {runtime} contains a bad runtime, game may not run correctly." -msgstr "" - -#, python-brace-format -msgid "Port {runtime} contains an unknown runtime, game may not run correctly." -msgstr "" - -msgid "PortMaster Options" -msgstr "" - -msgid "" -"PortMaster updates metadata when ports are installed so that it shows port " -"images and descriptions when browsing ports on your device." -msgstr "" - -msgid "PortMaster will now quit so you can run the restoration port." -msgstr "" - -msgid "PortMaster.zip is damaged, download it again." -msgstr "" - -msgid "Porters:" -msgstr "" - -msgid "Ports List" -msgstr "" - -msgid "Ports Location: " -msgstr "" - -msgid "" -"Ports have been added or removed, update your games list to see the new " -"games or remove the uninstalled ones." -msgstr "" - -msgid "Ports tab" -msgstr "" - -msgid "Portuguese (Brazil)" -msgstr "" - -msgid "Portuguese (Portugal)" -msgstr "" - -msgid "Puzzle" -msgstr "" - -msgid "Quit" -msgstr "" - -msgid "Quit PortMaster" -msgstr "" - -msgid "Racing" -msgstr "" - -msgid "Ready to Run" -msgstr "" - -msgid "Ready to Run Ports" -msgstr "" - -msgid "Recently Added" -msgstr "" - -msgid "Recently Updated" -msgstr "" - -msgid "Reinstall" -msgstr "" - -#, python-brace-format -msgid "Release Channel: {channel}" -msgstr "" - -#, python-brace-format -msgid "Removing {port_name}" -msgstr "" - -msgid "Requires Files" -msgstr "" - -msgid "Restore PortMaster" -msgstr "" - -msgid "Rhythm" -msgstr "" - -msgid "Role Playing Game" -msgstr "" - -msgid "Romanian" -msgstr "" - -msgid "Roms section" -msgstr "" - -msgid "Runtime Manager" -msgstr "" - -#, python-brace-format -msgid "Runtime {runtime} is broken, reinstalling." -msgstr "" - -msgid "Russian" -msgstr "" - -msgid "SD 1" -msgstr "" - -msgid "SD 2" -msgstr "" - -msgid "SDCARD 1" -msgstr "" - -msgid "SDCARD 2" -msgstr "" - -#, python-brace-format -msgid "Secret Mode {secret_mode}" -msgstr "" - -msgid "Secret Options" -msgstr "" - -msgid "Select" -msgstr "" - -msgid "Select Color Scheme" -msgstr "" - -msgid "Select Colour Scheme" -msgstr "" - -msgid "Select Theme" -msgstr "" - -msgid "Select a colour scheme for PortMaster" -msgstr "" - -msgid "Select a theme for PortMaster." -msgstr "" - -msgid "Select the language PortMaster uses." -msgstr "" - -msgid "Show Info" -msgstr "" - -msgid "Show all ports, ignoring requirements." -msgstr "" - -msgid "Show or hide experimental ports." -msgstr "" - -msgid "Simulation" -msgstr "" - -msgid "" -"Some files failed to install, select them to see why. They were left in the " -"autoinstall directory." -msgstr "" - -msgid "Sort:" -msgstr "" - -msgid "Sound FX: " -msgstr "" - -msgid "Source Manager" -msgstr "" - -msgid "Space" -msgstr "" - -msgid "Spanish" -msgstr "" - -msgid "Sports" -msgstr "" - -msgid "SpruceOS is not supported by PortMaster." -msgstr "" - -msgid "Strategy" -msgstr "" - -#, python-brace-format -msgid "Successfully downloaded {runtime}." -msgstr "" - -#, python-brace-format -msgid "Successfully uninstalled {port_name}" -msgstr "" - -msgid "Swedish" -msgstr "" - -msgid "System" -msgstr "" - -msgid "Theme {download_name!r} installed successfully." -msgstr "" - -#, python-brace-format -msgid "" -"There is a new version of PortMaster ({portmaster_version})\n" -"\n" -"Do you want to upgrade?" -msgstr "" - -msgid "This can break stuff, don't touch unless you know what you are doing." -msgstr "" - -msgid "This is not a valid zip file." -msgstr "" - -msgid "" -"This will restore PortMaster to the latest stable version of PortMaster." -msgstr "" - -msgid "Toggle between various controller layouts." -msgstr "" - -msgid "Total Downloads" -msgstr "" - -msgid "Try removing some filters." -msgstr "" - -msgid "Ukrainian" -msgstr "" - -msgid "Unable do download a runtime when in offline mode." -msgstr "" - -msgid "Unable to download file." -msgstr "" - -#, python-brace-format -msgid "Unable to download file. [{status_code}]" -msgstr "" - -msgid "Unable to download in offline mode." -msgstr "" - -msgid "Unable to download non zip files." -msgstr "" - -msgid "Unable to download verification file." -msgstr "" - -#, python-brace-format -msgid "Unable to download {runtime} in {device_arch}." -msgstr "" - -#, python-brace-format -msgid "Unable to download {runtime}, game may not run correctly." -msgstr "" - -#, python-brace-format -msgid "Unable to find a download for {runtime}." -msgstr "" - -#, python-brace-format -msgid "Unable to find a source for {port_name}" -msgstr "" - -#, python-brace-format -msgid "Unable to find {port_name}." -msgstr "" - -msgid "Unable to validate download." -msgstr "" - -msgid "Uninstall" -msgstr "" - -#, python-brace-format -msgid "Uninstalling {port_name}" -msgstr "" - -msgid "Unknown IP" -msgstr "" - -#, python-brace-format -msgid "Unknown port {port_name}" -msgstr "" - -msgid "Unverified" -msgstr "" - -msgid "Up to date already" -msgstr "" - -msgid "Update / Uninstall Ports" -msgstr "" - -msgid "Update Available" -msgstr "" - -msgid "Update PortMaster" -msgstr "" - -msgid "Update Ports" -msgstr "" - -msgid "Updating" -msgstr "" - -msgid "Updating all port sources:" -msgstr "" - -#, python-brace-format -msgid "Updating {runtime}." -msgstr "" - -msgid "Upper Case" -msgstr "" - -msgid "Used" -msgstr "" - -msgid "Verified" -msgstr "" - -#, python-brace-format -msgid "Verified {runtime} successfully." -msgstr "" - -#, python-brace-format -msgid "Verified {runtime}." -msgstr "" - -msgid "Verifying" -msgstr "" - -#, python-brace-format -msgid "Verifying runtime {runtime}" -msgstr "" - -msgid "Visual Novel" -msgstr "" - -msgid "Warning" -msgstr "" - -#, python-brace-format -msgid "" -"You are switching from the {old_release_channel} to the " -"{new_release_channel} version.\n" -"\n" -"Do you want to downgrade?" -msgstr "" - -msgid "Your OS is not supported by PortMaster." -msgstr "" - -msgid "Your device has less than 1GB of RAM." -msgstr "" - -#, python-brace-format -msgid "illegal file {file_name}" -msgstr "" - -msgid "the port has no directory" -msgstr "" - -msgid "the port has no launch script" -msgstr "" - -#, python-brace-format -msgid "unable to load {file_name}" -msgstr "" - -msgid "x86 64bit" -msgstr "" - -#, python-brace-format -msgid "{installed} installed, {failed} failed" -msgstr "" - -#, python-brace-format -msgid "{item_name} (Selected)" -msgstr "" - -#, python-brace-format -msgid "{lang_name} (Selected)" -msgstr "" - -#, python-brace-format -msgid "" -"{name} failed to install:\n" -"\n" -"{reason}" -msgstr "" - -#, python-brace-format -msgid "{name} installed successfully." -msgstr "" - -#, python-brace-format -msgid "{runtime_name} Runtime" -msgstr "" - -#, python-brace-format -msgid "{theme_name} (Selected)" -msgstr "" diff --git a/tests/test_build.py b/tests/test_build.py index b0a37129..dec63317 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -21,9 +21,9 @@ pytestmark = pytest.mark.skipif( - not all(shutil.which(tool) for tool in ("zip", "unzip", "msgfmt", "git", "bash")) or + not all(shutil.which(tool) for tool in ("zip", "unzip", "git", "bash")) or not (REPO_DIR / ".git").exists(), - reason="needs zip, unzip, msgfmt, bash and a git checkout") + reason="needs zip, unzip, bash and a git checkout") TODAY = datetime.date.today().strftime("%Y-%m-%d") @@ -42,8 +42,8 @@ def run(script, *args, check=True, **env): def git_status(): - """Everything git sees in the checkout, ignored files too (an old build left .mo files, - version, ...). Not coverage data: under pytest-cov any python the build runs writes one.""" + """Everything git sees in the checkout, ignored files too (an old build left files like + version behind). Not coverage data: under pytest-cov any python the build runs writes one.""" output = subprocess.check_output( ["git", "status", "--porcelain", "--ignored", "--untracked-files=all"], cwd=str(REPO_DIR), universal_newlines=True) @@ -136,12 +136,12 @@ def test_pylibs_zip_contents(test_build): assert {"pylibs/pugwash/app.py", "pylibs/harbourmaster/platform/__init__.py", "pylibs/harbourmaster/platform.py", "exlibs/loguru/__init__.py", - "pylibs/locales/de_DE/LC_MESSAGES/messages.mo"} <= names + "pylibs/harbourmaster/i18n.py", "pylibs/locales/de_DE.json"} <= names assert [name for name in names if "__pycache__" in name or name.endswith(".pyc")] == [] - ## Every translation is compiled. - for po_file in (PM_DIR / "pylibs" / "locales").glob("*/LC_MESSAGES/*.po"): - assert str(po_file.relative_to(PM_DIR).with_suffix(".mo")) in names + ## Every translation ships. + for language_file in (PM_DIR / "pylibs" / "locales").glob("*_*.json"): + assert str(language_file.relative_to(PM_DIR)) in names def test_release_updates_version_json(tmp_path): diff --git a/tests/test_i18n.py b/tests/test_i18n.py new file mode 100644 index 00000000..7f4f1ba6 --- /dev/null +++ b/tests/test_i18n.py @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: MIT +# +# Translations: PortMaster/pylibs/locales/.json, read by harbourmaster.i18n, and the +# list of every translatable string, locales/strings.json (tools/i18n.py extract). + +import json +import sys + +import pytest + +from conftest import PM_DIR, REPO_DIR + +from harbourmaster import i18n + +sys.path.insert(0, str(REPO_DIR / "tools")) +import i18n as i18n_tool # noqa: E402 + + +LOCALES_DIR = PM_DIR / "pylibs" / "locales" +LANGUAGES = i18n_tool.languages() + + +def test_strings_json_matches_the_code(): + ## Moving code around must not add, lose or change a translatable string without + ## strings.json (what translators see) knowing. After changing strings on purpose: + ## python3 -I tools/i18n.py extract + strings = i18n_tool.read_strings() + code = i18n_tool.code_strings() + assert sorted(code) == sorted(strings['messages']), "run: python3 -I tools/i18n.py extract" + assert set(i18n_tool.theme_strings(PM_DIR / "pylibs" / "default_theme")) <= set(strings['themes']) + + +def test_extraction_finds_every_call(tmp_path): + source = tmp_path / "example.py" + source.write_text( + 'from harbourmaster.i18n import _\n' + 'a = _("Plain")\n' + 'b = _("Joined "\n' + ' "together {name}").format(name=1)\n' + 'c = _(f"not {a}")\n' + 'd = other("Not this")\n') + assert list(i18n_tool.code_strings([source])) == ["Plain", "Joined together {name}"] + + +def test_files(): + ## Two per language, like messages.po and themes.po were. + names = {path.name for path in LOCALES_DIR.glob("*.json")} + assert {"strings.json", "strings.themes.json", "de_DE.json", "de_DE.themes.json"} <= names + assert "en_US" in LANGUAGES and len(LANGUAGES) >= 15 + for lang in LANGUAGES: + assert {f"{lang}.json", f"{lang}.themes.json"} <= names + + +@pytest.mark.parametrize("lang", LANGUAGES) +def test_language_file(lang): + strings = i18n_tool.read_strings() + for domain in i18n.DOMAINS: + translations = json.loads(i18n.language_file(LOCALES_DIR, lang, domain).read_text(encoding="utf-8")) + assert isinstance(translations, dict) + for source, translation in translations.items(): + assert source in strings[domain], f"{domain}: not used any more: {source!r}" + assert isinstance(translation, str) and translation + ## A lost {placeholder} shows the wrong text (line breaks are the translator's call). + assert not [problem for problem in i18n_tool.problems(source, translation) if "line breaks" not in problem], source + + +def test_problems(): + assert i18n_tool.problems("Install {name}", "{name} installieren") == [] + assert i18n_tool.problems("Install {name}", "Installieren") == ["missing {name}"] + assert i18n_tool.problems("A\nB", "A B") == ["1 line breaks in English, 0 here"] + + +@pytest.fixture +def restore_language(): + yield + i18n.load_language(LOCALES_DIR, "en_US") + i18n.set_theme_translations({}) + + +def test_load_language(tmp_path, restore_language): + (tmp_path / "xx_XX.json").write_text(json.dumps({"Hello": "Hallo", "Empty": ""})) + (tmp_path / "xx_XX.themes.json").write_text(json.dumps({"Genres: {port_info.genres}": "Genres: {port_info.genres}!"})) + + i18n.load_language(tmp_path, "xx_XX") + assert (i18n._("Hello"), i18n._("Empty"), i18n._("Unknown")) == ("Hallo", "Empty", "Unknown") + assert i18n.theme_text("Genres: {port_info.genres}") == "Genres: {port_info.genres}!" + + ## A theme's own translations come first, for the current language only. + i18n.set_theme_translations({"xx_XX": {"Genres: {port_info.genres}": "Mine"}, "yy_YY": {"Hello": "No"}}) + assert i18n.theme_text("Genres: {port_info.genres}") == "Mine" + i18n.set_theme_translations({}) + assert i18n.theme_text("Genres: {port_info.genres}") == "Genres: {port_info.genres}!" + + ## A missing or broken file is English. + (tmp_path / "zz_ZZ.json").write_text("{ not json") + for lang in ("zz_ZZ", "nope"): + i18n.load_language(tmp_path, lang) + assert i18n._("Hello") == "Hello" + + +def test_every_language_loads(restore_language): + import pugwash.lang + languages = pugwash.lang.lang_list() + assert {"en_US", "de_DE", "ja_JP"} <= set(languages) + for lang in languages: + assert pugwash.lang.check_lang(lang) == lang + assert pugwash.lang.check_lang("de_DE.UTF-8") == "de_DE" + assert pugwash.lang.check_lang("de") == "de_DE" + assert pugwash.lang.check_lang("xx_XX") is None + + i18n.load_language(LOCALES_DIR, "de_DE") + assert i18n._("Options") != "Options" diff --git a/tests/test_i18n_snapshot.py b/tests/test_i18n_snapshot.py deleted file mode 100644 index 3861f55e..00000000 --- a/tests/test_i18n_snapshot.py +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-License-Identifier: MIT -# -# Moving code around must not add, lose or change any translatable strings. -# -# Regenerate (only when strings change on purpose): -# PM_UPDATE_I18N_SNAPSHOT=1 pytest tests/test_i18n_snapshot.py - -import os -import shutil -import subprocess - -import pytest - -from conftest import DATA_DIR, PM_DIR - - -SNAPSHOT_FILE = DATA_DIR / "msgids.txt" - - -def source_files(): - """The files do_i18n.sh extracts strings from.""" - pylibs = PM_DIR / "pylibs" - files = [PM_DIR / "pugwash"] - files += sorted(pylibs.glob("pug*.py")) - - for package in ("harbourmaster", "pugwash"): - if (pylibs / package).is_dir(): - files += sorted((pylibs / package).rglob("*.py")) - - return files - - -def current_msgids(): - result = subprocess.run( - ["xgettext", "-L", "Python", "--no-location", "--sort-output", "--omit-header", "-o", "-", - *[str(f) for f in source_files()]], - stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True) - - return result.stdout.decode("utf-8") - - -@pytest.mark.skipif(shutil.which("xgettext") is None, reason="xgettext not installed (gettext)") -def test_msgids_unchanged(): - if os.environ.get("PM_UPDATE_I18N_SNAPSHOT") == "1": - SNAPSHOT_FILE.write_text(current_msgids()) - - assert current_msgids() == SNAPSHOT_FILE.read_text() diff --git a/theme_msgfmt.py b/theme_msgfmt.py deleted file mode 100644 index de9dc0b4..00000000 --- a/theme_msgfmt.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python3 -# -# SPDX-License-Identifier: MIT -# - -""" - -This is used to create the themes.json and themes.pot file on PR. - -""" - -import datetime -import fnmatch -import hashlib -import json -import os -import pathlib -import re -import sys -import tempfile -import urllib -import urllib.request - -from pathlib import Path - - -THEME_TRANSLATIONS = "https://github.com/PortsMaster/PortMaster-Themes/releases/latest/download/theme_translations.json" -TRANSLATION_HEADER = r"""# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-09 16:35+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" - -""" - - -def fetch_url(url): - try: - # Open the URL - with urllib.request.urlopen(url) as response: - # Read the content of the file - file_content = response.read() - - # Decode the bytes to a string (assuming the file is in UTF-8 encoding) - return file_content.decode('utf-8') - - except urllib.error.URLError as e: - return None - - except UnicodeDecodeError as e: - return None - - -def parse_translations(theme_name, translations, text): - source_name = f"{theme_name}/theme.json" - - for lineno, line in enumerate(text.split('\n'), 1): - line_1=line.strip() - if not line_1.startswith('"text":'): - continue - - line_2 = line_1.split('"', 3)[-1] - line_3 = line_2.rsplit('"', 1)[0] - - if line_3.strip() == "": - continue - - if re.match(r"^\{[^}]+\}$", line_3): - continue - - if re.match(r"^#[0-9a-f]+$", line_3, re.I): - continue - - source = f"{source_name}:{lineno}" - translations.setdefault(line_3, []).append(source) - - -def dump_tr_string(string): - if "\\n" in string: - result = ['""'] - items = string.split("\\n") - for line in items[:-1]: - result.append(f'"{line}\\n"') - result.append(f'"{items[-1]}"') - return "\n".join(result) - - return f'"{string}"' - - -def dump_translations(file_name, translations): - with open(file_name, "w") as fh: - print(TRANSLATION_HEADER, file=fh) - - for translation, sources in translations.items(): - for offset in range(0, len(sources), 5): - print(f"#: {', '.join(sources[offset:(offset+5)])}", file=fh) - - print(f"msgid {dump_tr_string(translation)}", file=fh) - print(f"msgstr {dump_tr_string('')}", file=fh) - print("", file=fh) - - -def main(): - theme_paths = Path('PortMaster/pylibs/') - - theme_translations = fetch_url(THEME_TRANSLATIONS) - - if theme_translations is not None: - translations = json.loads(theme_translations) - else: - translations = {} - - for theme_file in theme_paths.glob("*/theme.json"): - parse_translations(theme_file.parent.name, translations, theme_file.read_text()) - - dump_translations("PortMaster/pylibs/locales/themes.pot", translations) - - -if __name__ == '__main__': - main() diff --git a/tools/i18n.py b/tools/i18n.py new file mode 100644 index 00000000..210b089a --- /dev/null +++ b/tools/i18n.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +# +# PortMaster's translatable strings (see harbourmaster/i18n.py for the files). +# +# python3 -I tools/i18n.py extract [--themes DIR] +# Rewrites PortMaster/pylibs/locales/strings.json, every _("...") in the code, and +# strings.themes.json, every theme "text" in the built-in theme (and the themes in +# DIR, eg: a PortMaster-Themes checkout; themes it doesn't scan keep their strings). +# python3 -I tools/i18n.py check +# Each language file: strings that aren't in strings.json any more, and translations +# that lost a {placeholder} or a line break. +# +# Standard library only, Python 3.7. + +import argparse +import ast +import json +import re +import sys +from pathlib import Path + + +REPO_DIR = Path(__file__).resolve().parent.parent +PM_DIR = REPO_DIR / "PortMaster" +LOCALES_DIR = PM_DIR / "pylibs" / "locales" +DOMAINS = ("messages", "themes") +## The files: strings.json, de_DE.json (messages); strings.themes.json, de_DE.themes.json (themes). +LANGUAGE_FILE = re.compile(r"^([a-z]{2,3}_[A-Z]{2})(\.themes)?\.json$") + + +def domain_file(name, domain): + return LOCALES_DIR / (f"{name}.json" if domain == "messages" else f"{name}.{domain}.json") + + +def languages(): + """The languages with a translation file.""" + return sorted({LANGUAGE_FILE.match(path.name).group(1) for path in LOCALES_DIR.glob("*.json") if LANGUAGE_FILE.match(path.name)}) + + +def code_files(): + """The files with translatable strings: both scripts' own code and the two packages.""" + files = [PM_DIR / "pugwash", PM_DIR / "harbourmaster"] + for package in ("harbourmaster", "pugwash"): + files += sorted((PM_DIR / "pylibs" / package).rglob("*.py")) + return files + + +def _literal(node): + """The string of a str literal node, None for anything else (f-strings, names, ...).""" + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if isinstance(node, getattr(ast, "Str", ())) and isinstance(getattr(node, "s", None), str): + return node.s + return None + + +def _display(path): + """A file as strings.json names it: relative to PortMaster/ when it's in there.""" + try: + return Path(path).resolve().relative_to(PM_DIR).as_posix() + except ValueError: + return Path(path).as_posix() + + +def code_strings(files=None): + """{text: ["file:line", ...]} for every _("text") call, in file order.""" + found = {} + for path in files or code_files(): + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + calls = [node for node in ast.walk(tree) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "_" and node.args] + for node in sorted(calls, key=lambda node: (node.lineno, node.col_offset)): + text = _literal(node.args[0]) + if text: + found.setdefault(text, []).append(f"{_display(path)}:{node.lineno}") + + return found + + +def theme_strings(theme_dir, name=None): + """{text: ["theme/theme.json:line", ...]} for a theme's "text" values that are more + than a {template} or a colour.""" + theme_file = Path(theme_dir) / "theme.json" + source = theme_file.read_text(encoding="utf-8") + name = name or Path(theme_dir).name + found = {} + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key.split("[", 1)[0] == "text" and isinstance(item, str): + yield item + else: + yield from walk(item) + elif isinstance(value, list): + for item in value: + yield from walk(item) + + lines = source.split("\n") + for text in walk(json.loads(source)): + if not text.strip() or re.match(r"^\{[^}]+\}$", text) or re.match(r"^#[0-9a-f]+$", text, re.I): + continue + quoted = json.dumps(text, ensure_ascii=False)[1:-1] + line = next((number for number, line in enumerate(lines, 1) if quoted in line), 0) + found.setdefault(text, []).append(f"{name}/theme.json:{line}") + + return found + + +def read_json(path): + try: + data = json.loads(Path(path).read_text(encoding="utf-8")) + except (OSError, ValueError): + return {} + return data if isinstance(data, dict) else {} + + +def read_strings(): + """{domain: {text: [where it's used]}}""" + return {domain: read_json(domain_file("strings", domain)) for domain in DOMAINS} + + +def read_translations(lang): + """{domain: {text: translation}}""" + return {domain: read_json(domain_file(lang, domain)) for domain in DOMAINS} + + +def write_json(path, data): + path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + + +def extract(themes_dirs=()): + strings = read_strings() + themes = {"default_theme": PM_DIR / "pylibs" / "default_theme"} + for themes_dir in themes_dirs: + for theme_file in sorted(Path(themes_dir).glob("*/theme.json")): + themes[theme_file.parent.name] = theme_file.parent + + ## Themes not scanned this time keep their strings. + kept = {} + for text, sources in strings['themes'].items(): + others = [source for source in sources if source.split("/", 1)[0] not in themes] + if others: + kept[text] = others + + for name, theme_dir in themes.items(): + for text, sources in theme_strings(theme_dir, name).items(): + kept.setdefault(text, []).extend(sources) + + return {'messages': code_strings(), 'themes': {text: kept[text] for text in sorted(kept, key=lambda t: (kept[t][0], t))}} + + +PLACEHOLDER = re.compile(r"\{[^{}]*\}") + + +def problems(source, translation): + """What a translation lost: {placeholders} and line breaks the English has.""" + found = [] + missing = sorted(set(PLACEHOLDER.findall(source)) - set(PLACEHOLDER.findall(translation))) + if missing: + found.append("missing " + ", ".join(missing)) + extra = sorted(set(PLACEHOLDER.findall(translation)) - set(PLACEHOLDER.findall(source))) + if extra: + found.append("unknown " + ", ".join(extra)) + if source.count("\n") != translation.count("\n"): + found.append(f"{source.count(chr(10))} line breaks in English, {translation.count(chr(10))} here") + return found + + +def check(): + strings = read_strings() + issues = 0 + for lang in languages(): + for domain, translations in read_translations(lang).items(): + name = domain_file(lang, domain).name + for source, translation in translations.items(): + if source not in strings[domain]: + print(f"{name}: not used any more: {source!r}") + issues += 1 + for problem in problems(source, translation): + print(f"{name}: {problem}: {source!r}") + issues += 1 + + return 1 if issues else 0 + + +def main(argv=None): + parser = argparse.ArgumentParser(description="PortMaster's translatable strings") + commands = parser.add_subparsers(dest="command") + extract_parser = commands.add_parser("extract", help="rewrite locales/strings.json") + extract_parser.add_argument("--themes", action="append", default=[], help="a folder of themes to scan too") + commands.add_parser("check", help="check the language files") + options = parser.parse_args(argv) + + if options.command == "extract": + strings = extract(options.themes) + for domain in DOMAINS: + write_json(domain_file("strings", domain), strings[domain]) + print(f"{len(strings['messages'])} code strings, {len(strings['themes'])} theme strings -> {LOCALES_DIR}") + return 0 + + if options.command == "check": + return check() + + parser.print_help() + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/theme_server/serve.py b/tools/theme_server/serve.py index d9e7487c..ce25cfe5 100644 --- a/tools/theme_server/serve.py +++ b/tools/theme_server/serve.py @@ -118,7 +118,7 @@ def languages(): text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) locales = PM_DIR / "pylibs" / "locales" - codes = {"en_US"} | {path.name for path in locales.iterdir() if (path / "LC_MESSAGES").is_dir()} + codes = {"en_US"} | {match.group(1) for match in (re.match(r"^([a-z]{2,3}_[A-Z]{2})\.json$", path.name) for path in locales.glob("*.json")) if match} return [(code, names.get(code, code)) for code in names if code in codes] + \ [(code, code) for code in sorted(codes - set(names))] From 39dac3e1230ac22b3d24db740b52dc63a1e54403 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 12:53:10 +0800 Subject: [PATCH 04/28] Theme server: a translation editor A Translate view next to Preview lists every string of PortMaster and of the themes with a language's translation, filtered to the untranslated ones, those with problems or those on the rendered screens, and saves them into the checkout's language files (in strings.json's order, so diffs stay small; a new language gets both its files). Selecting a string shows the screens that use it, in that language, at the chosen resolution, and a saved translation re-renders them: the renderer records which strings each screen looked up, and renderers are keyed by the theme and the language files together. Saving only works from the page: /api/translate needs an X-Theme-Server header, which another site can't send without a CORS preflight the server never answers. tools/i18n.py check (and the editor) also flag a translation that lost a space the English starts or ends with. It found "Music: " and the like in German ("Musik", which showed "MusikDeaktiviert"), French and Korean: fixed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 3 + PortMaster/pylibs/locales/de_DE.json | 4 +- PortMaster/pylibs/locales/fr_FR.json | 10 +- PortMaster/pylibs/locales/ko_KR.json | 10 +- THEME.md | 18 ++ tests/test_theme_server.py | 95 +++++++++ tools/i18n.py | 4 + tools/theme_server/serve.py | 150 ++++++++++++-- tools/theme_server/shots.py | 38 ++++ tools/theme_server/static/index.html | 285 +++++++++++++++++++++++++-- tools/theme_server/worker.py | 3 +- 11 files changed, 576 insertions(+), 44 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 15e3916a..931f980c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,6 +78,9 @@ It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes ` - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode. - It links a shared ports cache (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`). - `shots.py` holds the screen catalogue: one builder per screen, pushing scenes with real port data. + - It records which translatable strings each render looked up, for the editor. +- **Translate view:** a translation editor. `/api/strings` lists a language's strings, and `/api/translate` saves one into the checkout's language files, in `strings.json` order. It only accepts requests from the page itself: they need the `X-Theme-Server` header. + - Renderers are keyed by the theme and the language files together, so a saved translation re-renders. - Tested in `tests/test_theme_server.py`. Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is fixed and the marker should then be removed. pugwash's `main()` is wrapped in `logger.catch`, so it exits 0 even after a crash; check its output for tracebacks rather than relying on the exit code. diff --git a/PortMaster/pylibs/locales/de_DE.json b/PortMaster/pylibs/locales/de_DE.json index 5a36eb89..63cf489b 100644 --- a/PortMaster/pylibs/locales/de_DE.json +++ b/PortMaster/pylibs/locales/de_DE.json @@ -166,9 +166,9 @@ "Select Color Scheme": "Farbschema auswählen", "Select a colour scheme for PortMaster": "Farbschema auswählen", "Audio": "Audio", - "Music: ": "Musik", + "Music: ": "Musik: ", "Enable or Disable background music in PortMaster.": "Hintergrundmusik in PortMaster aktivieren oder deaktivieren.", - "Sound FX: ": "Sound FX", + "Sound FX: ": "Sound FX: ", "Enable or Disable soundfx in PortMaster.": "Soundfx in PortMaster aktivieren oder deaktivieren.", "Port Metadata": "Port-Metadaten", "Metadata Update: ": "Metadaten Update: ", diff --git a/PortMaster/pylibs/locales/fr_FR.json b/PortMaster/pylibs/locales/fr_FR.json index aa265623..7a78f5eb 100644 --- a/PortMaster/pylibs/locales/fr_FR.json +++ b/PortMaster/pylibs/locales/fr_FR.json @@ -166,18 +166,18 @@ "Select Color Scheme": "Sélection de la palette de couleurs", "Select a colour scheme for PortMaster": "Sélectionnez un schéma de couleurs pour PortMaster", "Audio": "Audio", - "Music: ": "Musique :", + "Music: ": "Musique : ", "Enable or Disable background music in PortMaster.": "Activer ou désactiver la musique de fond dans PortMaster.", - "Sound FX: ": "Effets sonores :", + "Sound FX: ": "Effets sonores : ", "Enable or Disable soundfx in PortMaster.": "Activer ou désactiver les effets sonores dans PortMaster.", "Port Metadata": "Métadonnées du port", - "Metadata Update: ": "Mise à jour des métadonnées :", + "Metadata Update: ": "Mise à jour des métadonnées : ", "Auto": "Auto", "Manual": "Manuel", "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster met à jour les métadonnées pour les ports installés, pour que les illustrations et les descriptions de port soient affichées lors de la navigation sur votre appareil.", "Metadata Refresh": "Mise à jour des métadonnées", "Manually update port metadata with missing/updated information and artwork.": "Mettre à jour manuellement les métadonnées du port avec les informations manquantes/mises à jour et les illustrations.", - "Ports Location: ": "Emplacement des ports :", + "Ports Location: ": "Emplacement des ports : ", "Roms section": "Section des roms", "Ports tab": "Onglet des ports", "Location where ports should be installed to.": "Emplacement où les ports doivent être installés.", @@ -186,7 +186,7 @@ "System": "Système", "Runtime Manager": "Gestion des runtimes", "Manage port runtimes.": "Gérer les runtimes des ports.", - "Experimental Ports: ": "Ports expérimentaux :", + "Experimental Ports: ": "Ports expérimentaux : ", "Show or hide experimental ports.": "Afficher/Masquer les ports expérimentaux.", "Update Ports": "Mettre à Jour les Ports", "Fetch latest ports information.": "Récupérer les dernières informations sur les ports.", diff --git a/PortMaster/pylibs/locales/ko_KR.json b/PortMaster/pylibs/locales/ko_KR.json index b61fd572..2c3dcc9e 100644 --- a/PortMaster/pylibs/locales/ko_KR.json +++ b/PortMaster/pylibs/locales/ko_KR.json @@ -169,18 +169,18 @@ "Select Color Scheme": "색 구성표 선택", "Select a colour scheme for PortMaster": "색상 구성표를 선택하세요.", "Audio": "오디오", - "Music: ": "음악:", + "Music: ": "음악: ", "Enable or Disable background music in PortMaster.": "배경 음악을 활성화 또는 비활성화합니다. ", "Sound FX: ": "효과음: ", "Enable or Disable soundfx in PortMaster.": "효과음을 활성화 또는 비활성화합니다.", "Port Metadata": "포트 메타데이터", - "Metadata Update: ": "메타데이터 업데이트:", + "Metadata Update: ": "메타데이터 업데이트: ", "Auto": "자동", "Manual": "수동", "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "포트마스터는 포트가 설치될 때 메타데이터를 업데이트하여 장치에서 포트를 탐색할 때 포트 이미지와 설명을 표시합니다.", "Metadata Refresh": "메타데이터 새로 고침", "Manually update port metadata with missing/updated information and artwork.": "누락/업데이트된 정보 및 아트워크를 사용하여 포트 메타데이터를 수동으로 업데이트합니다.", - "Ports Location: ": "포트 위치:", + "Ports Location: ": "포트 위치: ", "Roms section": "롬 섹션", "Ports tab": "포트 탭", "Location where ports should be installed to.": "포트를 설치해야 하는 위치입니다.", @@ -189,7 +189,7 @@ "System": "시스템", "Runtime Manager": "런타임 관리자", "Manage port runtimes.": "포트 런타임을 관리합니다.", - "Experimental Ports: ": "실험 포트:", + "Experimental Ports: ": "실험 포트: ", "Show or hide experimental ports.": "실험 포트를 표시하거나 숨깁니다.", "Update Ports": "포트 업데이트", "Fetch latest ports information.": "최신 포트 정보를 가져옵니다.", @@ -206,7 +206,7 @@ "Secret Options": "비밀 옵션", "All Ports: ": "모든 포트: ", "Show all ports, ignoring requirements.": "요구 사항을 무시하고 모든 포트를 표시합니다.", - "CWTBE Mode: ": "CWTBE 방식:", + "CWTBE Mode: ": "CWTBE 방식: ", "Enable gptokeyb2 by default.": "기본적으로 gptokeyb2를 활성화합니다.", "Delete PortMaster Config": "구성 삭제", "This can break stuff, don't touch unless you know what you are doing.": "프로그램이 손상될 수 있으니, 잘 모르는 사람은 만지지 마세요.", diff --git a/THEME.md b/THEME.md index 2759bd63..b24e001c 100644 --- a/THEME.md +++ b/THEME.md @@ -35,6 +35,24 @@ To write the screens to files instead, for a theme's README or to compare before That writes `out/////.png`, with a `.json` next to each listing its regions. +### Translating PortMaster + +The same server is a translation editor: pick **Translate** at the top. It lists every string of PortMaster (and, under **Themes**, every theme's text) with the chosen language's translation. +- **Filters:** show only the untranslated strings, those with problems, or those on the rendered screens. +- **Preview:** selecting a string shows the screens that use it, in that language, at the chosen resolution. Saving (leave the box, or Ctrl/⌘+Enter for the next string) updates them a moment later, so you can see whether it fits. +- **Problems flagged:** a translation that drops a `{placeholder}`, a line break, or a space the English starts or ends with (text joined to other text, like "Music: " + "Disabled"). +- **Where it saves:** the language files in your checkout, `PortMaster/pylibs/locales/.json` and `.themes.json`. Pick a language marked "new" to start one. + +A theme can also carry its own translations, in a `#translations` section of its `theme.json`: + +```json + "#translations": { + "de_DE": { "Ports List": "Portliste" } + }, +``` + +They win over PortMaster's translations of the same text. + Every theme needs `main_menu`, `option_menu`, `ports_list` and `message_window` sections: pugwash fills in the other screens from those when a theme leaves them out. ## Scene Format diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index c3c1e712..95a7b748 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -267,3 +267,98 @@ def test_port_screens_with_port_data(online_hm, hm_dirs, tmp_path): finally: pool.stop() + + +################################################################################ +## The translation editor's side: which strings a screen uses, reading and saving. +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_render_lists_the_strings_it_used(pool): + _, info = pool.render("default_theme", None, "640x480", "de_DE", "options") + assert "Select Theme" in info['strings']['messages'] + ## Only translatable ones: not templates like "{system.cfw_name}". + known = serve.i18n_tool.read_strings() + assert all(text in known['messages'] for text in info['strings']['messages']) + assert all(text in known['themes'] for text in info['strings']['themes']) + + +@pytest.fixture +def locales(tmp_path, monkeypatch): + """The translation files, copied: saving writes there, not into the checkout.""" + import shutil as sh + locales = tmp_path / "locales" + sh.copytree(str(serve.i18n_tool.LOCALES_DIR), str(locales)) + monkeypatch.setattr(serve.i18n_tool, "LOCALES_DIR", locales) + return locales + + +def test_translations_save(locales): + translations = serve.Translations() + entries = {entry['text']: entry for entry in translations.language("de_DE")['messages']} + assert entries["Select Theme"]['translation'] and entries["Select Theme"]['sources'] + + before = serve.version(serve.DEFAULT_THEME_DIR, "de_DE") + assert translations.save("de_DE", "messages", "Select Theme", "Thema wählen") == [] + assert json.loads((locales / "de_DE.json").read_text(encoding="utf-8"))["Select Theme"] == "Thema wählen" + ## The renders depend on it. + assert serve.version(serve.DEFAULT_THEME_DIR, "de_DE") != before + + ## Kept in strings.json's order, so diffs stay small. + order = list(json.loads((locales / "strings.json").read_text(encoding="utf-8"))) + saved = list(json.loads((locales / "de_DE.json").read_text(encoding="utf-8"))) + assert saved == [text for text in order if text in saved] + + ## Problems come back; "" removes a translation. + assert translations.save("de_DE", "messages", "Music: ", "Musik:") == ["English ends with a space, this doesn't"] + translations.save("de_DE", "messages", "Select Theme", "") + assert "Select Theme" not in json.loads((locales / "de_DE.json").read_text(encoding="utf-8")) + + ## Only known strings; a new language gets both its files. + with pytest.raises(KeyError): + translations.save("de_DE", "messages", "Not a string PortMaster has", "x") + translations.save("da_DK", "themes", next(iter(serve.i18n_tool.read_strings()['themes'])), "Dansk") + assert (locales / "da_DK.json").is_file() and (locales / "da_DK.themes.json").is_file() + + +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_translate_api(pool, locales): + server = serve.Server(("127.0.0.1", 0), serve.Handler) + server.themes, server.pool, server.shots, server.verbose = pool.themes, pool, pool.shots(), False + threading.Thread(target=server.serve_forever, daemon=True).start() + base = f"http://127.0.0.1:{server.server_address[1]}" + + def post(body, headers={"Content-Type": "application/json", "X-Theme-Server": "1"}): + request = urllib.request.Request(base + "/api/translate", data=json.dumps(body).encode(), headers=headers, method="POST") + try: + with urllib.request.urlopen(request, timeout=30) as response: + return response.status, json.loads(response.read()) + except urllib.error.HTTPError as error: + return error.code, json.loads(error.read()) + + try: + with urllib.request.urlopen(base + "/api/strings?lang=de_DE", timeout=30) as response: + data = json.loads(response.read()) + assert {entry['text'] for entry in data['domains']['messages']} >= {"Select Theme", "Options"} + + good = {'lang': "de_DE", 'domain': "messages", 'text': "Select Theme", 'translation': "Thema wählen"} + ## Only from the page: another site's form or script can't send the header. + assert post(good, {"Content-Type": "application/json"})[0] == 403 + assert post(good, {"Content-Type": "text/plain", "X-Theme-Server": "1"})[0] == 403 + assert post(dict(good, lang="en_US"))[0] == 400 + assert post(dict(good, lang="../x"))[0] == 400 + assert post(dict(good, domain="other"))[0] == 400 + assert post(dict(good, text="Nope"))[0] == 404 + status, body = post(good) + assert status == 200 and body == {'ok': True, 'problems': []} + assert json.loads((locales / "de_DE.json").read_text(encoding="utf-8"))["Select Theme"] == "Thema wählen" + + with urllib.request.urlopen(base + "/api/info", timeout=30) as response: + names = {code: (name, has) for code, name, has in json.loads(response.read())['all_languages']} + assert names["de_DE"] == ("German", True) and names["da_DK"] == ("Danish", False) + + finally: + server.shutdown() + server.server_close() diff --git a/tools/i18n.py b/tools/i18n.py index 210b089a..1c7c4dbf 100644 --- a/tools/i18n.py +++ b/tools/i18n.py @@ -163,6 +163,10 @@ def problems(source, translation): extra = sorted(set(PLACEHOLDER.findall(translation)) - set(PLACEHOLDER.findall(source))) if extra: found.append("unknown " + ", ".join(extra)) + ## Joined to other text in the code ("Music: " + "Disabled"): the spaces at the ends matter. + for end, english, here in (("starts", source[:1], translation[:1]), ("ends", source[-1:], translation[-1:])): + if english.isspace() and not here.isspace(): + found.append(f"English {end} with a space, this doesn't") if source.count("\n") != translation.count("\n"): found.append(f"{source.count(chr(10))} line breaks in English, {translation.count(chr(10))} here") return found diff --git a/tools/theme_server/serve.py b/tools/theme_server/serve.py index ce25cfe5..1eb04045 100644 --- a/tools/theme_server/serve.py +++ b/tools/theme_server/serve.py @@ -36,6 +36,9 @@ HERE = Path(__file__).resolve().parent REPO_DIR = HERE.parents[1] +sys.path.insert(0, str(HERE.parent)) +import i18n as i18n_tool # noqa: E402 (tools/i18n.py) + PM_DIR = REPO_DIR / "PortMaster" DEFAULT_THEME_DIR = PM_DIR / "pylibs" / "default_theme" WORKER = HERE / "worker.py" @@ -97,22 +100,29 @@ def info(self, theme_id, theme_dir): 'schemes': schemes, 'default_scheme': default, 'error': None} @staticmethod - def fingerprint(theme_dir): - """Changes whenever a file in the theme is added, removed or saved.""" + def fingerprint(theme_dir, extra_files=()): + """Changes whenever a file in the theme (or one of extra_files) is added, removed or saved.""" digest = hashlib.md5() + paths = [] for root, dirs, files in os.walk(str(theme_dir)): dirs.sort() - for name in sorted(files): - path = os.path.join(root, name) - try: - stat = os.stat(path) - except OSError: - continue - digest.update(f"{path}\0{stat.st_size}\0{stat.st_mtime_ns}\n".encode("utf-8", "replace")) + paths += [os.path.join(root, name) for name in sorted(files)] + + for path in paths + [str(path) for path in extra_files]: + try: + stat = os.stat(path) + except OSError: + continue + digest.update(f"{path}\0{stat.st_size}\0{stat.st_mtime_ns}\n".encode("utf-8", "replace")) return digest.hexdigest()[:12] +def version(theme_dir, lang): + """The theme's fingerprint with the language's translation files: what a render depends on.""" + return Themes.fingerprint(theme_dir, [i18n_tool.domain_file(lang, domain) for domain in i18n_tool.DOMAINS]) + + def languages(): """[(code, name)]: the languages pugwash has translations for.""" text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") @@ -195,6 +205,7 @@ def __init__(self, themes, cache, work_dir): self.renders = {} self.lock = threading.Lock() self.start_lock = threading.Lock() + self.translations = Translations() self.counter = 0 def _worker(self, theme_id, scheme, resolution, lang): @@ -202,7 +213,7 @@ def _worker(self, theme_id, scheme, resolution, lang): if theme_dir is None: raise WorkerError(f"no theme {theme_id!r}") - fingerprint = Themes.fingerprint(theme_dir) + fingerprint = version(theme_dir, lang) key = (theme_id, fingerprint, scheme or "", resolution, lang) with self.lock: worker = self.workers.get(key) @@ -253,10 +264,11 @@ def render(self, theme_id, scheme, resolution, lang, shot): 'shot': shot, 'theme': theme_id, 'scheme': worker.ready.get('scheme'), 'resolution': resolution, 'lang': lang, 'version': fingerprint, 'regions': answer.get('regions', []), 'errors': answer.get('errors', []), 'missing': answer.get('missing'), 'ms': answer.get('ms'), + 'strings': self.translations.used(answer.get('strings', {})), } with self.lock: - ## Drop renders of older theme versions. - for old_key in [k for k in self.renders if k[0] == theme_id and k[1] != fingerprint]: + ## Drop renders of older versions of the theme and the language's translations. + for old_key in [k for k in self.renders if k[0] == theme_id and k[4] == lang and k[1] != fingerprint]: del self.renders[old_key] self.renders[render_key] = (png, info) @@ -273,6 +285,68 @@ def stop(self): worker.stop() +################################################################################ +## Translations: the language files in the checkout's PortMaster/pylibs/locales +class Translations: + """Reads and saves translations, one string at a time.""" + def __init__(self): + self.lock = threading.Lock() + + def strings(self): + return i18n_tool.read_strings() + + def used(self, lookups): + """The strings a render looked up that are translatable (not {templates} and the like).""" + strings = self.strings() + return {domain: [text for text in lookups.get(domain, []) if text in strings[domain]] for domain in i18n_tool.DOMAINS} + + def language(self, lang): + """Every string of both domains, with this language's translation and its problems.""" + strings, translations = self.strings(), i18n_tool.read_translations(lang) + result = {} + for domain in i18n_tool.DOMAINS: + entries = [] + for text, sources in strings[domain].items(): + translation = translations[domain].get(text, "") + entries.append({'text': text, 'translation': translation, 'sources': sources, + 'problems': i18n_tool.problems(text, translation) if translation else []}) + result[domain] = entries + return result + + def save(self, lang, domain, text, translation): + """Sets one translation ("" removes it), in strings.json's order. Returns its problems.""" + with self.lock: + strings = self.strings()[domain] + if text not in strings: + raise KeyError(text) + + translations = i18n_tool.read_translations(lang)[domain] + if translation: + translations[text] = translation + else: + translations.pop(text, None) + + ordered = {key: translations[key] for key in strings if key in translations} + path = i18n_tool.domain_file(lang, domain) + temp = path.with_name(path.name + ".tmp") + i18n_tool.write_json(temp, ordered) + os.replace(str(temp), str(path)) + ## Both files of a language exist once it has one. + for other in i18n_tool.DOMAINS: + if not i18n_tool.domain_file(lang, other).exists(): + i18n_tool.write_json(i18n_tool.domain_file(lang, other), {}) + + return i18n_tool.problems(text, translation) if translation else [] + + +def all_languages(): + """[(code, name, has files)]: every language PortMaster knows a name for, and any other with files.""" + have = {code for code, _ in languages()} + text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") + names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) + return [(code, names.get(code, code), code in have) for code in sorted(set(names) | have, key=lambda code: names.get(code, code))] + + ################################################################################ ## The ports cache def ensure_cache(cache, refresh=False): @@ -329,11 +403,23 @@ def do_GET(self): 'languages': languages(), 'resolutions': list(RESOLUTIONS), 'shots': self.server.shots, + 'all_languages': all_languages(), }) + elif url.path == "/api/strings": + lang = query.get('lang', "") + if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): + self.send(400, {'error': "bad language"}) + return + self.send(200, {'lang': lang, 'domains': self.server.pool.translations.language(lang)}) + elif url.path == "/api/version": theme_dir = self.server.themes.get(query.get('theme')) - self.send(200 if theme_dir else 404, {'version': Themes.fingerprint(theme_dir) if theme_dir else None}) + lang = query.get('lang', "en_US") + if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): + self.send(400, {'error': "bad language"}) + return + self.send(200 if theme_dir else 404, {'version': version(theme_dir, lang) if theme_dir else None}) elif url.path in ("/api/render.png", "/api/render.json"): theme_id = query.get('theme', "default_theme") @@ -364,6 +450,44 @@ def do_GET(self): except (BrokenPipeError, ConnectionResetError): pass + def do_POST(self): + url = urllib.parse.urlparse(self.path) + ## Only the page itself: another site can't send this header without a CORS + ## preflight, which this server never answers. + if self.headers.get("X-Theme-Server") != "1" or not (self.headers.get("Content-Type") or "").startswith("application/json"): + self.send(403, {'error': "forbidden"}) + return + + try: + length = int(self.headers.get("Content-Length") or 0) + if not 0 < length <= 256 * 1024: + self.send(400, {'error': "bad request"}) + return + body = json.loads(self.rfile.read(length).decode("utf-8")) + + if url.path == "/api/translate": + lang, domain = body.get('lang', ""), body.get('domain', "") + text, translation = body.get('text'), body.get('translation', "") + if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang) or lang == "en_US" or domain not in i18n_tool.DOMAINS or \ + not isinstance(text, str) or not isinstance(translation, str): + self.send(400, {'error': "bad language, domain or text"}) + return + try: + problems = self.server.pool.translations.save(lang, domain, text, translation) + except KeyError: + self.send(404, {'error': "not a translatable string"}) + return + self.send(200, {'ok': True, 'problems': problems}) + + else: + self.send(404, {'error': "not found"}) + + except (ValueError, UnicodeDecodeError, AttributeError): + self.send(400, {'error': "bad request"}) + + except (BrokenPipeError, ConnectionResetError): + pass + class Server(ThreadingHTTPServer): daemon_threads = True diff --git a/tools/theme_server/shots.py b/tools/theme_server/shots.py index 09fe4c99..f1b75d36 100644 --- a/tools/theme_server/shots.py +++ b/tools/theme_server/shots.py @@ -8,6 +8,7 @@ from pathlib import Path import harbourmaster +from harbourmaster import i18n from loguru import logger from pugwash import scenes as pug_scenes @@ -42,6 +43,35 @@ def __init__(self, gui): self.theme_section = "" +class RecordingDict(dict): + """A translations dict that notes every string looked up in it: which strings a screen uses.""" + def __init__(self, data, seen): + super().__init__(data) + self.seen = seen + + def get(self, key, default=None): + self.seen.append(key) + return super().get(key, default) + + +def record_lookups(): + """Swap harbourmaster.i18n's dicts for recording ones: {'messages': [...], 'themes': [...]}, + in lookup order, for the caller to clear.""" + seen = {'messages': [], 'themes': []} + i18n._messages = RecordingDict(i18n._messages, seen['messages']) + i18n._themes = RecordingDict(i18n._themes, seen['themes']) + i18n._theme_own = RecordingDict(i18n._theme_own, seen['themes']) + return seen + + +def _unique(items): + found = [] + for item in items: + if item not in found: + found.append(item) + return found + + class NoThemeDownloads: """The themes list without the online theme list: installed themes only.""" def get_theme_list(self): @@ -341,6 +371,8 @@ def __init__(self, work, theme_name): temp_dir=self.work / "temp", callback=self.gui) ## Screenshots load as they're drawn, so every image is there in the first frame. self.gui.images.disable_async() + ## Which strings each screen looks up, for the translation editor. + self.lookups = record_lookups() ## No sounds or music in a screenshot. self.gui.sounds.sound_is_disabled = True self.gui.sounds.music_is_disabled = True @@ -359,6 +391,8 @@ def render(self, shot, out_file): raise KeyError(f"no shot called {shot!r}") self.errors.clear() + for seen in self.lookups.values(): + seen.clear() gui = self.gui ## A screen that "quit" (eg: a scene with nothing to show) mustn't cancel the next one. gui.events.running = True @@ -377,5 +411,9 @@ def render(self, shot, out_file): return regions, list(self.errors) + def strings(self): + """The strings the last render looked up, once each: {'messages': [...], 'themes': [...]}.""" + return {domain: _unique(seen) for domain, seen in self.lookups.items()} + def quit(self): self.gui.quit() diff --git a/tools/theme_server/static/index.html b/tools/theme_server/static/index.html index 9ff6d1a6..b4e930f7 100644 --- a/tools/theme_server/static/index.html +++ b/tools/theme_server/static/index.html @@ -38,6 +38,33 @@ .status { margin-left: auto; font-size: 12px; color: var(--muted); padding-bottom: 5px; text-align: right; } .status .live { color: var(--accent); } main { padding: 16px; } +/* Translate view */ +.translate { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 16px; align-items: start; } +.tbar { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; margin-bottom: 10px; } +.tbar input[type=search] { font: 13px var(--sans); color: var(--fg); background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px; width: 16em; } +.progress { font-size: 12px; color: var(--muted); } +.tlist { display: grid; gap: 6px; } +.trow { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--line); border-radius: 6px; padding: 8px 10px; display: grid; gap: 6px; min-width: 0; } +.trow.done { border-left-color: var(--pass, #1f7a4d); } +.trow.todo { border-left-color: var(--muted); } +.trow.problem { border-left-color: var(--error); } +.trow.selected { outline: 2px solid var(--accent); outline-offset: -1px; } +.trow .source { white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; } +.trow textarea { font: 13px/1.4 var(--sans); color: var(--fg); background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px; width: 100%; resize: vertical; min-height: 2.4em; } +.trow textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; } +.trow .tmeta { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 11px; color: var(--muted); } +.trow .tmeta .where { font-family: var(--mono); } +.chip { display: inline-block; padding: 0 6px; border-radius: 999px; background: var(--bg); border: 1px solid var(--line); color: var(--fg); cursor: pointer; font: 11px var(--sans); } +.tproblem { color: var(--error); font-size: 12px; } +.twarn { color: var(--warn); font-size: 12px; } +.tpreview { position: sticky; top: 92px; display: grid; gap: 10px; max-height: calc(100vh - 110px); overflow: auto; } +.tpreview h2 { font-size: 13px; margin: 0; color: var(--muted); font-weight: 600; } +.tpreview .shots { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 8px; } +.tpreview .shots figure, .tpreview .big { margin: 0; background: var(--surface); border: 1px solid var(--line); border-radius: 6px; overflow: hidden; cursor: pointer; } +.tpreview figure img, .tpreview .big img { width: 100%; display: block; } +.tpreview figcaption { font-size: 11px; padding: 3px 6px; color: var(--muted); } +.tempty { color: var(--muted); font-size: 13px; } +@media (max-width: 900px) { .translate { grid-template-columns: 1fr; } .tpreview { position: static; max-height: none; } } .notice { background: var(--error-bg); color: var(--error); border-radius: 8px; padding: 10px 14px; margin-bottom: 16px; white-space: pre-wrap; font-family: var(--mono); font-size: 12px; } .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card, 300px), 1fr)); gap: 16px; align-items: start; } .card { background: var(--surface); border: 1px solid var(--line); border-radius: 8px; overflow: hidden; display: grid; min-width: 0; } @@ -84,21 +111,39 @@

Theme Preview

+ - Show + Show - - - + + +
Loading…
+
@@ -125,17 +170,19 @@

Region

"use strict"; const $ = (id) => document.getElementById(id); const state = { info: null, theme: "default_theme", scheme: "", res: "640x480", lang: "en_US", mode: "screens", - modeShot: "main_menu", debug: false, size: 300, version: null, renders: new Map(), detail: null }; + modeShot: "main_menu", debug: false, size: 300, version: null, renders: new Map(), detail: null, + view: "preview", tDomain: "messages", tFilter: "all", tSearch: "", tSelected: null, tData: null, usage: new Map(), usageKey: null }; function load() { try { Object.assign(state, JSON.parse(localStorage.getItem("pm-theme-preview") || "{}"), { info: null, renders: new Map(), version: null, detail: null }); } catch (e) {} const hash = new URLSearchParams(location.hash.slice(1)); - for (const key of ["theme", "scheme", "res", "lang", "mode", "modeShot"]) if (hash.get(key)) state[key] = hash.get(key); + for (const key of ["theme", "scheme", "res", "lang", "mode", "modeShot", "view"]) if (hash.get(key)) state[key] = hash.get(key); } function save() { - const keep = { theme: state.theme, scheme: state.scheme, res: state.res, lang: state.lang, mode: state.mode, modeShot: state.modeShot, debug: state.debug, size: state.size }; + const keep = { theme: state.theme, scheme: state.scheme, res: state.res, lang: state.lang, mode: state.mode, modeShot: state.modeShot, debug: state.debug, size: state.size, + view: state.view, tDomain: state.tDomain, tFilter: state.tFilter }; try { localStorage.setItem("pm-theme-preview", JSON.stringify(keep)); } catch (e) {} - history.replaceState(null, "", "#" + new URLSearchParams({ theme: state.theme, scheme: state.scheme, res: state.res, lang: state.lang, mode: state.mode, modeShot: state.modeShot })); + history.replaceState(null, "", "#" + new URLSearchParams({ theme: state.theme, scheme: state.scheme, res: state.res, lang: state.lang, mode: state.mode, modeShot: state.modeShot, view: state.view })); } function option(select, value, text) { const o = document.createElement("option"); o.value = value; o.textContent = text; select.append(o); } function theme() { return state.info.themes.find((t) => t.id === state.theme) || state.info.themes[0]; } @@ -149,14 +196,33 @@

Region

$("res").replaceChildren(); for (const r of info.resolutions) option($("res"), r, r.replace("x", " × ")); option($("res"), "custom", "Custom…"); if (info.resolutions.includes(state.res)) $("res").value = state.res; else { $("res").value = "custom"; $("custom-res").value = state.res; } $("custom-res-field").hidden = $("res").value !== "custom"; - $("lang").replaceChildren(); for (const [code, name] of info.languages) option($("lang"), code, `${name} (${code})`); - $("lang").value = state.lang; + fillLanguages(); $("mode-shot").replaceChildren(); for (const [name, title] of info.shots) option($("mode-shot"), name, title); $("mode-shot").value = state.modeShot; $("debug").checked = state.debug; $("size").value = state.size; document.body.style.setProperty("--card", state.size + "px"); setMode(state.mode, false); } +function fillLanguages() { + // Translate: every language PortMaster has a name for (English is the source); preview: the ones with files. + const list = state.view === "translate" + ? state.info.all_languages.filter(([code]) => code !== "en_US").map(([code, name, has]) => [code, `${name} (${code})${has ? "" : " · new"}`]) + : state.info.languages.map(([code, name]) => [code, `${name} (${code})`]); + $("lang").replaceChildren(); for (const [code, label] of list) option($("lang"), code, label); + if (!list.some(([code]) => code === state.lang)) state.lang = state.view === "translate" ? (list.find(([code]) => code === "de_DE") || list[0])[0] : "en_US"; + $("lang").value = state.lang; +} +function setView(view) { + state.view = view; + for (const b of $("view").children) b.setAttribute("aria-pressed", String(b.dataset.view === view)); + for (const el of document.querySelectorAll(".preview-only")) el.hidden = view !== "preview" || (el.id === "mode-shot-field" && state.mode !== "resolutions"); + $("grid").hidden = view !== "preview"; + $("translate").hidden = view !== "translate"; + fillLanguages(); + save(); + state.version = null; + poll().then(() => (view === "translate" ? buildTranslate() : build())); +} function fillSchemes() { const t = theme(); $("scheme").replaceChildren(); @@ -171,7 +237,7 @@

Region

function setMode(mode, rebuild = true) { state.mode = mode; for (const b of $("mode").children) b.setAttribute("aria-pressed", String(b.dataset.mode === mode)); - $("mode-shot-field").hidden = mode !== "resolutions"; + $("mode-shot-field").hidden = mode !== "resolutions" || state.view !== "preview"; if (rebuild) { save(); build(); } } @@ -360,7 +426,7 @@

Region

}); /* Controls */ -function changed() { state.renders.clear(); save(); build(); } +function changed() { state.renders.clear(); save(); if (state.view === "translate") buildTranslate(); else build(); } $("theme").onchange = () => { state.theme = $("theme").value; state.version = null; fillSchemes(); poll().then(changed); }; $("scheme").onchange = () => { state.scheme = $("scheme").value; changed(); }; $("res").onchange = () => { @@ -373,23 +439,208 @@

Region

if (!/^\d{2,4}x\d{2,4}$/.test(value)) { $("custom-res").setCustomValidity("Like 800x600"); $("custom-res").reportValidity(); return; } $("custom-res").setCustomValidity(""); state.res = value; changed(); }; -$("lang").onchange = () => { state.lang = $("lang").value; changed(); }; +$("lang").onchange = () => { state.lang = $("lang").value; state.version = null; poll().then(changed); }; +$("view").onclick = (event) => { if (event.target.dataset.view && event.target.dataset.view !== state.view) setView(event.target.dataset.view); }; $("mode").onclick = (event) => { if (event.target.dataset.mode) setMode(event.target.dataset.mode); }; $("mode-shot").onchange = () => { state.modeShot = $("mode-shot").value; save(); build(); }; $("debug").onchange = () => { state.debug = $("debug").checked; save(); for (const card of state.cards) if (card.info) fill(card); }; $("size").oninput = () => { state.size = Number($("size").value); document.body.style.setProperty("--card", state.size + "px"); save(); }; +/* Translate */ +const tkey = (domain, text) => domain + "\u0000" + text; +async function buildTranslate() { + $("tlist").replaceChildren(); + $("tprogress").textContent = "Loading…"; + for (const b of $("tdomain").children) b.setAttribute("aria-pressed", String(b.dataset.domain === state.tDomain)); + $("tfilter").value = state.tFilter; + try { + const r = await fetch("/api/strings?" + new URLSearchParams({ lang: state.lang })); + state.tData = (await r.json()).domains; + } catch (e) { $("tprogress").textContent = "Can't load the strings."; return; } + renderList(); + scanUsage(); +} +/* Which screens use each string: render every screen once (cached) and read what it looked up. */ +async function scanUsage() { + const key = [state.theme, state.scheme, state.res, state.lang].join("|"); + if (state.usageKey === key) return; + state.usageKey = key; + state.usage = new Map(); + state.scanning = true; + const shots = state.info.shots; + let done = 0; + updateProgress(0, shots.length); + await Promise.all(shots.map(async ([shot, title]) => { + try { + const info = await render(shot, state.res); + for (const domain of ["messages", "themes"]) { + for (const text of (info.strings || {})[domain] || []) { + const k = tkey(domain, text); + if (!state.usage.has(k)) state.usage.set(k, []); + if (!state.usage.get(k).some(([s]) => s === shot)) state.usage.get(k).push([shot, title]); + } + } + } catch (e) { /* that screen failed; it shows in Preview */ } + done += 1; + if (state.usageKey === key) updateProgress(done, shots.length); + })); + if (state.usageKey === key) { state.scanning = false; renderList(); showPreview(); } +} +function counts() { + const entries = state.tData[state.tDomain]; + return { total: entries.length, done: entries.filter((e) => e.translation).length, problems: entries.filter((e) => e.problems.some((p) => !p.includes("line breaks"))).length }; +} +function updateProgress(scanned, total) { + if (!state.tData) return; + const c = counts(); + $("tprogress").textContent = `${c.done} of ${c.total} translated` + (c.problems ? ` · ${c.problems} with problems` : "") + + (scanned !== undefined && scanned < total ? ` · finding where strings are used ${scanned}/${total}` : ""); +} +function visible(entry) { + const search = state.tSearch.toLowerCase(); + if (search && !entry.text.toLowerCase().includes(search) && !entry.translation.toLowerCase().includes(search)) return false; + if (state.tFilter === "todo") return !entry.translation; + if (state.tFilter === "problem") return entry.problems.length > 0; + if (state.tFilter === "screen") return state.usage.has(tkey(state.tDomain, entry.text)); + return true; +} +function rowClass(entry) { + return "trow " + (entry.problems.some((p) => !p.includes("line breaks")) ? "problem" : entry.translation ? "done" : "todo"); +} +function renderList() { + if (!state.tData) return; + const list = $("tlist"); + list.replaceChildren(); + const entries = state.tData[state.tDomain].filter(visible); + for (const entry of entries) list.append(row(entry)); + if (!entries.length) { const p = document.createElement("p"); p.className = "tempty"; p.textContent = "No strings match."; list.append(p); } + updateProgress(); +} +function row(entry) { + const el = document.createElement("div"); + const key = tkey(state.tDomain, entry.text); + el.className = rowClass(entry) + (state.tSelected === key ? " selected" : ""); + el.dataset.key = key; + const source = document.createElement("div"); source.className = "source"; source.textContent = entry.text; + const box = document.createElement("textarea"); + box.value = entry.translation; box.rows = Math.min(6, Math.max(1, entry.text.split("\n").length)); + box.placeholder = "Not translated: shows in English"; box.lang = state.lang.replace("_", "-"); + box.setAttribute("aria-label", `Translation of: ${entry.text}`); + const meta = document.createElement("div"); meta.className = "tmeta"; + const notes = document.createElement("div"); + const problems = () => { + notes.replaceChildren(); + for (const p of entry.problems) { const s = document.createElement("div"); s.className = p.includes("line breaks") ? "twarn" : "tproblem"; s.textContent = p; notes.append(s); } + }; + const where = document.createElement("span"); where.className = "where"; + where.textContent = entry.sources.slice(0, 2).join(", ") + (entry.sources.length > 2 ? ` +${entry.sources.length - 2}` : ""); + meta.append(where); + for (const [shot, title] of state.usage.get(key) || []) { + const chip = document.createElement("button"); chip.type = "button"; chip.className = "chip"; chip.textContent = title; + chip.onclick = () => { select(entry, el); showPreview(shot); }; + meta.append(chip); + } + problems(); + box.addEventListener("focus", () => select(entry, el)); + box.addEventListener("change", () => store(entry, box, el, problems)); + box.addEventListener("keydown", (event) => { + if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) { + event.preventDefault(); + store(entry, box, el, problems); + const next = el.nextElementSibling && el.nextElementSibling.querySelector("textarea"); + if (next) next.focus(); + } + }); + el.append(source, box, notes, meta); + return el; +} +function select(entry, el) { + const key = tkey(state.tDomain, entry.text); + if (state.tSelected === key) return; + state.tSelected = key; + for (const other of document.querySelectorAll(".trow.selected")) other.classList.remove("selected"); + el.classList.add("selected"); + showPreview(); +} +async function store(entry, box, el, problems) { + const translation = box.value.replace(/\r\n/g, "\n"); + if (translation === entry.translation) return; + try { + const r = await fetch("/api/translate", { + method: "POST", headers: { "Content-Type": "application/json", "X-Theme-Server": "1" }, + body: JSON.stringify({ lang: state.lang, domain: state.tDomain, text: entry.text, translation }) }); + const body = await r.json(); + if (!r.ok) throw new Error(body.error || r.statusText); + entry.translation = translation; entry.problems = body.problems; + el.className = rowClass(entry) + (state.tSelected === tkey(state.tDomain, entry.text) ? " selected" : ""); + problems(); updateProgress(); + poll(); + } catch (error) { + box.setCustomValidity(`Not saved: ${error.message}`); box.reportValidity(); setTimeout(() => box.setCustomValidity(""), 3000); + } +} +function showPreview(focusShot) { + const pane = $("tpreview"); + if (!state.tSelected || !state.tData) return; + const [domain, text] = state.tSelected.split("\u0000"); + const entry = state.tData[domain].find((e) => e.text === text); + const used = state.usage.get(state.tSelected) || []; + pane.replaceChildren(); + const h = document.createElement("h2"); + if (!used.length) { + h.textContent = state.scanning ? "Finding where it's used…" : "Not on any screen shown here"; + const p = document.createElement("p"); p.className = "tempty"; + p.textContent = `It's used in: ${entry ? entry.sources.join(", ") : ""}`; + pane.append(h, p); + return; + } + h.textContent = `On ${used.length} screen${used.length > 1 ? "s" : ""} · ${state.res.replace("x", "×")}`; + const order = focusShot ? [...used.filter(([s]) => s === focusShot), ...used.filter(([s]) => s !== focusShot)] : used; + const [first, ...rest] = order; + const big = document.createElement("figure"); big.className = "big"; + big.innerHTML = `
`; + big.querySelector("img").src = "/api/render.png?" + params(first[0]); + big.querySelector("figcaption").textContent = first[1]; + big.onclick = () => { openShot(first[0]); }; + const shots = document.createElement("div"); shots.className = "shots"; + for (const [shot, title] of rest) { + const fig = document.createElement("figure"); + fig.innerHTML = `
`; + fig.querySelector("img").src = "/api/render.png?" + params(shot); + fig.querySelector("figcaption").textContent = title; + fig.onclick = () => showPreview(shot); + shots.append(fig); + } + pane.append(h, big, shots); +} +function openShot(shot) { + // The screen's full view, with its region boxes. + state.cards = state.info.shots.map(([name, title]) => ({ shot: name, res: state.res, title })); + const index = state.cards.findIndex((c) => c.shot === shot); + render(shot, state.res).then((info) => { state.cards[index].info = info; openDetail(index); }); +} +$("tdomain").onclick = (event) => { + if (!event.target.dataset.domain) return; + state.tDomain = event.target.dataset.domain; state.tSelected = null; + for (const b of $("tdomain").children) b.setAttribute("aria-pressed", String(b.dataset.domain === state.tDomain)); + save(); renderList(); + $("tpreview").innerHTML = '

Select a string to see it on the screens that use it.

'; +}; +$("tfilter").onchange = () => { state.tFilter = $("tfilter").value; save(); renderList(); }; +$("tsearch").oninput = () => { state.tSearch = $("tsearch").value; renderList(); }; + /* Live reload: the server's fingerprint of the theme folder changes when a file is saved. */ let lastChange = null; async function poll() { try { - const r = await fetch("/api/version?" + new URLSearchParams({ theme: state.theme })); + const r = await fetch("/api/version?" + new URLSearchParams({ theme: state.theme, lang: state.lang })); const { version } = await r.json(); if (state.version !== null && version !== state.version) { state.version = version; lastChange = new Date(); state.renders.clear(); const detail = state.detail; - for (const card of state.cards) fill(card); + if (state.view === "translate") showPreview(); + for (const card of state.view === "preview" ? state.cards || [] : []) fill(card); if (detail !== null) Promise.all(state.cards.map((c) => render(c.shot, c.res))).then(() => { if (state.detail === detail) openDetail(detail); }); // Refresh the scheme list too, in case theme.json changed it. state.info = await (await fetch("/api/info")).json(); fillSchemes(); @@ -411,9 +662,7 @@

Region

$("status").textContent = "Can't reach the server."; return; } fillControls(); - await poll(); - save(); - build(); + setView(state.view === "translate" ? "translate" : "preview"); setInterval(poll, 1000); })(); diff --git a/tools/theme_server/worker.py b/tools/theme_server/worker.py index 653c6a6b..0afc2849 100644 --- a/tools/theme_server/worker.py +++ b/tools/theme_server/worker.py @@ -232,7 +232,8 @@ def reply(**answer): reply(ok=True, missing=str(missing), regions=[], errors=[]) continue - reply(ok=True, regions=regions, errors=errors, ms=int((time.monotonic() - started) * 1000)) + reply(ok=True, regions=regions, errors=errors, strings=renderer.strings(), + ms=int((time.monotonic() - started) * 1000)) else: reply(ok=False, error=f"unknown command {command.get('cmd')!r}") From 18f2e09c473a4b081a0712d6faa181d829e0e4b4 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 17:23:28 +0800 Subject: [PATCH 05/28] Theme server: render with no window, on devices too - PM_HEADLESS=Y (testing mode only): pugwash draws onto a surface with SDL's software renderer, with no video or controller subsystem. The workers use it, so they don't need SDL's dummy driver: muOS's SDL has only "mali". Tried on muOS Jacaranda (RG35XX-H): the server ran there, rendering every screen next to a pugwash that owned the screen. On the desktop the renders are pixel-identical to the dummy driver's. - Chinese, Japanese and Korean showed as boxes: the theme matches against harbourmaster.device_info(), and with PM_PRETEND_RESOLUTION pugwash added the language to a copy, so "font[ja_JP]" never matched. pugwash now writes the capabilities it works out back into device_info(). - serve.py unpacks the Noto fonts at start, as the PortMaster/pugwash script does: on a fresh checkout nothing else had. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 2 ++ PortMaster/pylibs/pugwash/app.py | 41 ++++++++++++++++++++----- PortMaster/pylibs/pugwash/sdl/events.py | 3 +- tests/test_theme_server.py | 35 +++++++++++++++++++++ tools/theme_server/serve.py | 18 +++++++++++ tools/theme_server/worker.py | 9 ++---- 6 files changed, 94 insertions(+), 14 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 931f980c..84f6e5d3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -76,6 +76,8 @@ It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes ` - `serve.py` serves a page showing every screen rendered with a theme, at any resolution, colour scheme and language, and exports PNGs with `--export`. - Each render process, `worker.py`, is a headless pugwash for one theme version, scheme, resolution and language. SDL allows one GUI per process, and the screen size, the theme's `[capability]` overrides and the language are fixed at start. A new one starts when the theme's files change, so no state carries over. - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode. + - It draws with no window (`PM_HEADLESS=Y`, also `HM_TESTING` only): SDL's software renderer on a surface, with no video or controller subsystem. So it needs no dummy video driver, and it runs on a device next to a GUI that owns the screen (tried on muOS, whose SDL has only `mali`). + - `serve.py` unpacks the Chinese/Japanese/Korean fonts (`NotoSans.tar.xz`) at start, as the `PortMaster/pugwash` script does: the workers don't run that script. - It links a shared ports cache (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`). - `shots.py` holds the screen catalogue: one builder per screen, pushing scenes with real port data. - It records which translatable strings each render looked up, for the editor. diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index 8b7a5afd..1707f011 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -51,9 +51,15 @@ class PortMasterGUI(CallbackMixin, CommandsMixin, FifoControlMixin, PortInfoMixi FRAME_TIME = 1000 // 30 def __init__(self, *, first_scene=None, force_theme=None): + ## PM_HEADLESS=Y (testing mode only): no window, no video and no controllers, it + ## draws onto a surface with the software renderer. tools/theme_server's workers + ## use it, on SDL builds with no dummy video driver too (muOS has only "mali"). + self.headless = harbourmaster.HM_TESTING and os.environ.get('PM_HEADLESS', '') == 'Y' + # Initialize SDL sdl2.ext.init( - controller=True) + video=not self.headless, + controller=not self.headless) def nice_version(v): return f"{v.major}.{v.minor}.{v.patch}" @@ -90,7 +96,10 @@ def nice_version(v): display_mode = sdl2.video.SDL_DisplayMode() capabilities = harbourmaster.device_info() - if sdl2.video.SDL_GetCurrentDisplayMode(0, display_mode) != 0: + if self.headless: + self.display_width, self.display_height = capabilities['resolution'] + + elif sdl2.video.SDL_GetCurrentDisplayMode(0, display_mode) != 0: logger.error("Failed to get display mode:", sdl2.SDL_GetError()) self.display_width, self.display_height = capabilities['resolution'] @@ -118,6 +127,13 @@ def nice_version(v): if capabilities['device'] == 'default' and pretend_resolution is None: capabilities = harbourmaster.expand_info(dict(capabilities), override_resolution=(self.display_width, self.display_height)) + ## The theme matches against harbourmaster.device_info(): give it the screen worked + ## out above and the language (its "font[ja_JP]" & co). + device_info = harbourmaster.device_info() + if capabilities is not device_info: + device_info.update(capabilities) + capabilities = device_info + capabilities['capabilities'].append(lang.CURRENT_LANG) logger.info(capabilities) @@ -147,11 +163,18 @@ def nice_version(v): touch_file.unlink() - self.window = sdl2.ext.Window("PortMaster", size=capabilities['resolution'], flags=window_flags) - self.window.show() + if self.headless: + self.window = None + self.surface = sdl2.SDL_CreateRGBSurfaceWithFormat( + 0, *capabilities['resolution'], 32, sdl2.SDL_PIXELFORMAT_ARGB8888) + renderer = sdl2.ext.Renderer(self.surface.contents) - # Create a renderer for drawing on the window - renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED) + else: + self.window = sdl2.ext.Window("PortMaster", size=capabilities['resolution'], flags=window_flags) + self.window.show() + + # Create a renderer for drawing on the window + renderer = sdl2.ext.Renderer(self.window, flags=sdl2.SDL_RENDERER_ACCELERATED) sdl2.SDL_SetHint(sdl2.SDL_HINT_RENDER_SCALE_QUALITY, b"2") @@ -517,7 +540,11 @@ def quit(self): ## Any texture still referenced becomes a no-op once its renderer is gone. self.renderer.destroy() - self.window.close() + if self.window is not None: + self.window.close() + + else: + sdl2.SDL_FreeSurface(self.surface) sdl2.ext.quit() diff --git a/PortMaster/pylibs/pugwash/sdl/events.py b/PortMaster/pylibs/pugwash/sdl/events.py index 0b331bd4..7d45e593 100644 --- a/PortMaster/pylibs/pugwash/sdl/events.py +++ b/PortMaster/pylibs/pugwash/sdl/events.py @@ -100,7 +100,8 @@ class EventManager: def __init__(self, gui): self.gui = gui - sdl2.ext.init(controller=True) + headless = getattr(gui, 'headless', False) + sdl2.ext.init(video=not headless, controller=not headless) self.XBOX_FIXED = False diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 95a7b748..42be3390 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -64,6 +64,30 @@ def test_prepare_tools_dir(tmp_path): assert not any((work / "tools" / "PortMaster" / "themes").iterdir()) +def test_ensure_fonts(tmp_path, monkeypatch): + ## A fresh checkout has only NotoSans.tar.xz: the PortMaster/pugwash script unpacks + ## it, and the workers don't run that script. + import io + import tarfile + + resources = tmp_path / "pylibs" / "resources" + resources.mkdir(parents=True) + with tarfile.open(str(resources / "NotoSans.tar.xz"), "w:xz") as tar: + for name in ("NotoSansJP-Regular.ttf", "NotoSansTC-Regular.ttf"): + info = tarfile.TarInfo(name) + info.size = 4 + tar.addfile(info, io.BytesIO(b"font")) + + monkeypatch.setattr(serve, "PM_DIR", tmp_path) + serve.ensure_fonts() + assert (resources / "NotoSansJP-Regular.ttf").read_bytes() == b"font" + + ## Unpacked already: left alone. + (resources / "NotoSansJP-Regular.ttf").write_bytes(b"mine") + serve.ensure_fonts() + assert (resources / "NotoSansJP-Regular.ttf").read_bytes() == b"mine" + + def test_themes(tmp_path): for name, data in (("Alpha", {"#info": {"name": "Alpha Theme"}, "#schemes": {"#base": {}, "Dark": {}, "Light": {}}}), ("Beta", {"#info": {"name": "Beta", "default-scheme": "Light"}, "#schemes": {"Dark": {}, "Light": {}}})): @@ -283,6 +307,17 @@ def test_render_lists_the_strings_it_used(pool): assert all(text in known['themes'] for text in info['strings']['themes']) +@pytest.mark.sdl +@pytest.mark.own_display +@needs_checkout +def test_language_capability_picks_the_font(pool): + ## The default theme's "font[ja_JP]": the language reaches the theme's capabilities + ## with a pretend resolution too. + _, info = pool.render("default_theme", None, "1280x720", "ja_JP", "main_menu") + fonts = {region['data'].get('font') for region in info['regions']} + assert fonts == {"NotoSansJP-Regular.ttf"} + + @pytest.fixture def locales(tmp_path, monkeypatch): """The translation files, copied: saving writes there, not into the checkout.""" diff --git a/tools/theme_server/serve.py b/tools/theme_server/serve.py index 1eb04045..ab9059c3 100644 --- a/tools/theme_server/serve.py +++ b/tools/theme_server/serve.py @@ -26,6 +26,7 @@ import shutil import subprocess import sys +import tarfile import tempfile import threading import time @@ -347,6 +348,22 @@ def all_languages(): return [(code, names.get(code, code), code in have) for code in sorted(set(names) | have, key=lambda code: names.get(code, code))] +################################################################################ +## The Chinese, Japanese and Korean fonts: the PortMaster/pugwash script unpacks them on +## its first run, the workers import pugwash without it. +def ensure_fonts(): + resources = PM_DIR / "pylibs" / "resources" + if (resources / "NotoSansTC-Regular.ttf").is_file(): + return + + try: + with tarfile.open(str(resources / "NotoSans.tar.xz"), "r:xz") as tar: + tar.extractall(str(resources)) + + except (OSError, EOFError, tarfile.TarError) as err: + print(f"Unable to extract the Noto fonts, Chinese, Japanese and Korean won't show: {err}") + + ################################################################################ ## The ports cache def ensure_cache(cache, refresh=False): @@ -539,6 +556,7 @@ def main(argv=None): parser.add_argument("--langs", default="en_US", help="export: comma separated") options = parser.parse_args(argv) + ensure_fonts() ensure_cache(options.cache, options.refresh_ports) themes = Themes(options.themes) work_dir = Path(tempfile.mkdtemp(prefix="pm-theme-server-")) diff --git a/tools/theme_server/worker.py b/tools/theme_server/worker.py index 0afc2849..14e5de44 100644 --- a/tools/theme_server/worker.py +++ b/tools/theme_server/worker.py @@ -81,12 +81,9 @@ def device_env(width, height, lang): def sdl_env(): - """Headless SDL, unless the environment already picked a driver (the tests on a device - whose SDL has no dummy driver).""" - if "SDL_VIDEODRIVER" in os.environ: - return {'SDL_AUDIODRIVER': os.environ.get('SDL_AUDIODRIVER', "dummy")} - - return {'SDL_VIDEODRIVER': "dummy", 'SDL_AUDIODRIVER': "dummy", 'SDL_RENDER_DRIVER': "software"} + """Headless pugwash: no window or video driver, it draws onto a surface (PM_HEADLESS), + so it runs on SDL builds with no dummy driver and next to a GUI that owns the screen.""" + return {'PM_HEADLESS': "Y", 'SDL_AUDIODRIVER': "dummy"} def prepare_tools_dir(work, cache, theme_dir, scheme): From 712daa8e78a8892e30ffd37d0c12f273d6cd8385 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 17:39:45 +0800 Subject: [PATCH 06/28] QR codes as theme images: "image": "qr:{tag}" An image name starting with "qr:" is a QR code of the rest of the text, made with the vendored qrcode package (unused until now). Through the image templates it updates when its tags change. It's black on white with the quiet zone, one pixel per module and scaled without smoothing, so it stays sharp at any size: per texture on SDL 2.0.12 and up, the scale quality hint before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 2 +- PortMaster/pylibs/pugwash/sdl/images.py | 83 +++++++++++++++++++++++++ THEME.md | 14 +++++ tests/test_qr.py | 65 +++++++++++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 tests/test_qr.py diff --git a/CLAUDE.md b/CLAUDE.md index 84f6e5d3..a6536c9e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -89,7 +89,7 @@ Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is f ## Architecture -**Import setup**: the scripts prepend `pylibs/` and `exlibs/` to `sys.path`. `exlibs/` holds vendored third-party packages (loguru, requests, urllib3, certifi, ...), and `pylibs/` holds the project packages plus vendored `sdl2`, `png` and `pyqrcode`. Don't edit vendored code unless that's the point of the change. There are no star imports and no injected builtins: import names explicitly. +**Import setup**: the scripts prepend `pylibs/` and `exlibs/` to `sys.path`. `exlibs/` holds vendored third-party packages (loguru, requests, urllib3, certifi, `sdl2`, `png`, `qrcode`, ...), and `pylibs/` holds the project packages. A theme `image` of `qr:` is a QR code (`sdl/images.py`, `tests/test_qr.py`). Don't edit vendored code unless that's the point of the change. There are no star imports and no injected builtins: import names explicitly. **`harbourmaster` library** (`pylibs/harbourmaster/`): - `harbour.py`: `HarbourMaster`, the engine both the GUI and CLI drive. It holds `__init__`, config, sources and GCD modes. Its other methods live in mixins grouped by job: diff --git a/PortMaster/pylibs/pugwash/sdl/images.py b/PortMaster/pylibs/pugwash/sdl/images.py index a1a77c54..a938da86 100644 --- a/PortMaster/pylibs/pugwash/sdl/images.py +++ b/PortMaster/pylibs/pugwash/sdl/images.py @@ -4,6 +4,7 @@ # Textures and images, including background loading of port screenshots. import contextlib +import ctypes import os import sdl2 import sdl2.ext @@ -14,6 +15,50 @@ from .helpers import set_color_mod +QR_PREFIX = "qr:" + + +def qr_matrix(text): + ''' + The QR code of text as rows of booleans (True is dark), with its quiet zone. None + for no text. + ''' + if not text: + return None + + import qrcode + + code = qrcode.QRCode(border=4, error_correction=qrcode.constants.ERROR_CORRECT_M) + code.add_data(text) + code.make(fit=True) + return code.get_matrix() + + +def qr_surface(text): + 'The QR code of text as an SDL surface, one pixel per module (free it after use).' + matrix = qr_matrix(text) + if matrix is None: + return None + + size = len(matrix) + dark, light = b"\x00\x00\x00\xff", b"\xff\xff\xff\xff" + pixels = b"".join(dark if module else light for row in matrix for module in row) + + surf = sdl2.SDL_CreateRGBSurfaceWithFormat(0, size, size, 32, sdl2.SDL_PIXELFORMAT_RGBA32) + if not surf: + raise GUIRuntimeError(sdl2.SDL_GetError().decode("utf-8", "replace")) + + sdl2.SDL_LockSurface(surf) + try: + for y in range(size): + ctypes.memmove(surf.contents.pixels + y * surf.contents.pitch, pixels[y * size * 4:(y + 1) * size * 4], size * 4) + + finally: + sdl2.SDL_UnlockSurface(surf) + + return surf + + class Texture: ''' The Texture class is a base texture class used within pySDL2gui. @@ -468,6 +513,14 @@ def load(self, filename): elif filename in self.images: return self.images[filename] + elif isinstance(filename, str) and filename.startswith(QR_PREFIX): + image = self._load_qr(filename) + if image is not None: + self.images[filename] = image + self.cache.insert(0, filename) + + return image + else: res_filename = self.gui.resources.find(filename) @@ -493,6 +546,36 @@ def load(self, filename): return self.images[filename] + def _load_qr(self, filename): + ''' + "qr:": a QR code of the text, black on white with a quiet zone, one pixel + per module and drawn without smoothing, so it stays sharp at any size. + ''' + try: + surf = qr_surface(filename[len(QR_PREFIX):]) + + except Exception as err: + logger.error(f"Unable to make a QR code of {filename!r}: {err}") + return None + + if surf is None: + return None + + ## No smoothing: per texture from SDL 2.0.12, before that the scale quality hint + ## is read when a texture is made. + quality = sdl2.SDL_GetHint(sdl2.SDL_HINT_RENDER_SCALE_QUALITY) + sdl2.SDL_SetHint(sdl2.SDL_HINT_RENDER_SCALE_QUALITY, b"0") + try: + texture = sdl2.ext.renderer.Texture(self.renderer, surf) + texture.set_scale_mode("nearest") + + finally: + sdl2.SDL_SetHint(sdl2.SDL_HINT_RENDER_SCALE_QUALITY, quality or b"") + sdl2.SDL_FreeSurface(surf) + + self.textures[filename] = texture + return Image(texture, renderer=self.renderer) + def load_data_lazy(self, file_name, data): res_filename = self.gui.resources.find(file_name) diff --git a/THEME.md b/THEME.md index b24e001c..f73d258c 100644 --- a/THEME.md +++ b/THEME.md @@ -375,6 +375,20 @@ It also supports text auto-scrolling if it doesnt fit within the area it is disp Probably should write something here. +#### QR codes + +An `image` starting with `qr:` is a QR code of the text after it, which can use text template tags. It's redrawn when a tag's value changes, and an empty text shows nothing. + +```json + "server_qr": { + "area": [0.65, 0.2, 0.3, 0.4], + "image": "qr:{some.url}", + "image-mode": "fit" + } +``` + +The code is black on white with a white border, as phone cameras read best, and it's scaled up without smoothing so it stays sharp at any size. Give it a square area, or `"image-mode": "fit"` (the default), so it isn't stretched. + ### Lists The list system is quite adequate diff --git a/tests/test_qr.py b/tests/test_qr.py new file mode 100644 index 00000000..f2f3f431 --- /dev/null +++ b/tests/test_qr.py @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: MIT +# +# QR codes as theme images: "image": "qr:{some.text}" (pugwash/sdl/images.py). + +import ctypes + +import pytest + + +def test_qr_matrix(): + from pugwash.sdl.images import qr_matrix + + matrix = qr_matrix("http://192.168.0.2:8090/") + ## A square of modules, with the 4 module quiet zone all round. + assert len(matrix) == len(matrix[0]) >= 21 + 8 + assert not any(matrix[0]) and not any(row[0] for row in matrix) + ## The top left finder pattern starts after the quiet zone. + assert all(matrix[4][4:11]) + + assert qr_matrix("") is None + + +def read_texture(gui, image, size): + """Draws image size x size into a target texture and reads it back, RGBA rows.""" + import sdl2 + + renderer = gui.renderer.sdlrenderer + target = sdl2.SDL_CreateTexture(renderer, sdl2.SDL_PIXELFORMAT_RGBA32, sdl2.SDL_TEXTUREACCESS_TARGET, size, size) + sdl2.SDL_SetRenderTarget(renderer, target) + try: + image.draw_in((0, 0, size, size)) + pixels = (ctypes.c_uint8 * (size * size * 4))() + sdl2.SDL_RenderReadPixels(renderer, None, sdl2.SDL_PIXELFORMAT_RGBA32, pixels, size * 4) + + finally: + sdl2.SDL_SetRenderTarget(renderer, None) + sdl2.SDL_DestroyTexture(target) + + return bytes(pixels) + + +@pytest.mark.sdl +def test_qr_image(gui): + from pugwash.sdl.images import qr_matrix + + text = "qr:http://192.168.0.2:8090/" + image = gui.images.load(text) + matrix = qr_matrix(text[3:]) + modules = len(matrix) + assert (image.srcrect.w, image.srcrect.h) == (modules, modules) + assert gui.images.load(text) is image + + ## Scaled up without smoothing: every pixel is pure black or white, matching its module. + scale = 5 + pixels = read_texture(gui, image, modules * scale) + stride = modules * scale * 4 + for y in range(modules * scale): + for x in range(modules * scale): + red, alpha = pixels[y * stride + x * 4], pixels[y * stride + x * 4 + 3] + assert alpha == 255 and red == (0 if matrix[y // scale][x // scale] else 255), (x, y) + + +@pytest.mark.sdl +def test_qr_image_without_text(gui): + assert gui.images.load("qr:") is None From d9f813aaac7c9731d59be4ff0a352b65090d7a93 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 18:11:02 +0800 Subject: [PATCH 07/28] Theme editor on the device: Options -> Theme Editor The theme server now ships with PortMaster, in pylibs/theme_server (about 100 KB; tools/theme_server/serve.py and tools/i18n.py run it from a checkout as before). Options -> Theme Editor starts it and shows its address as text and as a QR code; a computer or phone on the same network gets the previews and the translation editor, with the device's themes and ports list. B stops it. - pugwash/scenes/theme_server.py runs `python3 -m theme_server.serve --managed` on port 8090 or the next free one, with as many render processes as MemAvailable allows (1-4). --managed reports the port as a JSON line and stops when stdin closes, so pugwash dying stops it too. Log: PortMaster/theme_server.txt. - The default theme has a theme_server section ({theme_server.url}, {theme_server.status}, "qr:{theme_server.url}"); other themes get a plain screen in PortMaster's own font, so the address stays readable. The preview catalogue has the screen too. - Workers run outside testing mode: PM_HEADLESS is honoured anywhere, and PM_PRETEND_RESOLUTION when headless. They take the ports from a config folder (--ports-config): the cache's or PortMaster's own, copying the files and linking the screenshot folders, and never as a first run, so that folder is left as it was (tested). - The theme server tests no longer need a git checkout, so the Test Suite runs them on devices. Tried on muOS Jacaranda (RG35XX-H) with test build 2026-10-18_0069: the server ran from the installed copy, rendered from the device's own ports, saved and removed a translation on the exFAT card, and stopped cleanly when its stdin closed. Every QR code rendered decodes to its address. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- .github/workflows/tests.yaml | 3 +- .gitignore | 1 + CLAUDE.md | 16 +- PortMaster/pylibs/default_theme/theme.json | 29 + PortMaster/pylibs/locales/strings.json | 246 ++++--- PortMaster/pylibs/locales/strings.themes.json | 2 +- PortMaster/pylibs/pugwash/app.py | 15 +- PortMaster/pylibs/pugwash/scenes/__init__.py | 4 + PortMaster/pylibs/pugwash/scenes/options.py | 10 + .../pylibs/pugwash/scenes/theme_server.py | 175 +++++ PortMaster/pylibs/theme_server/__init__.py | 6 + PortMaster/pylibs/theme_server/serve.py | 670 ++++++++++++++++++ .../pylibs}/theme_server/shots.py | 10 + .../pylibs}/theme_server/static/index.html | 0 .../pylibs/theme_server/translations.py | 215 ++++++ .../pylibs}/theme_server/worker.py | 47 +- THEME.md | 11 +- tests/test_i18n.py | 6 +- tests/test_theme_server.py | 160 ++++- tools/i18n.py | 204 +----- tools/theme_server/serve.py | 589 +-------------- 21 files changed, 1458 insertions(+), 961 deletions(-) create mode 100644 PortMaster/pylibs/pugwash/scenes/theme_server.py create mode 100644 PortMaster/pylibs/theme_server/__init__.py create mode 100644 PortMaster/pylibs/theme_server/serve.py rename {tools => PortMaster/pylibs}/theme_server/shots.py (97%) rename {tools => PortMaster/pylibs}/theme_server/static/index.html (100%) create mode 100644 PortMaster/pylibs/theme_server/translations.py rename {tools => PortMaster/pylibs}/theme_server/worker.py (83%) mode change 100644 => 100755 tools/i18n.py mode change 100644 => 100755 tools/theme_server/serve.py diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 409bc664..dfbec95b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -42,7 +42,8 @@ jobs: run: | flake8 --select=E9,F63,F7,F82,F811 \ PortMaster/pugwash PortMaster/harbourmaster \ - PortMaster/pylibs/harbourmaster PortMaster/pylibs/pugwash tests tools/test_suite + PortMaster/pylibs/harbourmaster PortMaster/pylibs/pugwash PortMaster/pylibs/theme_server \ + tests tools/test_suite tools/theme_server tools/i18n.py # The launchers (shellcheck is preinstalled on the runners) cd PortMaster && shellcheck -s bash --severity=error \ utils/pm_launch.txt PortMaster.sh */PortMaster.txt */launch_hooks.txt ../tools/installer.sh \ diff --git a/.gitignore b/.gitignore index 9ff689a0..0a7519d2 100644 --- a/.gitignore +++ b/.gitignore @@ -177,6 +177,7 @@ themes/ crowdin.yml pugwash.txt harbourmaster.txt +theme_server.txt PortMaster.zip pylibs.zip version diff --git a/CLAUDE.md b/CLAUDE.md index a6536c9e..e04826dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,7 +24,7 @@ python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt .venv/bin/pytest --cov --cov-report=term-missing # with coverage (.coveragerc); CI fails below the floor set in .github/workflows/tests.yaml python3 PortMaster/pugwash # run the GUI locally (windowed) python3 PortMaster/harbourmaster help # CLI: list/ports/install/uninstall/runtime_list/device_info/... -flake8 --select=E9,F63,F7,F82,F811 PortMaster/pugwash PortMaster/harbourmaster PortMaster/pylibs/harbourmaster PortMaster/pylibs/pugwash tests # the lint CI enforces; full flake8 still reports lots of existing style issues +flake8 --select=E9,F63,F7,F82,F811 PortMaster/pugwash PortMaster/harbourmaster PortMaster/pylibs/harbourmaster PortMaster/pylibs/pugwash PortMaster/pylibs/theme_server tests tools/test_suite tools/theme_server tools/i18n.py # the lint CI enforces; full flake8 still reports lots of existing style issues ./do_release.sh test # a test build in dist/: version YYYY-MM-DD_XX69 (next free one today), checkout untouched ./do_release.sh testsuite # a test build + dist/pmtest.zip, the "PortMaster Test Suite" port for testers (see below) ./do_release.sh alpha|beta|stable [VER] # a release in dist/ (+ version.json); stable (or MAKE_INSTALL=Y) also builds the makeself installers @@ -62,7 +62,7 @@ Installing is covered in `test_install.py` (bad data, layouts, rollback, runtime Guard tests for restructuring: - `test_release_layout.py` runs both scripts from a `pylibs.zip` layout with no `.git`, which is the on-device bootstrap path. - `test_api_snapshot.py` checks that every public name in `tests/data/api_snapshot.json` is still reachable. Update its `AREAS` table when code moves. -- `test_i18n_snapshot.py` checks that the set of translatable strings (`tests/data/msgids.txt`) hasn't changed. +- `test_i18n.py` checks that the translatable strings in the code match `locales/strings.json` (`tools/i18n.py extract` updates it). **PortMaster Test Suite** (`tools/test_suite/`, built by `./do_release.sh testsuite` into `dist/pmtest.zip`): a port testers drop into `autoinstall/`. It installs the test build it carries, then runs, on the device: - the pytest suite (from its own copy of `tests/`, `tools/` and the code, with pytest 7.4 in `pmtest/site/`) @@ -72,17 +72,19 @@ Guard tests for restructuring: It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes `pmtest/reports/--.zip`, scrubbed of MAC/private IP addresses, serials, Wi-Fi names and the hostname (`suite.scrub`). The tester's own `pugwash.txt`/`harbourmaster.txt`/`log.txt` are set aside in `pmtest/saved-logs/` for the run and put back afterwards (or by the next run after a crash), so a report only holds the run's logs. pugwash and the GUI tests run in the environment the installed launcher gives the GUI (`pm_launch.txt` with `PM_LAUNCH_ENV_ONLY=Y`). The build also writes `dist/pmtest.zip.md5`, for `harbourmaster install /pmtest.zip.md5`. `python3 -I tools/test_suite/matrix.py --out DIR` turns collected reports into `matrix.json` and a self-contained `matrix.html` coverage page, starting with a device × CFW release heat map. `tools/test_suite/devices.py` names the hardware behind each CFW's device string (`DEVICES`, `FAMILIES`) and reads a vendored copy of PortMaster-Info's `device_info.json` (`--refresh` updates it) for supported-but-untested cells; a report it can't place is printed and listed under Other; it and `receive.py` must keep running on Python 3.7 with only the standard library. A build made with `PM_PMTEST_UPLOAD=http://:8770/` carries `pmtest/upload.txt` and, if the tester agrees on the start screen, POSTs each finished report there, where `python3 -I tools/test_suite/receive.py ` saves it on the LAN. The public endpoint is the `pmtest-reports` Cloudflare Worker (its own repo, with `collect.py` to download reports); HTTPS uses PortMaster's certifi bundle. Builds without the variable send nothing. Tested in `tests/test_test_suite.py` and `tests/test_build.py`. -**Theme preview server** (`tools/theme_server/`, for theme authors, documented in `THEME.md`): +**Theme preview server** (`PortMaster/pylibs/theme_server/`, run from a checkout with `tools/theme_server/serve.py`; documented in `THEME.md`): - `serve.py` serves a page showing every screen rendered with a theme, at any resolution, colour scheme and language, and exports PNGs with `--export`. - Each render process, `worker.py`, is a headless pugwash for one theme version, scheme, resolution and language. SDL allows one GUI per process, and the screen size, the theme's `[capability]` overrides and the language are fixed at start. A new one starts when the theme's files change, so no state carries over. - - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode. - - It draws with no window (`PM_HEADLESS=Y`, also `HM_TESTING` only): SDL's software renderer on a surface, with no video or controller subsystem. So it needs no dummy video driver, and it runs on a device next to a GUI that owns the screen (tried on muOS, whose SDL has only `mali`). + - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode or headless. + - It draws with no window (`PM_HEADLESS=Y`): SDL's software renderer on a surface, with no video or controller subsystem. So it needs no dummy video driver, and it runs on a device next to the GUI that owns the screen (muOS's SDL has only `mali`). + - It takes the ports from a PortMaster config folder (`--ports-config`): the cache's (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`) or, on a device, PortMaster's own. It copies the files and links the screenshot folders, so that folder is never changed. - `serve.py` unpacks the Chinese/Japanese/Korean fonts (`NotoSans.tar.xz`) at start, as the `PortMaster/pugwash` script does: the workers don't run that script. - - It links a shared ports cache (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`). - `shots.py` holds the screen catalogue: one builder per screen, pushing scenes with real port data. - It records which translatable strings each render looked up, for the editor. -- **Translate view:** a translation editor. `/api/strings` lists a language's strings, and `/api/translate` saves one into the checkout's language files, in `strings.json` order. It only accepts requests from the page itself: they need the `X-Theme-Server` header. +- **Translate view:** a translation editor. `/api/strings` lists a language's strings, and `/api/translate` saves one into the language files, in `strings.json` order. It only accepts requests from the page itself: they need the `X-Theme-Server` header. - Renderers are keyed by the theme and the language files together, so a saved translation re-renders. +- `translations.py` is the language-file code (`tools/i18n.py` runs its `extract`/`check`). +- **On a device:** Options → Theme Editor (`pugwash/scenes/theme_server.py`) runs `python3 -m theme_server.serve --managed` on port 8090 (or the next free one), with as many workers as `MemAvailable` allows. The screen shows the address as text and as a QR code (`"image": "qr:{theme_server.url}"`, the default theme's `theme_server` section, or a plain fallback). `--managed` reports `{"ok": true, "port": N}` on stdout and stops when stdin closes, so B, or pugwash dying, stops it. Its log is `PortMaster/theme_server.txt`. - Tested in `tests/test_theme_server.py`. Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is fixed and the marker should then be removed. pugwash's `main()` is wrapped in `logger.catch`, so it exits 0 even after a crash; check its output for tracebacks rather than relying on the exit code. diff --git a/PortMaster/pylibs/default_theme/theme.json b/PortMaster/pylibs/default_theme/theme.json index 35ff6405..8bf5d0de 100644 --- a/PortMaster/pylibs/default_theme/theme.json +++ b/PortMaster/pylibs/default_theme/theme.json @@ -590,6 +590,35 @@ }, "#element:button_bar": {} }, + "theme_server": { + "#element:background": {}, + "server_title": { + "area": [0.04, 0.02, 0.96, 0.12], + "text": "{scene.title}", + "font-size": 24, + "font-color": "list_selected", + "align": "center" + }, + "server_qr_panel": { + "area": [0.04, 0.14, 0.46, 0.9], + "fill": "panels" + }, + "server_qr": { + "area": [0.06, 0.16, 0.44, 0.88], + "image": "qr:{theme_server.url}", + "image-mode": "fit" + }, + "server_info": { + "area": [0.5, 0.14, 0.96, 0.9], + "fill": "panels", + "border": 10, + "font-size": 15, + "font-color": "general_font", + "text-wrap": true, + "text": "{theme_server.status}" + }, + "#element:button_bar": {} + }, "runtime_list": { "#element:background": { "music": null diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json index d6145555..7813cde2 100644 --- a/PortMaster/pylibs/locales/strings.json +++ b/PortMaster/pylibs/locales/strings.json @@ -266,10 +266,10 @@ "pylibs/harbourmaster/util/net.py:152" ], "Unknown IP": [ - "pylibs/pugwash/app.py:229" + "pylibs/pugwash/app.py:253" ], "N/A": [ - "pylibs/pugwash/app.py:382", + "pylibs/pugwash/app.py:406", "pylibs/pugwash/app_ports.py:70" ], "Fetching latest gameinfo.xml files and cover images.": [ @@ -432,7 +432,7 @@ "pylibs/pugwash/scenes/featured.py:30", "pylibs/pugwash/scenes/language.py:29", "pylibs/pugwash/scenes/language.py:30", - "pylibs/pugwash/scenes/options.py:158", + "pylibs/pugwash/scenes/options.py:164", "pylibs/pugwash/scenes/port_info.py:36", "pylibs/pugwash/scenes/port_info.py:39", "pylibs/pugwash/scenes/port_info.py:129", @@ -590,7 +590,7 @@ "pylibs/pugwash/scenes/keyboard.py:33", "pylibs/pugwash/scenes/keyboard.py:41", "pylibs/pugwash/scenes/main_menu.py:55", - "pylibs/pugwash/scenes/options.py:158" + "pylibs/pugwash/scenes/options.py:164" ], "Lower Case": [ "pylibs/pugwash/scenes/keyboard.py:41" @@ -653,33 +653,33 @@ ], "Enabled": [ "pylibs/pugwash/scenes/main_menu.py:76", - "pylibs/pugwash/scenes/options.py:49", - "pylibs/pugwash/scenes/options.py:53", - "pylibs/pugwash/scenes/options.py:93", - "pylibs/pugwash/scenes/options.py:136", - "pylibs/pugwash/scenes/options.py:140", - "pylibs/pugwash/scenes/options.py:144", - "pylibs/pugwash/scenes/options.py:200", - "pylibs/pugwash/scenes/options.py:212", - "pylibs/pugwash/scenes/options.py:226", - "pylibs/pugwash/scenes/options.py:238", - "pylibs/pugwash/scenes/options.py:314", - "pylibs/pugwash/scenes/options.py:323" + "pylibs/pugwash/scenes/options.py:55", + "pylibs/pugwash/scenes/options.py:59", + "pylibs/pugwash/scenes/options.py:99", + "pylibs/pugwash/scenes/options.py:142", + "pylibs/pugwash/scenes/options.py:146", + "pylibs/pugwash/scenes/options.py:150", + "pylibs/pugwash/scenes/options.py:206", + "pylibs/pugwash/scenes/options.py:218", + "pylibs/pugwash/scenes/options.py:232", + "pylibs/pugwash/scenes/options.py:244", + "pylibs/pugwash/scenes/options.py:320", + "pylibs/pugwash/scenes/options.py:329" ], "Disabled": [ "pylibs/pugwash/scenes/main_menu.py:76", - "pylibs/pugwash/scenes/options.py:49", - "pylibs/pugwash/scenes/options.py:53", - "pylibs/pugwash/scenes/options.py:93", - "pylibs/pugwash/scenes/options.py:136", - "pylibs/pugwash/scenes/options.py:140", - "pylibs/pugwash/scenes/options.py:144", - "pylibs/pugwash/scenes/options.py:200", - "pylibs/pugwash/scenes/options.py:212", - "pylibs/pugwash/scenes/options.py:226", - "pylibs/pugwash/scenes/options.py:238", - "pylibs/pugwash/scenes/options.py:314", - "pylibs/pugwash/scenes/options.py:323" + "pylibs/pugwash/scenes/options.py:55", + "pylibs/pugwash/scenes/options.py:59", + "pylibs/pugwash/scenes/options.py:99", + "pylibs/pugwash/scenes/options.py:142", + "pylibs/pugwash/scenes/options.py:146", + "pylibs/pugwash/scenes/options.py:150", + "pylibs/pugwash/scenes/options.py:206", + "pylibs/pugwash/scenes/options.py:218", + "pylibs/pugwash/scenes/options.py:232", + "pylibs/pugwash/scenes/options.py:244", + "pylibs/pugwash/scenes/options.py:320", + "pylibs/pugwash/scenes/options.py:329" ], "Are you sure you want to exit PortMaster?": [ "pylibs/pugwash/scenes/main_menu.py:124", @@ -687,217 +687,224 @@ "pylibs/pugwash/scenes/startup.py:242" ], "Options Menu": [ - "pylibs/pugwash/scenes/options.py:21" + "pylibs/pugwash/scenes/options.py:22" ], "Interface": [ - "pylibs/pugwash/scenes/options.py:27" + "pylibs/pugwash/scenes/options.py:28" ], "Choose Language": [ - "pylibs/pugwash/scenes/options.py:31" + "pylibs/pugwash/scenes/options.py:32" ], "Select the language PortMaster uses.": [ - "pylibs/pugwash/scenes/options.py:32" + "pylibs/pugwash/scenes/options.py:33" ], "Select Theme": [ - "pylibs/pugwash/scenes/options.py:35", + "pylibs/pugwash/scenes/options.py:36", "pylibs/pugwash/scenes/themes.py:13" ], "Select a theme for PortMaster.": [ - "pylibs/pugwash/scenes/options.py:36" + "pylibs/pugwash/scenes/options.py:37" ], "Select Color Scheme": [ - "pylibs/pugwash/scenes/options.py:42" + "pylibs/pugwash/scenes/options.py:43" ], "Select a colour scheme for PortMaster": [ - "pylibs/pugwash/scenes/options.py:43" + "pylibs/pugwash/scenes/options.py:44" + ], + "Theme Editor": [ + "pylibs/pugwash/scenes/options.py:48", + "pylibs/pugwash/scenes/theme_server.py:66" + ], + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": [ + "pylibs/pugwash/scenes/options.py:49" ], "Audio": [ - "pylibs/pugwash/scenes/options.py:45" + "pylibs/pugwash/scenes/options.py:51" ], "Music: ": [ - "pylibs/pugwash/scenes/options.py:49", - "pylibs/pugwash/scenes/options.py:314" + "pylibs/pugwash/scenes/options.py:55", + "pylibs/pugwash/scenes/options.py:320" ], "Enable or Disable background music in PortMaster.": [ - "pylibs/pugwash/scenes/options.py:50" + "pylibs/pugwash/scenes/options.py:56" ], "Sound FX: ": [ - "pylibs/pugwash/scenes/options.py:53", - "pylibs/pugwash/scenes/options.py:323" + "pylibs/pugwash/scenes/options.py:59", + "pylibs/pugwash/scenes/options.py:329" ], "Enable or Disable soundfx in PortMaster.": [ - "pylibs/pugwash/scenes/options.py:54" + "pylibs/pugwash/scenes/options.py:60" ], "Port Metadata": [ - "pylibs/pugwash/scenes/options.py:57" + "pylibs/pugwash/scenes/options.py:63" ], "Metadata Update: ": [ - "pylibs/pugwash/scenes/options.py:61", - "pylibs/pugwash/scenes/options.py:351" + "pylibs/pugwash/scenes/options.py:67", + "pylibs/pugwash/scenes/options.py:357" ], "Auto": [ - "pylibs/pugwash/scenes/options.py:61", - "pylibs/pugwash/scenes/options.py:351" + "pylibs/pugwash/scenes/options.py:67", + "pylibs/pugwash/scenes/options.py:357" ], "Manual": [ - "pylibs/pugwash/scenes/options.py:61", - "pylibs/pugwash/scenes/options.py:351" + "pylibs/pugwash/scenes/options.py:67", + "pylibs/pugwash/scenes/options.py:357" ], "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": [ - "pylibs/pugwash/scenes/options.py:62" + "pylibs/pugwash/scenes/options.py:68" ], "Metadata Refresh": [ - "pylibs/pugwash/scenes/options.py:66" + "pylibs/pugwash/scenes/options.py:72" ], "Manually update port metadata with missing/updated information and artwork.": [ - "pylibs/pugwash/scenes/options.py:67" + "pylibs/pugwash/scenes/options.py:73" ], "Ports Location: ": [ - "pylibs/pugwash/scenes/options.py:72", - "pylibs/pugwash/scenes/options.py:81", - "pylibs/pugwash/scenes/options.py:388" + "pylibs/pugwash/scenes/options.py:78", + "pylibs/pugwash/scenes/options.py:87", + "pylibs/pugwash/scenes/options.py:394" ], "Roms section": [ - "pylibs/pugwash/scenes/options.py:72", - "pylibs/pugwash/scenes/options.py:366" + "pylibs/pugwash/scenes/options.py:78", + "pylibs/pugwash/scenes/options.py:372" ], "Ports tab": [ - "pylibs/pugwash/scenes/options.py:72", - "pylibs/pugwash/scenes/options.py:367" + "pylibs/pugwash/scenes/options.py:78", + "pylibs/pugwash/scenes/options.py:373" ], "Location where ports should be installed to.": [ - "pylibs/pugwash/scenes/options.py:73", - "pylibs/pugwash/scenes/options.py:82" + "pylibs/pugwash/scenes/options.py:79", + "pylibs/pugwash/scenes/options.py:88" ], "SD 1": [ - "pylibs/pugwash/scenes/options.py:81" + "pylibs/pugwash/scenes/options.py:87" ], "SD 2": [ - "pylibs/pugwash/scenes/options.py:81" + "pylibs/pugwash/scenes/options.py:87" ], "System": [ - "pylibs/pugwash/scenes/options.py:84" + "pylibs/pugwash/scenes/options.py:90" ], "Runtime Manager": [ - "pylibs/pugwash/scenes/options.py:88", + "pylibs/pugwash/scenes/options.py:94", "pylibs/pugwash/scenes/runtimes.py:13" ], "Manage port runtimes.": [ - "pylibs/pugwash/scenes/options.py:89" + "pylibs/pugwash/scenes/options.py:95" ], "Experimental Ports: ": [ - "pylibs/pugwash/scenes/options.py:93", - "pylibs/pugwash/scenes/options.py:212" + "pylibs/pugwash/scenes/options.py:99", + "pylibs/pugwash/scenes/options.py:218" ], "Show or hide experimental ports.": [ - "pylibs/pugwash/scenes/options.py:94" + "pylibs/pugwash/scenes/options.py:100" ], "Update Ports": [ - "pylibs/pugwash/scenes/options.py:98" + "pylibs/pugwash/scenes/options.py:104" ], "Fetch latest ports information.": [ - "pylibs/pugwash/scenes/options.py:99" + "pylibs/pugwash/scenes/options.py:105" ], "Update PortMaster": [ - "pylibs/pugwash/scenes/options.py:103" + "pylibs/pugwash/scenes/options.py:109" ], "Force check for a new PortMaster version.": [ - "pylibs/pugwash/scenes/options.py:104" + "pylibs/pugwash/scenes/options.py:110" ], "Restore PortMaster": [ - "pylibs/pugwash/scenes/options.py:109" + "pylibs/pugwash/scenes/options.py:115" ], "This will restore PortMaster to the latest stable version of PortMaster.": [ - "pylibs/pugwash/scenes/options.py:110" + "pylibs/pugwash/scenes/options.py:116" ], "Release Channel: {channel}": [ - "pylibs/pugwash/scenes/options.py:114" + "pylibs/pugwash/scenes/options.py:120" ], "Change release channel of PortMaster, either beta or stable.": [ - "pylibs/pugwash/scenes/options.py:116" + "pylibs/pugwash/scenes/options.py:122" ], "Enable Alpha Releases": [ - "pylibs/pugwash/scenes/options.py:122" + "pylibs/pugwash/scenes/options.py:128" ], "Change release channel of PortMaster to alpha.": [ - "pylibs/pugwash/scenes/options.py:123" + "pylibs/pugwash/scenes/options.py:129" ], "Controller Mode: {controller_mode}": [ - "pylibs/pugwash/scenes/options.py:129", - "pylibs/pugwash/scenes/options.py:341" + "pylibs/pugwash/scenes/options.py:135", + "pylibs/pugwash/scenes/options.py:347" ], "Toggle between various controller layouts.": [ - "pylibs/pugwash/scenes/options.py:130" + "pylibs/pugwash/scenes/options.py:136" ], "Secret Options": [ - "pylibs/pugwash/scenes/options.py:133" + "pylibs/pugwash/scenes/options.py:139" ], "All Ports: ": [ - "pylibs/pugwash/scenes/options.py:136", - "pylibs/pugwash/scenes/options.py:200" + "pylibs/pugwash/scenes/options.py:142", + "pylibs/pugwash/scenes/options.py:206" ], "Show all ports, ignoring requirements.": [ - "pylibs/pugwash/scenes/options.py:137" + "pylibs/pugwash/scenes/options.py:143" ], "CWTBE Mode: ": [ - "pylibs/pugwash/scenes/options.py:140", - "pylibs/pugwash/scenes/options.py:226" + "pylibs/pugwash/scenes/options.py:146", + "pylibs/pugwash/scenes/options.py:232" ], "Enable gptokeyb2 by default.": [ - "pylibs/pugwash/scenes/options.py:141" + "pylibs/pugwash/scenes/options.py:147" ], "Debug Mode: ": [ - "pylibs/pugwash/scenes/options.py:144", - "pylibs/pugwash/scenes/options.py:238" + "pylibs/pugwash/scenes/options.py:150", + "pylibs/pugwash/scenes/options.py:244" ], "Enable debug logging all the time.": [ - "pylibs/pugwash/scenes/options.py:145" + "pylibs/pugwash/scenes/options.py:151" ], "Delete PortMaster Config": [ - "pylibs/pugwash/scenes/options.py:148" + "pylibs/pugwash/scenes/options.py:154" ], "This can break stuff, don't touch unless you know what you are doing.": [ - "pylibs/pugwash/scenes/options.py:149", - "pylibs/pugwash/scenes/options.py:153" + "pylibs/pugwash/scenes/options.py:155", + "pylibs/pugwash/scenes/options.py:159" ], "Delete PortMaster Runtimes": [ - "pylibs/pugwash/scenes/options.py:152" + "pylibs/pugwash/scenes/options.py:158" ], "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": [ - "pylibs/pugwash/scenes/options.py:242" + "pylibs/pugwash/scenes/options.py:248" ], "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": [ - "pylibs/pugwash/scenes/options.py:252" + "pylibs/pugwash/scenes/options.py:258" ], "PortMaster will now quit so you can run the restoration port.": [ - "pylibs/pugwash/scenes/options.py:255" + "pylibs/pugwash/scenes/options.py:261" ], "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": [ - "pylibs/pugwash/scenes/options.py:262" + "pylibs/pugwash/scenes/options.py:268" ], "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": [ - "pylibs/pugwash/scenes/options.py:289" + "pylibs/pugwash/scenes/options.py:295" ], "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": [ - "pylibs/pugwash/scenes/options.py:357" + "pylibs/pugwash/scenes/options.py:363" ], "Are you sure you want to move ports from the {from_mode} to the {to_mode}": [ - "pylibs/pugwash/scenes/options.py:378" + "pylibs/pugwash/scenes/options.py:384" ], "SDCARD 1": [ - "pylibs/pugwash/scenes/options.py:397" + "pylibs/pugwash/scenes/options.py:403" ], "SDCARD 2": [ - "pylibs/pugwash/scenes/options.py:398" + "pylibs/pugwash/scenes/options.py:404" ], "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": [ - "pylibs/pugwash/scenes/options.py:402" + "pylibs/pugwash/scenes/options.py:408" ], "Deleting Runtimes:": [ - "pylibs/pugwash/scenes/options.py:466" + "pylibs/pugwash/scenes/options.py:476" ], "Source Manager": [ - "pylibs/pugwash/scenes/options.py:491" + "pylibs/pugwash/scenes/options.py:501" ], "Port Info Popup": [ "pylibs/pugwash/scenes/port_info.py:13" @@ -1005,6 +1012,27 @@ "I Understand": [ "pylibs/pugwash/scenes/startup.py:212" ], + "Starting the theme editor...": [ + "pylibs/pugwash/scenes/theme_server.py:74" + ], + "Stop": [ + "pylibs/pugwash/scenes/theme_server.py:76" + ], + "it stopped": [ + "pylibs/pugwash/scenes/theme_server.py:109" + ], + "Unable to start the theme editor: {error}": [ + "pylibs/pugwash/scenes/theme_server.py:122" + ], + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": [ + "pylibs/pugwash/scenes/theme_server.py:129" + ], + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": [ + "pylibs/pugwash/scenes/theme_server.py:138" + ], + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": [ + "pylibs/pugwash/scenes/theme_server.py:166" + ], "{theme_name} (Selected)": [ "pylibs/pugwash/scenes/themes.py:31" ], diff --git a/PortMaster/pylibs/locales/strings.themes.json b/PortMaster/pylibs/locales/strings.themes.json index 851afbdb..1bab6e1a 100644 --- a/PortMaster/pylibs/locales/strings.themes.json +++ b/PortMaster/pylibs/locales/strings.themes.json @@ -87,7 +87,7 @@ ], "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": [ "hiviz/theme.json:526", - "default_theme/theme.json:621" + "default_theme/theme.json:650" ], "{ports_list.filter_ports} games": [ "jimmyhat999_grid_dark/theme.json:239", diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index 1707f011..62d8d096 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -51,10 +51,11 @@ class PortMasterGUI(CallbackMixin, CommandsMixin, FifoControlMixin, PortInfoMixi FRAME_TIME = 1000 // 30 def __init__(self, *, first_scene=None, force_theme=None): - ## PM_HEADLESS=Y (testing mode only): no window, no video and no controllers, it - ## draws onto a surface with the software renderer. tools/theme_server's workers - ## use it, on SDL builds with no dummy video driver too (muOS has only "mali"). - self.headless = harbourmaster.HM_TESTING and os.environ.get('PM_HEADLESS', '') == 'Y' + ## PM_HEADLESS=Y: no window, no video and no controllers, it draws onto a surface + ## with the software renderer. The theme server's render workers (theme_server/ + ## worker.py) use it, on a device too: muOS's SDL has only its "mali" driver, and + ## the PortMaster that started the server owns the screen. + self.headless = os.environ.get('PM_HEADLESS', '') == 'Y' # Initialize SDL sdl2.ext.init( @@ -110,10 +111,10 @@ def nice_version(v): logger.info(f"Display size: {self.display_width}x{self.display_height}") pretend_resolution = None - if harbourmaster.HM_TESTING: + if harbourmaster.HM_TESTING or self.headless: ## To pretend to be a different device set the device_info environment ## variables, eg: DEVICE_NAME=RG353V CFW_NAME=ArkOS DEVICE_ARCH=aarch64, - ## and PM_PRETEND_RESOLUTION=1024x768 for its screen (tools/theme_server). + ## and PM_PRETEND_RESOLUTION=1024x768 for its screen (the theme server). pretend_resolution = parse_resolution(os.environ.get('PM_PRETEND_RESOLUTION', '')) capabilities = harbourmaster.expand_info(dict(capabilities), override_resolution=pretend_resolution) @@ -140,7 +141,7 @@ def nice_version(v): print(capabilities) # Create the window - if harbourmaster.HM_TESTING: + if harbourmaster.HM_TESTING or self.headless: window_flags = None else: window_flags = sdl2.SDL_WINDOW_FULLSCREEN diff --git a/PortMaster/pylibs/pugwash/scenes/__init__.py b/PortMaster/pylibs/pugwash/scenes/__init__.py index 624e2fd7..25dc19d6 100644 --- a/PortMaster/pylibs/pugwash/scenes/__init__.py +++ b/PortMaster/pylibs/pugwash/scenes/__init__.py @@ -28,6 +28,9 @@ ThemesScene, ThemeSchemeScene, ) +from .theme_server import ( + ThemeServerScene, + ) from .language import ( LanguageScene, ) @@ -78,5 +81,6 @@ 'SourceScene', 'TempMenuScene', 'ThemeSchemeScene', + 'ThemeServerScene', 'ThemesScene', ) diff --git a/PortMaster/pylibs/pugwash/scenes/options.py b/PortMaster/pylibs/pugwash/scenes/options.py index cc319dd8..ff891d12 100644 --- a/PortMaster/pylibs/pugwash/scenes/options.py +++ b/PortMaster/pylibs/pugwash/scenes/options.py @@ -12,6 +12,7 @@ from .keyboard import OnScreenKeyboard from .language import LanguageScene from .runtimes import RuntimesScene +from .theme_server import ThemeServerScene from .themes import ThemeSchemeScene, ThemesScene @@ -42,6 +43,11 @@ def __init__(self, gui): _("Select Color Scheme"), description=_("Select a colour scheme for PortMaster")) + self.tags['option_list'].add_option( + 'theme-editor', + _("Theme Editor"), + description=_("Preview themes and edit PortMaster's translations from a computer or phone on the same network.")) + self.tags['option_list'].add_option(None, _("Audio")) self.tags['option_list'].add_option( @@ -438,6 +444,10 @@ def do_update(self, events): self.gui.push_scene('select-scheme', ThemeSchemeScene(self.gui)) return True + if selected_option == 'theme-editor': + self.gui.push_scene('theme-editor', ThemeServerScene(self.gui)) + return True + if selected_option == 'select-language': self.gui.push_scene('select-language', LanguageScene(self.gui)) return True diff --git a/PortMaster/pylibs/pugwash/scenes/theme_server.py b/PortMaster/pylibs/pugwash/scenes/theme_server.py new file mode 100644 index 00000000..7e25ae8b --- /dev/null +++ b/PortMaster/pylibs/pugwash/scenes/theme_server.py @@ -0,0 +1,175 @@ +# SPDX-License-Identifier: MIT +# +# The theme editor: runs the theme preview server (pylibs/theme_server) on the device and +# shows its address, as text and as a QR code, until B stops it. + +import json +import os +import subprocess +import sys +import threading + +from loguru import logger + +from harbourmaster.i18n import _ +from pugwash import PYLIB_PATH, lang +from pugwash.util import get_ip_address +from .base import BaseScene + + +PORT = 8090 +## Render processes are 60-90 MB each: as many as the free memory allows, up to this. +MAX_WORKERS = 4 + + +def worker_count(meminfo="/proc/meminfo"): + """Render processes for the free memory, keeping ~150 MB back.""" + try: + with open(meminfo) as fh: + for line in fh: + if line.startswith("MemAvailable:"): + available_mb = int(line.split()[1]) // 1024 + return max(1, min(MAX_WORKERS, (available_mb - 150) // 100)) + except (OSError, ValueError, IndexError): + pass + + return 2 + + +def fallback_section(): + """For themes without a "theme_server" section: plain, in PortMaster's own font for the + language (the default theme's "font[ja_JP]" & co), as the address has to be readable.""" + font = "DejaVuSans.ttf" + try: + with open(str(PYLIB_PATH / "default_theme" / "theme.json"), encoding="utf-8") as fh: + base = json.load(fh).get("#base", {}) + font = base.get(f"font[{lang.CURRENT_LANG}]") or base.get("font") or font + except (OSError, ValueError, AttributeError): + pass + + text = {"font": font, "font-color": "#e8e8ec", "font-size": 18} + return { + "background": {"area": [0.0, 0.0, 1.0, 1.0], "fill": "#1c1c22"}, + "server_title": {**text, "area": [0.05, 0.02, 0.95, 0.12], "font-size": 26, "align": "center", + "text": "{scene.title}"}, + "server_qr": {"area": [0.05, 0.15, 0.45, 0.85], "image": "qr:{theme_server.url}", "image-mode": "fit"}, + "server_info": {**text, "area": [0.5, 0.15, 0.95, 0.9], "text-wrap": True, "text": "{theme_server.status}"}, + } + + +class ThemeServerScene(BaseScene): + def __init__(self, gui, start=True): + """start=False: only the screen (the theme server's own preview of it).""" + super().__init__(gui) + self.scene_title = _("Theme Editor") + self.proc = None + self.url = None + self.stopped = False + + if "theme_server" not in self.gui.theme_data: + self.gui.theme_data["theme_server"] = fallback_section() + + self.gui.set_data("theme_server.url", "") + self.gui.set_data("theme_server.status", _("Starting the theme editor...")) + self.load_regions("theme_server", []) + self.set_buttons({'B': _('Stop')}) + if start: + self.start() + + def start(self): + hm = self.gui.hm + env = dict(os.environ) + env['PYTHONPATH'] = os.pathsep.join([str(PYLIB_PATH), str(PYLIB_PATH.parent / "exlibs")]) + command = [ + sys.executable, "-m", "theme_server.serve", "--managed", + "--host", "0.0.0.0", "--port", str(PORT), "--try-ports", "10", + "--themes", str(hm.themes_dir), + "--ports-config", str(hm.cfg_dir), + "--max-workers", str(worker_count()), + ] + + ## Next to pugwash.txt. + log_file = hm.cfg_dir.parent / "theme_server.txt" + try: + with open(str(log_file), "w") as log: + self.proc = subprocess.Popen( + command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=log, + env=env, universal_newlines=True) + + except OSError as err: + logger.error(f"Unable to start the theme editor: {err}") + self.started({'ok': False, 'error': str(err)}) + return + + proc = self.proc + + def wait_until_ready(): + line = proc.stdout.readline() + try: + status = json.loads(line) if line else {'ok': False, 'error': _("it stopped")} + except ValueError: + status = {'ok': False, 'error': line.strip()} + self.gui.dispatcher.post(self.started, status) + + threading.Thread(target=wait_until_ready, name="theme-server", daemon=True).start() + + def started(self, status): + if self.stopped: + return + + if not status.get('ok'): + logger.error(f"Theme editor: {status.get('error')}") + self.gui.set_data("theme_server.status", _("Unable to start the theme editor: {error}").format( + error=status.get('error', ""))) + self.stop() + return + + ip_address = get_ip_address(force_update=True) + if ip_address is None: + self.gui.set_data("theme_server.status", _( + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.")) + return + + self.show(f"http://{ip_address}:{status['port']}/") + + def show(self, url): + self.url = url + self.gui.set_data("theme_server.url", url) + self.gui.set_data("theme_server.status", _( + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\n" + "Preview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\n" + "Press B to stop the theme editor.").format(url=url)) + + def stop(self): + """Closing its stdin stops the server; a thread makes sure it does.""" + self.stopped = True + proc, self.proc = self.proc, None + if proc is None: + return + + def wait(): + try: + proc.stdin.close() + proc.wait(timeout=15) + except (OSError, subprocess.TimeoutExpired): + proc.kill() + + threading.Thread(target=wait, name="theme-server-stop", daemon=True).start() + + def do_update(self, events): + super().do_update(events) + + if self.proc is not None and self.url is not None and self.proc.poll() is not None: + logger.error(f"Theme editor stopped: {self.proc.returncode}") + self.proc = None + self.url = None + self.gui.set_data("theme_server.url", "") + self.gui.set_data("theme_server.status", _("The theme editor stopped. See theme_server.txt in the PortMaster folder.")) + + if events.was_pressed('B'): + self.button_back() + self.stop() + self.gui.pop_scene() + return True + + return False diff --git a/PortMaster/pylibs/theme_server/__init__.py b/PortMaster/pylibs/theme_server/__init__.py new file mode 100644 index 00000000..341a09e6 --- /dev/null +++ b/PortMaster/pylibs/theme_server/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: MIT +# +# The theme preview server and translation editor: serve.py (the web server), worker.py +# (a headless pugwash that renders screens), shots.py (the screens), translations.py (the +# language files). Run from a checkout with tools/theme_server/serve.py, or on a device +# from PortMaster's options. diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py new file mode 100644 index 00000000..a3640713 --- /dev/null +++ b/PortMaster/pylibs/theme_server/serve.py @@ -0,0 +1,670 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +# +# The theme dev server: renders every PortMaster screen with your theme, at any resolution, +# colour scheme and language, and shows them in a web page that refreshes when you save. +# +# python3 tools/theme_server/serve.py --themes DIR [--port 8090] (it runs this) +# DIR is a theme (it has a theme.json) or a folder of themes. The built-in default +# theme is always there too. Then open http://localhost:8090/ +# +# python3 tools/theme_server/serve.py --themes DIR --export OUT +# [--theme NAME] [--resolutions 640x480,1280x720] [--schemes all] [--langs en_US] +# Writes OUT/////.png and .json, no server. +# +# --refresh-ports downloads the ports list and screenshots again (they're cached in +# ~/.cache/portmaster-theme-server, or --cache DIR). +# +# On a device PortMaster's options start it (pugwash/scenes/theme_server.py) with +# --managed: it reads the ports from PortMaster's own config (--ports-config), reports +# on stdout as JSON lines and stops when PortMaster closes its stdin. +# +# Needs what running pugwash from a checkout needs (SDL2: pip install pysdl2-dll on macOS) +# and the network the first time, for the real ports list. + +import argparse +import hashlib +import json +import os +import re +import shutil +import signal +import subprocess +import sys +import tarfile +import tempfile +import threading +import time +import urllib.parse +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path + + +from theme_server import translations as i18n_tool + + +HERE = Path(__file__).resolve().parent +## PortMaster/pylibs/theme_server/serve.py +PM_DIR = HERE.parents[1] +DEFAULT_THEME_DIR = PM_DIR / "pylibs" / "default_theme" +WORKER = HERE / "worker.py" + +RESOLUTIONS = ( + "480x320", "640x480", "720x480", "720x720", "854x480", "960x544", "960x720", + "1024x768", "1280x720", "1280x800", "1920x1080", "1920x1152", + ) + +## Render processes kept running at once (each is a pugwash, ~60 MB). +MAX_WORKERS = 6 + + +def default_cache(): + base = os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache") + return Path(base) / "portmaster-theme-server" + + +def valid_resolution(text): + match = re.match(r"^(\d{2,4})x(\d{2,4})$", text or "") + return bool(match) and 0 < int(match.group(1)) <= 4096 and 0 < int(match.group(2)) <= 4096 + + +################################################################################ +## Themes and languages +class Themes: + """The themes the server shows: the default theme, then each theme in --themes.""" + def __init__(self, themes_dir): + self.themes_dir = Path(themes_dir).resolve() if themes_dir else None + + def all(self): + found = {"default_theme": DEFAULT_THEME_DIR} + if self.themes_dir is not None: + if (self.themes_dir / "theme.json").is_file(): + found[self.themes_dir.name] = self.themes_dir + elif self.themes_dir.is_dir(): + for theme_file in sorted(self.themes_dir.glob("*/theme.json")): + found[theme_file.parent.name] = theme_file.parent + + return found + + def get(self, theme_id): + return self.all().get(theme_id) + + def info(self, theme_id, theme_dir): + """Name, schemes and the default scheme, or the error reading theme.json.""" + try: + data = json.loads((theme_dir / "theme.json").read_text(encoding="utf-8")) + except (OSError, ValueError) as error: + return {'id': theme_id, 'name': theme_id, 'schemes': [], 'default_scheme': None, 'error': str(error)} + + info = data.get("#info", {}) + schemes = [name for name in data.get("#schemes", {}) if not name.startswith("#")] + default = info.get("default-scheme") + if default not in schemes: + default = schemes[0] if schemes else None + + return {'id': theme_id, 'name': info.get("name") or theme_id, 'creator': info.get("creator", ""), + 'schemes': schemes, 'default_scheme': default, 'error': None} + + @staticmethod + def fingerprint(theme_dir, extra_files=()): + """Changes whenever a file in the theme (or one of extra_files) is added, removed or saved.""" + digest = hashlib.md5() + paths = [] + for root, dirs, files in os.walk(str(theme_dir)): + dirs.sort() + paths += [os.path.join(root, name) for name in sorted(files)] + + for path in paths + [str(path) for path in extra_files]: + try: + stat = os.stat(path) + except OSError: + continue + digest.update(f"{path}\0{stat.st_size}\0{stat.st_mtime_ns}\n".encode("utf-8", "replace")) + + return digest.hexdigest()[:12] + + +def version(theme_dir, lang): + """The theme's fingerprint with the language's translation files: what a render depends on.""" + return Themes.fingerprint(theme_dir, [i18n_tool.domain_file(lang, domain) for domain in i18n_tool.DOMAINS]) + + +def languages(): + """[(code, name)]: the languages pugwash has translations for.""" + text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") + names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) + locales = PM_DIR / "pylibs" / "locales" + codes = {"en_US"} | {match.group(1) for match in (re.match(r"^([a-z]{2,3}_[A-Z]{2})\.json$", path.name) for path in locales.glob("*.json")) if match} + return [(code, names.get(code, code)) for code in names if code in codes] + \ + [(code, code) for code in sorted(codes - set(names))] + + +################################################################################ +## Render workers +class WorkerError(Exception): + pass + + +class Worker: + """One worker.py: a pugwash for one theme version, scheme, resolution and language.""" + def __init__(self, key, theme_dir, scheme, resolution, lang, ports_config, work): + self.key = key + self.lock = threading.Lock() + self.work = Path(work) + command = [sys.executable, str(WORKER), "--theme-dir", str(theme_dir), "--resolution", resolution, + "--lang", lang, "--ports-config", str(ports_config), "--work", str(self.work)] + if scheme: + command += ["--scheme", scheme] + + self.log_file = open(str(self.work.with_suffix(".log")), "w") + self.proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=self.log_file, universal_newlines=True, bufsize=1) + self.ready = self._read() + if not self.ready.get('ok'): + self.stop() + raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + str(self.log_file.name)) + + self.used = time.monotonic() + + def _read(self): + line = self.proc.stdout.readline() + if not line: + return {'ok': False, 'error': "the renderer stopped, see " + str(self.log_file.name)} + return json.loads(line) + + def request(self, **command): + with self.lock: + if self.proc.poll() is not None: + raise WorkerError("the renderer stopped, see " + str(self.log_file.name)) + + self.used = time.monotonic() + self.proc.stdin.write(json.dumps(command) + "\n") + self.proc.stdin.flush() + return self._read() + + def stop(self): + try: + if self.proc.poll() is None: + self.proc.stdin.write(json.dumps({'cmd': "quit"}) + "\n") + self.proc.stdin.flush() + self.proc.wait(timeout=5) + except (OSError, ValueError, subprocess.TimeoutExpired): + self.proc.kill() + finally: + self.log_file.close() + shutil.rmtree(str(self.work), ignore_errors=True) + try: + os.unlink(self.log_file.name) + except OSError: + pass + + +class RenderPool: + """Starts workers as they're needed, replaces them when their theme changes, and caches + what they rendered.""" + def __init__(self, themes, ports_config, work_dir, max_workers=MAX_WORKERS): + self.themes = themes + self.ports_config = Path(ports_config) + self.max_workers = max(1, max_workers) + self.work_dir = Path(work_dir) + self.work_dir.mkdir(parents=True, exist_ok=True) + self.workers = {} + self.renders = {} + self.lock = threading.Lock() + self.start_lock = threading.Lock() + self.translations = Translations() + self.counter = 0 + + def _worker(self, theme_id, scheme, resolution, lang): + theme_dir = self.themes.get(theme_id) + if theme_dir is None: + raise WorkerError(f"no theme {theme_id!r}") + + fingerprint = version(theme_dir, lang) + key = (theme_id, fingerprint, scheme or "", resolution, lang) + with self.lock: + worker = self.workers.get(key) + if worker is not None: + return worker, fingerprint + + ## One start at a time: the page asks for every screen at once. + with self.start_lock: + with self.lock: + worker = self.workers.get(key) + if worker is not None: + return worker, fingerprint + + ## The same settings for an older version of the theme, then the least + ## recently used if there are too many. + stale = [self.workers.pop(other) for other in list(self.workers) + if other[0] == theme_id and other[2:] == key[2:]] + while len(self.workers) >= self.max_workers: + stale.append(self.workers.pop(min(self.workers, key=lambda other: self.workers[other].used))) + + self.counter += 1 + work = self.work_dir / f"worker-{self.counter}" + + for old in stale: + old.stop() + + worker = Worker(key, theme_dir, scheme, resolution, lang, self.ports_config, work) + with self.lock: + self.workers[key] = worker + + return worker, fingerprint + + def render(self, theme_id, scheme, resolution, lang, shot): + """(png bytes, info dict) for one screen, cached until the theme changes.""" + worker, fingerprint = self._worker(theme_id, scheme, resolution, lang) + render_key = worker.key + (shot,) + with self.lock: + if render_key in self.renders: + return self.renders[render_key] + + out_file = worker.work / f"{shot}.png" + answer = worker.request(cmd="render", shot=shot, out=str(out_file)) + if not answer.get('ok'): + raise WorkerError(answer.get('error', "render failed")) + + png = out_file.read_bytes() if not answer.get('missing') else b"" + info = { + 'shot': shot, 'theme': theme_id, 'scheme': worker.ready.get('scheme'), 'resolution': resolution, + 'lang': lang, 'version': fingerprint, 'regions': answer.get('regions', []), + 'errors': answer.get('errors', []), 'missing': answer.get('missing'), 'ms': answer.get('ms'), + 'strings': self.translations.used(answer.get('strings', {})), + } + with self.lock: + ## Drop renders of older versions of the theme and the language's translations. + for old_key in [k for k in self.renders if k[0] == theme_id and k[4] == lang and k[1] != fingerprint]: + del self.renders[old_key] + self.renders[render_key] = (png, info) + + return png, info + + def shots(self): + worker, _ = self._worker("default_theme", None, "640x480", "en_US") + return worker.request(cmd="shots")['shots'] + + def stop(self): + with self.lock: + workers, self.workers = list(self.workers.values()), {} + for worker in workers: + worker.stop() + + +################################################################################ +## Translations: the language files in the checkout's PortMaster/pylibs/locales +class Translations: + """Reads and saves translations, one string at a time.""" + def __init__(self): + self.lock = threading.Lock() + + def strings(self): + return i18n_tool.read_strings() + + def used(self, lookups): + """The strings a render looked up that are translatable (not {templates} and the like).""" + strings = self.strings() + return {domain: [text for text in lookups.get(domain, []) if text in strings[domain]] for domain in i18n_tool.DOMAINS} + + def language(self, lang): + """Every string of both domains, with this language's translation and its problems.""" + strings, translations = self.strings(), i18n_tool.read_translations(lang) + result = {} + for domain in i18n_tool.DOMAINS: + entries = [] + for text, sources in strings[domain].items(): + translation = translations[domain].get(text, "") + entries.append({'text': text, 'translation': translation, 'sources': sources, + 'problems': i18n_tool.problems(text, translation) if translation else []}) + result[domain] = entries + return result + + def save(self, lang, domain, text, translation): + """Sets one translation ("" removes it), in strings.json's order. Returns its problems.""" + with self.lock: + strings = self.strings()[domain] + if text not in strings: + raise KeyError(text) + + translations = i18n_tool.read_translations(lang)[domain] + if translation: + translations[text] = translation + else: + translations.pop(text, None) + + ordered = {key: translations[key] for key in strings if key in translations} + path = i18n_tool.domain_file(lang, domain) + temp = path.with_name(path.name + ".tmp") + i18n_tool.write_json(temp, ordered) + os.replace(str(temp), str(path)) + ## Both files of a language exist once it has one. + for other in i18n_tool.DOMAINS: + if not i18n_tool.domain_file(lang, other).exists(): + i18n_tool.write_json(i18n_tool.domain_file(lang, other), {}) + + return i18n_tool.problems(text, translation) if translation else [] + + +def all_languages(): + """[(code, name, has files)]: every language PortMaster knows a name for, and any other with files.""" + have = {code for code, _ in languages()} + text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") + names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) + return [(code, names.get(code, code), code in have) for code in sorted(set(names) | have, key=lambda code: names.get(code, code))] + + +################################################################################ +## The Chinese, Japanese and Korean fonts: the PortMaster/pugwash script unpacks them on +## its first run, the workers import pugwash without it. +def ensure_fonts(): + resources = PM_DIR / "pylibs" / "resources" + if (resources / "NotoSansTC-Regular.ttf").is_file(): + return + + try: + with tarfile.open(str(resources / "NotoSans.tar.xz"), "r:xz") as tar: + tar.extractall(str(resources)) + + except (OSError, EOFError, tarfile.TarError) as err: + print(f"Unable to extract the Noto fonts, Chinese, Japanese and Korean won't show: {err}") + + +################################################################################ +## The ports cache +def ensure_cache(cache, refresh=False): + source = cache / "tools" / "PortMaster" / "config" / "020_portmaster.source.json" + if source.is_file() and not refresh: + try: + if json.loads(source.read_text()).get('data', {}).get('ports'): + return + except ValueError: + pass + + print(f"Downloading the ports list and screenshots into {cache} ...", flush=True) + if (cache / "tools").exists(): + shutil.rmtree(str(cache / "tools")) + + result = subprocess.run([sys.executable, str(WORKER), "--prepare-cache", str(cache)]) + if result.returncode != 0: + sys.exit("Couldn't download the ports list (no network?). Screens will show no ports.") + + +################################################################################ +## The web server +class Handler(BaseHTTPRequestHandler): + server_version = "PortMasterThemeServer/1" + + def log_message(self, format, *args): + if self.server.verbose: + super().log_message(format, *args) + + def send(self, status, body, content_type="application/json", cache=False): + if isinstance(body, (dict, list)): + body = json.dumps(body).encode("utf-8") + elif isinstance(body, str): + body = body.encode("utf-8") + + self.send_response(status) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "max-age=3600" if cache else "no-store") + self.end_headers() + self.wfile.write(body) + + def do_GET(self): + url = urllib.parse.urlparse(self.path) + query = {key: values[-1] for key, values in urllib.parse.parse_qs(url.query).items()} + try: + if url.path in ("/", "/index.html"): + self.send(200, (HERE / "static" / "index.html").read_bytes(), "text/html; charset=utf-8") + + elif url.path == "/api/info": + themes = self.server.themes + self.send(200, { + 'themes': [themes.info(theme_id, theme_dir) for theme_id, theme_dir in themes.all().items()], + 'languages': languages(), + 'resolutions': list(RESOLUTIONS), + 'shots': self.server.shots, + 'all_languages': all_languages(), + }) + + elif url.path == "/api/strings": + lang = query.get('lang', "") + if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): + self.send(400, {'error': "bad language"}) + return + self.send(200, {'lang': lang, 'domains': self.server.pool.translations.language(lang)}) + + elif url.path == "/api/version": + theme_dir = self.server.themes.get(query.get('theme')) + lang = query.get('lang', "en_US") + if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): + self.send(400, {'error': "bad language"}) + return + self.send(200 if theme_dir else 404, {'version': version(theme_dir, lang) if theme_dir else None}) + + elif url.path in ("/api/render.png", "/api/render.json"): + theme_id = query.get('theme', "default_theme") + resolution = query.get('res', "640x480") + lang = query.get('lang', "en_US") + shot = query.get('shot', "main_menu") + if not valid_resolution(resolution) or not re.match(r"^\w\w_\w\w$", lang) or \ + shot not in {name for name, _ in self.server.shots}: + self.send(400, {'error': "bad theme, resolution, language or screen"}) + return + + png, info = self.server.pool.render(theme_id, query.get('scheme') or None, resolution, lang, shot) + if url.path.endswith(".png"): + if not png: + self.send(404, {'error': info['missing']}) + else: + ## The URL carries the theme version, so it can be cached. + self.send(200, png, "image/png", cache='v' in query) + else: + self.send(200, info) + + else: + self.send(404, {'error': "not found"}) + + except WorkerError as error: + self.send(500, {'error': str(error)}) + + except (BrokenPipeError, ConnectionResetError): + pass + + def do_POST(self): + url = urllib.parse.urlparse(self.path) + ## Only the page itself: another site can't send this header without a CORS + ## preflight, which this server never answers. + if self.headers.get("X-Theme-Server") != "1" or not (self.headers.get("Content-Type") or "").startswith("application/json"): + self.send(403, {'error': "forbidden"}) + return + + try: + length = int(self.headers.get("Content-Length") or 0) + if not 0 < length <= 256 * 1024: + self.send(400, {'error': "bad request"}) + return + body = json.loads(self.rfile.read(length).decode("utf-8")) + + if url.path == "/api/translate": + lang, domain = body.get('lang', ""), body.get('domain', "") + text, translation = body.get('text'), body.get('translation', "") + if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang) or lang == "en_US" or domain not in i18n_tool.DOMAINS or \ + not isinstance(text, str) or not isinstance(translation, str): + self.send(400, {'error': "bad language, domain or text"}) + return + try: + problems = self.server.pool.translations.save(lang, domain, text, translation) + except KeyError: + self.send(404, {'error': "not a translatable string"}) + return + self.send(200, {'ok': True, 'problems': problems}) + + else: + self.send(404, {'error': "not found"}) + + except (ValueError, UnicodeDecodeError, AttributeError): + self.send(400, {'error': "bad request"}) + + except (BrokenPipeError, ConnectionResetError): + pass + + +class Server(ThreadingHTTPServer): + daemon_threads = True + verbose = False + + +################################################################################ +## Export +def export(pool, themes, out, theme_ids, resolutions, schemes, langs, shots): + failures = 0 + for theme_id in theme_ids: + info = themes.info(theme_id, themes.get(theme_id)) + theme_schemes = info['schemes'] if schemes == ["all"] else (schemes or [info['default_scheme']]) + for resolution in resolutions: + for scheme in theme_schemes or [None]: + for lang in langs: + folder = out / theme_id / resolution / (scheme or "default") / lang + folder.mkdir(parents=True, exist_ok=True) + for shot, _ in shots: + try: + png, info = pool.render(theme_id, scheme, resolution, lang, shot) + except WorkerError as error: + print(f"FAIL {theme_id} {resolution} {scheme} {lang} {shot}: {error}", file=sys.stderr) + failures += 1 + continue + + if png: + (folder / f"{shot}.png").write_bytes(png) + (folder / f"{shot}.json").write_text(json.dumps(info, indent=1)) + for error in info['errors']: + print(f"{theme_id} {resolution} {scheme} {lang} {shot}: {error}", file=sys.stderr) + + print(f"{folder}", flush=True) + + return 1 if failures else 0 + + +def listen(host, port, tries): + """The server on the first free port of port, port + 1, ... (tries of them).""" + for offset in range(max(1, tries)): + try: + return Server((host, port + offset), Handler) + except OSError as error: + last = error + + raise last + + +def reporter(): + """--managed: report(**status) writes a JSON line to the real stdout, which is for + PortMaster; everything else printed goes to stderr (its log).""" + report_file = os.fdopen(os.dup(1), "w") + os.dup2(2, 1) + + def report(**status): + report_file.write(json.dumps(status) + "\n") + report_file.flush() + + return report + + +def main(argv=None): + parser = argparse.ArgumentParser(description="Renders PortMaster's screens with your theme.") + parser.add_argument("--themes", help="a theme folder, or a folder of themes") + parser.add_argument("--port", type=int, default=8090) + parser.add_argument("--try-ports", type=int, default=1, metavar="N", help="if the port is busy, try the next N-1 too") + parser.add_argument("--host", default="127.0.0.1", help="use 0.0.0.0 to let other machines connect") + parser.add_argument("--cache", type=Path, default=default_cache()) + parser.add_argument("--ports-config", type=Path, help="a PortMaster config folder to take the ports from, instead of the cache") + parser.add_argument("--max-workers", type=int, default=MAX_WORKERS, help="render processes at once (each about 60-90 MB)") + parser.add_argument("--managed", action="store_true", help="run for PortMaster: JSON lines on stdout, stop when stdin closes") + parser.add_argument("--refresh-ports", action="store_true", help="download the ports list and screenshots again") + parser.add_argument("--verbose", action="store_true") + parser.add_argument("--export", type=Path, metavar="OUT", help="write PNGs instead of serving them") + parser.add_argument("--theme", action="append", help="export: these themes (default: all)") + parser.add_argument("--resolutions", default="640x480", help="export: comma separated, or 'all'") + parser.add_argument("--schemes", default="", help="export: comma separated, 'all', or the theme's default") + parser.add_argument("--langs", default="en_US", help="export: comma separated") + options = parser.parse_args(argv) + + report = reporter() if options.managed else None + + ## Stopped (eg: PortMaster quitting): clean up as for Ctrl+C. + def interrupted(signum, frame): + raise KeyboardInterrupt() + + signal.signal(signal.SIGTERM, interrupted) + + ensure_fonts() + if options.ports_config is None: + ensure_cache(options.cache, options.refresh_ports) + ports_config = options.cache / "tools" / "PortMaster" / "config" + else: + ports_config = options.ports_config + + themes = Themes(options.themes) + work_dir = Path(tempfile.mkdtemp(prefix="pm-theme-server-")) + pool = RenderPool(themes, ports_config, work_dir, options.max_workers) + + try: + try: + shots = pool.shots() + except WorkerError as error: + if report is None: + raise + report(ok=False, error=str(error).strip().splitlines()[-1]) + return 1 + + if options.export: + resolutions = list(RESOLUTIONS) if options.resolutions == "all" else options.resolutions.split(",") + bad = [resolution for resolution in resolutions if not valid_resolution(resolution)] + if bad: + sys.exit(f"Not a resolution: {', '.join(bad)}") + + return export(pool, themes, options.export, options.theme or list(themes.all()), resolutions, + [scheme for scheme in options.schemes.split(",") if scheme], options.langs.split(","), shots) + + try: + server = listen(options.host, options.port, options.try_ports) + except OSError as error: + if report is not None: + report(ok=False, error=f"port {options.port} is busy ({error.strerror})") + return 1 + sys.exit(f"Can't listen on {options.host}:{options.port} ({error.strerror}), try another --port.") + + server.themes, server.pool, server.shots, server.verbose = themes, pool, shots, options.verbose + port = server.server_address[1] + if report is not None: + report(ok=True, ready=True, port=port) + + ## PortMaster closing stdin (or quitting) stops the server. + def wait_for_eof(): + sys.stdin.read() + server.shutdown() + + threading.Thread(target=wait_for_eof, daemon=True).start() + + print(f"Theme server on http://{'localhost' if options.host == '127.0.0.1' else options.host}:{port}/" + f" ({len(themes.all())} theme{'s' if len(themes.all()) != 1 else ''}), Ctrl+C to stop.", flush=True) + try: + server.serve_forever() + except KeyboardInterrupt: + pass + finally: + server.server_close() + return 0 + + except KeyboardInterrupt: + return 0 + + finally: + pool.stop() + shutil.rmtree(str(work_dir), ignore_errors=True) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/theme_server/shots.py b/PortMaster/pylibs/theme_server/shots.py similarity index 97% rename from tools/theme_server/shots.py rename to PortMaster/pylibs/theme_server/shots.py index f1b75d36..de35a85e 100644 --- a/tools/theme_server/shots.py +++ b/PortMaster/pylibs/theme_server/shots.py @@ -209,6 +209,15 @@ def shot_keyboard(gui): gui.push_scene('osk', pug_scenes.OnScreenKeyboard(gui)) +def shot_theme_editor(gui): + ## Not started: a renderer mustn't run a server. A theme without a "theme_server" + ## section shows PortMaster's plain fallback, as on a device. + shot_options(gui) + scene = pug_scenes.ThemeServerScene(gui, start=False) + gui.push_scene('theme-editor', scene) + scene.show("http://192.168.0.2:8090/") + + def shot_messages(gui): _ports_list(gui, 'install') gui.messages_begin(internal=True) @@ -309,6 +318,7 @@ def shot_warning_long(gui): ("theme_schemes", "Colour schemes", shot_theme_schemes), ("language", "Language", shot_language), ("keyboard", "On-screen keyboard", shot_keyboard), + ("theme_editor", "Theme editor (its address and QR code)", shot_theme_editor), ("messages", "Messages and progress", shot_messages), ("message_box", "Message box", shot_message_box), ("selection_list", "Selection list", shot_selection_list), diff --git a/tools/theme_server/static/index.html b/PortMaster/pylibs/theme_server/static/index.html similarity index 100% rename from tools/theme_server/static/index.html rename to PortMaster/pylibs/theme_server/static/index.html diff --git a/PortMaster/pylibs/theme_server/translations.py b/PortMaster/pylibs/theme_server/translations.py new file mode 100644 index 00000000..6782076f --- /dev/null +++ b/PortMaster/pylibs/theme_server/translations.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +# +# PortMaster's translatable strings (see harbourmaster/i18n.py for the files). +# +# python3 -I tools/i18n.py extract [--themes DIR] (it runs this) +# Rewrites PortMaster/pylibs/locales/strings.json, every _("...") in the code, and +# strings.themes.json, every theme "text" in the built-in theme (and the themes in +# DIR, eg: a PortMaster-Themes checkout; themes it doesn't scan keep their strings). +# python3 -I tools/i18n.py check +# Each language file: strings that aren't in strings.json any more, and translations +# that lost a {placeholder} or a line break. +# +# Standard library only, Python 3.7. + +import argparse +import ast +import json +import re +import sys +from pathlib import Path + + +## PortMaster/pylibs/theme_server/translations.py +PM_DIR = Path(__file__).resolve().parents[2] +LOCALES_DIR = PM_DIR / "pylibs" / "locales" +DOMAINS = ("messages", "themes") +## The files: strings.json, de_DE.json (messages); strings.themes.json, de_DE.themes.json (themes). +LANGUAGE_FILE = re.compile(r"^([a-z]{2,3}_[A-Z]{2})(\.themes)?\.json$") + + +def domain_file(name, domain): + return LOCALES_DIR / (f"{name}.json" if domain == "messages" else f"{name}.{domain}.json") + + +def languages(): + """The languages with a translation file.""" + return sorted({LANGUAGE_FILE.match(path.name).group(1) for path in LOCALES_DIR.glob("*.json") if LANGUAGE_FILE.match(path.name)}) + + +def code_files(): + """The files with translatable strings: both scripts' own code and the two packages.""" + files = [PM_DIR / "pugwash", PM_DIR / "harbourmaster"] + for package in ("harbourmaster", "pugwash"): + files += sorted((PM_DIR / "pylibs" / package).rglob("*.py")) + return files + + +def _literal(node): + """The string of a str literal node, None for anything else (f-strings, names, ...).""" + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if isinstance(node, getattr(ast, "Str", ())) and isinstance(getattr(node, "s", None), str): + return node.s + return None + + +def _display(path): + """A file as strings.json names it: relative to PortMaster/ when it's in there.""" + try: + return Path(path).resolve().relative_to(PM_DIR).as_posix() + except ValueError: + return Path(path).as_posix() + + +def code_strings(files=None): + """{text: ["file:line", ...]} for every _("text") call, in file order.""" + found = {} + for path in files or code_files(): + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + calls = [node for node in ast.walk(tree) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "_" and node.args] + for node in sorted(calls, key=lambda node: (node.lineno, node.col_offset)): + text = _literal(node.args[0]) + if text: + found.setdefault(text, []).append(f"{_display(path)}:{node.lineno}") + + return found + + +def theme_strings(theme_dir, name=None): + """{text: ["theme/theme.json:line", ...]} for a theme's "text" values that are more + than a {template} or a colour.""" + theme_file = Path(theme_dir) / "theme.json" + source = theme_file.read_text(encoding="utf-8") + name = name or Path(theme_dir).name + found = {} + + def walk(value): + if isinstance(value, dict): + for key, item in value.items(): + if key.split("[", 1)[0] == "text" and isinstance(item, str): + yield item + else: + yield from walk(item) + elif isinstance(value, list): + for item in value: + yield from walk(item) + + lines = source.split("\n") + for text in walk(json.loads(source)): + if not text.strip() or re.match(r"^\{[^}]+\}$", text) or re.match(r"^#[0-9a-f]+$", text, re.I): + continue + quoted = json.dumps(text, ensure_ascii=False)[1:-1] + line = next((number for number, line in enumerate(lines, 1) if quoted in line), 0) + found.setdefault(text, []).append(f"{name}/theme.json:{line}") + + return found + + +def read_json(path): + try: + data = json.loads(Path(path).read_text(encoding="utf-8")) + except (OSError, ValueError): + return {} + return data if isinstance(data, dict) else {} + + +def read_strings(): + """{domain: {text: [where it's used]}}""" + return {domain: read_json(domain_file("strings", domain)) for domain in DOMAINS} + + +def read_translations(lang): + """{domain: {text: translation}}""" + return {domain: read_json(domain_file(lang, domain)) for domain in DOMAINS} + + +def write_json(path, data): + path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") + + +def extract(themes_dirs=()): + strings = read_strings() + themes = {"default_theme": PM_DIR / "pylibs" / "default_theme"} + for themes_dir in themes_dirs: + for theme_file in sorted(Path(themes_dir).glob("*/theme.json")): + themes[theme_file.parent.name] = theme_file.parent + + ## Themes not scanned this time keep their strings. + kept = {} + for text, sources in strings['themes'].items(): + others = [source for source in sources if source.split("/", 1)[0] not in themes] + if others: + kept[text] = others + + for name, theme_dir in themes.items(): + for text, sources in theme_strings(theme_dir, name).items(): + kept.setdefault(text, []).extend(sources) + + return {'messages': code_strings(), 'themes': {text: kept[text] for text in sorted(kept, key=lambda t: (kept[t][0], t))}} + + +PLACEHOLDER = re.compile(r"\{[^{}]*\}") + + +def problems(source, translation): + """What a translation lost: {placeholders} and line breaks the English has.""" + found = [] + missing = sorted(set(PLACEHOLDER.findall(source)) - set(PLACEHOLDER.findall(translation))) + if missing: + found.append("missing " + ", ".join(missing)) + extra = sorted(set(PLACEHOLDER.findall(translation)) - set(PLACEHOLDER.findall(source))) + if extra: + found.append("unknown " + ", ".join(extra)) + ## Joined to other text in the code ("Music: " + "Disabled"): the spaces at the ends matter. + for end, english, here in (("starts", source[:1], translation[:1]), ("ends", source[-1:], translation[-1:])): + if english.isspace() and not here.isspace(): + found.append(f"English {end} with a space, this doesn't") + if source.count("\n") != translation.count("\n"): + found.append(f"{source.count(chr(10))} line breaks in English, {translation.count(chr(10))} here") + return found + + +def check(): + strings = read_strings() + issues = 0 + for lang in languages(): + for domain, translations in read_translations(lang).items(): + name = domain_file(lang, domain).name + for source, translation in translations.items(): + if source not in strings[domain]: + print(f"{name}: not used any more: {source!r}") + issues += 1 + for problem in problems(source, translation): + print(f"{name}: {problem}: {source!r}") + issues += 1 + + return 1 if issues else 0 + + +def main(argv=None): + parser = argparse.ArgumentParser(description="PortMaster's translatable strings") + commands = parser.add_subparsers(dest="command") + extract_parser = commands.add_parser("extract", help="rewrite locales/strings.json") + extract_parser.add_argument("--themes", action="append", default=[], help="a folder of themes to scan too") + commands.add_parser("check", help="check the language files") + options = parser.parse_args(argv) + + if options.command == "extract": + strings = extract(options.themes) + for domain in DOMAINS: + write_json(domain_file("strings", domain), strings[domain]) + print(f"{len(strings['messages'])} code strings, {len(strings['themes'])} theme strings -> {LOCALES_DIR}") + return 0 + + if options.command == "check": + return check() + + parser.print_help() + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/theme_server/worker.py b/PortMaster/pylibs/theme_server/worker.py similarity index 83% rename from tools/theme_server/worker.py rename to PortMaster/pylibs/theme_server/worker.py index 14e5de44..7539c26f 100644 --- a/tools/theme_server/worker.py +++ b/PortMaster/pylibs/theme_server/worker.py @@ -10,7 +10,11 @@ # else pugwash prints goes to stderr. # # worker.py --theme-dir DIR --resolution 640x480 [--scheme NAME] [--lang en_US] -# --cache DIR --work DIR +# --ports-config DIR --work DIR +# +# --ports-config is a PortMaster config folder with the ports list and screenshots: the +# cache's (DIR/tools/PortMaster/config, see --prepare-cache), or on a device PortMaster's +# own. Nothing in it is changed. # # {"cmd": "render", "shot": "main_menu", "out": "/tmp/x.png"} # -> {"ok": true, "regions": [...], "errors": [...]} @@ -32,8 +36,8 @@ from pathlib import Path -REPO_DIR = Path(__file__).resolve().parents[2] -PM_DIR = REPO_DIR / "PortMaster" +## PortMaster/pylibs/theme_server/worker.py +PM_DIR = Path(__file__).resolve().parents[2] def screen_capabilities(width, height): @@ -86,8 +90,10 @@ def sdl_env(): return {'PM_HEADLESS': "Y", 'SDL_AUDIODRIVER': "dummy"} -def prepare_tools_dir(work, cache, theme_dir, scheme): - """work/tools: the cached ports (linked, not copied), the theme, and a config.json.""" +def prepare_tools_dir(work, ports_config, theme_dir, scheme): + """work/tools: the ports data from ports_config (files copied, the screenshot folders + linked: harbourmaster may rewrite a source file, it only reads the screenshots), the + theme, and a config.json.""" tools = work / "tools" config = tools / "PortMaster" / "config" if tools.exists(): @@ -99,11 +105,15 @@ def prepare_tools_dir(work, cache, theme_dir, scheme): (work / "ports").mkdir(exist_ok=True) (work / "temp").mkdir(exist_ok=True) - cached = cache / "tools" / "PortMaster" / "config" + cached = Path(ports_config) if cached.is_dir(): for entry in cached.iterdir(): - if entry.name != "config.json": - (config / entry.name).symlink_to(entry) + if entry.name == "config.json": + continue + if entry.is_dir(): + (config / entry.name).symlink_to(entry.resolve()) + elif entry.is_file(): + shutil.copyfile(str(entry), str(config / entry.name)) default_theme = PM_DIR / "pylibs" / "default_theme" if theme_dir.resolve() == default_theme.resolve(): @@ -112,14 +122,17 @@ def prepare_tools_dir(work, cache, theme_dir, scheme): theme_name = theme_dir.name (tools / "PortMaster" / "themes" / theme_name).symlink_to(theme_dir.resolve()) - ## The cache's own config.json (its "version", else harbourmaster upgrades the config by - ## deleting the image folders, which here are links into the cache). + ## Its "version", else harbourmaster upgrades the config by deleting the image folders + ## (here links), and not a first run, which writes the default sources. cfg_data = {} - if (cached / "config.json").is_file(): + try: cfg_data = json.loads((cached / "config.json").read_text()) + except (OSError, ValueError): + pass (config / "config.json").write_text(json.dumps({ - **cfg_data, + **{key: cfg_data[key] for key in ('version',) if key in cfg_data}, + 'first-run': False, 'theme': theme_name, 'theme-scheme': scheme, 'disclaimer': True, @@ -171,7 +184,7 @@ def main(argv=None): parser.add_argument("--scheme", default=None) parser.add_argument("--resolution", default="640x480") parser.add_argument("--lang", default="en_US") - parser.add_argument("--cache", required=True) + parser.add_argument("--ports-config", required=True) parser.add_argument("--work", required=True) options = parser.parse_args(argv) @@ -186,7 +199,7 @@ def reply(**answer): width, height = (int(part) for part in options.resolution.lower().split("x")) work = Path(options.work) work.mkdir(parents=True, exist_ok=True) - theme_name = prepare_tools_dir(work, Path(options.cache), Path(options.theme_dir), options.scheme) + theme_name = prepare_tools_dir(work, Path(options.ports_config), Path(options.theme_dir), options.scheme) os.environ.update(device_env(width, height, options.lang)) os.environ.update(sdl_env()) @@ -196,11 +209,11 @@ def reply(**answer): os.environ['HM_PORTS_DIR'] = str(work / "ports") os.environ['HM_SCRIPTS_DIR'] = str(work / "ports") - ## From the checkout: harbourmaster's testing mode (windowed, no update checks). - os.chdir(str(REPO_DIR)) + ## Next to PortMaster/: from a checkout that's harbourmaster's testing mode. + os.chdir(str(PM_DIR.parent)) sys.path[:0] = [str(PM_DIR / "pylibs"), str(PM_DIR / "exlibs")] - import shots + from theme_server import shots try: renderer = shots.Renderer(work, theme_name) diff --git a/THEME.md b/THEME.md index f73d258c..7664381e 100644 --- a/THEME.md +++ b/THEME.md @@ -18,7 +18,7 @@ Then open http://localhost:8090/. `--themes` is one theme folder (it has a `them - **The screens:** - main menu, all, ready-to-run and installed ports, port info (and its popup) - filters, featured ports, options, runtimes, themes, colour schemes, language - - on-screen keyboard, messages with a progress bar, message box + - on-screen keyboard, theme editor, messages with a progress bar, message box - the autoinstall and dialog screens, which always use the default theme on a device - the disclaimer and the unsupported-system warning, which are built in and look the same in every theme - They show the real ports list and screenshots, downloaded once into `~/.cache/portmaster-theme-server`. `--refresh-ports` gets the current ones. @@ -35,6 +35,14 @@ To write the screens to files instead, for a theme's README or to compare before That writes `out/////.png`, with a `.json` next to each listing its regions. +### On your device + +PortMaster carries the same server: **Options → Theme Editor** starts it on the device, and shows its address as text and as a QR code. Open it on a computer or phone on the same network: no checkout or install needed. It previews the themes installed on the device, with the device's own ports list, and the translation editor saves into the device's PortMaster. Press B on the device to stop it. + +- Translations saved on a device are in `PortMaster/pylibs/locales/` there. Copy them off before updating PortMaster, which replaces that folder. +- Each screen size, colour scheme and language starts a render process (60-90 MB): the first screen of a new combination takes 10-15 seconds on a handheld, the rest a second or two. How many run at once depends on the device's free memory. +- Anyone on the same network can open it while it runs. + ### Translating PortMaster The same server is a translation editor: pick **Translate** at the top. It lists every string of PortMaster (and, under **Themes**, every theme's text) with the chosen language's translation. @@ -161,6 +169,7 @@ Currently there are the following scenes: - `message_box`: An alert box - `filter_list`: Available filters - `themes_list`: List themes / downloader. +- `theme_server`: The theme editor (Options → Theme Editor), with the tags `{theme_server.url}`, the address, empty until the server is up, and `{theme_server.status}`, the instructions or what went wrong. Show the address as a QR code with `"image": "qr:{theme_server.url}"`. A theme without this section gets a plain screen in PortMaster's own font. ### Scene: main_menu diff --git a/tests/test_i18n.py b/tests/test_i18n.py index 7f4f1ba6..37d0572c 100644 --- a/tests/test_i18n.py +++ b/tests/test_i18n.py @@ -4,16 +4,14 @@ # list of every translatable string, locales/strings.json (tools/i18n.py extract). import json -import sys import pytest -from conftest import PM_DIR, REPO_DIR +from conftest import PM_DIR from harbourmaster import i18n -sys.path.insert(0, str(REPO_DIR / "tools")) -import i18n as i18n_tool # noqa: E402 +from theme_server import translations as i18n_tool LOCALES_DIR = PM_DIR / "pylibs" / "locales" diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 42be3390..0fd550cb 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -1,29 +1,23 @@ # SPDX-License-Identifier: MIT # -# tools/theme_server: the theme dev server. Its renderer (worker.py, a headless pugwash) +# theme_server (PortMaster/pylibs/theme_server): the theme dev server. Its renderer (worker.py, a headless pugwash) # draws every screen of a theme; serve.py runs renderers, serves the page and exports. # These use an empty ports cache, so no network. import json -import sys import threading import urllib.error import urllib.request import pytest -from conftest import PM_DIR, REPO_DIR +from conftest import PM_DIR -sys.path.insert(0, str(REPO_DIR / "tools" / "theme_server")) -import serve # noqa: E402 -import worker # noqa: E402 +from theme_server import serve, worker DEFAULT_THEME = PM_DIR / "pylibs" / "default_theme" -## The renderers rely on harbourmaster's testing mode (windowed, PM_PRETEND_RESOLUTION), -## which needs a git checkout: not the Test Suite's copy on a device. -needs_checkout = pytest.mark.skipif(not (REPO_DIR / ".git").exists(), reason="the theme server runs from a git checkout") @pytest.mark.parametrize("size, expected, absent", [ @@ -50,17 +44,20 @@ def test_prepare_tools_dir(tmp_path): (theme / "theme.json").write_text("{}") work = tmp_path / "work" - assert worker.prepare_tools_dir(work, tmp_path / "cache", theme, "Dark") == "My Theme" + assert worker.prepare_tools_dir(work, cache_config, theme, "Dark") == "My Theme" config = work / "tools" / "PortMaster" / "config" - ## The cache is linked, not copied; config.json is the renderer's own, with the cache's - ## version (else harbourmaster "upgrades" it by deleting the image folders). - assert (config / "images_pm").is_symlink() and (config / "020_portmaster.source.json").is_symlink() + ## The screenshot folders are linked, the files copied: on a device this is PortMaster's + ## own config, which nothing may change. config.json is the renderer's own, with the + ## version (else harbourmaster "upgrades" it by deleting the image folders) and not a + ## first run (which writes the default source files). + assert (config / "images_pm").is_symlink() + assert not (config / "020_portmaster.source.json").is_symlink() and (config / "020_portmaster.source.json").read_text() == "{}" cfg = json.loads((config / "config.json").read_text()) - assert (cfg['version'], cfg['theme'], cfg['theme-scheme'], cfg['disclaimer']) == (2, "My Theme", "Dark", True) + assert (cfg['version'], cfg['theme'], cfg['theme-scheme'], cfg['disclaimer'], cfg['first-run']) == (2, "My Theme", "Dark", True, False) assert (work / "tools" / "PortMaster" / "themes" / "My Theme").resolve() == theme.resolve() ## The built-in theme isn't linked, pugwash finds it by name. - assert worker.prepare_tools_dir(work, tmp_path / "cache", DEFAULT_THEME, None) == "default_theme" + assert worker.prepare_tools_dir(work, cache_config, DEFAULT_THEME, None) == "default_theme" assert not any((work / "tools" / "PortMaster" / "themes").iterdir()) @@ -136,7 +133,7 @@ def pool(tmp_path_factory): (themes_dir / "Tiny" / "theme.json").write_text(json.dumps(tiny)) themes = serve.Themes(themes_dir) - pool = serve.RenderPool(themes, tmp / "cache", tmp / "work") + pool = serve.RenderPool(themes, tmp / "cache" / "tools" / "PortMaster" / "config", tmp / "work") try: pool.shots() except serve.WorkerError as error: @@ -150,7 +147,6 @@ def pool(tmp_path_factory): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout @pytest.mark.parametrize("resolution", ["640x480", "1280x720"]) def test_every_screen_renders(pool, resolution): for shot, _ in pool.shots(): @@ -172,7 +168,6 @@ def test_every_screen_renders(pool, resolution): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_png_size_and_caching(pool): import png as pypng @@ -186,7 +181,6 @@ def test_png_size_and_caching(pool): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_missing_sections_and_default_only_screens(pool): _, info = pool.render("Tiny", None, "640x480", "en_US", "main_menu") assert not info['missing'] and info['regions'] @@ -199,7 +193,6 @@ def test_missing_sections_and_default_only_screens(pool): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_a_saved_theme_gets_a_new_renderer(pool): theme_json = pool.themes.get("Tiny") / "theme.json" _, before = pool.render("Tiny", None, "640x480", "en_US", "main_menu") @@ -217,7 +210,6 @@ def test_a_saved_theme_gets_a_new_renderer(pool): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_http_api(pool): server = serve.Server(("127.0.0.1", 0), serve.Handler) server.themes, server.pool, server.shots, server.verbose = pool.themes, pool, pool.shots(), False @@ -261,7 +253,6 @@ def get(path): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_export(pool, tmp_path): shots = [shot for shot in pool.shots() if shot[0] in ("main_menu", "filters")] assert serve.export(pool, pool.themes, tmp_path, ["Tiny"], ["640x480"], [], ["en_US"], shots) == 0 @@ -274,12 +265,13 @@ def test_export(pool, tmp_path): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_port_screens_with_port_data(online_hm, hm_dirs, tmp_path): - ## online_hm saved tests/data/ports.json's ports into hm_dirs' config: a ports cache. - cache = hm_dirs['tools_dir'].parent - assert (cache / "tools" / "PortMaster" / "config" / "020_portmaster.source.json").is_file() - pool = serve.RenderPool(serve.Themes(None), cache, tmp_path / "work") + ## online_hm saved tests/data/ports.json's ports into hm_dirs' config: as on a device, + ## the renderers take the ports from PortMaster's own config, and leave it as it was. + config = hm_dirs['tools_dir'] / "PortMaster" / "config" + assert (config / "020_portmaster.source.json").is_file() + before = {path: path.read_bytes() for path in config.rglob("*") if path.is_file()} + pool = serve.RenderPool(serve.Themes(None), config, tmp_path / "work") try: _, info = pool.render("default_theme", None, "640x480", "en_US", "port_info") assert not info['missing'] and not info['errors'] @@ -292,12 +284,13 @@ def test_port_screens_with_port_data(online_hm, hm_dirs, tmp_path): finally: pool.stop() + assert {path: path.read_bytes() for path in config.rglob("*") if path.is_file()} == before + ################################################################################ ## The translation editor's side: which strings a screen uses, reading and saving. @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_render_lists_the_strings_it_used(pool): _, info = pool.render("default_theme", None, "640x480", "de_DE", "options") assert "Select Theme" in info['strings']['messages'] @@ -309,7 +302,6 @@ def test_render_lists_the_strings_it_used(pool): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_language_capability_picks_the_font(pool): ## The default theme's "font[ja_JP]": the language reaches the theme's capabilities ## with a pretend resolution too. @@ -358,7 +350,6 @@ def test_translations_save(locales): @pytest.mark.sdl @pytest.mark.own_display -@needs_checkout def test_translate_api(pool, locales): server = serve.Server(("127.0.0.1", 0), serve.Handler) server.themes, server.pool, server.shots, server.verbose = pool.themes, pool, pool.shots(), False @@ -397,3 +388,112 @@ def post(body, headers={"Content-Type": "application/json", "X-Theme-Server": "1 finally: server.shutdown() server.server_close() + + +################################################################################ +## On a device: PortMaster's options start the server (pugwash/scenes/theme_server.py). +def test_listen_tries_the_next_ports(): + import socket + + busy = socket.socket() + busy.bind(("127.0.0.1", 0)) + busy.listen(1) + port = busy.getsockname()[1] + try: + server = serve.listen("127.0.0.1", port, 5) + try: + assert port < server.server_address[1] < port + 5 + finally: + server.server_close() + + with pytest.raises(OSError): + serve.listen("127.0.0.1", port, 1) + + finally: + busy.close() + + +def test_worker_count(tmp_path): + from pugwash.scenes.theme_server import worker_count + + meminfo = tmp_path / "meminfo" + for available_kb, expected in ((200 * 1024, 1), (480 * 1024, 3), (4000 * 1024, 4)): + meminfo.write_text(f"MemTotal: 999999 kB\nMemAvailable: {available_kb} kB\n") + assert worker_count(str(meminfo)) == expected + + assert worker_count(str(tmp_path / "missing")) == 2 + + +def start_managed(tmp_path, *args): + import os + import subprocess + import sys + + env = dict(os.environ) + env['PYTHONPATH'] = os.pathsep.join([str(PM_DIR / "pylibs"), str(PM_DIR / "exlibs")]) + env['TMPDIR'] = str(tmp_path / "tmp") + (tmp_path / "tmp").mkdir() + return subprocess.Popen( + [sys.executable, "-m", "theme_server.serve", "--managed", "--port", "0", + "--ports-config", str(tmp_path / "config"), "--max-workers", "1", *args], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, env=env, + universal_newlines=True, cwd=str(tmp_path)) + + +@pytest.mark.sdl +@pytest.mark.own_display +def test_managed_server(tmp_path): + proc = start_managed(tmp_path) + try: + ready = json.loads(proc.stdout.readline() or "{}") + if not ready.get('ok'): + pytest.skip(f"the renderer can't start here: {ready.get('error')}") + + with urllib.request.urlopen(f"http://127.0.0.1:{ready['port']}/api/info", timeout=30) as response: + assert json.loads(response.read())['themes'][0]['id'] == "default_theme" + + ## PortMaster closing stdin stops it, and it cleans up after itself. + proc.stdin.close() + assert proc.wait(timeout=30) == 0 + assert list((tmp_path / "tmp").iterdir()) == [] + + finally: + if proc.poll() is None: + proc.kill() + + +@pytest.mark.sdl +def test_theme_editor_scene(gui, hm_dirs, monkeypatch): + import time + import harbourmaster + from pugwash.scenes import theme_server as scene_module + + monkeypatch.setattr(scene_module, "get_ip_address", lambda force_update=False: "127.0.0.1") + gui.hm = harbourmaster.HarbourMaster({'offline': True, 'no-check': True}, callback=gui, **hm_dirs) + scene = scene_module.ThemeServerScene(gui) + gui.push_scene('theme-editor', scene) + proc = scene.proc + try: + end = time.monotonic() + 120 + while not gui.text_data.get('theme_server.url') and proc.poll() is None: + gui.do_loop(no_delay=True) + assert time.monotonic() < end + + url = gui.text_data['theme_server.url'] + assert url.startswith("http://127.0.0.1:") and url in gui.text_data['theme_server.status'] + ## The QR code of the address is on screen. + assert gui.images.load("qr:" + url) is scene.tags['server_qr'].image + + with urllib.request.urlopen(url + "api/info", timeout=30) as response: + assert response.status == 200 + + ## B stops it. + monkeypatch.setattr(gui.events, "was_pressed", lambda button: button == 'B') + gui.do_loop(no_delay=True) + assert proc.wait(timeout=30) == 0 + assert gui.scene_list() == ["root"] + + finally: + if proc.poll() is None: + proc.kill() + gui.hm = None diff --git a/tools/i18n.py b/tools/i18n.py old mode 100644 new mode 100755 index 1c7c4dbf..959e1a25 --- a/tools/i18n.py +++ b/tools/i18n.py @@ -1,215 +1,17 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: MIT # -# PortMaster's translatable strings (see harbourmaster/i18n.py for the files). +# PortMaster's translatable strings: the code is in PortMaster/pylibs/theme_server/translations.py. # # python3 -I tools/i18n.py extract [--themes DIR] -# Rewrites PortMaster/pylibs/locales/strings.json, every _("...") in the code, and -# strings.themes.json, every theme "text" in the built-in theme (and the themes in -# DIR, eg: a PortMaster-Themes checkout; themes it doesn't scan keep their strings). # python3 -I tools/i18n.py check -# Each language file: strings that aren't in strings.json any more, and translations -# that lost a {placeholder} or a line break. -# -# Standard library only, Python 3.7. -import argparse -import ast -import json -import re import sys from pathlib import Path +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "PortMaster" / "pylibs")) -REPO_DIR = Path(__file__).resolve().parent.parent -PM_DIR = REPO_DIR / "PortMaster" -LOCALES_DIR = PM_DIR / "pylibs" / "locales" -DOMAINS = ("messages", "themes") -## The files: strings.json, de_DE.json (messages); strings.themes.json, de_DE.themes.json (themes). -LANGUAGE_FILE = re.compile(r"^([a-z]{2,3}_[A-Z]{2})(\.themes)?\.json$") - - -def domain_file(name, domain): - return LOCALES_DIR / (f"{name}.json" if domain == "messages" else f"{name}.{domain}.json") - - -def languages(): - """The languages with a translation file.""" - return sorted({LANGUAGE_FILE.match(path.name).group(1) for path in LOCALES_DIR.glob("*.json") if LANGUAGE_FILE.match(path.name)}) - - -def code_files(): - """The files with translatable strings: both scripts' own code and the two packages.""" - files = [PM_DIR / "pugwash", PM_DIR / "harbourmaster"] - for package in ("harbourmaster", "pugwash"): - files += sorted((PM_DIR / "pylibs" / package).rglob("*.py")) - return files - - -def _literal(node): - """The string of a str literal node, None for anything else (f-strings, names, ...).""" - if isinstance(node, ast.Constant) and isinstance(node.value, str): - return node.value - if isinstance(node, getattr(ast, "Str", ())) and isinstance(getattr(node, "s", None), str): - return node.s - return None - - -def _display(path): - """A file as strings.json names it: relative to PortMaster/ when it's in there.""" - try: - return Path(path).resolve().relative_to(PM_DIR).as_posix() - except ValueError: - return Path(path).as_posix() - - -def code_strings(files=None): - """{text: ["file:line", ...]} for every _("text") call, in file order.""" - found = {} - for path in files or code_files(): - tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) - calls = [node for node in ast.walk(tree) - if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "_" and node.args] - for node in sorted(calls, key=lambda node: (node.lineno, node.col_offset)): - text = _literal(node.args[0]) - if text: - found.setdefault(text, []).append(f"{_display(path)}:{node.lineno}") - - return found - - -def theme_strings(theme_dir, name=None): - """{text: ["theme/theme.json:line", ...]} for a theme's "text" values that are more - than a {template} or a colour.""" - theme_file = Path(theme_dir) / "theme.json" - source = theme_file.read_text(encoding="utf-8") - name = name or Path(theme_dir).name - found = {} - - def walk(value): - if isinstance(value, dict): - for key, item in value.items(): - if key.split("[", 1)[0] == "text" and isinstance(item, str): - yield item - else: - yield from walk(item) - elif isinstance(value, list): - for item in value: - yield from walk(item) - - lines = source.split("\n") - for text in walk(json.loads(source)): - if not text.strip() or re.match(r"^\{[^}]+\}$", text) or re.match(r"^#[0-9a-f]+$", text, re.I): - continue - quoted = json.dumps(text, ensure_ascii=False)[1:-1] - line = next((number for number, line in enumerate(lines, 1) if quoted in line), 0) - found.setdefault(text, []).append(f"{name}/theme.json:{line}") - - return found - - -def read_json(path): - try: - data = json.loads(Path(path).read_text(encoding="utf-8")) - except (OSError, ValueError): - return {} - return data if isinstance(data, dict) else {} - - -def read_strings(): - """{domain: {text: [where it's used]}}""" - return {domain: read_json(domain_file("strings", domain)) for domain in DOMAINS} - - -def read_translations(lang): - """{domain: {text: translation}}""" - return {domain: read_json(domain_file(lang, domain)) for domain in DOMAINS} - - -def write_json(path, data): - path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8") - - -def extract(themes_dirs=()): - strings = read_strings() - themes = {"default_theme": PM_DIR / "pylibs" / "default_theme"} - for themes_dir in themes_dirs: - for theme_file in sorted(Path(themes_dir).glob("*/theme.json")): - themes[theme_file.parent.name] = theme_file.parent - - ## Themes not scanned this time keep their strings. - kept = {} - for text, sources in strings['themes'].items(): - others = [source for source in sources if source.split("/", 1)[0] not in themes] - if others: - kept[text] = others - - for name, theme_dir in themes.items(): - for text, sources in theme_strings(theme_dir, name).items(): - kept.setdefault(text, []).extend(sources) - - return {'messages': code_strings(), 'themes': {text: kept[text] for text in sorted(kept, key=lambda t: (kept[t][0], t))}} - - -PLACEHOLDER = re.compile(r"\{[^{}]*\}") - - -def problems(source, translation): - """What a translation lost: {placeholders} and line breaks the English has.""" - found = [] - missing = sorted(set(PLACEHOLDER.findall(source)) - set(PLACEHOLDER.findall(translation))) - if missing: - found.append("missing " + ", ".join(missing)) - extra = sorted(set(PLACEHOLDER.findall(translation)) - set(PLACEHOLDER.findall(source))) - if extra: - found.append("unknown " + ", ".join(extra)) - ## Joined to other text in the code ("Music: " + "Disabled"): the spaces at the ends matter. - for end, english, here in (("starts", source[:1], translation[:1]), ("ends", source[-1:], translation[-1:])): - if english.isspace() and not here.isspace(): - found.append(f"English {end} with a space, this doesn't") - if source.count("\n") != translation.count("\n"): - found.append(f"{source.count(chr(10))} line breaks in English, {translation.count(chr(10))} here") - return found - - -def check(): - strings = read_strings() - issues = 0 - for lang in languages(): - for domain, translations in read_translations(lang).items(): - name = domain_file(lang, domain).name - for source, translation in translations.items(): - if source not in strings[domain]: - print(f"{name}: not used any more: {source!r}") - issues += 1 - for problem in problems(source, translation): - print(f"{name}: {problem}: {source!r}") - issues += 1 - - return 1 if issues else 0 - - -def main(argv=None): - parser = argparse.ArgumentParser(description="PortMaster's translatable strings") - commands = parser.add_subparsers(dest="command") - extract_parser = commands.add_parser("extract", help="rewrite locales/strings.json") - extract_parser.add_argument("--themes", action="append", default=[], help="a folder of themes to scan too") - commands.add_parser("check", help="check the language files") - options = parser.parse_args(argv) - - if options.command == "extract": - strings = extract(options.themes) - for domain in DOMAINS: - write_json(domain_file("strings", domain), strings[domain]) - print(f"{len(strings['messages'])} code strings, {len(strings['themes'])} theme strings -> {LOCALES_DIR}") - return 0 - - if options.command == "check": - return check() - - parser.print_help() - return 2 - +from theme_server.translations import main # noqa: E402 if __name__ == "__main__": sys.exit(main()) diff --git a/tools/theme_server/serve.py b/tools/theme_server/serve.py old mode 100644 new mode 100755 index ab9059c3..de310a3e --- a/tools/theme_server/serve.py +++ b/tools/theme_server/serve.py @@ -1,596 +1,19 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: MIT # -# The theme dev server: renders every PortMaster screen with your theme, at any resolution, -# colour scheme and language, and shows them in a web page that refreshes when you save. +# The theme preview server, from a checkout. It lives in PortMaster/pylibs/theme_server, +# which ships with PortMaster for the on-device editor; see THEME.md and serve.py there. # # python3 tools/theme_server/serve.py --themes DIR [--port 8090] -# DIR is a theme (it has a theme.json) or a folder of themes. The built-in default -# theme is always there too. Then open http://localhost:8090/ -# -# python3 tools/theme_server/serve.py --themes DIR --export OUT -# [--theme NAME] [--resolutions 640x480,1280x720] [--schemes all] [--langs en_US] -# Writes OUT/////.png and .json, no server. -# -# --refresh-ports downloads the ports list and screenshots again (they're cached in -# ~/.cache/portmaster-theme-server, or --cache DIR). -# -# Needs what running pugwash from a checkout needs (SDL2: pip install pysdl2-dll on macOS) -# and the network the first time, for the real ports list. +# python3 tools/theme_server/serve.py --themes DIR --export OUT [...] -import argparse -import hashlib -import json -import os -import re -import shutil -import subprocess import sys -import tarfile -import tempfile -import threading -import time -import urllib.parse -from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path +PM_DIR = Path(__file__).resolve().parents[2] / "PortMaster" +sys.path[:0] = [str(PM_DIR / "pylibs"), str(PM_DIR / "exlibs")] -HERE = Path(__file__).resolve().parent -REPO_DIR = HERE.parents[1] -sys.path.insert(0, str(HERE.parent)) -import i18n as i18n_tool # noqa: E402 (tools/i18n.py) - -PM_DIR = REPO_DIR / "PortMaster" -DEFAULT_THEME_DIR = PM_DIR / "pylibs" / "default_theme" -WORKER = HERE / "worker.py" - -RESOLUTIONS = ( - "480x320", "640x480", "720x480", "720x720", "854x480", "960x544", "960x720", - "1024x768", "1280x720", "1280x800", "1920x1080", "1920x1152", - ) - -## Render processes kept running at once (each is a pugwash, ~60 MB). -MAX_WORKERS = 6 - - -def default_cache(): - base = os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache") - return Path(base) / "portmaster-theme-server" - - -def valid_resolution(text): - match = re.match(r"^(\d{2,4})x(\d{2,4})$", text or "") - return bool(match) and 0 < int(match.group(1)) <= 4096 and 0 < int(match.group(2)) <= 4096 - - -################################################################################ -## Themes and languages -class Themes: - """The themes the server shows: the default theme, then each theme in --themes.""" - def __init__(self, themes_dir): - self.themes_dir = Path(themes_dir).resolve() if themes_dir else None - - def all(self): - found = {"default_theme": DEFAULT_THEME_DIR} - if self.themes_dir is not None: - if (self.themes_dir / "theme.json").is_file(): - found[self.themes_dir.name] = self.themes_dir - elif self.themes_dir.is_dir(): - for theme_file in sorted(self.themes_dir.glob("*/theme.json")): - found[theme_file.parent.name] = theme_file.parent - - return found - - def get(self, theme_id): - return self.all().get(theme_id) - - def info(self, theme_id, theme_dir): - """Name, schemes and the default scheme, or the error reading theme.json.""" - try: - data = json.loads((theme_dir / "theme.json").read_text(encoding="utf-8")) - except (OSError, ValueError) as error: - return {'id': theme_id, 'name': theme_id, 'schemes': [], 'default_scheme': None, 'error': str(error)} - - info = data.get("#info", {}) - schemes = [name for name in data.get("#schemes", {}) if not name.startswith("#")] - default = info.get("default-scheme") - if default not in schemes: - default = schemes[0] if schemes else None - - return {'id': theme_id, 'name': info.get("name") or theme_id, 'creator': info.get("creator", ""), - 'schemes': schemes, 'default_scheme': default, 'error': None} - - @staticmethod - def fingerprint(theme_dir, extra_files=()): - """Changes whenever a file in the theme (or one of extra_files) is added, removed or saved.""" - digest = hashlib.md5() - paths = [] - for root, dirs, files in os.walk(str(theme_dir)): - dirs.sort() - paths += [os.path.join(root, name) for name in sorted(files)] - - for path in paths + [str(path) for path in extra_files]: - try: - stat = os.stat(path) - except OSError: - continue - digest.update(f"{path}\0{stat.st_size}\0{stat.st_mtime_ns}\n".encode("utf-8", "replace")) - - return digest.hexdigest()[:12] - - -def version(theme_dir, lang): - """The theme's fingerprint with the language's translation files: what a render depends on.""" - return Themes.fingerprint(theme_dir, [i18n_tool.domain_file(lang, domain) for domain in i18n_tool.DOMAINS]) - - -def languages(): - """[(code, name)]: the languages pugwash has translations for.""" - text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") - names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) - locales = PM_DIR / "pylibs" / "locales" - codes = {"en_US"} | {match.group(1) for match in (re.match(r"^([a-z]{2,3}_[A-Z]{2})\.json$", path.name) for path in locales.glob("*.json")) if match} - return [(code, names.get(code, code)) for code in names if code in codes] + \ - [(code, code) for code in sorted(codes - set(names))] - - -################################################################################ -## Render workers -class WorkerError(Exception): - pass - - -class Worker: - """One worker.py: a pugwash for one theme version, scheme, resolution and language.""" - def __init__(self, key, theme_dir, scheme, resolution, lang, cache, work): - self.key = key - self.lock = threading.Lock() - self.work = Path(work) - command = [sys.executable, str(WORKER), "--theme-dir", str(theme_dir), "--resolution", resolution, - "--lang", lang, "--cache", str(cache), "--work", str(self.work)] - if scheme: - command += ["--scheme", scheme] - - self.log_file = open(str(self.work.with_suffix(".log")), "w") - self.proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=self.log_file, universal_newlines=True, bufsize=1) - self.ready = self._read() - if not self.ready.get('ok'): - self.stop() - raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + str(self.log_file.name)) - - self.used = time.monotonic() - - def _read(self): - line = self.proc.stdout.readline() - if not line: - return {'ok': False, 'error': "the renderer stopped, see " + str(self.log_file.name)} - return json.loads(line) - - def request(self, **command): - with self.lock: - if self.proc.poll() is not None: - raise WorkerError("the renderer stopped, see " + str(self.log_file.name)) - - self.used = time.monotonic() - self.proc.stdin.write(json.dumps(command) + "\n") - self.proc.stdin.flush() - return self._read() - - def stop(self): - try: - if self.proc.poll() is None: - self.proc.stdin.write(json.dumps({'cmd': "quit"}) + "\n") - self.proc.stdin.flush() - self.proc.wait(timeout=5) - except (OSError, ValueError, subprocess.TimeoutExpired): - self.proc.kill() - finally: - self.log_file.close() - shutil.rmtree(str(self.work), ignore_errors=True) - try: - os.unlink(self.log_file.name) - except OSError: - pass - - -class RenderPool: - """Starts workers as they're needed, replaces them when their theme changes, and caches - what they rendered.""" - def __init__(self, themes, cache, work_dir): - self.themes = themes - self.cache = cache - self.work_dir = Path(work_dir) - self.work_dir.mkdir(parents=True, exist_ok=True) - self.workers = {} - self.renders = {} - self.lock = threading.Lock() - self.start_lock = threading.Lock() - self.translations = Translations() - self.counter = 0 - - def _worker(self, theme_id, scheme, resolution, lang): - theme_dir = self.themes.get(theme_id) - if theme_dir is None: - raise WorkerError(f"no theme {theme_id!r}") - - fingerprint = version(theme_dir, lang) - key = (theme_id, fingerprint, scheme or "", resolution, lang) - with self.lock: - worker = self.workers.get(key) - if worker is not None: - return worker, fingerprint - - ## One start at a time: the page asks for every screen at once. - with self.start_lock: - with self.lock: - worker = self.workers.get(key) - if worker is not None: - return worker, fingerprint - - ## The same settings for an older version of the theme, then the least - ## recently used if there are too many. - stale = [self.workers.pop(other) for other in list(self.workers) - if other[0] == theme_id and other[2:] == key[2:]] - while len(self.workers) >= MAX_WORKERS: - stale.append(self.workers.pop(min(self.workers, key=lambda other: self.workers[other].used))) - - self.counter += 1 - work = self.work_dir / f"worker-{self.counter}" - - for old in stale: - old.stop() - - worker = Worker(key, theme_dir, scheme, resolution, lang, self.cache, work) - with self.lock: - self.workers[key] = worker - - return worker, fingerprint - - def render(self, theme_id, scheme, resolution, lang, shot): - """(png bytes, info dict) for one screen, cached until the theme changes.""" - worker, fingerprint = self._worker(theme_id, scheme, resolution, lang) - render_key = worker.key + (shot,) - with self.lock: - if render_key in self.renders: - return self.renders[render_key] - - out_file = worker.work / f"{shot}.png" - answer = worker.request(cmd="render", shot=shot, out=str(out_file)) - if not answer.get('ok'): - raise WorkerError(answer.get('error', "render failed")) - - png = out_file.read_bytes() if not answer.get('missing') else b"" - info = { - 'shot': shot, 'theme': theme_id, 'scheme': worker.ready.get('scheme'), 'resolution': resolution, - 'lang': lang, 'version': fingerprint, 'regions': answer.get('regions', []), - 'errors': answer.get('errors', []), 'missing': answer.get('missing'), 'ms': answer.get('ms'), - 'strings': self.translations.used(answer.get('strings', {})), - } - with self.lock: - ## Drop renders of older versions of the theme and the language's translations. - for old_key in [k for k in self.renders if k[0] == theme_id and k[4] == lang and k[1] != fingerprint]: - del self.renders[old_key] - self.renders[render_key] = (png, info) - - return png, info - - def shots(self): - worker, _ = self._worker("default_theme", None, "640x480", "en_US") - return worker.request(cmd="shots")['shots'] - - def stop(self): - with self.lock: - workers, self.workers = list(self.workers.values()), {} - for worker in workers: - worker.stop() - - -################################################################################ -## Translations: the language files in the checkout's PortMaster/pylibs/locales -class Translations: - """Reads and saves translations, one string at a time.""" - def __init__(self): - self.lock = threading.Lock() - - def strings(self): - return i18n_tool.read_strings() - - def used(self, lookups): - """The strings a render looked up that are translatable (not {templates} and the like).""" - strings = self.strings() - return {domain: [text for text in lookups.get(domain, []) if text in strings[domain]] for domain in i18n_tool.DOMAINS} - - def language(self, lang): - """Every string of both domains, with this language's translation and its problems.""" - strings, translations = self.strings(), i18n_tool.read_translations(lang) - result = {} - for domain in i18n_tool.DOMAINS: - entries = [] - for text, sources in strings[domain].items(): - translation = translations[domain].get(text, "") - entries.append({'text': text, 'translation': translation, 'sources': sources, - 'problems': i18n_tool.problems(text, translation) if translation else []}) - result[domain] = entries - return result - - def save(self, lang, domain, text, translation): - """Sets one translation ("" removes it), in strings.json's order. Returns its problems.""" - with self.lock: - strings = self.strings()[domain] - if text not in strings: - raise KeyError(text) - - translations = i18n_tool.read_translations(lang)[domain] - if translation: - translations[text] = translation - else: - translations.pop(text, None) - - ordered = {key: translations[key] for key in strings if key in translations} - path = i18n_tool.domain_file(lang, domain) - temp = path.with_name(path.name + ".tmp") - i18n_tool.write_json(temp, ordered) - os.replace(str(temp), str(path)) - ## Both files of a language exist once it has one. - for other in i18n_tool.DOMAINS: - if not i18n_tool.domain_file(lang, other).exists(): - i18n_tool.write_json(i18n_tool.domain_file(lang, other), {}) - - return i18n_tool.problems(text, translation) if translation else [] - - -def all_languages(): - """[(code, name, has files)]: every language PortMaster knows a name for, and any other with files.""" - have = {code for code, _ in languages()} - text = (PM_DIR / "pylibs" / "pugwash" / "lang.py").read_text(encoding="utf-8") - names = dict(re.findall(r'"(\w\w_\w\w)": _\("([^"]+)"\)', text)) - return [(code, names.get(code, code), code in have) for code in sorted(set(names) | have, key=lambda code: names.get(code, code))] - - -################################################################################ -## The Chinese, Japanese and Korean fonts: the PortMaster/pugwash script unpacks them on -## its first run, the workers import pugwash without it. -def ensure_fonts(): - resources = PM_DIR / "pylibs" / "resources" - if (resources / "NotoSansTC-Regular.ttf").is_file(): - return - - try: - with tarfile.open(str(resources / "NotoSans.tar.xz"), "r:xz") as tar: - tar.extractall(str(resources)) - - except (OSError, EOFError, tarfile.TarError) as err: - print(f"Unable to extract the Noto fonts, Chinese, Japanese and Korean won't show: {err}") - - -################################################################################ -## The ports cache -def ensure_cache(cache, refresh=False): - source = cache / "tools" / "PortMaster" / "config" / "020_portmaster.source.json" - if source.is_file() and not refresh: - try: - if json.loads(source.read_text()).get('data', {}).get('ports'): - return - except ValueError: - pass - - print(f"Downloading the ports list and screenshots into {cache} ...", flush=True) - if (cache / "tools").exists(): - shutil.rmtree(str(cache / "tools")) - - result = subprocess.run([sys.executable, str(WORKER), "--prepare-cache", str(cache)]) - if result.returncode != 0: - sys.exit("Couldn't download the ports list (no network?). Screens will show no ports.") - - -################################################################################ -## The web server -class Handler(BaseHTTPRequestHandler): - server_version = "PortMasterThemeServer/1" - - def log_message(self, format, *args): - if self.server.verbose: - super().log_message(format, *args) - - def send(self, status, body, content_type="application/json", cache=False): - if isinstance(body, (dict, list)): - body = json.dumps(body).encode("utf-8") - elif isinstance(body, str): - body = body.encode("utf-8") - - self.send_response(status) - self.send_header("Content-Type", content_type) - self.send_header("Content-Length", str(len(body))) - self.send_header("Cache-Control", "max-age=3600" if cache else "no-store") - self.end_headers() - self.wfile.write(body) - - def do_GET(self): - url = urllib.parse.urlparse(self.path) - query = {key: values[-1] for key, values in urllib.parse.parse_qs(url.query).items()} - try: - if url.path in ("/", "/index.html"): - self.send(200, (HERE / "static" / "index.html").read_bytes(), "text/html; charset=utf-8") - - elif url.path == "/api/info": - themes = self.server.themes - self.send(200, { - 'themes': [themes.info(theme_id, theme_dir) for theme_id, theme_dir in themes.all().items()], - 'languages': languages(), - 'resolutions': list(RESOLUTIONS), - 'shots': self.server.shots, - 'all_languages': all_languages(), - }) - - elif url.path == "/api/strings": - lang = query.get('lang', "") - if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): - self.send(400, {'error': "bad language"}) - return - self.send(200, {'lang': lang, 'domains': self.server.pool.translations.language(lang)}) - - elif url.path == "/api/version": - theme_dir = self.server.themes.get(query.get('theme')) - lang = query.get('lang', "en_US") - if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): - self.send(400, {'error': "bad language"}) - return - self.send(200 if theme_dir else 404, {'version': version(theme_dir, lang) if theme_dir else None}) - - elif url.path in ("/api/render.png", "/api/render.json"): - theme_id = query.get('theme', "default_theme") - resolution = query.get('res', "640x480") - lang = query.get('lang', "en_US") - shot = query.get('shot', "main_menu") - if not valid_resolution(resolution) or not re.match(r"^\w\w_\w\w$", lang) or \ - shot not in {name for name, _ in self.server.shots}: - self.send(400, {'error': "bad theme, resolution, language or screen"}) - return - - png, info = self.server.pool.render(theme_id, query.get('scheme') or None, resolution, lang, shot) - if url.path.endswith(".png"): - if not png: - self.send(404, {'error': info['missing']}) - else: - ## The URL carries the theme version, so it can be cached. - self.send(200, png, "image/png", cache='v' in query) - else: - self.send(200, info) - - else: - self.send(404, {'error': "not found"}) - - except WorkerError as error: - self.send(500, {'error': str(error)}) - - except (BrokenPipeError, ConnectionResetError): - pass - - def do_POST(self): - url = urllib.parse.urlparse(self.path) - ## Only the page itself: another site can't send this header without a CORS - ## preflight, which this server never answers. - if self.headers.get("X-Theme-Server") != "1" or not (self.headers.get("Content-Type") or "").startswith("application/json"): - self.send(403, {'error': "forbidden"}) - return - - try: - length = int(self.headers.get("Content-Length") or 0) - if not 0 < length <= 256 * 1024: - self.send(400, {'error': "bad request"}) - return - body = json.loads(self.rfile.read(length).decode("utf-8")) - - if url.path == "/api/translate": - lang, domain = body.get('lang', ""), body.get('domain', "") - text, translation = body.get('text'), body.get('translation', "") - if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang) or lang == "en_US" or domain not in i18n_tool.DOMAINS or \ - not isinstance(text, str) or not isinstance(translation, str): - self.send(400, {'error': "bad language, domain or text"}) - return - try: - problems = self.server.pool.translations.save(lang, domain, text, translation) - except KeyError: - self.send(404, {'error': "not a translatable string"}) - return - self.send(200, {'ok': True, 'problems': problems}) - - else: - self.send(404, {'error': "not found"}) - - except (ValueError, UnicodeDecodeError, AttributeError): - self.send(400, {'error': "bad request"}) - - except (BrokenPipeError, ConnectionResetError): - pass - - -class Server(ThreadingHTTPServer): - daemon_threads = True - - -################################################################################ -## Export -def export(pool, themes, out, theme_ids, resolutions, schemes, langs, shots): - failures = 0 - for theme_id in theme_ids: - info = themes.info(theme_id, themes.get(theme_id)) - theme_schemes = info['schemes'] if schemes == ["all"] else (schemes or [info['default_scheme']]) - for resolution in resolutions: - for scheme in theme_schemes or [None]: - for lang in langs: - folder = out / theme_id / resolution / (scheme or "default") / lang - folder.mkdir(parents=True, exist_ok=True) - for shot, _ in shots: - try: - png, info = pool.render(theme_id, scheme, resolution, lang, shot) - except WorkerError as error: - print(f"FAIL {theme_id} {resolution} {scheme} {lang} {shot}: {error}", file=sys.stderr) - failures += 1 - continue - - if png: - (folder / f"{shot}.png").write_bytes(png) - (folder / f"{shot}.json").write_text(json.dumps(info, indent=1)) - for error in info['errors']: - print(f"{theme_id} {resolution} {scheme} {lang} {shot}: {error}", file=sys.stderr) - - print(f"{folder}", flush=True) - - return 1 if failures else 0 - - -def main(argv=None): - parser = argparse.ArgumentParser(description="Renders PortMaster's screens with your theme.") - parser.add_argument("--themes", help="a theme folder, or a folder of themes") - parser.add_argument("--port", type=int, default=8090) - parser.add_argument("--host", default="127.0.0.1", help="use 0.0.0.0 to let other machines connect") - parser.add_argument("--cache", type=Path, default=default_cache()) - parser.add_argument("--refresh-ports", action="store_true", help="download the ports list and screenshots again") - parser.add_argument("--verbose", action="store_true") - parser.add_argument("--export", type=Path, metavar="OUT", help="write PNGs instead of serving them") - parser.add_argument("--theme", action="append", help="export: these themes (default: all)") - parser.add_argument("--resolutions", default="640x480", help="export: comma separated, or 'all'") - parser.add_argument("--schemes", default="", help="export: comma separated, 'all', or the theme's default") - parser.add_argument("--langs", default="en_US", help="export: comma separated") - options = parser.parse_args(argv) - - ensure_fonts() - ensure_cache(options.cache, options.refresh_ports) - themes = Themes(options.themes) - work_dir = Path(tempfile.mkdtemp(prefix="pm-theme-server-")) - pool = RenderPool(themes, options.cache, work_dir) - - try: - shots = pool.shots() - if options.export: - resolutions = list(RESOLUTIONS) if options.resolutions == "all" else options.resolutions.split(",") - bad = [resolution for resolution in resolutions if not valid_resolution(resolution)] - if bad: - sys.exit(f"Not a resolution: {', '.join(bad)}") - - return export(pool, themes, options.export, options.theme or list(themes.all()), resolutions, - [scheme for scheme in options.schemes.split(",") if scheme], options.langs.split(","), shots) - - try: - server = Server((options.host, options.port), Handler) - except OSError as error: - sys.exit(f"Can't listen on {options.host}:{options.port} ({error.strerror}), try another --port.") - - server.themes, server.pool, server.shots, server.verbose = themes, pool, shots, options.verbose - print(f"Theme server on http://{'localhost' if options.host == '127.0.0.1' else options.host}:{options.port}/" - f" ({len(themes.all())} theme{'s' if len(themes.all()) != 1 else ''}), Ctrl+C to stop.", flush=True) - try: - server.serve_forever() - except KeyboardInterrupt: - pass - return 0 - - finally: - pool.stop() - shutil.rmtree(str(work_dir), ignore_errors=True) - +from theme_server.serve import main # noqa: E402 if __name__ == "__main__": sys.exit(main()) From 63b7c3208f03b5512e855a96bddb8780f3a77299 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 18:20:52 +0800 Subject: [PATCH 08/28] Theme server: up straight away, one renderer for the default scheme - The list of screens is in catalogue.py, so the server listens (and PortMaster shows the address) before any renderer starts: that took ~15 seconds on a handheld. The first renderer starts in the background meanwhile. - No scheme means the theme's default scheme by name, so the page (which asks for "Light Mode") uses the renderer the server warmed up instead of starting a second one. - --managed logs every request to theme_server.txt. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/theme_server/catalogue.py | 33 +++++++++++++++++++ PortMaster/pylibs/theme_server/serve.py | 35 ++++++++++++++------- PortMaster/pylibs/theme_server/shots.py | 31 +++--------------- tests/test_theme_server.py | 22 ++++++++++++- 4 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 PortMaster/pylibs/theme_server/catalogue.py diff --git a/PortMaster/pylibs/theme_server/catalogue.py b/PortMaster/pylibs/theme_server/catalogue.py new file mode 100644 index 00000000..091a5c4c --- /dev/null +++ b/PortMaster/pylibs/theme_server/catalogue.py @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: MIT +# +# The screens the theme server renders, in the page's order: (name, title). shots.py has +# a builder for each, shot_. Kept apart, so serve.py lists them without starting +# a renderer. + +SHOTS = ( + ("main_menu", "Main menu"), + ("ports_all", "All ports"), + ("ports_rtr", "Ready to run ports"), + ("ports_manage", "Manage ports"), + ("port_info", "Port info"), + ("port_info_popup", "Port info popup"), + ("filters", "Filters"), + ("featured", "Featured ports"), + ("featured_list", "Featured ports list"), + ("options", "Options"), + ("runtimes", "Runtimes"), + ("themes", "Themes"), + ("theme_schemes", "Colour schemes"), + ("language", "Language"), + ("keyboard", "On-screen keyboard"), + ("theme_editor", "Theme editor (its address and QR code)"), + ("messages", "Messages and progress"), + ("message_box", "Message box"), + ("selection_list", "Selection list"), + ("selection_description", "Selection list + description"), + ("selection_images", "Selection list + images"), + ("selection_description_images", "Selection list + description + images"), + ("disclaimer", "Disclaimer"), + ("warning_trimui", "Unsupported system warning (TrimUI)"), + ("warning_long", "Unsupported system warning (spruce, low RAM)"), + ) diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index a3640713..033676e6 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -40,7 +40,7 @@ from pathlib import Path -from theme_server import translations as i18n_tool +from theme_server import catalogue, translations as i18n_tool HERE = Path(__file__).resolve().parent @@ -220,6 +220,8 @@ def _worker(self, theme_id, scheme, resolution, lang): if theme_dir is None: raise WorkerError(f"no theme {theme_id!r}") + ## No scheme is the theme's default one, by name: the same renderer the page uses. + scheme = scheme or self.themes.info(theme_id, theme_dir)['default_scheme'] fingerprint = version(theme_dir, lang) key = (theme_id, fingerprint, scheme or "", resolution, lang) with self.lock: @@ -282,8 +284,13 @@ def render(self, theme_id, scheme, resolution, lang, shot): return png, info def shots(self): - worker, _ = self._worker("default_theme", None, "640x480", "en_US") - return worker.request(cmd="shots")['shots'] + """[[name, title]] of every screen, in the page's order.""" + return [[name, title] for name, title in catalogue.SHOTS] + + def warm(self, theme_id="default_theme", scheme=None, resolution="640x480", lang="en_US"): + """Starts the renderer the page asks for first (the default theme, scheme and + language), so its screens come quicker. Raises WorkerError if it can't start.""" + self._worker(theme_id, scheme, resolution, lang) def stop(self): with self.lock: @@ -611,14 +618,7 @@ def interrupted(signum, frame): pool = RenderPool(themes, ports_config, work_dir, options.max_workers) try: - try: - shots = pool.shots() - except WorkerError as error: - if report is None: - raise - report(ok=False, error=str(error).strip().splitlines()[-1]) - return 1 - + shots = pool.shots() if options.export: resolutions = list(RESOLUTIONS) if options.resolutions == "all" else options.resolutions.split(",") bad = [resolution for resolution in resolutions if not valid_resolution(resolution)] @@ -636,8 +636,19 @@ def interrupted(signum, frame): return 1 sys.exit(f"Can't listen on {options.host}:{options.port} ({error.strerror}), try another --port.") - server.themes, server.pool, server.shots, server.verbose = themes, pool, shots, options.verbose + ## Managed: every request goes in PortMaster's theme_server.txt. + server.themes, server.pool, server.shots, server.verbose = themes, pool, shots, options.verbose or report is not None port = server.server_address[1] + + ## Up straight away; the first renderer starts meanwhile (6-15 seconds on a handheld). + def warm(): + try: + pool.warm() + except WorkerError as error: + print(f"The renderer didn't start: {error}", file=sys.stderr, flush=True) + + threading.Thread(target=warm, name="warm", daemon=True).start() + if report is not None: report(ok=True, ready=True, port=port) diff --git a/PortMaster/pylibs/theme_server/shots.py b/PortMaster/pylibs/theme_server/shots.py index de35a85e..5c8d9fe2 100644 --- a/PortMaster/pylibs/theme_server/shots.py +++ b/PortMaster/pylibs/theme_server/shots.py @@ -15,6 +15,8 @@ from pugwash.app import PortMasterGUI from pugwash.scenes.base import BaseScene +from theme_server import catalogue + class MissingSection(Exception): """A screen needs a theme section this theme doesn't have.""" @@ -302,33 +304,8 @@ def shot_warning_long(gui): _startup_warning(gui, {"spruce", "lowram"}) -SHOTS = ( - ("main_menu", "Main menu", shot_main_menu), - ("ports_all", "All ports", shot_ports_all), - ("ports_rtr", "Ready to run ports", shot_ports_rtr), - ("ports_manage", "Manage ports", shot_ports_manage), - ("port_info", "Port info", shot_port_info), - ("port_info_popup", "Port info popup", shot_port_info_popup), - ("filters", "Filters", shot_filters), - ("featured", "Featured ports", shot_featured), - ("featured_list", "Featured ports list", shot_featured_list), - ("options", "Options", shot_options), - ("runtimes", "Runtimes", shot_runtimes), - ("themes", "Themes", shot_themes), - ("theme_schemes", "Colour schemes", shot_theme_schemes), - ("language", "Language", shot_language), - ("keyboard", "On-screen keyboard", shot_keyboard), - ("theme_editor", "Theme editor (its address and QR code)", shot_theme_editor), - ("messages", "Messages and progress", shot_messages), - ("message_box", "Message box", shot_message_box), - ("selection_list", "Selection list", shot_selection_list), - ("selection_description", "Selection list + description", shot_selection_description), - ("selection_images", "Selection list + images", shot_selection_images), - ("selection_description_images", "Selection list + description + images", shot_selection_description_images), - ("disclaimer", "Disclaimer", shot_disclaimer), - ("warning_trimui", "Unsupported system warning (TrimUI)", shot_warning_trimui), - ("warning_long", "Unsupported system warning (spruce, low RAM)", shot_warning_long), - ) +## The catalogue's screens with their builders. +SHOTS = tuple((name, title, globals()["shot_" + name]) for name, title in catalogue.SHOTS) SHOT_BUILDERS = {name: builder for name, _, builder in SHOTS} diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 0fd550cb..32af22ef 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -135,7 +135,7 @@ def pool(tmp_path_factory): themes = serve.Themes(themes_dir) pool = serve.RenderPool(themes, tmp / "cache" / "tools" / "PortMaster" / "config", tmp / "work") try: - pool.shots() + pool.warm() except serve.WorkerError as error: pool.stop() pytest.skip(f"the renderer can't start here: {str(error).strip().splitlines()[-1]}") @@ -497,3 +497,23 @@ def test_theme_editor_scene(gui, hm_dirs, monkeypatch): if proc.poll() is None: proc.kill() gui.hm = None + + +def test_screens_listed_without_a_renderer(tmp_path): + ## The page's list of screens comes from the catalogue: the server is up (and PortMaster + ## shows its address) before any renderer has started. + pool = serve.RenderPool(serve.Themes(None), tmp_path / "config", tmp_path / "work") + shots = pool.shots() + assert shots[0] == ["main_menu", "Main menu"] and ["theme_editor", "Theme editor (its address and QR code)"] in shots + assert pool.workers == {} + + +@pytest.mark.sdl +@pytest.mark.own_display +def test_no_scheme_is_the_default_scheme(pool): + ## The page asks for the default scheme by name; warm() and the API without a scheme + ## must use that same renderer, not start another. + pool.render("default_theme", None, "640x480", "en_US", "main_menu") + workers = dict(pool.workers) + pool.render("default_theme", "Light Mode", "640x480", "en_US", "main_menu") + assert pool.workers == workers From 4a7ffdcf708124fae92593dd7a18499c3621be53 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:00:20 +0800 Subject: [PATCH 09/28] Change theme, colour scheme and language without restarting Choosing a theme, colour scheme or language used to say "You will have to restart" and quit PortMaster. Now it applies straight away, and you're back in the same list with the new look. - PortMasterGUI.reload_theme(then=...) drops what the old theme loaded (images, fonts and rendered text, sounds and music, animations, pallet, overrides, element rects, its resource path), loads the configured theme again and rebuilds the screens: a new main menu with `then`'s scenes on it. - reload_language() also switches the language (lang.set_language: the translations, CURRENT_LANG, the environment) and swaps the language capability, which picks the theme's font for it (font[ja_JP] & co). - The three "restart" prompts are gone, with their translations. - tests/test_reload.py: a screen after switching to another theme, scheme or language and back is pixel-identical to before, German translates, Japanese picks the Noto font, and the themes list applies a choice without quitting. The theme server can now reuse a renderer instead of starting another for each change (next). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 1 + PortMaster/pylibs/locales/de_DE.json | 3 - PortMaster/pylibs/locales/es_ES.json | 3 - PortMaster/pylibs/locales/fr_FR.json | 3 - PortMaster/pylibs/locales/it_IT.json | 3 - PortMaster/pylibs/locales/ja_JP.json | 3 - PortMaster/pylibs/locales/ko_KR.json | 3 - PortMaster/pylibs/locales/nl_NL.json | 3 - PortMaster/pylibs/locales/pl_PL.json | 3 - PortMaster/pylibs/locales/pt_BR.json | 3 - PortMaster/pylibs/locales/ro_RO.json | 3 - PortMaster/pylibs/locales/ru_RU.json | 3 - PortMaster/pylibs/locales/strings.json | 80 +++++------ PortMaster/pylibs/locales/sv_SE.json | 3 - PortMaster/pylibs/locales/uk_UA.json | 3 - PortMaster/pylibs/locales/zh_CN.json | 3 - PortMaster/pylibs/pugwash/app.py | 54 ++++++- PortMaster/pylibs/pugwash/lang.py | 20 ++- PortMaster/pylibs/pugwash/scenes/language.py | 32 ++--- PortMaster/pylibs/pugwash/scenes/themes.py | 42 +++--- PortMaster/pylibs/pugwash/sdl/images.py | 13 ++ PortMaster/pylibs/pugwash/sdl/sound.py | 13 ++ PortMaster/pylibs/pugwash/sdl/text.py | 15 ++ PortMaster/pylibs/pugwash/sdl/timing.py | 4 + tests/test_reload.py | 141 +++++++++++++++++++ 25 files changed, 325 insertions(+), 132 deletions(-) create mode 100644 tests/test_reload.py diff --git a/CLAUDE.md b/CLAUDE.md index e04826dc..611c7eb0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -123,6 +123,7 @@ Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is f - `app.py`: `PortMasterGUI`, covering init, the main loop, drawing and the scene stack. It subclasses the mixins in `app_callbacks.py` (the thread-aware `harbourmaster.Callback` implementation and `run_task`), `app_commands.py` (install/uninstall/update front ends), `app_fifo.py` (fifo_control) and `app_ports.py` (port info/size for templates), plus `sdl.GUI` and `harbourmaster.Callback`. - `scenes/`: screens as `BaseScene` subclasses, one module per screen or group of screens. `scenes/base.py` imports no other scene, which keeps the modules free of import cycles. - `theme.py`: theme loading. Themes are JSON describing per-scene elements; `pylibs/default_theme/theme.json` is the built-in one. See `THEME.md` for the element/scene spec, inheritance order and text-template tags. + - Choosing a theme, colour scheme or language applies at once: `PortMasterGUI.reload_theme(then=...)` drops what the old theme loaded (images, fonts and text textures, sounds, animations, pallet, element rects, its resource path), loads the configured theme again and rebuilds the screens from the main menu up. `reload_language()` also swaps `lang.CURRENT_LANG`, the translations and the language capability (the theme's `font[ja_JP]` & co). Anything a theme loads into the GUI has to be undone there; `tests/test_reload.py` checks a screen is pixel-identical after switching away and back. - `tasks.py`: threading helpers (dispatcher, `Task`, `DirectoryScanner`, `FifoReader`, `FrameStats`). It doesn't import SDL. - `sdl/`: the low-level SDL2 widget and rendering layer. It's pySDL2gui (port_gui), split by class group; `Region` in `sdl/region.py` is the themeable widget. diff --git a/PortMaster/pylibs/locales/de_DE.json b/PortMaster/pylibs/locales/de_DE.json index 63cf489b..51ae4250 100644 --- a/PortMaster/pylibs/locales/de_DE.json +++ b/PortMaster/pylibs/locales/de_DE.json @@ -139,7 +139,6 @@ "Lower Case": "Kleinbuchstaben", "Language Select": "Sprachauswahl", "{lang_name} (Selected)": "{lang_name} (Ausgewählt)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Sprache wechseln?\n\nNeustart erforderlich, damit Änderungen wirksam werden.", "Main Menu": "Hauptmenü", "Hand curated lists of ports": "Von Hand ausgewählte Ports", "All Ports": "Alle Ports", @@ -244,10 +243,8 @@ "Accept": "Akzeptieren", "{theme_name} (Selected)": "{theme_name} (Ausgewählt)", "Download": "Herunterladen", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Theme wechseln?\n\nNeustart erforderlich, damit Änderungen wirksam werden.", "Select Colour Scheme": "Farbschema auswählen", "{item_name} (Selected)": "{item_name} (Ausgewählt)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Farbschema für das Theme ändern?\n\nNeustart erforderlich, damit Änderungen wirksam werden.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Es wird von {old_release_channel} zur {new_release_channel} Version gewechselt.\n\nSoll das Downgrade durchgeführt werden?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Es gibt eine neue Version von PortMaster ({portmaster_version})\n\nSoll das Upgrade durchgeführt werden?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "PortMaster wieder installieren?\n\nDies wird vom {release_channel} Kanal zur Version {portmaster_version} installiert." diff --git a/PortMaster/pylibs/locales/es_ES.json b/PortMaster/pylibs/locales/es_ES.json index a6137224..632fe6a9 100644 --- a/PortMaster/pylibs/locales/es_ES.json +++ b/PortMaster/pylibs/locales/es_ES.json @@ -139,7 +139,6 @@ "Lower Case": "Minúsculas", "Language Select": "Eligir Idioma", "{lang_name} (Selected)": "{lang_name} (seleccionado)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "¿Quieres cambiar de idioma?\n\nTendrás que reiniciar para que surta efecto.", "Main Menu": "Menú Principal", "Hand curated lists of ports": "Lista de ports supervisados a mano", "All Ports": "Todos los ports", @@ -244,10 +243,8 @@ "Accept": "Aceptar", "{theme_name} (Selected)": "{theme_name} (seleccionado)", "Download": "Descargar", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "¿Quieres cambiar de tema?\n\nTendrás que reiniciar para que surta efecto.", "Select Colour Scheme": "Elige el Esquema de Color", "{item_name} (Selected)": "{item_name} (seleccionado)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "¿Quieres cambiar el esquema de color del tema?\n\nTendrás que reiniciar para que surta efecto.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Estas cambiando de la version {old_release_channel} a la version {new_release_channel}.\n\nQuieres hacer el downgrade?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Hay una nueva version de PortMaster ({portmaster_version})\n\nTe gustaría actualizarla?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Te gustaría reinstalar PortMaster?\n\nEsto va a reinstalar desde el canal {release_channel} al canal {portmaster_version}." diff --git a/PortMaster/pylibs/locales/fr_FR.json b/PortMaster/pylibs/locales/fr_FR.json index 7a78f5eb..5576a42d 100644 --- a/PortMaster/pylibs/locales/fr_FR.json +++ b/PortMaster/pylibs/locales/fr_FR.json @@ -139,7 +139,6 @@ "Lower Case": "Minuscule", "Language Select": "Sélection de la langue", "{lang_name} (Selected)": "{lang_name} (Sélectionné)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Voulez-vous changer de langue ?\n\nVous devrez redémarrer pour que cela prenne effet.", "Main Menu": "Menu principal", "Hand curated lists of ports": "Ports sélectionnés", "All Ports": "Tous les ports", @@ -244,10 +243,8 @@ "Accept": "Accepter", "{theme_name} (Selected)": "{theme_name} (Sélectionné)", "Download": "Télécharger", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Voulez-vous changer de thème ?\n\nVous devrez redémarrer pour que ce changement prenne effet.", "Select Colour Scheme": "Sélection de la palette de couleurs", "{item_name} (Selected)": "{item_name} (Sélectionné)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Voulez-vous changer le schéma de couleurs du thème ?\n\nVous devrez redémarrer pour que ce changement prenne effet.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Vous passez de l' {old_release_channel} à la version {new_release_channel} .\n\nVoulez-vous rétrograder ?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Il y a une nouvelle version de PortMaster ({portmaster_version})\n\nVoulez-vous mettre à jour ?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Voulez-vous réinstaller PortMaster ?\n\nCela va réinstaller depuis le canal {release_channel} vers {portmaster_version}." diff --git a/PortMaster/pylibs/locales/it_IT.json b/PortMaster/pylibs/locales/it_IT.json index 9bf8f192..81f6a6e7 100644 --- a/PortMaster/pylibs/locales/it_IT.json +++ b/PortMaster/pylibs/locales/it_IT.json @@ -120,7 +120,6 @@ "Lower Case": "Minuscolo", "Language Select": "Selezione Lingua", "{lang_name} (Selected)": "{lang_name} (Attuale)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Vuoi cambiare lingua?\n\nRiavvia per applicare le modifiche.", "Main Menu": "Menu principale", "Hand curated lists of ports": "Selezione di ports raccomandati dal Team", "All Ports": "Tutti i Ports", @@ -198,10 +197,8 @@ "Deleted runtime {runtime}": "Runtime {runtime} eliminata", "{theme_name} (Selected)": "{theme_name} (Attuale)", "Download": "Download", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Vuoi cambiare Tema?\n\nRiavvia per applicare le modifiche.", "Select Colour Scheme": "Selezionare schema colori", "{item_name} (Selected)": "{item_name} (Attuale)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Vuoi Cambiare lo Schema Colore?\n\nDovrai riavviare per applicare le modifiche.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Stai passando dalla versione {old_release_channel} alla versione {new_release_channel} .\n\nVuoi tornare alla versione precedente?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "C'è una nuova versione di PortMaster ({portmaster_version})\n\nVuoi aggiornare?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Vuoi reinstallare PortMaster?\n\nProseguendo scaricherai da {release_channel} la versione {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ja_JP.json b/PortMaster/pylibs/locales/ja_JP.json index 5797a8f1..16432bdc 100644 --- a/PortMaster/pylibs/locales/ja_JP.json +++ b/PortMaster/pylibs/locales/ja_JP.json @@ -127,7 +127,6 @@ "Lower Case": "小文字", "Language Select": "言語を選択してください", "{lang_name} (Selected)": "{lang_name} (選択済み)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "言語を変更しますか?\n\n影響を受けるには再起動する必要があります。", "Main Menu": "メインメニュー", "Hand curated lists of ports": "ポートのハンドキュレーションリスト", "All Ports": "すべてのポート", @@ -197,10 +196,8 @@ "Deleted runtime {runtime}": "ランタイム {runtime} を削除しました", "{theme_name} (Selected)": "{theme_name} (選択済み)", "Download": "ダウンロード", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "テーマを変更しますか?\n\n適用するには再起動する必要があります。", "Select Colour Scheme": "配色を選択", "{item_name} (Selected)": "{item_name} (選択済み)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "テーマを変更しますか?\n\n影響を受けるには再起動する必要があります。", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel} から {new_release_channel} へ切り替えています。\n\nダウングレードしますか?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster ({portmaster_version})\n\nアップグレードしますか?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "PortMasterを再インストールしますか?\n\n {release_channel} チャンネルから {portmaster_version} に再インストールします。" diff --git a/PortMaster/pylibs/locales/ko_KR.json b/PortMaster/pylibs/locales/ko_KR.json index 2c3dcc9e..4db7bd9d 100644 --- a/PortMaster/pylibs/locales/ko_KR.json +++ b/PortMaster/pylibs/locales/ko_KR.json @@ -142,7 +142,6 @@ "Lower Case": "소문자", "Language Select": "언어 선택", "{lang_name} (Selected)": "{lang_name} (선택됨)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "언어를 변경하시겠습니까?\n\n적용하려면 다시 시작해야 합니다.", "Main Menu": "메인 메뉴", "Hand curated lists of ports": "엄선한 포트 목록", "All Ports": "모든 포트", @@ -247,10 +246,8 @@ "Accept": "수락", "{theme_name} (Selected)": "{theme_name} (선택됨)", "Download": "다운로드", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "테마를 변경하시겠습니까?\n\n적용하려면 다시 시작해야 합니다.", "Select Colour Scheme": "색상 구성표 선택", "{item_name} (Selected)": "{item_name} (선택됨)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "테마 색 구성표를 변경하시겠습니까?\n\n적용하려면 다시 시작해야 합니다.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel}에서 {new_release_channel}로 전환하고 있습니다.\n\n다운그레이드하시겠습니까?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "새로운 ({portmaster_version}) 버전이 있습니다.\n\n업그레이드하시겠습니까?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "포트마스터를 다시 설치 하겠습니까?\n\n {release_channel} 채널에서 {portmaster_version}로 다시 설치합니다." diff --git a/PortMaster/pylibs/locales/nl_NL.json b/PortMaster/pylibs/locales/nl_NL.json index 2b97e084..6a386662 100644 --- a/PortMaster/pylibs/locales/nl_NL.json +++ b/PortMaster/pylibs/locales/nl_NL.json @@ -139,7 +139,6 @@ "Lower Case": "Kleine letters", "Language Select": "Taal selecteren", "{lang_name} (Selected)": "{lang_name} (Geselecteerd)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Wilt u de taal wijzigen?\n\nU moet opnieuw opstarten voordat het in effect gaat.", "Main Menu": "Hoofdmenu", "Hand curated lists of ports": "Hand samengestelde lijst van ports", "All Ports": "Alle ports", @@ -244,10 +243,8 @@ "Accept": "Accepteren", "{theme_name} (Selected)": "{theme_name} (Geselecteerd)", "Download": "Download", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Wilt u het thema wijzigen?\n\nU moet opnieuw opstarten voordat het in effect gaat.", "Select Colour Scheme": "Selecteer Kleurenschema", "{item_name} (Selected)": "{item_name} (Geselecteerd)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Wilt u het thema's kleurenschema wijzigen?\n\nU moet opnieuw opstarten voordat het in effect gaat.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "U wisselt van {old_release_channel} naar {new_release_channel} versie.\n\nWilt u downgraden?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Er is een nieuwe versie van PortMaster ({portmaster_version})\n\nWilt u upgraden?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Wilt u PortMaster opnieuw installeren?\n\nDit zal opnieuw installeren van het {release_channel} kanaal naar {portmaster_version}." diff --git a/PortMaster/pylibs/locales/pl_PL.json b/PortMaster/pylibs/locales/pl_PL.json index 527b3fe3..32ee7ae1 100644 --- a/PortMaster/pylibs/locales/pl_PL.json +++ b/PortMaster/pylibs/locales/pl_PL.json @@ -142,7 +142,6 @@ "Lower Case": "Małe litery", "Language Select": "Wybór języka", "{lang_name} (Selected)": "{lang_name} (Wybrany)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Czy chcesz zmienić język?\n\nBędziesz musiał zrestartować, aby zastosować.", "Main Menu": "Menu Główne", "Hand curated lists of ports": "Ręcznie wyselekcjonowane listy portów", "All Ports": "Wszystkie porty", @@ -247,10 +246,8 @@ "Accept": "Akceptuj", "{theme_name} (Selected)": "{theme_name} (Wybrany)", "Download": "Pobierz", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Czy chcesz zmienić motyw?\n\nBędziesz musiał zrestartować, aby go zmienić.", "Select Colour Scheme": "Wybierz schemat kolorów", "{item_name} (Selected)": "{item_name} (Wybrany)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Czy chcesz zmienić schemat kolorów motywu?\n\nBędziesz musiał zrestartować, aby go zmienić.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Przechodzisz z {old_release_channel} na wersję {new_release_channel}.\n\nCzy chcesz kontynuować?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Dostępna jest nowa wersja PortMaster ({portmaster_version})\n\nCzy chcesz zaktualizować?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Czy chcesz ponownie zainstalować PortMaster?\n\nZostanie to ponownie zainstalowane z kanału {release_channel} do {portmaster_version}." diff --git a/PortMaster/pylibs/locales/pt_BR.json b/PortMaster/pylibs/locales/pt_BR.json index f1d39da5..9b5d9f0d 100644 --- a/PortMaster/pylibs/locales/pt_BR.json +++ b/PortMaster/pylibs/locales/pt_BR.json @@ -142,7 +142,6 @@ "Lower Case": "Letra minúscula", "Language Select": "Seleção de Idioma", "{lang_name} (Selected)": "{lang_name} (Selecionado)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Você quer trocar de idioma?\n\nVocê terá que reiniciar para trocar o idioma.", "Main Menu": "Menu Principal", "Hand curated lists of ports": "Listas de jogos selecionados", "All Ports": "Todos os Jogos", @@ -247,10 +246,8 @@ "Accept": "Aceitar", "{theme_name} (Selected)": "{theme_name} (Selecionado)", "Download": "Baixar", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Você quer trocar de tema?\n\nVocê terá que reiniciar para trocar de tema.", "Select Colour Scheme": "Selecionar Esquema de Cores", "{item_name} (Selected)": "{item_name} (Selecionado)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Você quer trocar o esquema de cores do tema?\n\nVocê terá que reiniciar para trocar as cores.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Você está trocando da versão {old_release_channel} para a versão {new_release_channel}.\n\nVocê deseja retroceder a versão?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Há uma nova versão do Portmaster ({portmaster_version})\n\nDeseja atualizar?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Você quer reinstalar PortMaster?\n\nIsso irá reinstalar do canal {release_channel} para {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ro_RO.json b/PortMaster/pylibs/locales/ro_RO.json index f07b4307..1ca2ce4d 100644 --- a/PortMaster/pylibs/locales/ro_RO.json +++ b/PortMaster/pylibs/locales/ro_RO.json @@ -135,7 +135,6 @@ "Lower Case": "Minuscule", "Language Select": "Selectare Limbă", "{lang_name} (Selected)": "{lang_name} (Selectat)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Vrei să schimbi limba?\n\nVa trebui să repornești pentru ca modificarea să aibă efect.", "Main Menu": "Meniu Principal", "Hand curated lists of ports": "Liste de porturi selectate manual", "All Ports": "Toate Porturile", @@ -237,10 +236,8 @@ "Accept": "Acceptă", "{theme_name} (Selected)": "{theme_name} (Selectat)", "Download": "Descarcă", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Vrei să schimbi tema?\n\nVa trebui să repornești pentru ca modificarea să aibă efect.", "Select Colour Scheme": "Selectează Schema de Culori", "{item_name} (Selected)": "{item_name} (Selectat)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Vrei să schimbi schema de culori a temei?\n\nVa trebui să repornești pentru ca modificarea să aibă efect.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Schimbi de la versiunea {old_release_channel} la versiunea {new_release_channel}.\n\nDorești să faci un downgrade?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Există o nouă versiune a PortMaster ({portmaster_version})\n\nDorești să faci un upgrade?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Dorești să reinstalezi PortMaster?\n\nAceasta va reinstala din canalul {release_channel} la versiunea {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ru_RU.json b/PortMaster/pylibs/locales/ru_RU.json index 106db2f8..57a0d188 100644 --- a/PortMaster/pylibs/locales/ru_RU.json +++ b/PortMaster/pylibs/locales/ru_RU.json @@ -142,7 +142,6 @@ "Lower Case": "Нижний регистр", "Language Select": "Выберите язык", "{lang_name} (Selected)": "{lang_name} (Выбранный)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Вы хотите сменить язык?\n\nВам придется перезагрузиться, чтобы изменения вступили в силу.", "Main Menu": "Главное меню", "Hand curated lists of ports": "Создать вручную списки портов", "All Ports": "Все порты", @@ -247,10 +246,8 @@ "Accept": "Принять", "{theme_name} (Selected)": "{theme_name} (Выбрана)", "Download": "Скачать", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Вы хотите сменить тему?\n\nВам придется перезагрузиться, чтобы изменения вступили в силу.", "Select Colour Scheme": "Выбрать цветовую схему", "{item_name} (Selected)": "{item_name} (Выбранная)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Хотите изменить цветовую схему темы?\n\nВам придется перезагрузиться, чтобы изменения вступили в силу.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Вы переключаетесь с версии {old_release_channel} на версию {new_release_channel}.\n\nВы желаете сменить версию?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна новая версия PortMaster ({portmaster_version})\n\nВы хотите обновиться?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Вы хотите переустановить PortMaster?\n\nЭто переустановит канал обновления {release_channel} на {portmaster_version}." diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json index 7813cde2..ee8d2fba 100644 --- a/PortMaster/pylibs/locales/strings.json +++ b/PortMaster/pylibs/locales/strings.json @@ -266,10 +266,11 @@ "pylibs/harbourmaster/util/net.py:152" ], "Unknown IP": [ - "pylibs/pugwash/app.py:253" + "pylibs/pugwash/app.py:254", + "pylibs/pugwash/app.py:582" ], "N/A": [ - "pylibs/pugwash/app.py:406", + "pylibs/pugwash/app.py:407", "pylibs/pugwash/app_ports.py:70" ], "Fetching latest gameinfo.xml files and cover images.": [ @@ -353,61 +354,61 @@ "pylibs/pugwash/autoinstall.py:237" ], "Danish": [ - "pylibs/pugwash/lang.py:22" + "pylibs/pugwash/lang.py:24" ], "German": [ - "pylibs/pugwash/lang.py:23" + "pylibs/pugwash/lang.py:25" ], "English": [ - "pylibs/pugwash/lang.py:24" + "pylibs/pugwash/lang.py:26" ], "Spanish": [ - "pylibs/pugwash/lang.py:25" + "pylibs/pugwash/lang.py:27" ], "Esperanto": [ - "pylibs/pugwash/lang.py:26" + "pylibs/pugwash/lang.py:28" ], "Finnish": [ - "pylibs/pugwash/lang.py:27" + "pylibs/pugwash/lang.py:29" ], "French": [ - "pylibs/pugwash/lang.py:28" + "pylibs/pugwash/lang.py:30" ], "Italian": [ - "pylibs/pugwash/lang.py:29" + "pylibs/pugwash/lang.py:31" ], "Japanese": [ - "pylibs/pugwash/lang.py:30" + "pylibs/pugwash/lang.py:32" ], "Korean": [ - "pylibs/pugwash/lang.py:31" + "pylibs/pugwash/lang.py:33" ], "Dutch": [ - "pylibs/pugwash/lang.py:32" + "pylibs/pugwash/lang.py:34" ], "Polish": [ - "pylibs/pugwash/lang.py:33" + "pylibs/pugwash/lang.py:35" ], "Portuguese (Portugal)": [ - "pylibs/pugwash/lang.py:34" + "pylibs/pugwash/lang.py:36" ], "Portuguese (Brazil)": [ - "pylibs/pugwash/lang.py:35" + "pylibs/pugwash/lang.py:37" ], "Romanian": [ - "pylibs/pugwash/lang.py:36" + "pylibs/pugwash/lang.py:38" ], "Russian": [ - "pylibs/pugwash/lang.py:37" + "pylibs/pugwash/lang.py:39" ], "Swedish": [ - "pylibs/pugwash/lang.py:38" + "pylibs/pugwash/lang.py:40" ], "Ukrainian": [ - "pylibs/pugwash/lang.py:39" + "pylibs/pugwash/lang.py:41" ], "Chinese Simplified": [ - "pylibs/pugwash/lang.py:40" + "pylibs/pugwash/lang.py:42" ], "Messages": [ "pylibs/pugwash/scenes/dialogs.py:17" @@ -425,8 +426,8 @@ "pylibs/pugwash/scenes/keyboard.py:33", "pylibs/pugwash/scenes/keyboard.py:41", "pylibs/pugwash/scenes/language.py:30", - "pylibs/pugwash/scenes/themes.py:45", - "pylibs/pugwash/scenes/themes.py:130" + "pylibs/pugwash/scenes/themes.py:46", + "pylibs/pugwash/scenes/themes.py:127" ], "Back": [ "pylibs/pugwash/scenes/featured.py:30", @@ -442,9 +443,9 @@ "pylibs/pugwash/scenes/ports.py:269", "pylibs/pugwash/scenes/runtimes.py:107", "pylibs/pugwash/scenes/runtimes.py:110", - "pylibs/pugwash/scenes/themes.py:47", - "pylibs/pugwash/scenes/themes.py:129", - "pylibs/pugwash/scenes/themes.py:130" + "pylibs/pugwash/scenes/themes.py:48", + "pylibs/pugwash/scenes/themes.py:126", + "pylibs/pugwash/scenes/themes.py:127" ], "No featured ports found, internet connection is required.": [ "pylibs/pugwash/scenes/featured.py:47" @@ -596,13 +597,10 @@ "pylibs/pugwash/scenes/keyboard.py:41" ], "Language Select": [ - "pylibs/pugwash/scenes/language.py:14" + "pylibs/pugwash/scenes/language.py:13" ], "{lang_name} (Selected)": [ - "pylibs/pugwash/scenes/language.py:24" - ], - "Do you want to change language?\n\nYou will have to restart for it to take affect.": [ - "pylibs/pugwash/scenes/language.py:47" + "pylibs/pugwash/scenes/language.py:23" ], "Main Menu": [ "pylibs/pugwash/scenes/main_menu.py:17", @@ -713,7 +711,7 @@ ], "Theme Editor": [ "pylibs/pugwash/scenes/options.py:48", - "pylibs/pugwash/scenes/theme_server.py:66" + "pylibs/pugwash/scenes/theme_server.py:64" ], "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": [ "pylibs/pugwash/scenes/options.py:49" @@ -1013,10 +1011,10 @@ "pylibs/pugwash/scenes/startup.py:212" ], "Starting the theme editor...": [ - "pylibs/pugwash/scenes/theme_server.py:74" + "pylibs/pugwash/scenes/theme_server.py:73" ], "Stop": [ - "pylibs/pugwash/scenes/theme_server.py:76" + "pylibs/pugwash/scenes/theme_server.py:75" ], "it stopped": [ "pylibs/pugwash/scenes/theme_server.py:109" @@ -1031,25 +1029,19 @@ "pylibs/pugwash/scenes/theme_server.py:138" ], "The theme editor stopped. See theme_server.txt in the PortMaster folder.": [ - "pylibs/pugwash/scenes/theme_server.py:166" + "pylibs/pugwash/scenes/theme_server.py:167" ], "{theme_name} (Selected)": [ "pylibs/pugwash/scenes/themes.py:31" ], "Download": [ - "pylibs/pugwash/scenes/themes.py:50" - ], - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": [ - "pylibs/pugwash/scenes/themes.py:69" + "pylibs/pugwash/scenes/themes.py:51" ], "Select Colour Scheme": [ - "pylibs/pugwash/scenes/themes.py:110" + "pylibs/pugwash/scenes/themes.py:106" ], "{item_name} (Selected)": [ - "pylibs/pugwash/scenes/themes.py:124" - ], - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": [ - "pylibs/pugwash/scenes/themes.py:147" + "pylibs/pugwash/scenes/themes.py:120" ], "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": [ "pylibs/pugwash/update.py:79" diff --git a/PortMaster/pylibs/locales/sv_SE.json b/PortMaster/pylibs/locales/sv_SE.json index 363d7e33..e55e7235 100644 --- a/PortMaster/pylibs/locales/sv_SE.json +++ b/PortMaster/pylibs/locales/sv_SE.json @@ -127,7 +127,6 @@ "Lower Case": "Gemener", "Language Select": "Välj Språk", "{lang_name} (Selected)": "{lang_name} (Vald)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Vill du ändra språk?\n\nOmstart krävs för att visa ändringar.", "Main Menu": "Huvudmeny", "Hand curated lists of ports": "Listor av handplockade portar", "All Ports": "Alla portar", @@ -222,10 +221,8 @@ "Deleted runtime {runtime}": "Raderat körtid {runtime}", "{theme_name} (Selected)": "{theme_name} (Vald)", "Download": "Hämta", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Vill du ändra tema?\n\nOmstart krävs för att visa ändringar.", "Select Colour Scheme": "Välj färgschema", "{item_name} (Selected)": "{item_name} (Vald)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Vill du ändra temats färgschema?\n\nOmstart krävs för att visa ändringar.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Du kommer byta från {old_release_channel} till {new_release_channel}.\n\nVill du nedgradera?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Det finns en ny version av PortMaster ({portmaster_version})\n\nVill du uppgradera?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Vill du ominstallera PortMaster?\n\nDetta kommer ominstallera {portmaster_version} från {release_channel}-kanalen." diff --git a/PortMaster/pylibs/locales/uk_UA.json b/PortMaster/pylibs/locales/uk_UA.json index 0747d498..6e6f2305 100644 --- a/PortMaster/pylibs/locales/uk_UA.json +++ b/PortMaster/pylibs/locales/uk_UA.json @@ -139,7 +139,6 @@ "Lower Case": "Нижній регістр", "Language Select": "Вибір мови", "{lang_name} (Selected)": "{lang_name} (Обрано)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "Ви хочете змінити мову?\n\nВам доведеться перезапустити застосунок, щоб вона набула чинності.", "Main Menu": "Головне меню", "Hand curated lists of ports": "Кураторський список портів", "All Ports": "Всі порти", @@ -244,10 +243,8 @@ "Accept": "Прийняти", "{theme_name} (Selected)": "{theme_name} (Обрано)", "Download": "Завантажити", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "Ви хочете змінити тему?\n\nВам доведеться перезапустити застосунок, щоб вона набула чинності.", "Select Colour Scheme": "Оберіть кольорову палітру", "{item_name} (Selected)": "{item_name} (Обрано)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "Ви хочете змінити кольорову палітру?\n\nВам доведеться перезапустити застосунок, щоб вона набула чинності.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Ви переходите з {old_release_channel} на {new_release_channel} версію.\n\nХочете знизити версію?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна нова версія PortMaster ({portmaster_version})\n\nХочете оновити?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Хочете перевстановити PortMaster?\n\nЦе перевстановить застосунок з {release_channel} каналу до {portmaster_version}." diff --git a/PortMaster/pylibs/locales/zh_CN.json b/PortMaster/pylibs/locales/zh_CN.json index 4aa04f7d..a43783f3 100644 --- a/PortMaster/pylibs/locales/zh_CN.json +++ b/PortMaster/pylibs/locales/zh_CN.json @@ -140,7 +140,6 @@ "Lower Case": "小写", "Language Select": "选择语言", "{lang_name} (Selected)": "{lang_name} (已选)", - "Do you want to change language?\n\nYou will have to restart for it to take affect.": "您想要更改语言吗?\n\n需要重启程序以适用更改。", "Main Menu": "主菜单", "Hand curated lists of ports": "社区推荐的移植作品", "All Ports": "所有移植作品", @@ -243,10 +242,8 @@ "Accept": "同意", "{theme_name} (Selected)": "{theme_name} (已选)", "Download": "下载", - "Do you want to change theme?\n\nYou will have to restart for it to take effect.": "您确定想更换主题吗?\n\n需要重启程序以适用更改。", "Select Colour Scheme": "选择配色方案", "{item_name} (Selected)": "{item_name} (已选)", - "Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect.": "您确定想更改主题颜色方案吗?\n\n需要重启程序以适用更改。", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "正在从 {old_release_channel} 切换到 {new_release_channel} 版本。希望退回吗?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster有新版本 ({portmaster_version}) 可用,希望升级吗?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "您希望重装PortMaster吗?\n\n这会安装来自 {release_channel} 的 {portmaster_version} 版本。" diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index 62d8d096..96f8b339 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -18,7 +18,7 @@ from pugwash import tasks from loguru import logger from pugwash.theme import ThemeEngine -from pugwash.scenes import StringFormatter, TempMenuScene +from pugwash.scenes import MainMenuScene, StringFormatter, TempMenuScene from harbourmaster.i18n import _ from pugwash import PYLIB_PATH, PORTMASTER_VERSION, PORTMASTER_DEBUG from pugwash.lang import lang_list @@ -219,6 +219,7 @@ def nice_version(v): self.themes = ThemeEngine(self, force_theme=force_theme) self.theme_data = self.themes.gui_init() + self.theme_dir = self.themes.get_theme_dir(self.themes.get_current_theme()) self.resources.add_path(PYLIB_PATH / 'resources') self.theme_downloader = None @@ -530,6 +531,57 @@ def get_data(self, key): def format_data(self, input_string, used_keys=None): return self.formatter.format_string(input_string, used_keys) + def reload_theme(self, then=()): + """ + Loads the theme again, the one in the config (or force_theme) with its colour + scheme, so a new theme, scheme or language applies without a restart. Everything + the old theme loaded is dropped first. The screens were made from the old theme: + this makes a new main menu, then pushes then's (layer name, scene class) on it. + """ + logger.info("Reloading the theme") + self.sounds.unload_all() + self.images.unload_all() + self.text.unload_all() + self.animations.clear() + self.pallet.clear() + self.override.clear() + self.default_rects = sdl.NamedRects([0, 0, *self.renderer.logical_size]) + + ## The theme's folder first, then PortMaster's own resources, as at start. + self.resources.remove_path(self.theme_dir) + self.resources.remove_path(PYLIB_PATH / 'resources') + self.theme_data = self.themes.gui_init() + self.theme_dir = self.themes.get_theme_dir(self.themes.get_current_theme()) + self.resources.add_path(PYLIB_PATH / 'resources') + + for scene in self.all_scenes(): + scene.scene_deactivate() + + self.message_box_scene = None + self.scenes = [('root', [MainMenuScene(self)])] + self.scenes[-1][1][-1].scene_activate() + for name, scene_class in then: + self.push_scene(name, scene_class(self)) + + self.updated = True + + def reload_language(self, language, then=()): + """ + Use another language now (None for the system's): its translations, the theme's + font for it ("font[ja_JP]" & co, matched as a capability), and new screens, as + reload_theme(). + """ + old_lang = lang.CURRENT_LANG + new_lang = lang.set_language(language) + + capabilities = harbourmaster.device_info()['capabilities'] + if old_lang in capabilities: + capabilities.remove(old_lang) + capabilities.append(new_lang) + + self.set_data('system.ip_address', get_ip_address() or _("Unknown IP")) + self.reload_theme(then) + def quit(self): # Clean up. Anything SDL owns has to be destroyed before SDL_Quit, otherwise the # garbage collector frees it afterwards, which segfaults on some drivers (muOS/Mali). diff --git a/PortMaster/pylibs/pugwash/lang.py b/PortMaster/pylibs/pugwash/lang.py index cd439908..77b02a42 100644 --- a/PortMaster/pylibs/pugwash/lang.py +++ b/PortMaster/pylibs/pugwash/lang.py @@ -15,6 +15,8 @@ ## Set by load_lang(). DEFAULT_LANG = None CURRENT_LANG = None +## The environment variable that holds the language. +ENVIRON_KEY = 'LANG' ALL_LANGUAGES = { @@ -78,7 +80,7 @@ def load_lang(): Load the desired language, we check for an override in our config.json, otherwise use the environment variables. """ - global DEFAULT_LANG, CURRENT_LANG + global DEFAULT_LANG, CURRENT_LANG, ENVIRON_KEY config = harbourmaster.HM_TOOLS_DIR / "PortMaster" / "config" / "config.json" @@ -121,6 +123,22 @@ def load_lang(): os.environ[environ_key] = 'en_US' CURRENT_LANG = os.environ[environ_key] + ENVIRON_KEY = environ_key + + +def set_language(language): + """ + Use another language from now on, None for the system's: its translations, CURRENT_LANG + and the environment (ports and the scripts PortMaster runs see it too). The GUI also + swaps the language capability, which picks the theme's font for it, and reloads the + theme: see PortMasterGUI.reload_language(). + """ + global CURRENT_LANG + + CURRENT_LANG = (language and check_lang(language)) or DEFAULT_LANG or 'en_US' + os.environ[ENVIRON_KEY] = CURRENT_LANG + i18n.load_language(LANG_DIR, CURRENT_LANG) + return CURRENT_LANG def lang_list(): diff --git a/PortMaster/pylibs/pugwash/scenes/language.py b/PortMaster/pylibs/pugwash/scenes/language.py index caac92cb..e98e310f 100644 --- a/PortMaster/pylibs/pugwash/scenes/language.py +++ b/PortMaster/pylibs/pugwash/scenes/language.py @@ -2,9 +2,8 @@ # # Language selection. -import os -import harbourmaster from harbourmaster.i18n import _ +from pugwash import lang from .base import BaseScene @@ -16,12 +15,13 @@ def __init__(self, gui): self.load_regions("option_menu", ['option_list', ]) languages = gui.lang_list - selected_lang = os.environ['LANG'] + selected_lang = lang.CURRENT_LANG self.tags['option_list'].reset_options() - for lang_code, lang_name in languages.items(): + for index, (lang_code, lang_name) in enumerate(languages.items()): if lang_code == selected_lang: self.tags['option_list'].add_option((None, ''), _("{lang_name} (Selected)").format(lang_name=lang_name)) + self.tags['option_list'].list_select(index) else: self.tags['option_list'].add_option(('select-language', lang_code), lang_name) @@ -44,23 +44,17 @@ def do_update(self, events): return True elif selected_option == 'select-language': - if self.gui.message_box(_("Do you want to change language?\n\nYou will have to restart for it to take affect."), want_cancel=True): - from pugwash import lang - if selected_parameter == lang.DEFAULT_LANG: - del self.gui.hm.cfg_data['language'] + ## Applies straight away, back in this list. + from .options import OptionScene + if selected_parameter == lang.DEFAULT_LANG: + self.gui.hm.cfg_data.pop('language', None) - else: - self.gui.hm.cfg_data['language'] = selected_parameter + else: + self.gui.hm.cfg_data['language'] = selected_parameter - self.gui.hm.save_config() - self.gui.events.running = False - - if not harbourmaster.HM_TESTING: - reboot_file = (harbourmaster.HM_TOOLS_DIR / "PortMaster" / ".pugwash-reboot") - if not reboot_file.is_file(): - reboot_file.touch(0o644) - - return True + self.gui.hm.save_config() + self.gui.reload_language(selected_parameter, then=[('option', OptionScene), ('select-language', LanguageScene)]) + return True elif events.was_pressed('B'): self.button_back() diff --git a/PortMaster/pylibs/pugwash/scenes/themes.py b/PortMaster/pylibs/pugwash/scenes/themes.py index 98d11185..cc6cb4d7 100644 --- a/PortMaster/pylibs/pugwash/scenes/themes.py +++ b/PortMaster/pylibs/pugwash/scenes/themes.py @@ -26,9 +26,10 @@ def __init__(self, gui): selected_theme = self.gui.hm.cfg_data['theme'] self.tags['themes_list'].reset_options() - for theme_name, theme_data in self.themes.items(): + for index, (theme_name, theme_data) in enumerate(self.themes.items()): if theme_name == selected_theme: self.tags['themes_list'].add_option(theme_name, _("{theme_name} (Selected)").format(theme_name=theme_data['name'])) + self.tags['themes_list'].list_select(index) else: self.tags['themes_list'].add_option(theme_name, theme_data['name']) @@ -66,18 +67,13 @@ def do_update(self, events): self.button_activate() - if self.gui.message_box(_("Do you want to change theme?\n\nYou will have to restart for it to take effect."), want_cancel=True): - self.gui.hm.cfg_data['theme'] = self.last_select - self.gui.hm.cfg_data['theme-scheme'] = None - self.gui.hm.save_config() - self.gui.events.running = False - - if not harbourmaster.HM_TESTING: - reboot_file = (harbourmaster.HM_TOOLS_DIR / "PortMaster" / ".pugwash-reboot") - if not reboot_file.is_file(): - reboot_file.touch(0o644) - - return True + ## Applies straight away, back in this list. + from .options import OptionScene + self.gui.hm.cfg_data['theme'] = self.last_select + self.gui.hm.cfg_data['theme-scheme'] = None + self.gui.hm.save_config() + self.gui.reload_theme(then=[('option', OptionScene), ('select-theme', ThemesScene)]) + return True if events.was_pressed('X'): theme_info = self.themes[self.last_select] @@ -118,10 +114,11 @@ def __init__(self, gui): selected_scheme = self.gui.hm.cfg_data.get('theme-scheme', default_scheme) self.tags['option_list'].reset_options() - for scheme_name in schemes: + for index, scheme_name in enumerate(schemes): if selected_scheme is None or scheme_name == selected_scheme: selected_scheme = scheme_name self.tags['option_list'].add_option((None, ''), _("{item_name} (Selected)").format(item_name=scheme_name)) + self.tags['option_list'].list_select(index) else: self.tags['option_list'].add_option(('select-scheme', scheme_name), scheme_name) @@ -144,17 +141,12 @@ def do_update(self, events): return True elif selected_option == 'select-scheme': - if self.gui.message_box(_("Do you want to change the themes color scheme?\n\nYou will have to restart for it to take affect."), want_cancel=True): - self.gui.hm.cfg_data['theme-scheme'] = selected_parameter - self.gui.hm.save_config() - self.gui.events.running = False - - if not harbourmaster.HM_TESTING: - reboot_file = (harbourmaster.HM_TOOLS_DIR / "PortMaster" / ".pugwash-reboot") - if not reboot_file.is_file(): - reboot_file.touch(0o644) - - return True + ## Applies straight away, back in this list. + from .options import OptionScene + self.gui.hm.cfg_data['theme-scheme'] = selected_parameter + self.gui.hm.save_config() + self.gui.reload_theme(then=[('option', OptionScene), ('select-scheme', ThemeSchemeScene)]) + return True elif events.was_pressed('B'): self.button_back() diff --git a/PortMaster/pylibs/pugwash/sdl/images.py b/PortMaster/pylibs/pugwash/sdl/images.py index a938da86..c31114dd 100644 --- a/PortMaster/pylibs/pugwash/sdl/images.py +++ b/PortMaster/pylibs/pugwash/sdl/images.py @@ -458,6 +458,19 @@ def enable_async(self, dispatcher, max_workers=2): self.async_dispatcher = dispatcher self.async_executor = ThreadPoolExecutor(max_workers=max_workers, thread_name_prefix="image-load") + def unload_all(self): + ''' + Forget every image and destroy its texture: a theme reload. Screenshots still + decoding are dropped when they finish (_decoded checks they're still wanted). + ''' + for texture in self.textures.values(): + texture.destroy() + + self.images = {} + self.textures = {} + self.cache = [] + self.max_images = self.MAX_IMAGES + def disable_async(self): if self.async_executor is not None: self.async_executor.shutdown(wait=False) diff --git a/PortMaster/pylibs/pugwash/sdl/sound.py b/PortMaster/pylibs/pugwash/sdl/sound.py index 2feb2393..6a722c52 100644 --- a/PortMaster/pylibs/pugwash/sdl/sound.py +++ b/PortMaster/pylibs/pugwash/sdl/sound.py @@ -155,6 +155,19 @@ def stop(self): self.song = None self.filename = None + def unload_all(self): + ''' + Stop the music and free every sound: a theme reload. + ''' + self.stop() + self.music_position = {} + if self.is_init: + sdl2.sdlmixer.Mix_HaltChannel(-1) + for sample in self.sounds.values(): + sdl2.sdlmixer.Mix_FreeChunk(sample) + + self.sounds = {} + @property def music_is_disabled(self): return self._music_is_disabled diff --git a/PortMaster/pylibs/pugwash/sdl/text.py b/PortMaster/pylibs/pugwash/sdl/text.py index becea948..b8322016 100644 --- a/PortMaster/pylibs/pugwash/sdl/text.py +++ b/PortMaster/pylibs/pugwash/sdl/text.py @@ -72,6 +72,21 @@ def add_font(self, font_name, font_file): if font_name not in self.fonts: self.fonts[font_name] = FontTTF(str(font_file), 22, (255, 255, 255, 255)) + def unload_all(self): + """ + Forget every font and rendered text: a theme reload (a font name can be another + file in the next theme). + """ + for texture in self._textures.values(): + texture.texture.destroy() + + for font in self.fonts.values(): + font.close() + + self._textures = {} + self._texture_list = collections.deque([]) + self.fonts = {} + def clean(self): """ Call at the end of a frame to clean up any of the oldest textures. diff --git a/PortMaster/pylibs/pugwash/sdl/timing.py b/PortMaster/pylibs/pugwash/sdl/timing.py index 144badc3..08eb4332 100644 --- a/PortMaster/pylibs/pugwash/sdl/timing.py +++ b/PortMaster/pylibs/pugwash/sdl/timing.py @@ -86,6 +86,10 @@ def add_animation(self, animation_name, options): self._animations[animation_name] = animation self._gui.set_data(f"animation.{animation_name}", f"{animation['frame']:03d}") + def clear(self): + 'Forget every animation: a theme reload.' + self._animations = {} + def update_animations(self): time = sdl2.SDL_GetTicks64() for animation_name, animation in self._animations.items(): diff --git a/tests/test_reload.py b/tests/test_reload.py new file mode 100644 index 00000000..741f6b8c --- /dev/null +++ b/tests/test_reload.py @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: MIT +# +# Changing theme, colour scheme or language without a restart (PortMasterGUI.reload_theme +# and reload_language): the screens after switching away and back must be the same as +# before, so nothing of the other theme or language is left behind. + +import json +import shutil + +import pytest + +import harbourmaster + +from conftest import PM_DIR + + +pytestmark = pytest.mark.sdl + + +@pytest.fixture +def app(gui, hm_dirs): + from pugwash import lang + + gui.hm = harbourmaster.HarbourMaster({'offline': True, 'no-check': True}, callback=gui, **hm_dirs) + start_lang = lang.CURRENT_LANG + yield gui + + ## Back as the other tests expect it. + gui.themes.force_theme = "default_theme" + gui.hm.cfg_data['theme-scheme'] = None + if lang.CURRENT_LANG != start_lang: + gui.reload_language(start_lang) + else: + gui.reload_theme() + gui.hm = None + + +@pytest.fixture +def other_theme(): + """A copy of the default theme with other colours, installed where themes go.""" + theme_dir = harbourmaster.HM_TOOLS_DIR / "PortMaster" / "themes" / "Other Theme" + shutil.copytree(str(PM_DIR / "pylibs" / "default_theme"), str(theme_dir)) + data = json.loads((theme_dir / "theme.json").read_text()) + data["#info"]["name"] = "Other Theme" + for scheme in data["#schemes"].values(): + if isinstance(scheme, dict) and "#pallet" in scheme: + scheme["#pallet"] = {name: [40, 120, 40] for name in scheme["#pallet"]} + (theme_dir / "theme.json").write_text(json.dumps(data)) + yield "Other Theme" + shutil.rmtree(str(theme_dir)) + + +def screen(gui, tmp_path, name): + """The screen as PNG bytes, after a few frames.""" + for _ in range(3): + gui.updated = True + gui.draw_counter = 0 + gui.do_loop(no_delay=True) + + out_file = tmp_path / f"{name}.png" + gui.create_screenshot(str(out_file)) + return out_file.read_bytes() + + +def options(gui): + from pugwash.scenes import OptionScene + return [('option', OptionScene)] + + +def test_theme_reload(app, other_theme, tmp_path): + app.reload_theme(then=options(app)) + before = screen(app, tmp_path, "before") + + app.themes.force_theme = other_theme + app.reload_theme(then=options(app)) + assert app.scene_list() == ["root", "option"] + assert app.theme_dir.name == other_theme + other = screen(app, tmp_path, "other") + assert other != before + + app.themes.force_theme = "default_theme" + app.reload_theme(then=options(app)) + assert screen(app, tmp_path, "after") == before + + +def test_scheme_reload(app, tmp_path): + app.reload_theme(then=options(app)) + before = screen(app, tmp_path, "before") + + app.hm.cfg_data['theme-scheme'] = "Dracula" + app.reload_theme(then=options(app)) + assert screen(app, tmp_path, "dracula") != before + + app.hm.cfg_data['theme-scheme'] = None + app.reload_theme(then=options(app)) + assert screen(app, tmp_path, "after") == before + + +def test_language_reload(app, tmp_path): + from pugwash import lang + + app.reload_language("en_US", then=options(app)) + before = screen(app, tmp_path, "before") + + app.reload_language("de_DE", then=options(app)) + assert lang.CURRENT_LANG == "de_DE" + option_list = app.scenes[-1][1][-1].tags['option_list'] + assert "Sprache wählen" in option_list.list + assert "de_DE" in harbourmaster.device_info()['capabilities'] and "en_US" not in harbourmaster.device_info()['capabilities'] + + ## The theme's font for the language: "font[ja_JP]". + app.reload_language("ja_JP", then=options(app)) + assert app.scenes[-1][1][-1].tags['option_list'].font == "NotoSansJP-Regular.ttf" + + app.reload_language("en_US", then=options(app)) + assert screen(app, tmp_path, "after") == before + + +def test_choosing_a_theme_applies_it(app, other_theme, monkeypatch): + ## The themes list: A on another theme applies it, no restart, back in the list. + from pugwash.scenes import ThemesScene + from pugwash import theme + + app.hm.cfg_data['theme'] = "default_theme" + app.themes.force_theme = None + monkeypatch.setattr(theme.ThemeDownloader, "get_theme_list", lambda self: {}) + app.theme_downloader = theme.ThemeDownloader(app, app.themes) + app.reload_theme(then=options(app) + [('select-theme', ThemesScene)]) + + scene = app.scenes[-1][1][-1] + scene.tags['themes_list'].list_select(list(scene.themes).index(other_theme)) + monkeypatch.setattr(app.events, "was_pressed", lambda button: button == 'A') + app.do_loop(no_delay=True) + + assert app.events.running + assert app.hm.cfg_data['theme'] == other_theme and app.theme_dir.name == other_theme + assert app.scene_list() == ["root", "option", "select-theme"] + assert app.scenes[-1][1][-1].tags['themes_list'].selected_option() == other_theme + + app.hm.cfg_data['theme'] = "default_theme" + app.theme_downloader = None From 9efd70ec699b46931c5755976da0dd5ddd1f6968 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:07:30 +0800 Subject: [PATCH 10/28] Theme server: one renderer per screen size, switched in place Renderers were keyed by theme version, scheme, resolution and language, so every saved theme file, scheme or language change started a new pugwash (8-10 seconds on a handheld). Now there's one per screen size: the worker's new "load" command switches theme, scheme and language with reload_language(), and serve.py sends it when a render needs something else loaded. On the RG35XX-H (muOS Jacaranda): switching scheme 2.5 s and language 2.8 s, each including a ~1.1 s render, instead of a new renderer. A new screen size still starts one. Tests: a renderer switched to a theme, scheme and language draws the same as a new one with those settings; a saved theme reloads in the same process. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 2 +- PortMaster/pylibs/theme_server/serve.py | 104 ++++++++++++++--------- PortMaster/pylibs/theme_server/shots.py | 12 +++ PortMaster/pylibs/theme_server/worker.py | 30 +++++-- THEME.md | 2 +- tests/test_theme_server.py | 30 ++++++- 6 files changed, 129 insertions(+), 51 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 611c7eb0..ee15b555 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -74,7 +74,7 @@ It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes ` **Theme preview server** (`PortMaster/pylibs/theme_server/`, run from a checkout with `tools/theme_server/serve.py`; documented in `THEME.md`): - `serve.py` serves a page showing every screen rendered with a theme, at any resolution, colour scheme and language, and exports PNGs with `--export`. -- Each render process, `worker.py`, is a headless pugwash for one theme version, scheme, resolution and language. SDL allows one GUI per process, and the screen size, the theme's `[capability]` overrides and the language are fixed at start. A new one starts when the theme's files change, so no state carries over. +- Each render process, `worker.py`, is a headless pugwash for one screen size (SDL allows one GUI per process, and the size is fixed at start). Its `load` command switches the theme, colour scheme and language in place with `reload_language()`/`reload_theme()`, about a second; `serve.py` sends it whenever a render needs another theme version (a saved file), scheme or language (or saved translations). `tests/test_theme_server.py` checks a switched renderer draws the same as a new one. - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode or headless. - It draws with no window (`PM_HEADLESS=Y`): SDL's software renderer on a surface, with no video or controller subsystem. So it needs no dummy video driver, and it runs on a device next to the GUI that owns the screen (muOS's SDL has only `mali`). - It takes the ports from a PortMaster config folder (`--ports-config`): the cache's (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`) or, on a device, PortMaster's own. It copies the files and links the screenshot folders, so that folder is never changed. diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index 033676e6..3aa19814 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -54,7 +54,7 @@ "1024x768", "1280x720", "1280x800", "1920x1080", "1920x1152", ) -## Render processes kept running at once (each is a pugwash, ~60 MB). +## Render processes kept running at once, one per screen size (each is a pugwash, ~60 MB). MAX_WORKERS = 6 @@ -146,9 +146,10 @@ class WorkerError(Exception): class Worker: - """One worker.py: a pugwash for one theme version, scheme, resolution and language.""" - def __init__(self, key, theme_dir, scheme, resolution, lang, ports_config, work): - self.key = key + """One worker.py: a pugwash at one screen size, switched in place to the theme version, + colour scheme and language each render needs.""" + def __init__(self, resolution, theme_dir, scheme, lang, ports_config, work, loaded=None): + self.resolution = resolution self.lock = threading.Lock() self.work = Path(work) command = [sys.executable, str(WORKER), "--theme-dir", str(theme_dir), "--resolution", resolution, @@ -164,6 +165,8 @@ def __init__(self, key, theme_dir, scheme, resolution, lang, ports_config, work) self.stop() raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + str(self.log_file.name)) + ## What it has loaded: (theme id, theme version, scheme, language). + self.loaded = loaded self.used = time.monotonic() def _read(self): @@ -172,15 +175,38 @@ def _read(self): return {'ok': False, 'error': "the renderer stopped, see " + str(self.log_file.name)} return json.loads(line) + def _request(self, command): + if self.proc.poll() is not None: + raise WorkerError("the renderer stopped, see " + str(self.log_file.name)) + + self.used = time.monotonic() + self.proc.stdin.write(json.dumps(command) + "\n") + self.proc.stdin.flush() + return self._read() + + def alive(self): + return self.proc.poll() is None + def request(self, **command): with self.lock: - if self.proc.poll() is not None: - raise WorkerError("the renderer stopped, see " + str(self.log_file.name)) + return self._request(command) + + def render(self, loaded, theme_dir, scheme, lang, shot): + """(answer, png bytes): the theme, scheme and language loaded first if they aren't.""" + with self.lock: + if self.loaded != loaded: + answer = self._request({'cmd': "load", 'theme_dir': str(theme_dir), 'scheme': scheme, 'lang': lang}) + if not answer.get('ok'): + self.loaded = None + raise WorkerError(answer.get('error', "loading the theme failed")) + self.loaded = loaded + + out_file = self.work / "render.png" + answer = self._request({'cmd': "render", 'shot': shot, 'out': str(out_file)}) + if not answer.get('ok'): + raise WorkerError(answer.get('error', "render failed")) - self.used = time.monotonic() - self.proc.stdin.write(json.dumps(command) + "\n") - self.proc.stdin.flush() - return self._read() + return answer, (out_file.read_bytes() if not answer.get('missing') else b"") def stop(self): try: @@ -200,8 +226,9 @@ def stop(self): class RenderPool: - """Starts workers as they're needed, replaces them when their theme changes, and caches - what they rendered.""" + """A renderer per screen size, started as needed (the least recently used goes when + there are too many), switched to each theme, scheme and language in place; and the + renders, until their theme or language files change.""" def __init__(self, themes, ports_config, work_dir, max_workers=MAX_WORKERS): self.themes = themes self.ports_config = Path(ports_config) @@ -215,31 +242,30 @@ def __init__(self, themes, ports_config, work_dir, max_workers=MAX_WORKERS): self.translations = Translations() self.counter = 0 - def _worker(self, theme_id, scheme, resolution, lang): + def _wanted(self, theme_id, scheme, lang): + """(theme dir, scheme, (theme id, version, scheme, lang)) for a render.""" theme_dir = self.themes.get(theme_id) if theme_dir is None: raise WorkerError(f"no theme {theme_id!r}") - ## No scheme is the theme's default one, by name: the same renderer the page uses. + ## No scheme is the theme's default one, by name: the same as the page asks for. scheme = scheme or self.themes.info(theme_id, theme_dir)['default_scheme'] - fingerprint = version(theme_dir, lang) - key = (theme_id, fingerprint, scheme or "", resolution, lang) + return theme_dir, scheme, (theme_id, version(theme_dir, lang), scheme or "", lang) + + def _worker(self, resolution, theme_dir, scheme, lang, loaded): with self.lock: - worker = self.workers.get(key) - if worker is not None: - return worker, fingerprint + worker = self.workers.get(resolution) + if worker is not None and worker.alive(): + return worker ## One start at a time: the page asks for every screen at once. with self.start_lock: with self.lock: - worker = self.workers.get(key) - if worker is not None: - return worker, fingerprint - - ## The same settings for an older version of the theme, then the least - ## recently used if there are too many. - stale = [self.workers.pop(other) for other in list(self.workers) - if other[0] == theme_id and other[2:] == key[2:]] + worker = self.workers.get(resolution) + if worker is not None and worker.alive(): + return worker + + stale = [self.workers.pop(resolution)] if resolution in self.workers else [] while len(self.workers) >= self.max_workers: stale.append(self.workers.pop(min(self.workers, key=lambda other: self.workers[other].used))) @@ -249,28 +275,25 @@ def _worker(self, theme_id, scheme, resolution, lang): for old in stale: old.stop() - worker = Worker(key, theme_dir, scheme, resolution, lang, self.ports_config, work) + worker = Worker(resolution, theme_dir, scheme, lang, self.ports_config, work, loaded) with self.lock: - self.workers[key] = worker + self.workers[resolution] = worker - return worker, fingerprint + return worker def render(self, theme_id, scheme, resolution, lang, shot): """(png bytes, info dict) for one screen, cached until the theme changes.""" - worker, fingerprint = self._worker(theme_id, scheme, resolution, lang) - render_key = worker.key + (shot,) + theme_dir, scheme, loaded = self._wanted(theme_id, scheme, lang) + fingerprint = loaded[1] + render_key = (theme_id, fingerprint, scheme or "", resolution, lang, shot) with self.lock: if render_key in self.renders: return self.renders[render_key] - out_file = worker.work / f"{shot}.png" - answer = worker.request(cmd="render", shot=shot, out=str(out_file)) - if not answer.get('ok'): - raise WorkerError(answer.get('error', "render failed")) - - png = out_file.read_bytes() if not answer.get('missing') else b"" + worker = self._worker(resolution, theme_dir, scheme, lang, loaded) + answer, png = worker.render(loaded, theme_dir, scheme, lang, shot) info = { - 'shot': shot, 'theme': theme_id, 'scheme': worker.ready.get('scheme'), 'resolution': resolution, + 'shot': shot, 'theme': theme_id, 'scheme': scheme, 'resolution': resolution, 'lang': lang, 'version': fingerprint, 'regions': answer.get('regions', []), 'errors': answer.get('errors', []), 'missing': answer.get('missing'), 'ms': answer.get('ms'), 'strings': self.translations.used(answer.get('strings', {})), @@ -290,7 +313,8 @@ def shots(self): def warm(self, theme_id="default_theme", scheme=None, resolution="640x480", lang="en_US"): """Starts the renderer the page asks for first (the default theme, scheme and language), so its screens come quicker. Raises WorkerError if it can't start.""" - self._worker(theme_id, scheme, resolution, lang) + theme_dir, scheme, loaded = self._wanted(theme_id, scheme, lang) + self._worker(resolution, theme_dir, scheme, lang, loaded) def stop(self): with self.lock: diff --git a/PortMaster/pylibs/theme_server/shots.py b/PortMaster/pylibs/theme_server/shots.py index 5c8d9fe2..c8b171a2 100644 --- a/PortMaster/pylibs/theme_server/shots.py +++ b/PortMaster/pylibs/theme_server/shots.py @@ -364,6 +364,18 @@ def __init__(self, work, theme_name): self.gui.sounds.sound_is_disabled = True self.gui.sounds.music_is_disabled = True + def load(self, theme_name, scheme, lang): + """Another theme, colour scheme or language (or the same, saved again): reloaded in + place, much quicker than a new renderer.""" + gui = self.gui + gui.themes.force_theme = theme_name + gui.hm.cfg_data['theme-scheme'] = scheme + ## Reloads the translations (saved ones too) and then the theme. + gui.reload_language(lang) + self.lookups = record_lookups() + self.theme_name = theme_name + self.scheme = gui.themes.get_current_theme_scheme() + def settle(self, frames=3): """A few frames, so scenes fill in their lists and text.""" gui = self.gui diff --git a/PortMaster/pylibs/theme_server/worker.py b/PortMaster/pylibs/theme_server/worker.py index 7539c26f..120dcc0d 100644 --- a/PortMaster/pylibs/theme_server/worker.py +++ b/PortMaster/pylibs/theme_server/worker.py @@ -16,6 +16,9 @@ # cache's (DIR/tools/PortMaster/config, see --prepare-cache), or on a device PortMaster's # own. Nothing in it is changed. # +# {"cmd": "load", "theme_dir": DIR, "scheme": NAME or null, "lang": "de_DE"} +# -> {"ok": true, "theme": name, "scheme": ...}: switches theme, scheme and language +# in place (or reloads them after a save), about a second instead of a new renderer. # {"cmd": "render", "shot": "main_menu", "out": "/tmp/x.png"} # -> {"ok": true, "regions": [...], "errors": [...]} # {"cmd": "shots"} -> {"ok": true, "shots": [[name, title], ...]} @@ -115,12 +118,7 @@ def prepare_tools_dir(work, ports_config, theme_dir, scheme): elif entry.is_file(): shutil.copyfile(str(entry), str(config / entry.name)) - default_theme = PM_DIR / "pylibs" / "default_theme" - if theme_dir.resolve() == default_theme.resolve(): - theme_name = "default_theme" - else: - theme_name = theme_dir.name - (tools / "PortMaster" / "themes" / theme_name).symlink_to(theme_dir.resolve()) + theme_name = link_theme(work, theme_dir) ## Its "version", else harbourmaster upgrades the config by deleting the image folders ## (here links), and not a first run, which writes the default sources. @@ -144,6 +142,20 @@ def prepare_tools_dir(work, ports_config, theme_dir, scheme): return theme_name +def link_theme(work, theme_dir): + """The theme's name, linked into work/tools/PortMaster/themes unless it's the built-in one.""" + default_theme = PM_DIR / "pylibs" / "default_theme" + if Path(theme_dir).resolve() == default_theme.resolve(): + return "default_theme" + + theme_name = Path(theme_dir).name + link = work / "tools" / "PortMaster" / "themes" / theme_name + if link.is_symlink() or link.exists(): + link.unlink() + link.symlink_to(Path(theme_dir).resolve()) + return theme_name + + def prepare_cache(cache): """Fill the cache like a first PortMaster start online would, then exit.""" cache = Path(cache) @@ -233,6 +245,12 @@ def reply(**answer): elif command.get('cmd') == "shots": reply(ok=True, shots=[[name, title] for name, title, _ in shots.SHOTS]) + elif command.get('cmd') == "load": + started = time.monotonic() + theme_name = link_theme(work, command['theme_dir']) + renderer.load(theme_name, command.get('scheme'), command.get('lang') or options.lang) + reply(ok=True, theme=theme_name, scheme=renderer.scheme, ms=int((time.monotonic() - started) * 1000)) + elif command.get('cmd') == "render": started = time.monotonic() try: diff --git a/THEME.md b/THEME.md index 7664381e..73b051ad 100644 --- a/THEME.md +++ b/THEME.md @@ -40,7 +40,7 @@ That writes `out/////.png`, with a PortMaster carries the same server: **Options → Theme Editor** starts it on the device, and shows its address as text and as a QR code. Open it on a computer or phone on the same network: no checkout or install needed. It previews the themes installed on the device, with the device's own ports list, and the translation editor saves into the device's PortMaster. Press B on the device to stop it. - Translations saved on a device are in `PortMaster/pylibs/locales/` there. Copy them off before updating PortMaster, which replaces that folder. -- Each screen size, colour scheme and language starts a render process (60-90 MB): the first screen of a new combination takes 10-15 seconds on a handheld, the rest a second or two. How many run at once depends on the device's free memory. +- Each screen size starts a render process (60-90 MB): its first screen takes about 10 seconds on a handheld. Switching theme, colour scheme or language, or saving, takes 2-3 seconds, and other screens a second or two. How many sizes are kept at once depends on the device's free memory. - Anyone on the same network can open it while it runs. ### Translating PortMaster diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 32af22ef..841e6f69 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -193,9 +193,10 @@ def test_missing_sections_and_default_only_screens(pool): @pytest.mark.sdl @pytest.mark.own_display -def test_a_saved_theme_gets_a_new_renderer(pool): +def test_a_saved_theme_reloads_in_the_same_renderer(pool): theme_json = pool.themes.get("Tiny") / "theme.json" _, before = pool.render("Tiny", None, "640x480", "en_US", "main_menu") + renderer = pool.workers["640x480"] data = json.loads(theme_json.read_text()) data["main_menu"]["#element:option_list"]["area"] = [0.0, 0.0, 0.5, 0.5] theme_json.write_text(json.dumps(data)) @@ -204,8 +205,31 @@ def test_a_saved_theme_gets_a_new_renderer(pool): assert after['version'] != before['version'] rect = next(region['rect'] for region in after['regions'] if region['tag'] == "option_list") assert rect == [0, 0, 320, 240] - ## The old renderer for those settings is gone. - assert sum(1 for key in pool.workers if key[0] == "Tiny" and key[3] == "640x480") == 1 + ## Reloaded in place: the same process, no new one. + assert pool.workers["640x480"] is renderer and renderer.alive() + assert renderer.loaded[:2] == ("Tiny", after['version']) + + +@pytest.mark.sdl +@pytest.mark.own_display +def test_one_renderer_switches_theme_scheme_and_language(pool): + ## Each screen size has one renderer, switched in place: renders match a renderer + ## started with those settings. + renders = {} + for theme_id, scheme, lang in (("default_theme", None, "en_US"), ("default_theme", "Dracula", "en_US"), + ("default_theme", None, "de_DE"), ("Tiny", None, "en_US")): + png, info = pool.render(theme_id, scheme, "854x480", lang, "options") + assert not info['errors'] + renders[(theme_id, scheme, lang)] = png + assert len(set(renders.values())) == 4 + assert list(pool.workers).count("854x480") == 1 + + fresh = serve.RenderPool(pool.themes, pool.ports_config, pool.work_dir / "fresh") + try: + png, _ = fresh.render("default_theme", "Dracula", "854x480", "en_US", "options") + assert png == renders[("default_theme", "Dracula", "en_US")] + finally: + fresh.stop() @pytest.mark.sdl From 22c17ad211ceeff723a092413480c92d4ecbb735 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:15:21 +0800 Subject: [PATCH 11/28] Theme server: fork renderers from a zygote A renderer spent most of its start importing pugwash and loading the ports. Now one process, worker.py --zygote, imports pugwash once and forks a renderer per screen size. serve.py sends it a request with the renderer's end of a socket pair attached (SCM_RIGHTS); the renderer talks on that socket as a worker.py does on stdin/stdout. If the zygote can't start, renderers start on their own as before. - Each forked renderer's HarbourMaster has its own tools folder (Renderer(tools_dir=...)); the zygote's holds the theme links. - ThemeEngine.force_scheme (and PortMasterGUI(force_scheme=...)), like force_theme, so a renderer starts in the scheme asked for. - The zygote has no threads (select on its control socket and stdin): it forks. On the RG35XX-H (muOS Jacaranda): the first screen 2.4 s (was 8.4), a new screen size 2.0 s (was 12.5), switching scheme or language ~1.1 s, and each screen ~0.45 s (was ~1.1). Tests: forked renderers draw the same as one of their own, and a renderer that dies is replaced. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 1 + PortMaster/pylibs/pugwash/app.py | 4 +- PortMaster/pylibs/pugwash/theme.py | 6 +- PortMaster/pylibs/theme_server/serve.py | 174 +++++++++++++++--- PortMaster/pylibs/theme_server/shots.py | 13 +- PortMaster/pylibs/theme_server/worker.py | 214 ++++++++++++++++++----- THEME.md | 2 +- tests/test_theme_server.py | 31 ++++ 8 files changed, 367 insertions(+), 78 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ee15b555..a6f856e5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,6 +75,7 @@ It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes ` **Theme preview server** (`PortMaster/pylibs/theme_server/`, run from a checkout with `tools/theme_server/serve.py`; documented in `THEME.md`): - `serve.py` serves a page showing every screen rendered with a theme, at any resolution, colour scheme and language, and exports PNGs with `--export`. - Each render process, `worker.py`, is a headless pugwash for one screen size (SDL allows one GUI per process, and the size is fixed at start). Its `load` command switches the theme, colour scheme and language in place with `reload_language()`/`reload_theme()`, about a second; `serve.py` sends it whenever a render needs another theme version (a saved file), scheme or language (or saved translations). `tests/test_theme_server.py` checks a switched renderer draws the same as a new one. + - Renderers are forked from a zygote (`worker.py --zygote`), which imports pugwash once: `serve.py` sends it a request with the renderer's end of a socket pair attached (`SCM_RIGHTS`), and the renderer talks on that socket. Each renderer's HarbourMaster has its own tools folder (`Renderer(tools_dir=...)`), the zygote's holds the theme links. Without a zygote (it failed to start) each renderer is a `worker.py` of its own. No threads in the zygote: it forks. - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode or headless. - It draws with no window (`PM_HEADLESS=Y`): SDL's software renderer on a surface, with no video or controller subsystem. So it needs no dummy video driver, and it runs on a device next to the GUI that owns the screen (muOS's SDL has only `mali`). - It takes the ports from a PortMaster config folder (`--ports-config`): the cache's (`~/.cache/portmaster-theme-server`, filled by `worker.py --prepare-cache`) or, on a device, PortMaster's own. It copies the files and links the screenshot folders, so that folder is never changed. diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index 96f8b339..4e143f78 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -50,7 +50,7 @@ class PortMasterGUI(CallbackMixin, CommandsMixin, FifoControlMixin, PortInfoMixi TASK_INPUT_LAYERS = ('messages', 'message_box') FRAME_TIME = 1000 // 30 - def __init__(self, *, first_scene=None, force_theme=None): + def __init__(self, *, first_scene=None, force_theme=None, force_scheme=None): ## PM_HEADLESS=Y: no window, no video and no controllers, it draws onto a surface ## with the software renderer. The theme server's render workers (theme_server/ ## worker.py) use it, on a device too: muOS's SDL has only its "mali" driver, and @@ -216,7 +216,7 @@ def nice_version(v): self.cancellable = True - self.themes = ThemeEngine(self, force_theme=force_theme) + self.themes = ThemeEngine(self, force_theme=force_theme, force_scheme=force_scheme) self.theme_data = self.themes.gui_init() self.theme_dir = self.themes.get_theme_dir(self.themes.get_current_theme()) diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 0cd4c859..38176322 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -557,9 +557,11 @@ def get_theme_list(self): class ThemeEngine: - def __init__(self, gui, force_theme=None): + def __init__(self, gui, force_theme=None, force_scheme=None): self.gui = gui + ## Instead of the config's, eg: the theme server's renderers. self.force_theme = force_theme + self.force_scheme = force_scheme def get_pm_config(self): cfg_dir = harbourmaster.HM_TOOLS_DIR / "PortMaster" @@ -599,6 +601,8 @@ def get_current_theme_scheme(self): """ Returns the colour scheme of the current theme """ + if self.force_scheme is not None: + return self.force_scheme cfg_data = self.get_pm_config() return cfg_data.setdefault("theme-scheme", None) diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index 3aa19814..0c00c5b8 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -145,47 +145,142 @@ class WorkerError(Exception): pass +class Zygote: + """worker.py --zygote: pugwash imported once, forking a renderer per screen size (a + new one starts in about half the time).""" + def __init__(self, ports_config, work): + import socket + + self.work = Path(work) + parent, child = socket.socketpair() + self.log_file = open(str(self.work.with_suffix(".log")), "w") + try: + self.proc = subprocess.Popen( + [sys.executable, str(WORKER), "--zygote", "--control-fd", str(child.fileno()), + "--ports-config", str(ports_config), "--work", str(self.work)], + stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=self.log_file, pass_fds=[child.fileno()]) + finally: + child.close() + + self.control = parent + self.reader = parent.makefile("r") + self.lock = threading.Lock() + ready = self._read() + if not ready.get('ok'): + self.stop() + raise WorkerError(ready.get('error') or "the zygote didn't start, see " + self.log_file.name) + + def _read(self): + line = self.reader.readline() + return json.loads(line) if line else {'ok': False} + + def spawn(self, **request): + """(pid, socket) of a new renderer.""" + import array + import socket + + ours, theirs = socket.socketpair() + try: + with self.lock: + self.control.sendmsg([json.dumps(request).encode("utf-8")], + [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", [theirs.fileno()]))]) + answer = self._read() + except OSError as error: + ours.close() + raise WorkerError(f"the zygote stopped: {error}") + finally: + theirs.close() + + if not answer.get('ok'): + ours.close() + raise WorkerError(answer.get('error') or "the zygote stopped, see " + self.log_file.name) + + return answer['pid'], ours + + def alive(self): + return self.proc.poll() is None + + def stop(self): + try: + self.proc.stdin.close() + self.proc.wait(timeout=5) + except (OSError, subprocess.TimeoutExpired): + self.proc.kill() + finally: + self.control.close() + self.log_file.close() + shutil.rmtree(str(self.work), ignore_errors=True) + try: + os.unlink(self.log_file.name) + except OSError: + pass + + class Worker: - """One worker.py: a pugwash at one screen size, switched in place to the theme version, - colour scheme and language each render needs.""" - def __init__(self, resolution, theme_dir, scheme, lang, ports_config, work, loaded=None): + """One renderer: a pugwash at one screen size, switched in place to the theme version, + colour scheme and language each render needs. Forked by the zygote, or (without + one) a worker.py of its own.""" + def __init__(self, resolution, theme_dir, scheme, lang, ports_config, work, loaded=None, zygote=None): self.resolution = resolution self.lock = threading.Lock() self.work = Path(work) - command = [sys.executable, str(WORKER), "--theme-dir", str(theme_dir), "--resolution", resolution, - "--lang", lang, "--ports-config", str(ports_config), "--work", str(self.work)] - if scheme: - command += ["--scheme", scheme] + self.log_name = str(self.work.with_suffix(".log")) + self.proc = self.pid = self.sock = self.log_file = None + if zygote is not None: + self.pid, self.sock = zygote.spawn(resolution=resolution, theme_dir=str(theme_dir), scheme=scheme, + lang=lang, work=str(self.work)) + self.reader, self.writer = self.sock.makefile("r"), self.sock.makefile("w") + + else: + command = [sys.executable, str(WORKER), "--theme-dir", str(theme_dir), "--resolution", resolution, + "--lang", lang, "--ports-config", str(ports_config), "--work", str(self.work)] + if scheme: + command += ["--scheme", scheme] + + self.log_file = open(self.log_name, "w") + self.proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=self.log_file, universal_newlines=True, bufsize=1) + self.reader, self.writer = self.proc.stdout, self.proc.stdin - self.log_file = open(str(self.work.with_suffix(".log")), "w") - self.proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=self.log_file, universal_newlines=True, bufsize=1) self.ready = self._read() if not self.ready.get('ok'): self.stop() - raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + str(self.log_file.name)) + raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + self.log_name) ## What it has loaded: (theme id, theme version, scheme, language). self.loaded = loaded self.used = time.monotonic() def _read(self): - line = self.proc.stdout.readline() + try: + line = self.reader.readline() + except OSError: + line = "" if not line: - return {'ok': False, 'error': "the renderer stopped, see " + str(self.log_file.name)} + return {'ok': False, 'error': "the renderer stopped, see " + self.log_name} return json.loads(line) def _request(self, command): - if self.proc.poll() is not None: - raise WorkerError("the renderer stopped, see " + str(self.log_file.name)) + if not self.alive(): + raise WorkerError("the renderer stopped, see " + self.log_name) self.used = time.monotonic() - self.proc.stdin.write(json.dumps(command) + "\n") - self.proc.stdin.flush() + try: + self.writer.write(json.dumps(command) + "\n") + self.writer.flush() + except OSError: + raise WorkerError("the renderer stopped, see " + self.log_name) return self._read() def alive(self): - return self.proc.poll() is None + if self.proc is not None: + return self.proc.poll() is None + + try: + os.kill(self.pid, 0) + return True + except OSError: + return False def request(self, **command): with self.lock: @@ -210,17 +305,32 @@ def render(self, loaded, theme_dir, scheme, lang, shot): def stop(self): try: - if self.proc.poll() is None: - self.proc.stdin.write(json.dumps({'cmd': "quit"}) + "\n") - self.proc.stdin.flush() + if self.alive(): + self.writer.write(json.dumps({'cmd': "quit"}) + "\n") + self.writer.flush() + if self.proc is not None: self.proc.wait(timeout=5) + else: + end = time.monotonic() + 5 + while self.alive() and time.monotonic() < end: + time.sleep(0.05) + if self.alive(): + os.kill(self.pid, signal.SIGKILL) except (OSError, ValueError, subprocess.TimeoutExpired): - self.proc.kill() + if self.proc is not None: + self.proc.kill() + elif self.pid is not None: + try: + os.kill(self.pid, signal.SIGKILL) + except OSError: + pass finally: - self.log_file.close() + for file in (self.log_file, self.sock): + if file is not None: + file.close() shutil.rmtree(str(self.work), ignore_errors=True) try: - os.unlink(self.log_file.name) + os.unlink(self.log_name) except OSError: pass @@ -229,8 +339,10 @@ class RenderPool: """A renderer per screen size, started as needed (the least recently used goes when there are too many), switched to each theme, scheme and language in place; and the renders, until their theme or language files change.""" - def __init__(self, themes, ports_config, work_dir, max_workers=MAX_WORKERS): + def __init__(self, themes, ports_config, work_dir, max_workers=MAX_WORKERS, use_zygote=True): self.themes = themes + ## Started with the first renderer; False once it failed (then worker.py each). + self.zygote = None if use_zygote else False self.ports_config = Path(ports_config) self.max_workers = max(1, max_workers) self.work_dir = Path(work_dir) @@ -275,7 +387,14 @@ def _worker(self, resolution, theme_dir, scheme, lang, loaded): for old in stale: old.stop() - worker = Worker(resolution, theme_dir, scheme, lang, self.ports_config, work, loaded) + if self.zygote is None or (self.zygote and not self.zygote.alive()): + try: + self.zygote = Zygote(self.ports_config, self.work_dir / "zygote") + except (WorkerError, OSError) as error: + print(f"No zygote, renderers start on their own: {error}", file=sys.stderr, flush=True) + self.zygote = False + + worker = Worker(resolution, theme_dir, scheme, lang, self.ports_config, work, loaded, self.zygote or None) with self.lock: self.workers[resolution] = worker @@ -321,6 +440,9 @@ def stop(self): workers, self.workers = list(self.workers.values()), {} for worker in workers: worker.stop() + if self.zygote: + self.zygote.stop() + self.zygote = None ################################################################################ diff --git a/PortMaster/pylibs/theme_server/shots.py b/PortMaster/pylibs/theme_server/shots.py index c8b171a2..2eec3ae6 100644 --- a/PortMaster/pylibs/theme_server/shots.py +++ b/PortMaster/pylibs/theme_server/shots.py @@ -343,7 +343,9 @@ def scene_regions(gui): class Renderer: - def __init__(self, work, theme_name): + def __init__(self, work, theme_name, scheme=None, tools_dir=None): + """tools_dir: the HarbourMaster's (its ports and config), when it isn't + harbourmaster.HM_TOOLS_DIR (a renderer forked from worker.py's zygote).""" self.work = Path(work) self.errors = [] ## pugwash's errors (the theme's), not harbourmaster's (the port data's). @@ -351,11 +353,14 @@ def __init__(self, work, theme_name): filter=lambda record: not record["name"].startswith("harbourmaster")) self.theme_name = theme_name - self.gui = PortMasterGUI(first_scene=EmptyScene, force_theme=theme_name) + self.gui = PortMasterGUI(first_scene=EmptyScene, force_theme=theme_name, force_scheme=scheme) self.scheme = self.gui.themes.get_current_theme_scheme() + dirs = {} + if tools_dir is not None: + dirs = {'tools_dir': Path(tools_dir), 'ports_dir': self.work / "ports", 'scripts_dir': self.work / "ports"} self.gui.hm = harbourmaster.HarbourMaster( {'offline': True, 'no-check': True}, - temp_dir=self.work / "temp", callback=self.gui) + temp_dir=self.work / "temp", callback=self.gui, **dirs) ## Screenshots load as they're drawn, so every image is there in the first frame. self.gui.images.disable_async() ## Which strings each screen looks up, for the translation editor. @@ -369,7 +374,7 @@ def load(self, theme_name, scheme, lang): place, much quicker than a new renderer.""" gui = self.gui gui.themes.force_theme = theme_name - gui.hm.cfg_data['theme-scheme'] = scheme + gui.themes.force_scheme = scheme ## Reloads the translations (saved ones too) and then the theme. gui.reload_language(lang) self.lookups = record_lookups() diff --git a/PortMaster/pylibs/theme_server/worker.py b/PortMaster/pylibs/theme_server/worker.py index 120dcc0d..58b1cb34 100644 --- a/PortMaster/pylibs/theme_server/worker.py +++ b/PortMaster/pylibs/theme_server/worker.py @@ -118,7 +118,7 @@ def prepare_tools_dir(work, ports_config, theme_dir, scheme): elif entry.is_file(): shutil.copyfile(str(entry), str(config / entry.name)) - theme_name = link_theme(work, theme_dir) + theme_name = link_theme(tools / "PortMaster" / "themes", theme_dir) ## Its "version", else harbourmaster upgrades the config by deleting the image folders ## (here links), and not a first run, which writes the default sources. @@ -142,18 +142,20 @@ def prepare_tools_dir(work, ports_config, theme_dir, scheme): return theme_name -def link_theme(work, theme_dir): - """The theme's name, linked into work/tools/PortMaster/themes unless it's the built-in one.""" +def link_theme(themes_dir, theme_dir): + """The theme's name, linked into themes_dir (PortMaster/themes) unless it's the built-in one.""" default_theme = PM_DIR / "pylibs" / "default_theme" - if Path(theme_dir).resolve() == default_theme.resolve(): + target = Path(theme_dir).resolve() + if target == default_theme.resolve(): return "default_theme" - theme_name = Path(theme_dir).name - link = work / "tools" / "PortMaster" / "themes" / theme_name + link = Path(themes_dir) / target.name + if link.is_symlink() and Path(os.readlink(str(link))) == target: + return target.name if link.is_symlink() or link.exists(): link.unlink() - link.symlink_to(Path(theme_dir).resolve()) - return theme_name + link.symlink_to(target) + return target.name def prepare_cache(cache): @@ -185,58 +187,45 @@ def message(self, message): return 0 if sum(counts.values()) > 0 else 1 -def main(argv=None): - if argv is None: - argv = sys.argv[1:] - if argv[:1] == ["--prepare-cache"] and len(argv) == 2: - return prepare_cache(argv[1]) - - parser = argparse.ArgumentParser(description="theme dev server renderer") - parser.add_argument("--theme-dir", required=True) - parser.add_argument("--scheme", default=None) - parser.add_argument("--resolution", default="640x480") - parser.add_argument("--lang", default="en_US") - parser.add_argument("--ports-config", required=True) - parser.add_argument("--work", required=True) - options = parser.parse_args(argv) - - ## Answers go to the real stdout, everything pugwash prints to stderr. - reply_file = os.fdopen(os.dup(1), 'w') - os.dup2(2, 1) - - def reply(**answer): - reply_file.write(json.dumps(answer) + "\n") - reply_file.flush() - - width, height = (int(part) for part in options.resolution.lower().split("x")) - work = Path(options.work) - work.mkdir(parents=True, exist_ok=True) - theme_name = prepare_tools_dir(work, Path(options.ports_config), Path(options.theme_dir), options.scheme) - - os.environ.update(device_env(width, height, options.lang)) +def renderer_env(width, height, lang): + """The environment pugwash and harbourmaster read: the pretend device, headless SDL.""" + os.environ.update(device_env(width, height, lang)) os.environ.update(sdl_env()) for key in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES'): os.environ.pop(key, None) + + +def use_tools_dir(work): os.environ['HM_TOOLS_DIR'] = str(work / "tools") os.environ['HM_PORTS_DIR'] = str(work / "ports") os.environ['HM_SCRIPTS_DIR'] = str(work / "ports") - ## Next to PortMaster/: from a checkout that's harbourmaster's testing mode. + +def import_pugwash(): + """Next to PortMaster/ (from a checkout that's harbourmaster's testing mode), then the + modules: once the environment above is set, they read it on import.""" os.chdir(str(PM_DIR.parent)) sys.path[:0] = [str(PM_DIR / "pylibs"), str(PM_DIR / "exlibs")] - from theme_server import shots + return shots + +def run_renderer(shots, work, theme_dir, scheme, lang, resolution, lines, reply, tools_dir=None): + """Starts the renderer and answers commands from lines until "quit" or the end.""" + import harbourmaster + + themes_dir = harbourmaster.HM_TOOLS_DIR / "PortMaster" / "themes" try: - renderer = shots.Renderer(work, theme_name) + theme_name = link_theme(themes_dir, theme_dir) + renderer = shots.Renderer(work, theme_name, scheme=scheme, tools_dir=tools_dir) except Exception: reply(ok=False, error=traceback.format_exc()) return 1 - reply(ok=True, ready=True, resolution=[width, height], theme=theme_name, scheme=renderer.scheme) + reply(ok=True, ready=True, resolution=resolution, theme=theme_name, scheme=renderer.scheme) - for line in sys.stdin: + for line in lines: try: command = json.loads(line) if command.get('cmd') == "quit": @@ -247,8 +236,8 @@ def reply(**answer): elif command.get('cmd') == "load": started = time.monotonic() - theme_name = link_theme(work, command['theme_dir']) - renderer.load(theme_name, command.get('scheme'), command.get('lang') or options.lang) + theme_name = link_theme(themes_dir, command['theme_dir']) + renderer.load(theme_name, command.get('scheme'), command.get('lang') or lang) reply(ok=True, theme=theme_name, scheme=renderer.scheme, ms=int((time.monotonic() - started) * 1000)) elif command.get('cmd') == "render": @@ -273,5 +262,142 @@ def reply(**answer): return 0 +def json_writer(file): + def reply(**answer): + file.write(json.dumps(answer) + "\n") + file.flush() + return reply + + +def parse_resolution(text): + return [int(part) for part in text.lower().split("x")] + + +################################################################################ +## The zygote: imports pugwash once, then forks a renderer for each screen size, which +## saves the imports (half of a renderer's start) each time. +## +## worker.py --zygote --control-fd N --ports-config DIR --work DIR +## +## serve.py sends on the control socket a JSON message, with the renderer's end of a +## socket pair attached (SCM_RIGHTS): +## {"resolution": "640x480", "theme_dir": DIR, "scheme": ..., "lang": ..., "work": DIR} +## -> {"ok": true, "pid": N} +## The renderer then talks on that socket as a worker.py does on stdin/stdout. The +## zygote stops when its stdin closes. +def zygote(options): + import array + import select + import signal + import socket + + control = socket.socket(fileno=options.control_fd) + shared = Path(options.work) + shared.mkdir(parents=True, exist_ok=True) + ## The themes (linked as renderers load them) and the config pugwash reads at start; + ## each renderer's HarbourMaster has its own copy of the ports. + prepare_tools_dir(shared, Path(options.ports_config), PM_DIR / "pylibs" / "default_theme", None) + renderer_env(640, 480, "en_US") + use_tools_dir(shared) + shots = import_pugwash() + from harbourmaster import hardware + from pugwash import lang as pug_lang + + ## Renderers are reaped by the system. + signal.signal(signal.SIGCHLD, signal.SIG_IGN) + send = json_writer(control.makefile("w")) + send(ok=True, ready=True) + + ## No threads: it forks. Its stdin closing (serve.py gone) stops it. + fd_size = array.array("i").itemsize + while True: + readable, _, _ = select.select([control, 0], [], []) + if 0 in readable and not os.read(0, 4096): + return 0 + if control not in readable: + continue + + data, ancdata, _, _ = control.recvmsg(65536, socket.CMSG_SPACE(fd_size)) + if not data: + return 0 + + fds = array.array("i") + for level, kind, payload in ancdata: + if level == socket.SOL_SOCKET and kind == socket.SCM_RIGHTS: + fds.frombytes(payload[:len(payload) - (len(payload) % fd_size)]) + + try: + spawn = json.loads(data.decode("utf-8")) + channel = socket.socket(fileno=fds[0]) + except (ValueError, IndexError, OSError) as error: + send(ok=False, error=f"bad spawn request: {error}") + continue + + pid = os.fork() + if pid == 0: + ## The renderer. + code = 1 + try: + control.close() + signal.signal(signal.SIGCHLD, signal.SIG_DFL) + work = Path(spawn['work']) + work.mkdir(parents=True, exist_ok=True) + log = open(str(work.with_suffix(".log")), "w") + os.dup2(log.fileno(), 2) + os.dup2(log.fileno(), 1) + devnull = os.open(os.devnull, os.O_RDONLY) + os.dup2(devnull, 0) + + width, height = parse_resolution(spawn['resolution']) + prepare_tools_dir(work, Path(options.ports_config), PM_DIR / "pylibs" / "default_theme", None) + renderer_env(width, height, spawn['lang']) + hardware._CACHED_DICT = None + pug_lang.set_language(spawn['lang']) + code = run_renderer(shots, work, Path(spawn['theme_dir']), spawn.get('scheme'), spawn['lang'], + [width, height], channel.makefile("r"), json_writer(channel.makefile("w")), + tools_dir=work / "tools") + except BaseException: + traceback.print_exc() + finally: + os._exit(code) + + channel.close() + send(ok=True, pid=pid) + + +def main(argv=None): + if argv is None: + argv = sys.argv[1:] + if argv[:1] == ["--prepare-cache"] and len(argv) == 2: + return prepare_cache(argv[1]) + + parser = argparse.ArgumentParser(description="theme dev server renderer") + parser.add_argument("--zygote", action="store_true") + parser.add_argument("--control-fd", type=int) + parser.add_argument("--theme-dir") + parser.add_argument("--scheme", default=None) + parser.add_argument("--resolution", default="640x480") + parser.add_argument("--lang", default="en_US") + parser.add_argument("--ports-config", required=True) + parser.add_argument("--work", required=True) + options = parser.parse_args(argv) + + if options.zygote: + return zygote(options) + + ## Answers go to the real stdout, everything pugwash prints to stderr. + reply = json_writer(os.fdopen(os.dup(1), 'w')) + os.dup2(2, 1) + + width, height = parse_resolution(options.resolution) + work = Path(options.work) + work.mkdir(parents=True, exist_ok=True) + prepare_tools_dir(work, Path(options.ports_config), PM_DIR / "pylibs" / "default_theme", None) + renderer_env(width, height, options.lang) + use_tools_dir(work) + shots = import_pugwash() + return run_renderer(shots, work, Path(options.theme_dir), options.scheme, options.lang, [width, height], sys.stdin, reply) + + if __name__ == "__main__": sys.exit(main()) diff --git a/THEME.md b/THEME.md index 73b051ad..dcd120b2 100644 --- a/THEME.md +++ b/THEME.md @@ -40,7 +40,7 @@ That writes `out/////.png`, with a PortMaster carries the same server: **Options → Theme Editor** starts it on the device, and shows its address as text and as a QR code. Open it on a computer or phone on the same network: no checkout or install needed. It previews the themes installed on the device, with the device's own ports list, and the translation editor saves into the device's PortMaster. Press B on the device to stop it. - Translations saved on a device are in `PortMaster/pylibs/locales/` there. Copy them off before updating PortMaster, which replaces that folder. -- Each screen size starts a render process (60-90 MB): its first screen takes about 10 seconds on a handheld. Switching theme, colour scheme or language, or saving, takes 2-3 seconds, and other screens a second or two. How many sizes are kept at once depends on the device's free memory. +- On a handheld (an RG35XX-H) the first screen takes 2-3 seconds, a new screen size about 2, switching theme, colour scheme or language (or saving) about a second, and each other screen half a second. Each screen size has a render process; how many are kept at once depends on the device's free memory. - Anyone on the same network can open it while it runs. ### Translating PortMaster diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 841e6f69..6d3ab4bf 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -541,3 +541,34 @@ def test_no_scheme_is_the_default_scheme(pool): workers = dict(pool.workers) pool.render("default_theme", "Light Mode", "640x480", "en_US", "main_menu") assert pool.workers == workers + + +@pytest.mark.sdl +@pytest.mark.own_display +def test_renderers_fork_from_the_zygote(pool, tmp_path): + ## The zygote imports pugwash once and forks a renderer per screen size; they draw + ## the same as a worker.py of their own. + import os + import signal + + png, _ = pool.render("default_theme", "Dracula", "720x720", "de_DE", "options") + forked = pool.workers["720x720"] + assert pool.zygote and forked.pid and forked.proc is None + + alone = serve.RenderPool(pool.themes, pool.ports_config, tmp_path / "alone", use_zygote=False) + try: + own_png, _ = alone.render("default_theme", "Dracula", "720x720", "de_DE", "options") + assert alone.workers["720x720"].proc is not None + assert own_png == png + finally: + alone.stop() + + ## A renderer that died is replaced. + os.kill(forked.pid, signal.SIGKILL) + for _ in range(100): + if not forked.alive(): + break + import time + time.sleep(0.05) + png, info = pool.render("default_theme", "Dracula", "720x720", "de_DE", "main_menu") + assert png and pool.workers["720x720"] is not forked From 5c3ef94011469269ae320b17473c8069e2545951 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:32:22 +0800 Subject: [PATCH 12/28] Theme server page: renders queued and cancelled, and say what's happening - The page asked for every screen at once, so after changing a setting the server still drew the whole old batch first. Renders now go through a queue, two at a time; a change (theme, scheme, size, language, view, a saved file) drops what's waiting and aborts what's running. - A line with a spinner says "Rendering 3 of 25...", or "Starting the renderer for 1024x768, a few seconds..." / "Loading the theme and language..." from the new /api/status (which renderers are starting, loading or rendering). Tried in Chromium: changing the scheme mid-render, no more renders of the old one were asked for and the new set finished 1.4 s later. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/theme_server/serve.py | 70 +++++++++++++----- .../pylibs/theme_server/static/index.html | 74 +++++++++++++++++-- tests/test_theme_server.py | 22 ++++++ 3 files changed, 139 insertions(+), 27 deletions(-) diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index 0c00c5b8..87ffa187 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -247,8 +247,10 @@ def __init__(self, resolution, theme_dir, scheme, lang, ports_config, work, load self.stop() raise WorkerError(self.ready.get('error') or "the renderer didn't start, see " + self.log_name) - ## What it has loaded: (theme id, theme version, scheme, language). + ## What it has loaded: (theme id, theme version, scheme, language), and what it's + ## doing for /api/status: None, "loading" or "rendering". self.loaded = loaded + self.state = None self.used = time.monotonic() def _read(self): @@ -289,19 +291,25 @@ def request(self, **command): def render(self, loaded, theme_dir, scheme, lang, shot): """(answer, png bytes): the theme, scheme and language loaded first if they aren't.""" with self.lock: - if self.loaded != loaded: - answer = self._request({'cmd': "load", 'theme_dir': str(theme_dir), 'scheme': scheme, 'lang': lang}) + try: + if self.loaded != loaded: + self.state = "loading" + answer = self._request({'cmd': "load", 'theme_dir': str(theme_dir), 'scheme': scheme, 'lang': lang}) + if not answer.get('ok'): + self.loaded = None + raise WorkerError(answer.get('error', "loading the theme failed")) + self.loaded = loaded + + self.state = "rendering" + out_file = self.work / "render.png" + answer = self._request({'cmd': "render", 'shot': shot, 'out': str(out_file)}) if not answer.get('ok'): - self.loaded = None - raise WorkerError(answer.get('error', "loading the theme failed")) - self.loaded = loaded + raise WorkerError(answer.get('error', "render failed")) - out_file = self.work / "render.png" - answer = self._request({'cmd': "render", 'shot': shot, 'out': str(out_file)}) - if not answer.get('ok'): - raise WorkerError(answer.get('error', "render failed")) + return answer, (out_file.read_bytes() if not answer.get('missing') else b"") - return answer, (out_file.read_bytes() if not answer.get('missing') else b"") + finally: + self.state = None def stop(self): try: @@ -353,6 +361,21 @@ def __init__(self, themes, ports_config, work_dir, max_workers=MAX_WORKERS, use_ self.start_lock = threading.Lock() self.translations = Translations() self.counter = 0 + ## Screen sizes whose renderer is starting, for /api/status. + self.starting = set() + + def status(self): + """What the renderers are doing, so the page can say: {'starting': [resolution], + 'loading': [...], 'rendering': [...], 'running': [...]}.""" + with self.lock: + states = {resolution: worker.state for resolution, worker in self.workers.items()} + starting = sorted(self.starting) + return { + 'starting': starting, + 'loading': sorted(res for res, state in states.items() if state == "loading"), + 'rendering': sorted(res for res, state in states.items() if state == "rendering"), + 'running': sorted(states), + } def _wanted(self, theme_id, scheme, lang): """(theme dir, scheme, (theme id, version, scheme, lang)) for a render.""" @@ -387,14 +410,20 @@ def _worker(self, resolution, theme_dir, scheme, lang, loaded): for old in stale: old.stop() - if self.zygote is None or (self.zygote and not self.zygote.alive()): - try: - self.zygote = Zygote(self.ports_config, self.work_dir / "zygote") - except (WorkerError, OSError) as error: - print(f"No zygote, renderers start on their own: {error}", file=sys.stderr, flush=True) - self.zygote = False - - worker = Worker(resolution, theme_dir, scheme, lang, self.ports_config, work, loaded, self.zygote or None) + with self.lock: + self.starting.add(resolution) + try: + if self.zygote is None or (self.zygote and not self.zygote.alive()): + try: + self.zygote = Zygote(self.ports_config, self.work_dir / "zygote") + except (WorkerError, OSError) as error: + print(f"No zygote, renderers start on their own: {error}", file=sys.stderr, flush=True) + self.zygote = False + + worker = Worker(resolution, theme_dir, scheme, lang, self.ports_config, work, loaded, self.zygote or None) + finally: + with self.lock: + self.starting.discard(resolution) with self.lock: self.workers[resolution] = worker @@ -582,6 +611,9 @@ def do_GET(self): 'all_languages': all_languages(), }) + elif url.path == "/api/status": + self.send(200, self.server.pool.status()) + elif url.path == "/api/strings": lang = query.get('lang', "") if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang): diff --git a/PortMaster/pylibs/theme_server/static/index.html b/PortMaster/pylibs/theme_server/static/index.html index b4e930f7..d1b30f88 100644 --- a/PortMaster/pylibs/theme_server/static/index.html +++ b/PortMaster/pylibs/theme_server/static/index.html @@ -37,6 +37,10 @@ .seg button[aria-pressed=true] { background: var(--accent); color: #fff; } .status { margin-left: auto; font-size: 12px; color: var(--muted); padding-bottom: 5px; text-align: right; } .status .live { color: var(--accent); } +.busy { display: inline-flex; gap: 8px; align-items: center; font-size: 12px; color: var(--fg); padding-bottom: 5px; } +.spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid var(--line); border-top-color: var(--accent); animation: spin .8s linear infinite; flex: none; } +@keyframes spin { to { transform: rotate(360deg); } } +@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 2.4s; } } main { padding: 16px; } /* Translate view */ .translate { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 16px; align-items: start; } @@ -122,6 +126,7 @@

Theme Preview

+
Loading…
@@ -213,6 +218,7 @@

Region

$("lang").value = state.lang; } function setView(view) { + cancelRenders(); state.view = view; for (const b of $("view").children) b.setAttribute("aria-pressed", String(b.dataset.view === view)); for (const el of document.querySelectorAll(".preview-only")) el.hidden = view !== "preview" || (el.id === "mode-shot-field" && state.mode !== "resolutions"); @@ -244,18 +250,68 @@

Region

function params(shot, res) { return new URLSearchParams({ theme: state.theme, scheme: state.scheme, res: res || state.res, lang: state.lang, shot, v: state.version || "" }); } +/* Renders go through a queue, two at a time: changing a setting drops what's waiting + and cancels what's running, so the server isn't busy with screens nobody wants. */ +const queue = { jobs: [], running: 0, gen: 0, total: 0, done: 0, controllers: new Set() }; +const QUEUE_LIMIT = 2; +function cancelled() { const error = new Error("cancelled"); error.cancelled = true; return error; } +function cancelRenders() { + queue.gen += 1; + for (const job of queue.jobs) job.reject(cancelled()); + queue.jobs = []; + for (const controller of queue.controllers) controller.abort(); + queue.total = 0; queue.done = 0; + state.renders.clear(); + // A usage scan cut short starts again (buildTranslate, or the next poll). + if (state.scanning) { state.usageKey = null; state.scanning = false; } + busy(); +} +function pump() { + while (queue.running < QUEUE_LIMIT && queue.jobs.length) { + const job = queue.jobs.shift(); + const controller = new AbortController(); + queue.running += 1; queue.controllers.add(controller); + fetch("/api/render.json?" + job.key, { signal: controller.signal }) + .then(async (r) => { const body = await r.json(); if (!r.ok) throw new Error(body.error || r.statusText); return body; }) + .then(job.resolve, (error) => job.reject(controller.signal.aborted ? cancelled() : error)) + .finally(() => { + queue.running -= 1; queue.controllers.delete(controller); + if (job.gen === queue.gen) queue.done += 1; + pump(); + }); + } + busy(); +} async function render(shot, res) { const key = params(shot, res).toString(); if (!state.renders.has(key)) { - state.renders.set(key, fetch("/api/render.json?" + key).then(async (r) => { - const body = await r.json(); - if (!r.ok) throw new Error(body.error || r.statusText); - return body; - })); + const promise = new Promise((resolve, reject) => queue.jobs.push({ key, resolve, reject, gen: queue.gen })); + promise.catch(() => { if (state.renders.get(key) === promise) state.renders.delete(key); }); + state.renders.set(key, promise); + queue.total += 1; + pump(); } return state.renders.get(key); } +/* What's happening, from the queue and the server's /api/status. */ +let statusTimer = null; +async function serverStatus() { + try { state.server = await (await fetch("/api/status")).json(); } catch (e) { state.server = null; } + busy(); +} +function busy() { + const active = queue.running + queue.jobs.length; + $("busy").hidden = !active; + if (!active) { clearInterval(statusTimer); statusTimer = null; state.server = null; return; } + if (!statusTimer) { statusTimer = setInterval(serverStatus, 500); serverStatus(); } + const server = state.server; + let text = `Rendering ${Math.min(queue.done + 1, queue.total)} of ${queue.total}…`; + if (server && server.starting.length) text = `Starting the renderer for ${server.starting[0].replace("x", "×")}, a few seconds…`; + else if (server && server.loading.length) text = "Loading the theme and language…"; + $("busy-text").textContent = text; +} + function cards() { if (state.mode === "resolutions") { const title = (state.info.shots.find(([n]) => n === state.modeShot) || [, state.modeShot])[1]; @@ -265,6 +321,7 @@

Region

} function build() { + cancelRenders(); const grid = $("grid"); grid.replaceChildren(); const list = cards(); @@ -304,6 +361,7 @@

Region

card.el.classList.toggle("has-errors", info.errors.length > 0); meta.innerHTML = info.errors.length ? `${info.errors.length} error${info.errors.length > 1 ? "s" : ""}` : `${info.ms ?? "–"} ms`; } catch (error) { + if (error.cancelled) return; card.el.classList.add("has-errors"); shotEl.innerHTML = `
`; shotEl.firstChild.textContent = error.message.split("\n").filter(Boolean).slice(-1)[0] || "Render failed"; meta.innerHTML = `failed`; @@ -426,7 +484,7 @@

Region

}); /* Controls */ -function changed() { state.renders.clear(); save(); if (state.view === "translate") buildTranslate(); else build(); } +function changed() { cancelRenders(); save(); if (state.view === "translate") buildTranslate(); else build(); } $("theme").onchange = () => { state.theme = $("theme").value; state.version = null; fillSchemes(); poll().then(changed); }; $("scheme").onchange = () => { state.scheme = $("scheme").value; changed(); }; $("res").onchange = () => { @@ -637,9 +695,9 @@

Region

const { version } = await r.json(); if (state.version !== null && version !== state.version) { state.version = version; lastChange = new Date(); - state.renders.clear(); + cancelRenders(); const detail = state.detail; - if (state.view === "translate") showPreview(); + if (state.view === "translate") { if (!state.usageKey) scanUsage(); showPreview(); } for (const card of state.view === "preview" ? state.cards || [] : []) fill(card); if (detail !== null) Promise.all(state.cards.map((c) => render(c.shot, c.res))).then(() => { if (state.detail === detail) openDetail(detail); }); // Refresh the scheme list too, in case theme.json changed it. diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 6d3ab4bf..9ab0fdbd 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -572,3 +572,25 @@ def test_renderers_fork_from_the_zygote(pool, tmp_path): time.sleep(0.05) png, info = pool.render("default_theme", "Dracula", "720x720", "de_DE", "main_menu") assert png and pool.workers["720x720"] is not forked + + +@pytest.mark.sdl +@pytest.mark.own_display +def test_status_says_what_the_renderers_do(pool): + ## For the page's "Starting the renderer..." / "Loading..." line. + seen = [] + original = serve.Worker.__init__ + + def slow_start(self, *args, **kwargs): + seen.append(pool.status()) + original(self, *args, **kwargs) + + serve.Worker.__init__ = slow_start + try: + pool.render("default_theme", None, "960x544", "en_US", "main_menu") + finally: + serve.Worker.__init__ = original + + assert seen and seen[0]['starting'] == ["960x544"] + status = pool.status() + assert "960x544" in status['running'] and status['starting'] == [] and status['loading'] == [] From b8b4ec12c913a3a3b6cb2e234c00dd8cb59479ac Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:32:22 +0800 Subject: [PATCH 13/28] Language names in the current language The language list was made when pugwash.lang was imported, before any translation loaded, so its names were always English; now that the language can change without a restart that showed. lang_list() translates them when it's called, and the language screen asks for it each time. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/pugwash/app.py | 1 + PortMaster/pylibs/pugwash/lang.py | 8 +++++--- PortMaster/pylibs/pugwash/scenes/language.py | 2 +- tests/test_reload.py | 5 +++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/PortMaster/pylibs/pugwash/app.py b/PortMaster/pylibs/pugwash/app.py index 4e143f78..aafbd6d1 100644 --- a/PortMaster/pylibs/pugwash/app.py +++ b/PortMaster/pylibs/pugwash/app.py @@ -580,6 +580,7 @@ def reload_language(self, language, then=()): capabilities.append(new_lang) self.set_data('system.ip_address', get_ip_address() or _("Unknown IP")) + self.lang_list = lang_list() self.reload_theme(then) def quit(self): diff --git a/PortMaster/pylibs/pugwash/lang.py b/PortMaster/pylibs/pugwash/lang.py index 77b02a42..5615dd21 100644 --- a/PortMaster/pylibs/pugwash/lang.py +++ b/PortMaster/pylibs/pugwash/lang.py @@ -142,12 +142,14 @@ def set_language(language): def lang_list(): + """{code: name} of the languages with a translation, named in the current language + (ALL_LANGUAGES holds the English names: it's made before any translation loads).""" languages = { - lang_id: lang_name - for lang_id, lang_name in sorted(ALL_LANGUAGES.items(), key=lambda item: (item[1])) + lang_id: _(lang_name) + for lang_id, lang_name in ALL_LANGUAGES.items() if i18n.language_file(LANG_DIR, lang_id).is_file()} - return languages + return dict(sorted(languages.items(), key=lambda item: item[1])) load_lang() diff --git a/PortMaster/pylibs/pugwash/scenes/language.py b/PortMaster/pylibs/pugwash/scenes/language.py index e98e310f..7daf34d0 100644 --- a/PortMaster/pylibs/pugwash/scenes/language.py +++ b/PortMaster/pylibs/pugwash/scenes/language.py @@ -14,7 +14,7 @@ def __init__(self, gui): self.load_regions("option_menu", ['option_list', ]) - languages = gui.lang_list + languages = lang.lang_list() selected_lang = lang.CURRENT_LANG self.tags['option_list'].reset_options() diff --git a/tests/test_reload.py b/tests/test_reload.py index 741f6b8c..285fb688 100644 --- a/tests/test_reload.py +++ b/tests/test_reload.py @@ -108,6 +108,11 @@ def test_language_reload(app, tmp_path): assert "Sprache wählen" in option_list.list assert "de_DE" in harbourmaster.device_info()['capabilities'] and "en_US" not in harbourmaster.device_info()['capabilities'] + ## The languages are named in the new language too. + from pugwash.scenes import LanguageScene + names = LanguageScene(app).tags['option_list'].list + assert "Französisch" in names and "French" not in names + ## The theme's font for the language: "font[ja_JP]". app.reload_language("ja_JP", then=options(app)) assert app.scenes[-1][1][-1].tags['option_list'].font == "NotoSansJP-Regular.ttf" From 7da89c7ae19a8b904cfa8c1ed45b75bd6cdbe874 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:47:36 +0800 Subject: [PATCH 14/28] Theme server tests: two renderers at most, for handhelds The Test Suite runs these on devices now; up to six renderers at 60-90 MB each is too much for the low-memory ones. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- tests/test_theme_server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 9ab0fdbd..d994ccd0 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -133,7 +133,8 @@ def pool(tmp_path_factory): (themes_dir / "Tiny" / "theme.json").write_text(json.dumps(tiny)) themes = serve.Themes(themes_dir) - pool = serve.RenderPool(themes, tmp / "cache" / "tools" / "PortMaster" / "config", tmp / "work") + ## Two renderers at most (60-90 MB each): the Test Suite runs this on handhelds too. + pool = serve.RenderPool(themes, tmp / "cache" / "tools" / "PortMaster" / "config", tmp / "work", max_workers=2) try: pool.warm() except serve.WorkerError as error: From 0520c545aa5435adf5c54f7c21acd6c36eb3c4d1 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 19:53:47 +0800 Subject: [PATCH 15/28] Tests: Japanese font checks only where the Noto fonts are The Test Suite leaves NotoSans.tar.xz out, so on a device there's no Japanese font for these to load (found running them on muOS). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- tests/test_reload.py | 7 ++++--- tests/test_theme_server.py | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_reload.py b/tests/test_reload.py index 285fb688..f379b9a2 100644 --- a/tests/test_reload.py +++ b/tests/test_reload.py @@ -113,9 +113,10 @@ def test_language_reload(app, tmp_path): names = LanguageScene(app).tags['option_list'].list assert "Französisch" in names and "French" not in names - ## The theme's font for the language: "font[ja_JP]". - app.reload_language("ja_JP", then=options(app)) - assert app.scenes[-1][1][-1].tags['option_list'].font == "NotoSansJP-Regular.ttf" + ## The theme's font for the language: "font[ja_JP]" (the Test Suite has no Noto fonts). + if (PM_DIR / "pylibs" / "resources" / "NotoSansJP-Regular.ttf").is_file(): + app.reload_language("ja_JP", then=options(app)) + assert app.scenes[-1][1][-1].tags['option_list'].font == "NotoSansJP-Regular.ttf" app.reload_language("en_US", then=options(app)) assert screen(app, tmp_path, "after") == before diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index d994ccd0..eefb941b 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -330,6 +330,8 @@ def test_render_lists_the_strings_it_used(pool): def test_language_capability_picks_the_font(pool): ## The default theme's "font[ja_JP]": the language reaches the theme's capabilities ## with a pretend resolution too. + if not (PM_DIR / "pylibs" / "resources" / "NotoSansJP-Regular.ttf").is_file(): + pytest.skip("no Noto fonts here (the Test Suite leaves them out)") _, info = pool.render("default_theme", None, "1280x720", "ja_JP", "main_menu") fonts = {region['data'].get('font') for region in info['regions']} assert fonts == {"NotoSansJP-Regular.ttf"} From cfabfa6188857d779b2b57145376147c3a66ff3f Mon Sep 17 00:00:00 2001 From: kloptops Date: Sun, 18 Oct 2026 20:36:36 +0800 Subject: [PATCH 16/28] test_managed_server: check only for the server's own temporary folder On GitHub's runners ctypes.util.find_library (pysdl2 looking for SDL) runs gcc, which can leave its temporary files in TMPDIR, so the folder wasn't always empty and the test failed now and then. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- tests/test_theme_server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index eefb941b..4e5499d8 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -479,10 +479,11 @@ def test_managed_server(tmp_path): with urllib.request.urlopen(f"http://127.0.0.1:{ready['port']}/api/info", timeout=30) as response: assert json.loads(response.read())['themes'][0]['id'] == "default_theme" - ## PortMaster closing stdin stops it, and it cleans up after itself. + ## PortMaster closing stdin stops it, and it cleans up after itself. (Only its own + ## folder: ctypes' find_library can leave gcc's temporary files there too.) proc.stdin.close() assert proc.wait(timeout=30) == 0 - assert list((tmp_path / "tmp").iterdir()) == [] + assert [path for path in (tmp_path / "tmp").iterdir() if path.name.startswith("pm-theme-server-")] == [] finally: if proc.poll() is None: From 9be9d132a6b89799e2b02a83e6217e7da1e7efe2 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 05:08:24 +0800 Subject: [PATCH 17/28] Theme editor: a random port from the dynamic range, kept per device Port 8090 (and the nine after it) could clash with a CFW's own services. The on-device editor now picks a random port from 49152-65535, which is never assigned to a service, and keeps it in config.json (theme_server_port) so the address stays the same between sessions. If it's taken, the next free one of the 20 after it, or any free port (serve.py --any-port), is used and kept instead. listen() stops at 65535. The desktop serve.py keeps --port 8090. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 2 +- .../pylibs/pugwash/scenes/theme_server.py | 22 +++- PortMaster/pylibs/theme_server/serve.py | 17 ++- THEME.md | 2 +- tests/test_theme_server.py | 110 ++++++++++++++++++ 5 files changed, 146 insertions(+), 7 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4785308d..4fc72b0c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,7 +87,7 @@ It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes ` - **Translate view:** a translation editor. `/api/strings` lists a language's strings, and `/api/translate` saves one into the language files, in `strings.json` order. It only accepts requests from the page itself: they need the `X-Theme-Server` header. - Renderers are keyed by the theme and the language files together, so a saved translation re-renders. - `translations.py` is the language-file code (`tools/i18n.py` runs its `extract`/`check`). -- **On a device:** Options → Theme Editor (`pugwash/scenes/theme_server.py`) runs `python3 -m theme_server.serve --managed` on port 8090 (or the next free one), with as many workers as `MemAvailable` allows. The screen shows the address as text and as a QR code (`"image": "qr:{theme_server.url}"`, the default theme's `theme_server` section, or a plain fallback). `--managed` reports `{"ok": true, "port": N}` on stdout and stops when stdin closes, so B, or pugwash dying, stops it. Its log is `PortMaster/theme_server.txt`. +- **On a device:** Options → Theme Editor (`pugwash/scenes/theme_server.py`) runs `python3 -m theme_server.serve --managed` on a port from the dynamic range (49152-65535, where no CFW service listens): random the first time, then kept in config.json as `theme_server_port` so the address stays the same; if it's taken, the next free one or any (`--try-ports`, `--any-port`), which is kept instead, with as many workers as `MemAvailable` allows. The screen shows the address as text and as a QR code (`"image": "qr:{theme_server.url}"`, the default theme's `theme_server` section, or a plain fallback). `--managed` reports `{"ok": true, "port": N}` on stdout and stops when stdin closes, so B, or pugwash dying, stops it. Its log is `PortMaster/theme_server.txt`. - Tested in `tests/test_theme_server.py`. Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is fixed and the marker should then be removed. pugwash's `main()` is wrapped in `logger.catch`, so it exits 0 even after a crash; check its output for tracebacks rather than relying on the exit code. diff --git a/PortMaster/pylibs/pugwash/scenes/theme_server.py b/PortMaster/pylibs/pugwash/scenes/theme_server.py index 7e25ae8b..d0798dfa 100644 --- a/PortMaster/pylibs/pugwash/scenes/theme_server.py +++ b/PortMaster/pylibs/pugwash/scenes/theme_server.py @@ -5,6 +5,7 @@ import json import os +import random import subprocess import sys import threading @@ -17,7 +18,10 @@ from .base import BaseScene -PORT = 8090 +## The dynamic (private) port range: never assigned to a service, so no CFW's own daemons +## are listening on it. Each device picks a random one and keeps it, so the address stays +## the same between sessions; if it's taken, the server takes any free port and that's kept. +PORT_RANGE = (49152, 65535) ## Render processes are 60-90 MB each: as many as the free memory allows, up to this. MAX_WORKERS = 4 @@ -36,6 +40,15 @@ def worker_count(meminfo="/proc/meminfo"): return 2 +def server_port(cfg_data): + """The port this device used last time, or a random one the first time.""" + port = cfg_data.get('theme_server_port') + if isinstance(port, bool) or not isinstance(port, int) or not PORT_RANGE[0] <= port <= PORT_RANGE[1]: + port = random.randint(*PORT_RANGE) + + return port + + def fallback_section(): """For themes without a "theme_server" section: plain, in PortMaster's own font for the language (the default theme's "font[ja_JP]" & co), as the address has to be readable.""" @@ -82,7 +95,7 @@ def start(self): env['PYTHONPATH'] = os.pathsep.join([str(PYLIB_PATH), str(PYLIB_PATH.parent / "exlibs")]) command = [ sys.executable, "-m", "theme_server.serve", "--managed", - "--host", "0.0.0.0", "--port", str(PORT), "--try-ports", "10", + "--host", "0.0.0.0", "--port", str(server_port(hm.cfg_data)), "--try-ports", "20", "--any-port", "--themes", str(hm.themes_dir), "--ports-config", str(hm.cfg_dir), "--max-workers", str(worker_count()), @@ -124,6 +137,11 @@ def started(self, status): self.stop() return + ## Kept for next time (it was random, or the old one was busy). + if self.gui.hm.cfg_data.get('theme_server_port') != status['port']: + self.gui.hm.cfg_data['theme_server_port'] = status['port'] + self.gui.hm.save_config() + ip_address = get_ip_address(force_update=True) if ip_address is None: self.gui.set_data("theme_server.status", _( diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index 87ffa187..23027882 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -733,14 +733,24 @@ def export(pool, themes, out, theme_ids, resolutions, schemes, langs, shots): return 1 if failures else 0 -def listen(host, port, tries): - """The server on the first free port of port, port + 1, ... (tries of them).""" +def listen(host, port, tries, any_port=False): + """ + The server on the first free port of port, port + 1, ... (tries of them). any_port: if + none is free, on whichever port the system gives it. + """ + last = OSError(f"no ports to try from {port}") for offset in range(max(1, tries)): + if port + offset > 65535: + break + try: return Server((host, port + offset), Handler) except OSError as error: last = error + if any_port: + return Server((host, 0), Handler) + raise last @@ -762,6 +772,7 @@ def main(argv=None): parser.add_argument("--themes", help="a theme folder, or a folder of themes") parser.add_argument("--port", type=int, default=8090) parser.add_argument("--try-ports", type=int, default=1, metavar="N", help="if the port is busy, try the next N-1 too") + parser.add_argument("--any-port", action="store_true", help="if those are busy, take any free port") parser.add_argument("--host", default="127.0.0.1", help="use 0.0.0.0 to let other machines connect") parser.add_argument("--cache", type=Path, default=default_cache()) parser.add_argument("--ports-config", type=Path, help="a PortMaster config folder to take the ports from, instead of the cache") @@ -807,7 +818,7 @@ def interrupted(signum, frame): [scheme for scheme in options.schemes.split(",") if scheme], options.langs.split(","), shots) try: - server = listen(options.host, options.port, options.try_ports) + server = listen(options.host, options.port, options.try_ports, options.any_port) except OSError as error: if report is not None: report(ok=False, error=f"port {options.port} is busy ({error.strerror})") diff --git a/THEME.md b/THEME.md index dcd120b2..9ed1390a 100644 --- a/THEME.md +++ b/THEME.md @@ -37,7 +37,7 @@ That writes `out/////.png`, with a ### On your device -PortMaster carries the same server: **Options → Theme Editor** starts it on the device, and shows its address as text and as a QR code. Open it on a computer or phone on the same network: no checkout or install needed. It previews the themes installed on the device, with the device's own ports list, and the translation editor saves into the device's PortMaster. Press B on the device to stop it. +PortMaster carries the same server: **Options → Theme Editor** starts it on the device, and shows its address as text and as a QR code. Each device picks a random port the first time and keeps it, so the address stays the same. Open it on a computer or phone on the same network: no checkout or install needed. It previews the themes installed on the device, with the device's own ports list, and the translation editor saves into the device's PortMaster. Press B on the device to stop it. - Translations saved on a device are in `PortMaster/pylibs/locales/` there. Copy them off before updating PortMaster, which replaces that folder. - On a handheld (an RG35XX-H) the first screen takes 2-3 seconds, a new screen size about 2, switching theme, colour scheme or language (or saving) about a second, and each other screen half a second. Each screen size has a render process; how many are kept at once depends on the device's free memory. diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 4e5499d8..9fcdacae 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -436,10 +436,57 @@ def test_listen_tries_the_next_ports(): with pytest.raises(OSError): serve.listen("127.0.0.1", port, 1) + ## Never past the last port. + with pytest.raises(OSError): + serve.listen("127.0.0.1", 65536, 3) + + ## --any-port: none of them free, so whichever the system gives. + server = serve.listen("127.0.0.1", port, 1, any_port=True) + try: + assert server.server_address[1] not in (0, port) + finally: + server.server_close() + finally: busy.close() +def busy_port_in_range(): + """A socket listening on a port in the theme editor's range.""" + import random + import socket + from pugwash.scenes.theme_server import PORT_RANGE + + for _ in range(200): + busy = socket.socket() + try: + busy.bind(("0.0.0.0", random.randint(PORT_RANGE[0], PORT_RANGE[1] - 20))) + busy.listen(1) + return busy + except OSError: + busy.close() + + pytest.skip("no free port in the range to make busy") + + +@pytest.mark.parametrize("saved", [None, 8090, 80, 70000, "50000", True, 49151, 65536]) +def test_server_port_random_in_the_range(saved): + ## Not 8090 or anything a CFW's own services might listen on. + from pugwash.scenes.theme_server import PORT_RANGE, server_port + + ports = {server_port({} if saved is None else {'theme_server_port': saved}) for _ in range(50)} + + assert all(PORT_RANGE[0] <= port <= PORT_RANGE[1] for port in ports) + assert len(ports) > 1 + + +@pytest.mark.parametrize("saved", [49152, 55555, 65535]) +def test_server_port_kept(saved): + from pugwash.scenes.theme_server import server_port + + assert server_port({'theme_server_port': saved}) == saved + + def test_worker_count(tmp_path): from pugwash.scenes.theme_server import worker_count @@ -509,6 +556,12 @@ def test_theme_editor_scene(gui, hm_dirs, monkeypatch): url = gui.text_data['theme_server.url'] assert url.startswith("http://127.0.0.1:") and url in gui.text_data['theme_server.status'] + + ## A random port in the range, kept in the config for next time. + port = int(url.rsplit(':', 1)[1].strip('/')) + assert scene_module.PORT_RANGE[0] <= port <= scene_module.PORT_RANGE[1] + assert gui.hm.cfg_data['theme_server_port'] == port + assert json.loads((gui.hm.cfg_dir / "config.json").read_text())['theme_server_port'] == port ## The QR code of the address is on screen. assert gui.images.load("qr:" + url) is scene.tags['server_qr'].image @@ -598,3 +651,60 @@ def slow_start(self, *args, **kwargs): assert seen and seen[0]['starting'] == ["960x544"] status = pool.status() assert "960x544" in status['running'] and status['starting'] == [] and status['loading'] == [] + + +def run_editor(gui, scene_module, monkeypatch): + """Starts the theme editor scene and returns (its url, the scene), stopped again.""" + import time + + scene = scene_module.ThemeServerScene(gui) + gui.push_scene('theme-editor', scene) + proc = scene.proc + try: + end = time.monotonic() + 120 + while not gui.text_data.get('theme_server.url') and proc.poll() is None: + gui.do_loop(no_delay=True) + assert time.monotonic() < end + + return gui.text_data['theme_server.url'], scene + + finally: + with monkeypatch.context() as patch: + patch.setattr(gui.events, "was_pressed", lambda button: button == 'B') + gui.do_loop(no_delay=True) + + try: + proc.wait(timeout=30) + except Exception: + proc.kill() + + +@pytest.mark.sdl +def test_theme_editor_keeps_its_port(gui, hm_dirs, monkeypatch): + ## The same address every time; a saved port that's taken is swapped for a free one. + import harbourmaster + from pugwash.scenes import theme_server as scene_module + + monkeypatch.setattr(scene_module, "get_ip_address", lambda force_update=False: "127.0.0.1") + gui.hm = harbourmaster.HarbourMaster({'offline': True, 'no-check': True}, callback=gui, **hm_dirs) + try: + first, _ = run_editor(gui, scene_module, monkeypatch) + again, _ = run_editor(gui, scene_module, monkeypatch) + assert first == again + + busy = busy_port_in_range() + try: + taken = busy.getsockname()[1] + gui.hm.cfg_data['theme_server_port'] = taken + moved, _ = run_editor(gui, scene_module, monkeypatch) + finally: + busy.close() + + ## The next free one, still in the range. + moved_port = int(moved.rsplit(':', 1)[1].strip('/')) + assert moved_port != taken + assert scene_module.PORT_RANGE[0] <= moved_port <= scene_module.PORT_RANGE[1] + assert gui.hm.cfg_data['theme_server_port'] == moved_port + + finally: + gui.hm = None From 964cd7dfd64cece73eb427c9904c60dcca9f7945 Mon Sep 17 00:00:00 2001 From: kloptops Date: Mon, 19 Oct 2026 20:05:29 +0800 Subject: [PATCH 18/28] Port links: the website and store pages of a port, as QR codes X on the port info screen opens a port's links: its page on the PortMaster website (ports from the PortMaster and Multiverse sources) and every store in its port.json, the game's page and the developer's, each shown as a QR code and its address, to open on a phone. port.json "store" names are typed by hand ("Itch", "itch.io", "GoG ", "Gamejolt", ...), so they're tidied into one name per store; entries that aren't http(s) links are skipped. The default theme has a port_links section, with a smaller QR and text at 320x240. Themes without one get a plain built-in screen in PortMaster's own font, as the theme editor's screen does (plain_font() is shared now). The theme designer lists the screen, using a port with several links. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 2 +- PortMaster/pylibs/default_theme/theme.json | 57 ++++++ PortMaster/pylibs/locales/strings.json | 67 ++++--- PortMaster/pylibs/locales/strings.themes.json | 15 +- PortMaster/pylibs/pugwash/app_ports.py | 100 +++++++++++ PortMaster/pylibs/pugwash/scenes/__init__.py | 4 + PortMaster/pylibs/pugwash/scenes/base.py | 19 ++ PortMaster/pylibs/pugwash/scenes/port_info.py | 32 ++++ .../pylibs/pugwash/scenes/port_links.py | 75 ++++++++ .../pylibs/pugwash/scenes/theme_server.py | 15 +- PortMaster/pylibs/theme_server/catalogue.py | 1 + PortMaster/pylibs/theme_server/shots.py | 28 +++ THEME.md | 4 +- tests/test_port_links.py | 166 ++++++++++++++++++ tests/test_theme_server.py | 2 +- 15 files changed, 541 insertions(+), 46 deletions(-) create mode 100644 PortMaster/pylibs/pugwash/scenes/port_links.py create mode 100644 tests/test_port_links.py diff --git a/CLAUDE.md b/CLAUDE.md index 4fc72b0c..5f90bff0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,7 +94,7 @@ Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is f ## Architecture -**Import setup**: the scripts prepend `pylibs/` and `exlibs/` to `sys.path`. `exlibs/` holds vendored third-party packages (loguru, requests, urllib3, certifi, `sdl2`, `png`, `qrcode`, ...), and `pylibs/` holds the project packages. A theme `image` of `qr:` is a QR code (`sdl/images.py`, `tests/test_qr.py`). Don't edit vendored code unless that's the point of the change. There are no star imports and no injected builtins: import names explicitly. +**Import setup**: the scripts prepend `pylibs/` and `exlibs/` to `sys.path`. `exlibs/` holds vendored third-party packages (loguru, requests, urllib3, certifi, `sdl2`, `png`, `qrcode`, ...), and `pylibs/` holds the project packages. A theme `image` of `qr:` is a QR code (`sdl/images.py`, `tests/test_qr.py`). X on the port info screen opens `scenes/port_links.py`: the port's website page and its `port.json` stores (`app_ports.port_links()`, which tidies the hand-typed store names), each as a QR code (`tests/test_port_links.py`). Don't edit vendored code unless that's the point of the change. There are no star imports and no injected builtins: import names explicitly. **`harbourmaster` library** (`pylibs/harbourmaster/`): - `harbour.py`: `HarbourMaster`, the engine both the GUI and CLI drive. It holds `__init__`, config, sources and GCD modes. Its other methods live in mixins grouped by job: diff --git a/PortMaster/pylibs/default_theme/theme.json b/PortMaster/pylibs/default_theme/theme.json index 378aa591..82746ac4 100644 --- a/PortMaster/pylibs/default_theme/theme.json +++ b/PortMaster/pylibs/default_theme/theme.json @@ -431,6 +431,63 @@ }, "#element:button_bar": {} }, + "port_links": { + "#element:background": {}, + "links_list": { + "parent": "port_info_list_area", + "area": [ 0.0, 0.0, 1.0, 1.0 ], + "border-x": 16, + "font-size": 17, + "font-size[427:240]": 22, + "font-size[16:9]": 19, + "font-size[1920x1152]": 22, + "alt-fill": "secondary-selection-fill", + "select-fill": "selection-fill", + "font-color": "list_text", + "select-color": "list_selected", + "text-clip": true, + "text-wrap": false, + "autoscroll": "slide", + "scroll-speed": 30, + "scroll-delay-start": 500, + "scroll-delay-end": 500 + }, + "links_panel": { + "parent": "port_info_area", + "area": [ 0.0, 0.0, 1.0, 1.0 ], + "fill": "panels" + }, + "links_title": { + "parent": "port_info_title_area", + "area": [ 0.0, 0.0, 1.0, 1.0 ], + "text": "{port_info.title}", + "font-size": 22, + "font-color": "list_selected", + "text-clip": true, + "align": "center" + }, + "link_qr": { + "parent": "port_info_area", + "area": [ 0.05, 0.11, 0.95, 0.74 ], + "area[320x240]": [ 0.1, 0.11, 0.9, 0.6 ], + "image": "qr:{port_links.url}", + "image-mode": "fit" + }, + "link_info": { + "parent": "port_info_area", + "area": [ 0.03, 0.76, 0.97, 0.99 ], + "area[320x240]": [ 0.02, 0.61, 0.98, 0.86 ], + "text": "{port_links.name}\n{port_links.url}", + "font-size": 15, + "font-size[320x240]": 10, + "font-size[427:240]": 18, + "font-size[1920x1152]": 20, + "font-color": "general_font", + "text-wrap": true, + "align": "center" + }, + "#element:button_bar": {} + }, "main_menu": { "#element:background": {}, "#element:option_list": { diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json index 987e7c61..bf86e5cd 100644 --- a/PortMaster/pylibs/locales/strings.json +++ b/PortMaster/pylibs/locales/strings.json @@ -149,13 +149,13 @@ ], "Not Installed": [ "pylibs/harbourmaster/hm_runtimes.py:127", - "pylibs/pugwash/app_ports.py:93", + "pylibs/pugwash/app_ports.py:193", "pylibs/pugwash/scenes/filters.py:69", "pylibs/pugwash/scenes/runtimes.py:93" ], "Update Available": [ "pylibs/harbourmaster/hm_runtimes.py:128", - "pylibs/pugwash/app_ports.py:92", + "pylibs/pugwash/app_ports.py:192", "pylibs/pugwash/scenes/filters.py:70" ], "Verified": [ @@ -267,7 +267,7 @@ ], "N/A": [ "pylibs/pugwash/app.py:406", - "pylibs/pugwash/app_ports.py:70" + "pylibs/pugwash/app_ports.py:170" ], "Fetching latest gameinfo.xml files and cover images.": [ "pylibs/pugwash/app_commands.py:47" @@ -291,24 +291,30 @@ "pylibs/pugwash/app_fifo.py:356", "pylibs/pugwash/autoinstall.py:168" ], + "PortMaster website": [ + "pylibs/pugwash/app_ports.py:74" + ], + "{store}: developer": [ + "pylibs/pugwash/app_ports.py:90" + ], "** NO PORT **": [ - "pylibs/pugwash/app_ports.py:33" + "pylibs/pugwash/app_ports.py:129" ], "Ready to Run": [ - "pylibs/pugwash/app_ports.py:52", + "pylibs/pugwash/app_ports.py:149", "pylibs/pugwash/scenes/filters.py:66", "pylibs/pugwash/scenes/main_menu.py:96" ], "Requires Files": [ - "pylibs/pugwash/app_ports.py:52" + "pylibs/pugwash/app_ports.py:149" ], "Installed": [ - "pylibs/pugwash/app_ports.py:64", - "pylibs/pugwash/app_ports.py:91", + "pylibs/pugwash/app_ports.py:164", + "pylibs/pugwash/app_ports.py:191", "pylibs/pugwash/scenes/runtimes.py:93" ], "Missing": [ - "pylibs/pugwash/app_ports.py:66" + "pylibs/pugwash/app_ports.py:166" ], "Installation failed, see log.txt for details.": [ "pylibs/pugwash/autoinstall.py:101" @@ -432,8 +438,9 @@ "pylibs/pugwash/scenes/options.py:164", "pylibs/pugwash/scenes/port_info.py:36", "pylibs/pugwash/scenes/port_info.py:39", - "pylibs/pugwash/scenes/port_info.py:129", - "pylibs/pugwash/scenes/port_info.py:131", + "pylibs/pugwash/scenes/port_info.py:138", + "pylibs/pugwash/scenes/port_info.py:140", + "pylibs/pugwash/scenes/port_links.py:51", "pylibs/pugwash/scenes/ports.py:241", "pylibs/pugwash/scenes/ports.py:267", "pylibs/pugwash/scenes/ports.py:269", @@ -707,7 +714,7 @@ ], "Theme Editor": [ "pylibs/pugwash/scenes/options.py:48", - "pylibs/pugwash/scenes/theme_server.py:64" + "pylibs/pugwash/scenes/theme_server.py:70" ], "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": [ "pylibs/pugwash/scenes/options.py:49" @@ -909,30 +916,38 @@ ], "Reinstall": [ "pylibs/pugwash/scenes/port_info.py:36", - "pylibs/pugwash/scenes/port_info.py:129" + "pylibs/pugwash/scenes/port_info.py:138" ], "Uninstall": [ "pylibs/pugwash/scenes/port_info.py:36", - "pylibs/pugwash/scenes/port_info.py:129", + "pylibs/pugwash/scenes/port_info.py:138", "pylibs/pugwash/scenes/runtimes.py:109" ], "Install": [ "pylibs/pugwash/scenes/port_info.py:39", - "pylibs/pugwash/scenes/port_info.py:131", + "pylibs/pugwash/scenes/port_info.py:140", "pylibs/pugwash/scenes/runtimes.py:112" ], + "Links": [ + "pylibs/pugwash/scenes/port_info.py:42", + "pylibs/pugwash/scenes/port_info.py:143", + "pylibs/pugwash/scenes/port_links.py:28" + ], "Port Info": [ - "pylibs/pugwash/scenes/port_info.py:63" + "pylibs/pugwash/scenes/port_info.py:72" ], "Show Info": [ - "pylibs/pugwash/scenes/port_info.py:96", - "pylibs/pugwash/scenes/port_info.py:101", + "pylibs/pugwash/scenes/port_info.py:105", + "pylibs/pugwash/scenes/port_info.py:110", "pylibs/pugwash/scenes/ports.py:241", "pylibs/pugwash/scenes/ports.py:267", "pylibs/pugwash/scenes/ports.py:269" ], "Are you sure you want to uninstall {port_name}?": [ - "pylibs/pugwash/scenes/port_info.py:151" + "pylibs/pugwash/scenes/port_info.py:176" + ], + "No links for this port.": [ + "pylibs/pugwash/scenes/port_links.py:46" ], "NO PORTS": [ "pylibs/pugwash/scenes/ports.py:34", @@ -1007,25 +1022,25 @@ "pylibs/pugwash/scenes/startup.py:212" ], "Starting the theme editor...": [ - "pylibs/pugwash/scenes/theme_server.py:73" + "pylibs/pugwash/scenes/theme_server.py:79" ], "Stop": [ - "pylibs/pugwash/scenes/theme_server.py:75" + "pylibs/pugwash/scenes/theme_server.py:81" ], "it stopped": [ - "pylibs/pugwash/scenes/theme_server.py:109" + "pylibs/pugwash/scenes/theme_server.py:115" ], "Unable to start the theme editor: {error}": [ - "pylibs/pugwash/scenes/theme_server.py:122" + "pylibs/pugwash/scenes/theme_server.py:128" ], "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": [ - "pylibs/pugwash/scenes/theme_server.py:129" + "pylibs/pugwash/scenes/theme_server.py:140" ], "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": [ - "pylibs/pugwash/scenes/theme_server.py:138" + "pylibs/pugwash/scenes/theme_server.py:149" ], "The theme editor stopped. See theme_server.txt in the PortMaster folder.": [ - "pylibs/pugwash/scenes/theme_server.py:167" + "pylibs/pugwash/scenes/theme_server.py:178" ], "{theme_name} (Selected)": [ "pylibs/pugwash/scenes/themes.py:31" diff --git a/PortMaster/pylibs/locales/strings.themes.json b/PortMaster/pylibs/locales/strings.themes.json index 52e554ad..4587c10c 100644 --- a/PortMaster/pylibs/locales/strings.themes.json +++ b/PortMaster/pylibs/locales/strings.themes.json @@ -31,8 +31,8 @@ "ff_VII/theme.json:523", "Zelda/theme.json:551", "Stardew/theme.json:651", - "default_theme/theme.json:510", - "default_theme/theme.json:510" + "default_theme/theme.json:567", + "default_theme/theme.json:567" ], "Creator: {theme_info.creator}\nStatus: {theme_info.status}": [ "Fallout/theme.json:831", @@ -40,7 +40,7 @@ "ff_VII/theme.json:612", "Zelda/theme.json:633", "Stardew/theme.json:739", - "default_theme/theme.json:560" + "default_theme/theme.json:617" ], "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": [ "Stardew/theme.json:613" @@ -64,14 +64,17 @@ "default_theme/theme.json:353", "default_theme/theme.json:353" ], + "{port_links.name}\n{port_links.url}": [ + "default_theme/theme.json:480" + ], "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": [ "ff_VII/theme.json:480", "Stardew/theme.json:605", - "default_theme/theme.json:450" + "default_theme/theme.json:507" ], "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": [ "ff_VII/theme.json:488", - "default_theme/theme.json:458" + "default_theme/theme.json:515" ], "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": [ "hiviz/theme.json:267" @@ -87,7 +90,7 @@ ], "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": [ "hiviz/theme.json:526", - "default_theme/theme.json:653" + "default_theme/theme.json:710" ], "{ports_list.filter_ports} games": [ "jimmyhat999_grid_dark/theme.json:239", diff --git a/PortMaster/pylibs/pugwash/app_ports.py b/PortMaster/pylibs/pugwash/app_ports.py index efe673c9..55e75595 100644 --- a/PortMaster/pylibs/pugwash/app_ports.py +++ b/PortMaster/pylibs/pugwash/app_ports.py @@ -3,10 +3,95 @@ # PortMasterGUI PortInfoMixin. import os +import urllib.parse import harbourmaster from harbourmaster.i18n import _ +## The port's page on the website, for ports from the PortMaster and Multiverse sources. +WEBSITE_PORT_URL = "https://portmaster.games/detail.html?name={name}" + +## port.json "store" names are typed by hand: "Itch", "itch.io", "GoG", "Steam ", ... +STORE_NAMES = { + 'steam': "Steam", + 'itch': "itch.io", + 'itchio': "itch.io", + 'gog': "GOG", + 'gogcom': "GOG", + 'gamejolt': "Game Jolt", + 'playstore': "Google Play", + 'googleplay': "Google Play", + 'appstore': "App Store", + 'humble': "Humble Bundle", + 'humblebundle': "Humble Bundle", + 'github': "GitHub", + 'dlsite': "DLsite", + 'epic': "Epic Games Store", + 'epicgames': "Epic Games Store", + 'epicgamesstore': "Epic Games Store", + } + + +def store_name(name, url): + key = ''.join(char for char in name.casefold() if char.isalnum()) + if key in STORE_NAMES: + return STORE_NAMES[key] + + if name.strip() != '': + return name.strip() + + host = urllib.parse.urlsplit(url).hostname or url + if host.startswith('www.'): + host = host[4:] + + return host + + +def port_links(port_name, port_info, website=False): + """ + The links for a port, as a list of (label, kind, url): the PortMaster website first (if + `website`), then each of its stores, the game's page and then the developer's. + """ + links = [] + seen = set() + + def add(label, kind, url): + if not isinstance(url, str): + return + + url = url.strip() + if not url.casefold().startswith(('http://', 'https://')) or url in seen: + return + + seen.add(url) + links.append((label, kind, url)) + + if website: + name = harbourmaster.name_cleaner(port_name) + if name.endswith('.zip'): + name = name[:-4] + + add(_("PortMaster website"), 'website', WEBSITE_PORT_URL.format(name=urllib.parse.quote(name, safe=''))) + + stores = (port_info or {}).get('attr', {}).get('store', None) + if not isinstance(stores, list): + stores = [] + + for store in stores: + if not isinstance(store, dict): + continue + + game_url = store.get('gameurl') or store.get('url') or '' + dev_url = store.get('developerurl') or '' + name = store.get('name') if isinstance(store.get('name'), str) else '' + label = store_name(name, isinstance(game_url, str) and game_url or str(dev_url)) + + add(label, 'store', game_url) + add(_("{store}: developer").format(store=label), 'developer', dev_url) + + return links + + class PortInfoMixin: """ PortMasterGUI methods: Port details for the template data: images, info and installed size. @@ -25,6 +110,17 @@ def get_port_image(self, port_name): return image + def get_port_links(self, port_name, port_info): + """port_links() for a port, with the website link if it's from an official source.""" + website = False + official_urls = set(harbourmaster.HarbourMaster.LEGACY_SOURCE_URLS.values()) + for source in getattr(self.hm, 'sources', {}).values(): + if source._config.get('url') in official_urls and source.clean_name(port_name) in source.ports: + website = True + break + + return port_links(port_name, port_info, website=website) + def set_port_info(self, port_name, port_info, want_install_size=False): ## TODO: make this better :D if port_name is None: @@ -39,6 +135,7 @@ def set_port_info(self, port_name, port_info, want_install_size=False): self.set_data("port_info.runtime", "") self.set_data("port_info.download_size", "") self.set_data("port_info.install_size", "") + self.set_data("port_info.links", "") # self.set_data("port_info.image", "no-image") return @@ -50,6 +147,9 @@ def set_port_info(self, port_name, port_info, want_install_size=False): self.set_data("port_info.genres", ', '.join(port_info['attr']['genres'])) self.set_data("port_info.porter", harbourmaster.oc_join(port_info['attr']['porter'])) self.set_data("port_info.ready_to_run", port_info['attr']['rtr'] and _("Ready to Run") or _("Requires Files")) + ## How many links the port has (empty for none, for {if:port_info.links}). + links = len(self.get_port_links(port_name, port_info)) + self.set_data("port_info.links", links and str(links) or "") # self.set_data("port_info.image", port_image) runtimes = port_info['attr']['runtime'] diff --git a/PortMaster/pylibs/pugwash/scenes/__init__.py b/PortMaster/pylibs/pugwash/scenes/__init__.py index 25dc19d6..251a323e 100644 --- a/PortMaster/pylibs/pugwash/scenes/__init__.py +++ b/PortMaster/pylibs/pugwash/scenes/__init__.py @@ -50,6 +50,9 @@ PortInfoPopup, PortInfoScene, ) +from .port_links import ( + PortLinksScene, + ) from .filters import ( FiltersScene, ) @@ -76,6 +79,7 @@ 'OnScreenKeyboard', 'OptionScene', 'PortInfoScene', + 'PortLinksScene', 'PortsListScene', 'RuntimesScene', 'SourceScene', diff --git a/PortMaster/pylibs/pugwash/scenes/base.py b/PortMaster/pylibs/pugwash/scenes/base.py index 3489529a..291836af 100644 --- a/PortMaster/pylibs/pugwash/scenes/base.py +++ b/PortMaster/pylibs/pugwash/scenes/base.py @@ -3,11 +3,30 @@ # StringFormatter (the {tag} template text) and BaseScene, which loads a theme section into regions. import functools +import json from harbourmaster.i18n import theme_text from pugwash import sdl from loguru import logger +def plain_font(): + """ + PortMaster's own font for the language (the default theme's "font[ja_JP]" & co), for the + sections a theme doesn't have, where the text has to be readable whatever the theme. + """ + from pugwash import PYLIB_PATH, lang + + font = "DejaVuSans.ttf" + try: + with open(str(PYLIB_PATH / "default_theme" / "theme.json"), encoding="utf-8") as fh: + base = json.load(fh).get("#base", {}) + font = base.get(f"font[{lang.CURRENT_LANG}]") or base.get("font") or font + except (OSError, ValueError, AttributeError): + pass + + return font + + class StringFormatter: def __init__(self, data_dict): self.data_dict = data_dict diff --git a/PortMaster/pylibs/pugwash/scenes/port_info.py b/PortMaster/pylibs/pugwash/scenes/port_info.py index 28e98468..f254a1a7 100644 --- a/PortMaster/pylibs/pugwash/scenes/port_info.py +++ b/PortMaster/pylibs/pugwash/scenes/port_info.py @@ -38,6 +38,9 @@ def update_selection(self): else: buttons.update({'A': _('Install'), 'B': _('Back')}) + if self.parent_info_scene.has_links(): + buttons['X'] = _('Links') + self.port_name = self.gui.get_data("port_info.name") self.set_buttons(buttons) @@ -47,7 +50,13 @@ def do_update(self, events): if self.port_name != self.gui.get_data("port_info.name"): self.update_selection() + if events.was_pressed('X') and self.parent_info_scene.show_links(): + return True + for action in self.config_buttons(events): + if action == "port_links" and self.parent_info_scene.show_links(): + return True + if action == "pop_scene": self.parent_info_scene.popup_shown = False self.button_back() @@ -130,10 +139,26 @@ def update_port(self): else: buttons.update({'A': _('Install'), 'B': _('Back')}) + if self.has_links(): + buttons['X'] = _('Links') + self.set_buttons(buttons) self.ready = True + def has_links(self): + return self.gui.get_data("port_info.links") not in ("", None) + + def show_links(self): + """The port's links (website, stores) with QR codes, if it has any.""" + if not self.has_links(): + return False + + from .port_links import PortLinksScene + self.button_activate() + self.gui.push_scene('port_links', PortLinksScene(self.gui, self.port_name, self.port_info)) + return True + def do_update(self, events): super().do_update(events) @@ -159,7 +184,14 @@ def do_update(self, events): self.gui.pop_scene() return True + if events.was_pressed('X') and self.show_links(): + return True + for action in self.config_buttons(events): + if action == "port_links": + self.show_links() + return True + if action == "port_info_popup": if 'port_info_popup' in self.gui.theme_data: if self.popup_shown: diff --git a/PortMaster/pylibs/pugwash/scenes/port_links.py b/PortMaster/pylibs/pugwash/scenes/port_links.py new file mode 100644 index 00000000..4ea1e048 --- /dev/null +++ b/PortMaster/pylibs/pugwash/scenes/port_links.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: MIT +# +# A port's links: its page on the PortMaster website and its stores, each shown as a QR code +# and an address, so it can be opened on a phone. Opened with X from the port info screen. + +from harbourmaster.i18n import _ +from .base import BaseScene, plain_font + + +def fallback_section(): + """For themes without a "port_links" section: plain, in PortMaster's own font.""" + text = {"font": plain_font(), "font-color": "#e8e8ec", "font-size": 16} + return { + "background": {"area": [0.0, 0.0, 1.0, 1.0], "fill": "#1c1c22"}, + "links_title": {**text, "area": [0.04, 0.02, 0.96, 0.12], "font-size": 24, "align": "center", + "text-clip": True, "text": "{port_info.title}"}, + "links_list": {**text, "area": [0.04, 0.15, 0.44, 0.95], "select-fill": "#3c3c48", + "select-color": "#ffffff", "text-clip": True}, + "link_qr": {"area": [0.5, 0.15, 0.96, 0.7], "image": "qr:{port_links.url}", "image-mode": "fit"}, + "link_info": {**text, "area": [0.5, 0.72, 0.96, 0.98], "font-size": 14, "text-wrap": True, + "align": "center", "text": "{port_links.name}\n{port_links.url}"}, + } + + +class PortLinksScene(BaseScene): + def __init__(self, gui, port_name, port_info): + super().__init__(gui) + self.scene_title = _("Links") + + self.links = self.gui.get_port_links(port_name, port_info) + + if "port_links" not in self.gui.theme_data: + self.gui.theme_data["port_links"] = fallback_section() + + self.gui.set_data("port_links.count", str(len(self.links))) + self.last_select = None + self.show_link(0) + + self.load_regions("port_links", ['links_list']) + + self.tags['links_list'].reset_options() + for index, (label, kind, url) in enumerate(self.links): + self.tags['links_list'].add_option(index, label) + + if len(self.links) == 0: + self.tags['links_list'].add_option(None, _("No links for this port.")) + + self.tags['links_list'].list_select(0) + self.last_select = self.tags['links_list'].selected_option() + + self.set_buttons({'B': _('Back')}) + + def show_link(self, index): + if index is None or not (0 <= index < len(self.links)): + label, kind, url = "", "", "" + else: + label, kind, url = self.links[index] + + self.gui.set_data("port_links.name", label) + self.gui.set_data("port_links.kind", kind) + self.gui.set_data("port_links.url", url) + + def do_update(self, events): + super().do_update(events) + + if self.tags['links_list'].selected_option() != self.last_select: + self.last_select = self.tags['links_list'].selected_option() + self.show_link(self.last_select) + + if events.was_pressed('B'): + self.button_back() + self.gui.pop_scene() + return True + + return False diff --git a/PortMaster/pylibs/pugwash/scenes/theme_server.py b/PortMaster/pylibs/pugwash/scenes/theme_server.py index d0798dfa..180e0456 100644 --- a/PortMaster/pylibs/pugwash/scenes/theme_server.py +++ b/PortMaster/pylibs/pugwash/scenes/theme_server.py @@ -13,9 +13,9 @@ from loguru import logger from harbourmaster.i18n import _ -from pugwash import PYLIB_PATH, lang +from pugwash import PYLIB_PATH from pugwash.util import get_ip_address -from .base import BaseScene +from .base import BaseScene, plain_font ## The dynamic (private) port range: never assigned to a service, so no CFW's own daemons @@ -51,15 +51,8 @@ def server_port(cfg_data): def fallback_section(): """For themes without a "theme_server" section: plain, in PortMaster's own font for the - language (the default theme's "font[ja_JP]" & co), as the address has to be readable.""" - font = "DejaVuSans.ttf" - try: - with open(str(PYLIB_PATH / "default_theme" / "theme.json"), encoding="utf-8") as fh: - base = json.load(fh).get("#base", {}) - font = base.get(f"font[{lang.CURRENT_LANG}]") or base.get("font") or font - except (OSError, ValueError, AttributeError): - pass - + language, as the address has to be readable.""" + font = plain_font() text = {"font": font, "font-color": "#e8e8ec", "font-size": 18} return { "background": {"area": [0.0, 0.0, 1.0, 1.0], "fill": "#1c1c22"}, diff --git a/PortMaster/pylibs/theme_server/catalogue.py b/PortMaster/pylibs/theme_server/catalogue.py index 091a5c4c..20ce008c 100644 --- a/PortMaster/pylibs/theme_server/catalogue.py +++ b/PortMaster/pylibs/theme_server/catalogue.py @@ -11,6 +11,7 @@ ("ports_manage", "Manage ports"), ("port_info", "Port info"), ("port_info_popup", "Port info popup"), + ("port_links", "Port links (store and website QR codes)"), ("filters", "Filters"), ("featured", "Featured ports"), ("featured_list", "Featured ports list"), diff --git a/PortMaster/pylibs/theme_server/shots.py b/PortMaster/pylibs/theme_server/shots.py index 2eec3ae6..8b040d08 100644 --- a/PortMaster/pylibs/theme_server/shots.py +++ b/PortMaster/pylibs/theme_server/shots.py @@ -141,6 +141,34 @@ def shot_port_info_popup(gui): gui.push_scene('port_info', PortInfoPopup(gui, info_scene)) +def _links_port(gui): + """A port with a few links (website and stores): one of PORT_INFO_PORTS if it has, or + the port with the most.""" + def links(name): + return len(gui.get_port_links(name, gui.hm.port_info(name))) + + for name in PORT_INFO_PORTS: + if gui.hm.port_info(name) is not None and links(name) >= 3: + return name + + return max(gui.hm.list_ports(), key=links, default=None) or _info_port(gui) + + +def shot_port_links(gui): + name = _links_port(gui) + list_scene = _ports_list(gui, 'install') + info_scene = pug_scenes.PortInfoScene(gui, name, 'install', list_scene) + gui.push_scene('port_info', info_scene) + gui.push_scene('port_links', pug_scenes.PortLinksScene(gui, name, info_scene.port_info)) + + ## A store, not the website, so a store's address shows. + scene = gui.scenes[-1][1][-1] + if len(scene.links) > 1: + scene.tags['links_list'].list_select(1) + scene.last_select = 1 + scene.show_link(1) + + def shot_filters(gui): list_scene = _ports_list(gui, 'install') gui.push_scene('ports', pug_scenes.FiltersScene(gui, list_scene)) diff --git a/THEME.md b/THEME.md index 9ed1390a..7b9666d5 100644 --- a/THEME.md +++ b/THEME.md @@ -164,7 +164,8 @@ Currently there are the following scenes: - `main_menu`: The main menu - `option_menu`: Options screen - `ports_list`: The list of ports -- `port_info`: Detailed port information. +- `port_info`: Detailed port information. X opens the port's links (`port_links`) when it has any; a theme that maps buttons in `#config` can use the action `port_links`. +- `port_links`: A port's links, its page on the PortMaster website (ports from the PortMaster and Multiverse sources) and the stores in its `port.json`, each as a QR code. The list is the region tagged `links_list`. The selected link is in `{port_links.name}` ("Steam", "itch.io", "PortMaster website", "Steam: developer", ...), `{port_links.url}` and `{port_links.kind}` (`website`, `store` or `developer`); `{port_links.count}` is how many there are. Show the QR code with `"image": "qr:{port_links.url}"`. A theme without this section gets a plain screen in PortMaster's own font. - `message_window`: A scrolling list of messages, used during downloading, installation, and fetching the latest ports information. - `message_box`: An alert box - `filter_list`: Available filters @@ -653,6 +654,7 @@ The format is: - port_info.install_size - port_info.runtime - port_info.runtime_status +- port_info.links: how many links the port has (the `port_links` screen), empty if none, so `{if:port_info.links}Press X for links{endif}` works. ## Ports List tags diff --git a/tests/test_port_links.py b/tests/test_port_links.py new file mode 100644 index 00000000..55ae097f --- /dev/null +++ b/tests/test_port_links.py @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: MIT +# +# A port's links (its PortMaster website page and its stores) with QR codes: the list +# port_links() makes from port.json "store" entries, and the screen X opens on port info. + +import pytest + + +def links(store, website=False, name="game.zip"): + from pugwash.app_ports import port_links + return port_links(name, {'attr': {'store': store}}, website=website) + + +def test_store_links(): + result = links([ + {'name': 'Steam', 'gameurl': 'https://store.steampowered.com/app/1/', 'developerurl': 'https://dev.example/'}, + {'name': 'Itch', 'gameurl': 'https://dev.itch.io/game', 'developerurl': ''}, + {'name': 'GoG ', 'url': 'https://www.gog.com/game/game'}, + ]) + + assert result == [ + ("Steam", 'store', 'https://store.steampowered.com/app/1/'), + ("Steam: developer", 'developer', 'https://dev.example/'), + ("itch.io", 'store', 'https://dev.itch.io/game'), + ("GOG", 'store', 'https://www.gog.com/game/game'), + ] + + +def test_website_link_first(): + ## The port's name as PortMaster cleans it, which is the name the website uses. + result = links([{'name': 'Steam', 'gameurl': 'https://store.steampowered.com/app/1/'}], website=True, name="Cave Story+.zip") + assert result[0] == ("PortMaster website", 'website', "https://portmaster.games/detail.html?name=cave.story") + assert result[1][0] == "Steam" + + +def test_bad_store_entries(): + result = links([ + "Steam", + None, + {'name': 'Steam', 'gameurl': ''}, + {'name': 'Steam', 'gameurl': 'store.steampowered.com/app/1/'}, + {'name': 'Steam', 'gameurl': 'javascript:alert(1)'}, + {'name': 'Steam', 'gameurl': 42}, + {'name': '', 'gameurl': 'https://www.example.com/game'}, + {'name': 'Unknown Shop', 'gameurl': 'https://www.example.com/game'}, + {'name': 'Mine', 'gameurl': 'https://mine.example/game'}, + ]) + + ## Unknown names as they are, no name: the host, a link only once. + assert result == [ + ("example.com", 'store', 'https://www.example.com/game'), + ("Mine", 'store', 'https://mine.example/game'), + ] + + assert links("not a list") == [] + from pugwash.app_ports import port_links + assert port_links("game.zip", None) == [] + assert port_links("game.zip", {'attr': {}}) == [] + + +def test_store_names(): + from pugwash.app_ports import store_name + for name in ("itch", "Itch.io", "ITCH.IO", " itch "): + assert store_name(name, "") == "itch.io" + + assert store_name("GameJolt", "") == "Game Jolt" + assert store_name("Play Store", "") == "Google Play" + assert store_name("STEAM", "") == "Steam" + + +################################################################################ +## The screen. +@pytest.fixture +def links_gui(gui, online_hm): + gui.hm = online_hm + yield gui + while len(gui.scenes) > 1: + gui.pop_scene() + + gui.hm = None + + +def port_info_scene(gui, port_name): + from pugwash import scenes + list_scene = scenes.PortsListScene(gui, {'mode': 'install', 'base_filters': [], 'name': None}) + gui.push_scene('ports', list_scene) + info_scene = scenes.PortInfoScene(gui, port_name, 'install', list_scene) + gui.push_scene('port_info', info_scene) + return info_scene + + +def bar_labels(scene): + """The button bar's labels, it keeps them as (rect, label) once laid out.""" + return [item[1] if isinstance(item, tuple) else item for item in scene.tags['button_bar'].bar] + + +def press(gui, monkeypatch, button): + with monkeypatch.context() as patch: + patch.setattr(gui.events, "was_pressed", lambda pressed: pressed == button) + gui.do_loop(no_delay=True) + + gui.do_loop(no_delay=True) + + +@pytest.mark.sdl +def test_links_screen(links_gui, monkeypatch): + gui = links_gui + info_scene = port_info_scene(gui, "sprout.zip") + + ## The official source: the website, then Steam (game and developer). + assert gui.get_data("port_info.links") == "3" + assert "Links" in bar_labels(info_scene) + + press(gui, monkeypatch, 'X') + assert gui.scene_list()[-1] == "port_links" + + scene = gui.scenes[-1][1][-1] + assert scene.tags['links_list'].list == ["PortMaster website", "Steam", "Steam: developer"] + assert gui.get_data("port_links.url") == "https://portmaster.games/detail.html?name=sprout" + assert gui.images.load("qr:" + gui.get_data("port_links.url")) is not None + + press(gui, monkeypatch, 'DOWN') + assert gui.get_data("port_links.name") == "Steam" + assert gui.get_data("port_links.url") == "https://store.steampowered.com/app/1336340/SPROUT/" + + press(gui, monkeypatch, 'B') + assert gui.scene_list()[-1] == "port_info" + + +@pytest.mark.sdl +def test_no_links(links_gui, monkeypatch): + gui = links_gui + from pugwash.app_ports import port_links + + ## starfighter has no stores; not from an official source, so no website link either. + for source in gui.hm.sources.values(): + monkeypatch.setitem(source._config, 'url', "https://example.com/ports.json") + + info_scene = port_info_scene(gui, "starfighter.zip") + assert port_links("starfighter.zip", info_scene.port_info) == [] + assert gui.get_data("port_info.links") == "" + assert "Links" not in bar_labels(info_scene) + + press(gui, monkeypatch, 'X') + assert gui.scene_list()[-1] == "port_info" + + +@pytest.mark.sdl +def test_theme_without_links_section(links_gui, monkeypatch, tmp_path): + ## Themes made before port_links get a plain built-in screen. + gui = links_gui + from pugwash import scenes + + info_scene = port_info_scene(gui, "sprout.zip") + monkeypatch.delitem(gui.theme_data, "port_links") + gui.push_scene('port_links', scenes.PortLinksScene(gui, "sprout.zip", info_scene.port_info)) + + for _ in range(3): + gui.updated = True + gui.do_loop(no_delay=True) + + gui.create_screenshot(str(tmp_path / "links.png")) + assert (tmp_path / "links.png").stat().st_size > 0 + from pugwash.scenes.base import plain_font + assert gui.theme_data["port_links"]["links_list"]["font"] == plain_font() + assert gui.scenes[-1][1][-1].tags['links_list'].list[0] == "PortMaster website" diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 9fcdacae..260d23cb 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -153,7 +153,7 @@ def test_every_screen_renders(pool, resolution): for shot, _ in pool.shots(): png, info = pool.render("default_theme", None, resolution, "en_US", shot) assert info['errors'] == [], (shot, info['errors']) - if shot in ("port_info", "port_info_popup", "featured", "featured_list"): + if shot in ("port_info", "port_info_popup", "port_links", "featured", "featured_list"): ## No ports or featured lists in an empty cache (and the default theme has no popup section). assert info['missing'] and png == b"" continue From 712c796978244a08507b3339e24cc14616aca31d Mon Sep 17 00:00:00 2001 From: kloptops Date: Tue, 20 Oct 2026 04:49:51 +0800 Subject: [PATCH 19/28] Old themes: port links and theme editor screens made from their themes list Themes made before the port links and theme editor screens showed a plain built-in screen for both, and custom themes that ship with CFWs (or are on people's devices) won't be updated. When a theme has no "port_links" or "theme_server" section, one is now made from its "themes_list" section as it loads: its list, panels and fonts, the QR code where the theme screenshot goes, the address where the description goes (the editor's instructions where the list goes, a size smaller). A font with capitals only (Doom's) is swapped for the list's, so addresses read right. A themes list without a screenshot region (basic_theme) still gets the plain screen. Checked by rendering the themes as published before (the nine in PortMaster-Themes, and the MinUI theme PakUI and TRIMUI ship) at 480x320 to 1280x720. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 2 +- PortMaster/pylibs/locales/strings.json | 10 +- PortMaster/pylibs/pugwash/theme.py | 113 +++++++++++++++++++++ THEME.md | 4 +- tests/test_port_links.py | 131 +++++++++++++++++++++++++ 5 files changed, 252 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5f90bff0..a295b5f4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,7 +94,7 @@ Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is f ## Architecture -**Import setup**: the scripts prepend `pylibs/` and `exlibs/` to `sys.path`. `exlibs/` holds vendored third-party packages (loguru, requests, urllib3, certifi, `sdl2`, `png`, `qrcode`, ...), and `pylibs/` holds the project packages. A theme `image` of `qr:` is a QR code (`sdl/images.py`, `tests/test_qr.py`). X on the port info screen opens `scenes/port_links.py`: the port's website page and its `port.json` stores (`app_ports.port_links()`, which tidies the hand-typed store names), each as a QR code (`tests/test_port_links.py`). Don't edit vendored code unless that's the point of the change. There are no star imports and no injected builtins: import names explicitly. +**Import setup**: the scripts prepend `pylibs/` and `exlibs/` to `sys.path`. `exlibs/` holds vendored third-party packages (loguru, requests, urllib3, certifi, `sdl2`, `png`, `qrcode`, ...), and `pylibs/` holds the project packages. A theme `image` of `qr:` is a QR code (`sdl/images.py`, `tests/test_qr.py`). X on the port info screen opens `scenes/port_links.py`: the port's website page and its `port.json` stores (`app_ports.port_links()`, which tidies the hand-typed store names), each as a QR code (`tests/test_port_links.py`). Themes made before it, or before the theme editor, get those sections made from their `themes_list` at load time (`theme.section_from_themes_list()`), so custom themes on devices keep their look. Don't edit vendored code unless that's the point of the change. There are no star imports and no injected builtins: import names explicitly. **`harbourmaster` library** (`pylibs/harbourmaster/`): - `harbour.py`: `HarbourMaster`, the engine both the GUI and CLI drive. It holds `__init__`, config, sources and GCD modes. Its other methods live in mixins grouped by job: diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json index bf86e5cd..8dfb289e 100644 --- a/PortMaster/pylibs/locales/strings.json +++ b/PortMaster/pylibs/locales/strings.json @@ -225,19 +225,19 @@ ], "Updating": [ "pylibs/harbourmaster/source/portmaster.py:42", - "pylibs/pugwash/theme.py:449" + "pylibs/pugwash/theme.py:562" ], "Fetching latest info": [ "pylibs/harbourmaster/source/portmaster.py:46", - "pylibs/pugwash/theme.py:459" + "pylibs/pugwash/theme.py:572" ], "Done.": [ "pylibs/harbourmaster/source/portmaster.py:165", - "pylibs/pugwash/theme.py:482" + "pylibs/pugwash/theme.py:595" ], "Fetching info": [ "pylibs/harbourmaster/source/portmaster.py:197", - "pylibs/pugwash/theme.py:488" + "pylibs/pugwash/theme.py:601" ], "Unable to find {port_name}.": [ "pylibs/harbourmaster/source/portmaster.py:418" @@ -1055,7 +1055,7 @@ "pylibs/pugwash/scenes/themes.py:120" ], "Up to date already": [ - "pylibs/pugwash/theme.py:456" + "pylibs/pugwash/theme.py:569" ], "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": [ "pylibs/pugwash/update.py:79" diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 5fef9d72..475f1a23 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -129,6 +129,110 @@ def theme_apply(gui, section_data, base_data, elements): return new_data +## Fonts with capital letters only, an address in them reads wrong: the list's font instead. +CAPS_ONLY_FONTS = ('AmazDooMLeft.ttf', ) + +## Keys only a list uses, left out when a list region becomes text. +LIST_ONLY_KEYS = ('select', 'alt-fill', 'no-select', 'pointer', 'list', 'line-height', 'autoscroll', + 'scroll', 'columns', 'grid', 'text-clip', 'border-x', 'tag') + + +def section_from_themes_list(section, mode): + """ + A "port_links" (mode 'links') or "theme_server" (mode 'server') section made from a + theme's "themes_list" section (with its elements applied), for themes made before those + screens: the theme's own panels and fonts, the QR code where the theme screenshot goes + and the address where the description goes. None if the section hasn't the list and + screenshot regions to make it from. + """ + def tag(name, region): + return region.get('tag', name) + + list_regions = [(name, region) for name, region in section.items() + if isinstance(region, dict) and tag(name, region) == 'themes_list'] + if len(list_regions) == 0: + return None + + list_name, list_region = list_regions[0] + list_fonts = {key: value for key, value in list_region.items() if key == 'font' or key.startswith('font[')} + + def address(region, text): + new = copy.deepcopy(region) + if new.get('font') in CAPS_ONLY_FONTS: + new.update(list_fonts) + + new.pop('tag', None) + new.pop('autoscroll', None) + new['text'] = text + return new + + address_text = "{port_links.name}\n{port_links.url}" if mode == 'links' else "{theme_server.url}" + + out = {} + dropped = set() + has_qr = False + for name, region in section.items(): + if not isinstance(region, dict) or name == '#config': + continue + + text = region.get('text', '') + text = text if isinstance(text, str) else '' + image = region.get('image', '') + image = image if isinstance(image, str) else '' + + if name == list_name: + if mode == 'links': + new = copy.deepcopy(region) + new.pop('tag', None) + out['links_list'] = new + + else: + new = {key: value for key, value in region.items() if not key.startswith(LIST_ONLY_KEYS)} + for key, value in list(new.items()): + if key.startswith('font-size') and isinstance(value, (int, float)): + new[key] = max(10, round(value * 0.75)) + + new.update({"text": "{theme_server.status}", "text-wrap": True}) + out['server_info'] = new + + elif '{theme_info.name}' in text: + new = copy.deepcopy(region) + new.pop('tag', None) + new['text'] = "{port_info.title}" if mode == 'links' else "{scene.title}" + out['links_title' if mode == 'links' else 'server_title'] = new + + elif '{theme_info.description}' in text and '{theme_info.creator}' not in text: + out['link_info' if mode == 'links' else 'server_url'] = address(region, address_text) + + elif '{theme_info.' in text: + dropped.add(name) + + elif '{theme_info.image}' in image: + new = {key: value for key, value in region.items() if not key.startswith(('image[', 'image-mode', 'tag'))} + new['image'] = "qr:{port_links.url}" if mode == 'links' else "qr:{theme_server.url}" + new['image-mode'] = "fit" + out['link_qr' if mode == 'links' else 'server_qr'] = new + has_qr = True + + else: + out[name] = copy.deepcopy(region) + + if not has_qr: + return None + + ## A description that also had the creator in it (hiviz): the text goes there. + info_name = 'link_info' if mode == 'links' else 'server_url' + for name in sorted(dropped): + if info_name not in out and '{theme_info.description}' in section[name].get('text', ''): + out[info_name] = address(section[name], address_text) + + ## The panels behind the text left out ("theme_general_info_back"). + for name in dropped: + out.pop(name + '_back', None) + + return out + + def theme_load(gui, theme_file, color_scheme=None): logger.info(f"Loading theme {theme_file}") @@ -344,6 +448,15 @@ def theme_load(gui, theme_file, color_scheme=None): ## Temporary Hack sections['featured_ports'] = copy.deepcopy(sections['ports_list']) + ## Themes made before the port links and theme editor screens: made from their themes list. + ## (Without a screenshot region there, the scenes show their own plain screen.) + for section_name, mode in (('port_links', 'links'), ('theme_server', 'server')): + if section_name not in sections and 'themes_list' in sections: + section = section_from_themes_list(sections['themes_list'], mode) + if section is not None: + logger.debug(f" - {section_name} made from themes_list") + sections[section_name] = section + return sections diff --git a/THEME.md b/THEME.md index 7b9666d5..d1099722 100644 --- a/THEME.md +++ b/THEME.md @@ -165,12 +165,12 @@ Currently there are the following scenes: - `option_menu`: Options screen - `ports_list`: The list of ports - `port_info`: Detailed port information. X opens the port's links (`port_links`) when it has any; a theme that maps buttons in `#config` can use the action `port_links`. -- `port_links`: A port's links, its page on the PortMaster website (ports from the PortMaster and Multiverse sources) and the stores in its `port.json`, each as a QR code. The list is the region tagged `links_list`. The selected link is in `{port_links.name}` ("Steam", "itch.io", "PortMaster website", "Steam: developer", ...), `{port_links.url}` and `{port_links.kind}` (`website`, `store` or `developer`); `{port_links.count}` is how many there are. Show the QR code with `"image": "qr:{port_links.url}"`. A theme without this section gets a plain screen in PortMaster's own font. +- `port_links`: A port's links, its page on the PortMaster website (ports from the PortMaster and Multiverse sources) and the stores in its `port.json`, each as a QR code. The list is the region tagged `links_list`. The selected link is in `{port_links.name}` ("Steam", "itch.io", "PortMaster website", "Steam: developer", ...), `{port_links.url}` and `{port_links.kind}` (`website`, `store` or `developer`); `{port_links.count}` is how many there are. Show the QR code with `"image": "qr:{port_links.url}"`. A theme without this section (one made before it) gets one made from its `themes_list` section: its list, the QR code where the theme screenshot goes and the address where the description goes; if that has no `{theme_info.image}` region, a plain screen in PortMaster's own font. - `message_window`: A scrolling list of messages, used during downloading, installation, and fetching the latest ports information. - `message_box`: An alert box - `filter_list`: Available filters - `themes_list`: List themes / downloader. -- `theme_server`: The theme editor (Options → Theme Editor), with the tags `{theme_server.url}`, the address, empty until the server is up, and `{theme_server.status}`, the instructions or what went wrong. Show the address as a QR code with `"image": "qr:{theme_server.url}"`. A theme without this section gets a plain screen in PortMaster's own font. +- `theme_server`: The theme editor (Options → Theme Editor), with the tags `{theme_server.url}`, the address, empty until the server is up, and `{theme_server.status}`, the instructions or what went wrong. Show the address as a QR code with `"image": "qr:{theme_server.url}"`. A theme without this section gets one made from its `themes_list` section, as `port_links` does (the instructions where the list goes), or else a plain screen in PortMaster's own font. ### Scene: main_menu diff --git a/tests/test_port_links.py b/tests/test_port_links.py index 55ae097f..d3035d8e 100644 --- a/tests/test_port_links.py +++ b/tests/test_port_links.py @@ -164,3 +164,134 @@ def test_theme_without_links_section(links_gui, monkeypatch, tmp_path): from pugwash.scenes.base import plain_font assert gui.theme_data["port_links"]["links_list"]["font"] == plain_font() assert gui.scenes[-1][1][-1].tags['links_list'].list[0] == "PortMaster website" + + +################################################################################ +## Themes made before these screens: theirs are made from the theme's themes list. +THEMES_LIST = { + "background": {"area": [0, 0, 1.0, 1.0], "fill": "background"}, + "themes_list": {"parent": "port_info_list_area", "area": [0, 0, 1.0, 1.0], "font": "List.ttf", + "font[ja_JP]": "NotoSansJP-Regular.ttf", "font-size": 20, "select-fill": "selection", + "autoscroll": "slide", "list": []}, + "theme_general_info": {"parent": "port_info_text_area", "area": [0, 0, 1.0, 0.3], + "text": "Creator: {theme_info.creator}\nStatus: {theme_info.status}"}, + "theme_general_info_back": {"parent": "port_info_text_area", "area": [0, 0, 1.0, 0.3], "image": "panel.png"}, + "theme_desc": {"parent": "port_info_text_area", "area": [0, 0.3, 1.0, 1.0], "font": "Display.ttf", + "text": "{theme_info.description}", "autoscroll": "marquee"}, + "theme_name": {"parent": "port_info_title_area", "area": [0, 0, 1.0, 1.0], "text": "{theme_info.name}"}, + "theme_image": {"parent": "port_info_image_area", "area": [0, 0, 1.0, 1.0], "image": "{theme_info.image}", + "image[hires]": "{theme_info.image}", "image-mode": "stretch"}, + "button_bar": {"area": [0, 0.9, 1.0, 1.0], "bar": []}, + } + + +def test_links_section_from_themes_list(): + from pugwash.theme import section_from_themes_list + section = section_from_themes_list(THEMES_LIST, 'links') + + assert set(section) == {"background", "links_list", "link_info", "links_title", "link_qr", "button_bar"} + + ## The list as it was, the screenshot is the QR code (square, not stretched). + assert section["links_list"]["select-fill"] == "selection" + assert section["link_qr"]["image"] == "qr:{port_links.url}" and section["link_qr"]["image-mode"] == "fit" + assert "image[hires]" not in section["link_qr"] + assert section["links_title"]["text"] == "{port_info.title}" + ## The address where the description was, in its font, not scrolling. + assert section["link_info"]["text"] == "{port_links.name}\n{port_links.url}" + assert section["link_info"]["font"] == "Display.ttf" and "autoscroll" not in section["link_info"] + ## The theme's own data isn't changed. + assert THEMES_LIST["theme_image"]["image"] == "{theme_info.image}" + + +def test_server_section_from_themes_list(): + from pugwash.theme import section_from_themes_list + section = section_from_themes_list(THEMES_LIST, 'server') + + assert set(section) == {"background", "server_info", "server_url", "server_title", "server_qr", "button_bar"} + ## The list area has the instructions, a size smaller, with no list keys. + info = section["server_info"] + assert info["text"] == "{theme_server.status}" and info["text-wrap"] is True + assert info["font-size"] == 15 and info["font"] == "List.ttf" + assert not any(key.startswith(("select", "list", "autoscroll")) for key in info) + assert section["server_qr"]["image"] == "qr:{theme_server.url}" + assert section["server_url"]["text"] == "{theme_server.url}" + assert section["server_title"]["text"] == "{scene.title}" + + +def test_section_from_other_themes_lists(): + from pugwash.theme import section_from_themes_list + + ## hiviz: one text with the creator and the description, the address goes there. + hiviz = { + "themes_list": {"area": [0, 0, 0.5, 1.0], "list": []}, + "theme_general_info": {"area": [0.5, 0.5, 1.0, 1.0], "text": "Creator: {theme_info.creator}\n{theme_info.description}"}, + "theme_image": {"area": [0.5, 0, 1.0, 0.5], "image": "{theme_info.image}"}, + } + assert section_from_themes_list(hiviz, 'links')["link_info"]["area"] == [0.5, 0.5, 1.0, 1.0] + + ## Elements applied: the list is found by its tag. + tagged = { + "menu": {"area": [0, 0, 0.5, 1.0], "list": [], "tag": "themes_list"}, + "side_image": {"area": [0.5, 0, 1.0, 0.5], "image": "{theme_info.image}", "tag": "theme_image"}, + } + section = section_from_themes_list(tagged, 'links') + assert "links_list" in section and "tag" not in section["links_list"] and "tag" not in section["link_qr"] + + ## Doom: a font with capitals only, the address in the list's font. + doom = { + "themes_list": {"area": [0, 0, 0.5, 1.0], "list": [], "font": "DooM.ttf"}, + "theme_desc": {"area": [0.5, 0.5, 1.0, 1.0], "font": "AmazDooMLeft.ttf", "text": "{theme_info.description}"}, + "theme_image": {"area": [0.5, 0, 1.0, 0.5], "image": "{theme_info.image}"}, + } + assert section_from_themes_list(doom, 'links')["link_info"]["font"] == "DooM.ttf" + + ## Nothing to make the QR code from (basic_theme's list is the options menu's): None, + ## the scene's plain screen is used. + assert section_from_themes_list({"themes_list": {"list": []}}, 'links') is None + assert section_from_themes_list({"theme_image": {"image": "{theme_info.image}"}}, 'links') is None + + +@pytest.fixture +def old_theme(links_gui): + """The default theme without the port links and theme editor sections, as themes made before.""" + import json + import shutil + import harbourmaster + from conftest import PM_DIR + + theme_dir = harbourmaster.HM_TOOLS_DIR / "PortMaster" / "themes" / "Old Theme" + shutil.copytree(str(PM_DIR / "pylibs" / "default_theme"), str(theme_dir)) + data = json.loads((theme_dir / "theme.json").read_text()) + data["#info"]["name"] = "Old Theme" + del data["port_links"], data["theme_server"] + (theme_dir / "theme.json").write_text(json.dumps(data)) + + links_gui.themes.force_theme = "Old Theme" + links_gui.reload_theme() + yield links_gui + + links_gui.themes.force_theme = "default_theme" + links_gui.reload_theme() + shutil.rmtree(str(theme_dir)) + + +@pytest.mark.sdl +def test_old_theme_gets_themed_screens(old_theme, monkeypatch, tmp_path): + gui = old_theme + from pugwash import scenes + + for name in ("port_links", "theme_server"): + section = gui.theme_data[name] + assert any(region.get("parent") == "port_info_list_area" for region in section.values() if isinstance(region, dict)), name + + assert gui.theme_data["port_links"]["link_qr"]["image"] == "qr:{port_links.url}" + + port_info_scene(gui, "sprout.zip") + press(gui, monkeypatch, 'X') + assert gui.scene_list()[-1] == "port_links" + assert gui.scenes[-1][1][-1].tags['links_list'].list[0] == "PortMaster website" + + gui.push_scene('theme-editor', scenes.ThemeServerScene(gui, start=False)) + gui.do_loop(no_delay=True) + gui.create_screenshot(str(tmp_path / "editor.png")) + assert (tmp_path / "editor.png").stat().st_size > 0 From 9e85ac6f4a0cdb2ea91e0270fe008ed60b1c3f2f Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 05:16:42 +0800 Subject: [PATCH 20/28] Translations can't break .format(); the editor's writes need a direct Host - The code calls .format() on what _() returns, so a translation with a {field} the English doesn't have, or a stray { or }, raised there ({lang_name} (Selected) -> {bogus} stopped the language screen with a KeyError). harbourmaster.i18n leaves those out when it loads a language (the string shows in English), and the translation editor refuses to save them, saying why. No shipped translation is affected. - The editor's POSTs (saving translations) also need a Host header that is an IP address or localhost: a DNS rebinding page reaches the server under its own domain name, and could otherwise send the page's header. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/harbourmaster/i18n.py | 33 ++++++++++++- PortMaster/pylibs/theme_server/serve.py | 30 +++++++++++- .../pylibs/theme_server/translations.py | 21 ++++++++ tests/test_i18n.py | 48 +++++++++++++++++++ tests/test_theme_server.py | 16 +++++++ 5 files changed, 145 insertions(+), 3 deletions(-) diff --git a/PortMaster/pylibs/harbourmaster/i18n.py b/PortMaster/pylibs/harbourmaster/i18n.py index b522aeb5..e8f963b8 100644 --- a/PortMaster/pylibs/harbourmaster/i18n.py +++ b/PortMaster/pylibs/harbourmaster/i18n.py @@ -10,6 +10,7 @@ # the code and the themes); the theme server's translation editor edits the language files. import json +import string from pathlib import Path @@ -51,9 +52,36 @@ def _read(path): return {key: value for key, value in data.items() if isinstance(key, str) and isinstance(value, str) and value} +def _fields(text): + """The names of a string's {fields}, as str.format reads them. Raises ValueError for a stray { or }.""" + return {field.split('.', 1)[0].split('[', 1)[0] for _, field, _, _ in string.Formatter().parse(text) if field is not None} + + +def safe_translation(text, translation): + """ + The code calls .format() on what _() returns: a translation with a {field} the English + doesn't have, or a stray { or }, would raise there (and stop PortMaster). + """ + try: + english = _fields(text) + except ValueError: + ## Not a format string, so nothing formats it. + return True + + try: + return _fields(translation) <= english + except ValueError: + return False + + def read_language(locales_dir, lang): - """{'messages': {...}, 'themes': {...}} for a language, empty where a file can't be read.""" - return {domain: _read(language_file(locales_dir, lang, domain)) for domain in DOMAINS} + """ + {'messages': {...}, 'themes': {...}} for a language, empty where a file can't be read. A + translation that would break .format() is left out: that string shows in English. + """ + data = {domain: _read(language_file(locales_dir, lang, domain)) for domain in DOMAINS} + data['messages'] = {text: translation for text, translation in data['messages'].items() if safe_translation(text, translation)} + return data def load_language(locales_dir, lang): @@ -76,6 +104,7 @@ def set_theme_translations(translations): 'theme_text', 'load_language', 'read_language', + 'safe_translation', 'language_file', 'DOMAINS', 'set_theme_translations', diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index 23027882..95375368 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -24,6 +24,7 @@ import argparse import hashlib +import ipaddress import json import os import re @@ -574,6 +575,28 @@ def ensure_cache(cache, refresh=False): ################################################################################ ## The web server +def direct_host(host_header): + """ + The Host header names this server directly: an IP address (the address PortMaster shows) + or localhost. A DNS rebinding page reaches it under its own domain name instead, so it + can't send the page's writes. + """ + host = (host_header or "").strip().lower() + if host.startswith("["): + host = host[1:].split("]", 1)[0] + elif host.count(":") == 1: + host = host.split(":", 1)[0] + + if host == "localhost": + return True + + try: + ipaddress.ip_address(host) + return True + except ValueError: + return False + + class Handler(BaseHTTPRequestHandler): server_version = "PortMasterThemeServer/1" @@ -662,7 +685,8 @@ def do_POST(self): url = urllib.parse.urlparse(self.path) ## Only the page itself: another site can't send this header without a CORS ## preflight, which this server never answers. - if self.headers.get("X-Theme-Server") != "1" or not (self.headers.get("Content-Type") or "").startswith("application/json"): + if self.headers.get("X-Theme-Server") != "1" or not (self.headers.get("Content-Type") or "").startswith("application/json") or \ + not direct_host(self.headers.get("Host")): self.send(403, {'error': "forbidden"}) return @@ -680,6 +704,10 @@ def do_POST(self): not isinstance(text, str) or not isinstance(translation, str): self.send(400, {'error': "bad language, domain or text"}) return + if domain == "messages" and translation and not i18n_tool.safe_translation(text, translation): + ## PortMaster would leave it out (it would break the code's .format()), so say why now. + self.send(400, {'error': "it has a {placeholder} the English doesn't, or a { or } on its own (write {{ or }})"}) + return try: problems = self.server.pool.translations.save(lang, domain, text, translation) except KeyError: diff --git a/PortMaster/pylibs/theme_server/translations.py b/PortMaster/pylibs/theme_server/translations.py index 6782076f..0f061623 100644 --- a/PortMaster/pylibs/theme_server/translations.py +++ b/PortMaster/pylibs/theme_server/translations.py @@ -17,6 +17,7 @@ import ast import json import re +import string import sys from pathlib import Path @@ -154,6 +155,26 @@ def extract(themes_dirs=()): PLACEHOLDER = re.compile(r"\{[^{}]*\}") +def _format_fields(text): + return {field.split('.', 1)[0].split('[', 1)[0] for _, field, _, _ in string.Formatter().parse(text) if field is not None} + + +def safe_translation(text, translation): + """ + As harbourmaster.i18n.safe_translation (which PortMaster uses to leave out the ones that + would break the code's .format()); this module is standard library only. + """ + try: + english = _format_fields(text) + except ValueError: + return True + + try: + return _format_fields(translation) <= english + except ValueError: + return False + + def problems(source, translation): """What a translation lost: {placeholders} and line breaks the English has.""" found = [] diff --git a/tests/test_i18n.py b/tests/test_i18n.py index 37d0572c..61eba0a3 100644 --- a/tests/test_i18n.py +++ b/tests/test_i18n.py @@ -108,3 +108,51 @@ def test_every_language_loads(restore_language): i18n.load_language(LOCALES_DIR, "de_DE") assert i18n._("Options") != "Options" + + +SAFETY_CASES = [ + ("{lang_name} (Selected)", "{lang_name} (Ausgewählt)", True), + ("{lang_name} (Selected)", "(Ausgewählt)", True), + ("{lang_name} (Selected)", "{bogus} (Ausgewählt)", False), + ("{lang_name} (Selected)", "{lang_name (Ausgewählt)", False), + ("{lang_name} (Selected)", "lang_name} (Ausgewählt)", False), + ("{lang_name} (Selected)", "{{lang_name}} literal", True), + ("{lang_name} (Selected)", "{0}", False), + ("Installing {port_name}", "{port_name} wird installiert", True), + ("Installing {port_name}", "{port_name.title} wird installiert", True), + ("Installing {port_name}", "{port_name!r:>10}", True), + ("Options", "Optionen {", False), + ("Options", "Optionen {x}", False), + ("Options", "Optionen", True), + ("Not {a format", "anything {goes", True), + ] + + +@pytest.mark.parametrize("text, translation, safe", SAFETY_CASES) +def test_safe_translation(text, translation, safe): + ## The same answer from PortMaster and from the translation editor's own copy. + assert i18n.safe_translation(text, translation) is safe + assert i18n_tool.safe_translation(text, translation) is safe + + +def test_unsafe_translations_are_english(tmp_path, restore_language): + ## Would raise in the code's .format() and stop PortMaster: shown in English instead. + (tmp_path / "xx_XX.json").write_text(json.dumps({ + "{lang_name} (Selected)": "{bogus} (Ausgewählt)", + "Installing {port_name}": "{port_name} wird installiert", + "Options": "Optionen {", + })) + + i18n.load_language(tmp_path, "xx_XX") + + assert i18n._("{lang_name} (Selected)").format(lang_name="Deutsch") == "Deutsch (Selected)" + assert i18n._("Installing {port_name}").format(port_name="2048") == "2048 wird installiert" + assert i18n._("Options") == "Options" + + +def test_every_shipped_translation_formats(restore_language): + ## Each language's translations are safe to .format() as the English is. + for lang in LANGUAGES: + translations = i18n_tool.read_translations(lang)['messages'] + bad = [text for text, translation in translations.items() if translation and not i18n.safe_translation(text, translation)] + assert bad == [], f"{lang}: {bad}" diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 260d23cb..c9a0d713 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -400,10 +400,16 @@ def post(body, headers={"Content-Type": "application/json", "X-Theme-Server": "1 ## Only from the page: another site's form or script can't send the header. assert post(good, {"Content-Type": "application/json"})[0] == 403 assert post(good, {"Content-Type": "text/plain", "X-Theme-Server": "1"})[0] == 403 + ## A DNS rebinding page comes with its own domain name as the Host. + assert post(good, {"Content-Type": "application/json", "X-Theme-Server": "1", "Host": "evil.example:8090"})[0] == 403 assert post(dict(good, lang="en_US"))[0] == 400 assert post(dict(good, lang="../x"))[0] == 400 assert post(dict(good, domain="other"))[0] == 400 assert post(dict(good, text="Nope"))[0] == 404 + ## One that would break the code's .format(): refused, with why. + status, body = post(dict(good, text="{lang_name} (Selected)", translation="{bogus} (Ausgewählt)")) + assert status == 400 and "placeholder" in body['error'] + assert post(dict(good, translation="Thema {")) [0] == 400 status, body = post(good) assert status == 200 and body == {'ok': True, 'problems': []} assert json.loads((locales / "de_DE.json").read_text(encoding="utf-8"))["Select Theme"] == "Thema wählen" @@ -708,3 +714,13 @@ def test_theme_editor_keeps_its_port(gui, hm_dirs, monkeypatch): finally: gui.hm = None + + +@pytest.mark.parametrize("host, direct", [ + ("192.168.68.119:55555", True), ("127.0.0.1", True), ("localhost:8090", True), ("LOCALHOST", True), + ("[::1]:8090", True), ("[fe80::1]", True), ("::1", True), + ("evil.example", False), ("evil.example:8090", False), ("192.168.68.119.evil.example", False), + ("", False), (None, False), ("localhost.evil.example", False), + ]) +def test_direct_host(host, direct): + assert serve.direct_host(host) is direct From 22227b0b513013266c927355198b35754fdaf1a9 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 05:25:00 +0800 Subject: [PATCH 21/28] Default theme: CJK lines at 0.8 of the font's height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Noto Sans JP/SC/TC/HK/KR have a much taller line height than DejaVu Sans at the same size, so in Japanese the main menu's six entries ran into the footer at 640x480 ("終了" over "PortMaster: ..."). The theme server's export showed it. Lines and list rows are now 0.8 of the font's height in those languages: everything fits where it does in English. The theme editor's preview shows an address with a port from its new range. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/default_theme/theme.json | 1 + PortMaster/pylibs/theme_server/shots.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/PortMaster/pylibs/default_theme/theme.json b/PortMaster/pylibs/default_theme/theme.json index 82746ac4..a01e5d1a 100644 --- a/PortMaster/pylibs/default_theme/theme.json +++ b/PortMaster/pylibs/default_theme/theme.json @@ -168,6 +168,7 @@ "font[zh_HK]": "NotoSansHK-Regular.ttf", "font[ko_KR]": "NotoSansKR-Regular.ttf", "font[ja_JP]": "NotoSansJP-Regular.ttf", + "line-height[ja_JP|zh_TW|zh_CN|zh_HK|ko_KR]": 0.8, "font-scale[30:17]": 1.0, "font-scale[16:9]": 1.4, "font-scale[3:2]": 0.85, diff --git a/PortMaster/pylibs/theme_server/shots.py b/PortMaster/pylibs/theme_server/shots.py index 8b040d08..3970d724 100644 --- a/PortMaster/pylibs/theme_server/shots.py +++ b/PortMaster/pylibs/theme_server/shots.py @@ -245,7 +245,7 @@ def shot_theme_editor(gui): shot_options(gui) scene = pug_scenes.ThemeServerScene(gui, start=False) gui.push_scene('theme-editor', scene) - scene.show("http://192.168.0.2:8090/") + scene.show("http://192.168.0.2:52817/") def shot_messages(gui): From 3a60ab007d33e25c1a4620d31fe58d261a041a32 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 05:29:06 +0800 Subject: [PATCH 22/28] Theme server: 404 for a theme that isn't there, 400 for non-string fields Found by sending it bad requests: render.png?theme=../../../etc was a 500 (the renderer's "no theme" error), a translation with "lang": 5 dropped the connection (a TypeError nothing caught), and render.png took any \w\w_\w\w as a language where the rest of the API takes xx_XX only. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/theme_server/serve.py | 9 ++++++++- tests/test_theme_server.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/PortMaster/pylibs/theme_server/serve.py b/PortMaster/pylibs/theme_server/serve.py index 95375368..148c4bd2 100644 --- a/PortMaster/pylibs/theme_server/serve.py +++ b/PortMaster/pylibs/theme_server/serve.py @@ -657,11 +657,15 @@ def do_GET(self): resolution = query.get('res', "640x480") lang = query.get('lang', "en_US") shot = query.get('shot', "main_menu") - if not valid_resolution(resolution) or not re.match(r"^\w\w_\w\w$", lang) or \ + if not valid_resolution(resolution) or not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang) or \ shot not in {name for name, _ in self.server.shots}: self.send(400, {'error': "bad theme, resolution, language or screen"}) return + if self.server.themes.get(theme_id) is None: + self.send(404, {'error': f"no theme {theme_id!r}"}) + return + png, info = self.server.pool.render(theme_id, query.get('scheme') or None, resolution, lang, shot) if url.path.endswith(".png"): if not png: @@ -699,6 +703,9 @@ def do_POST(self): if url.path == "/api/translate": lang, domain = body.get('lang', ""), body.get('domain', "") + if not isinstance(lang, str) or not isinstance(domain, str): + self.send(400, {'error': "bad language, domain or text"}) + return text, translation = body.get('text'), body.get('translation', "") if not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", lang) or lang == "en_US" or domain not in i18n_tool.DOMAINS or \ not isinstance(text, str) or not isinstance(translation, str): diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index c9a0d713..0fbf3292 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -405,6 +405,8 @@ def post(body, headers={"Content-Type": "application/json", "X-Theme-Server": "1 assert post(dict(good, lang="en_US"))[0] == 400 assert post(dict(good, lang="../x"))[0] == 400 assert post(dict(good, domain="other"))[0] == 400 + assert post(dict(good, lang=5))[0] == 400 + assert post(dict(good, domain=["messages"]))[0] == 400 assert post(dict(good, text="Nope"))[0] == 404 ## One that would break the code's .format(): refused, with why. status, body = post(dict(good, text="{lang_name} (Selected)", translation="{bogus} (Ausgewählt)")) @@ -414,6 +416,14 @@ def post(body, headers={"Content-Type": "application/json", "X-Theme-Server": "1 assert status == 200 and body == {'ok': True, 'problems': []} assert json.loads((locales / "de_DE.json").read_text(encoding="utf-8"))["Select Theme"] == "Thema wählen" + ## A theme that isn't there, or a language that isn't one. + for query, code in (("theme=../../../etc", 404), ("theme=nope", 404), ("lang=../x", 400), ("lang=%C3%A9%C3%A9_%C3%A9%C3%A9", 400)): + try: + urllib.request.urlopen(base + "/api/render.png?" + query, timeout=30) + assert False, query + except urllib.error.HTTPError as error: + assert error.code == code, query + with urllib.request.urlopen(base + "/api/info", timeout=30) as response: names = {code: (name, has) for code, name, has in json.loads(response.read())['all_languages']} assert names["de_DE"] == ("German", True) and names["da_DK"] == ("Danish", False) From 94f45c5d00433b2280e484d263a7c59d3dcafe43 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 05:32:49 +0800 Subject: [PATCH 23/28] test_http_api: an unknown theme is a 404 now Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- tests/test_theme_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_theme_server.py b/tests/test_theme_server.py index 0fbf3292..16097284 100644 --- a/tests/test_theme_server.py +++ b/tests/test_theme_server.py @@ -265,7 +265,7 @@ def get(path): assert get("/api/render.png?theme=default_theme&res=640x480&shot=no_such_screen")[0] == 400 assert get("/api/render.png?theme=default_theme&res=huge&shot=options")[0] == 400 - assert get("/api/render.png?theme=nope&res=640x480&shot=options")[0] == 500 + assert get("/api/render.png?theme=nope&res=640x480&shot=options")[0] == 404 assert get("/api/render.png?theme=Tiny&res=640x480&shot=filters")[0] == 404 assert json.loads(get("/api/version?theme=Tiny")[2])['version'] assert get("/api/version?theme=nope")[0] == 404 From 3d93a1fde41a2c251d11c5583e5ab097bda26449 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 06:00:52 +0800 Subject: [PATCH 24/28] Old themes' theme editor screen: keep the list's margins for the text The theme editor screen made from a theme's themes list put the instructions in the list's area without its border-x, so the text ran under the panel's edges (Doom, FFVII) or past it (Zelda). Seen rendering the device's themes with their own sections taken out. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/pugwash/theme.py | 3 ++- tests/test_port_links.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 475f1a23..8c671899 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -133,8 +133,9 @@ def theme_apply(gui, section_data, base_data, elements): CAPS_ONLY_FONTS = ('AmazDooMLeft.ttf', ) ## Keys only a list uses, left out when a list region becomes text. +## (Not border-x: the list's margins keep the theme editor's text inside the list's panel.) LIST_ONLY_KEYS = ('select', 'alt-fill', 'no-select', 'pointer', 'list', 'line-height', 'autoscroll', - 'scroll', 'columns', 'grid', 'text-clip', 'border-x', 'tag') + 'scroll', 'columns', 'grid', 'text-clip', 'tag') def section_from_themes_list(section, mode): diff --git a/tests/test_port_links.py b/tests/test_port_links.py index d3035d8e..c3e7b349 100644 --- a/tests/test_port_links.py +++ b/tests/test_port_links.py @@ -172,7 +172,7 @@ def test_theme_without_links_section(links_gui, monkeypatch, tmp_path): "background": {"area": [0, 0, 1.0, 1.0], "fill": "background"}, "themes_list": {"parent": "port_info_list_area", "area": [0, 0, 1.0, 1.0], "font": "List.ttf", "font[ja_JP]": "NotoSansJP-Regular.ttf", "font-size": 20, "select-fill": "selection", - "autoscroll": "slide", "list": []}, + "autoscroll": "slide", "border-x": 16, "list": []}, "theme_general_info": {"parent": "port_info_text_area", "area": [0, 0, 1.0, 0.3], "text": "Creator: {theme_info.creator}\nStatus: {theme_info.status}"}, "theme_general_info_back": {"parent": "port_info_text_area", "area": [0, 0, 1.0, 0.3], "image": "panel.png"}, @@ -213,6 +213,8 @@ def test_server_section_from_themes_list(): assert info["text"] == "{theme_server.status}" and info["text-wrap"] is True assert info["font-size"] == 15 and info["font"] == "List.ttf" assert not any(key.startswith(("select", "list", "autoscroll")) for key in info) + ## Its margins keep the text inside the list's panel (Doom, FFVII and Zelda ran into its edges). + assert info["border-x"] == 16 assert section["server_qr"]["image"] == "qr:{theme_server.url}" assert section["server_url"]["text"] == "{theme_server.url}" assert section["server_title"]["text"] == "{scene.title}" From d3fb38a1804d7a7466bc624211875a12c901f505 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 19:48:31 +0800 Subject: [PATCH 25/28] Strings: fix the English, and translations that broke their layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit English: - "Port X installed failed." -> "Port X failed to install." - "sucessfully" -> "successfully"; "Unable do download a runtime when in offline mode." -> "Unable to download a runtime in offline mode." - One "Select Color Scheme" (Options said Color, the screen Colour), with a full stop on its description; "soundfx" -> "sound effects"; the move-ports question gets its question mark. - PortMaster's own update says "PortMaster installed successfully." / "PortMaster failed to install." instead of "Port 'PortMaster' ...". - American spelling ("color") in the theme editor page and the docs; pugwash's --no-colour/--force-colour flags are unchanged. - A theme string from no known theme ("... dance until tomorrow") is dropped. Each language keeps its translations under the corrected English (a full stop or question mark added where the English gained one). Translations whose line breaks didn't match the English: Russian ran "HM: ..." and the free space together on one line, Dutch put three lines of port info on one, Romanian split a one-line layout in three, and German/Chinese had extra or missing blank lines in message boxes. German also: "Möchten Sie", "installierte". tools/i18n.py check is clean for every language. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- CLAUDE.md | 6 +-- PortMaster/pylibs/harbourmaster/hm_install.py | 8 ++-- .../pylibs/harbourmaster/hm_runtimes.py | 2 +- PortMaster/pylibs/locales/de_DE.json | 21 +++++----- PortMaster/pylibs/locales/es_ES.json | 19 ++++----- PortMaster/pylibs/locales/fr_FR.json | 17 ++++---- PortMaster/pylibs/locales/it_IT.json | 17 ++++---- PortMaster/pylibs/locales/ja_JP.json | 15 ++++--- PortMaster/pylibs/locales/ko_KR.json | 19 ++++----- PortMaster/pylibs/locales/nl_NL.json | 17 ++++---- PortMaster/pylibs/locales/nl_NL.themes.json | 2 +- PortMaster/pylibs/locales/pl_PL.json | 17 ++++---- PortMaster/pylibs/locales/pt_BR.json | 17 ++++---- PortMaster/pylibs/locales/ro_RO.json | 17 ++++---- PortMaster/pylibs/locales/ro_RO.themes.json | 2 +- PortMaster/pylibs/locales/ru_RU.json | 19 ++++----- PortMaster/pylibs/locales/ru_RU.themes.json | 2 +- PortMaster/pylibs/locales/strings.json | 42 ++++++++++--------- PortMaster/pylibs/locales/strings.themes.json | 25 +++++------ PortMaster/pylibs/locales/sv_SE.json | 17 ++++---- PortMaster/pylibs/locales/uk_UA.json | 17 ++++---- PortMaster/pylibs/locales/zh_CN.json | 27 ++++++------ PortMaster/pylibs/pugwash/scenes/options.py | 6 +-- PortMaster/pylibs/pugwash/scenes/themes.py | 4 +- .../pylibs/theme_server/static/index.html | 2 +- README.md | 2 +- THEME.md | 12 +++--- tests/test_fifo_install.py | 2 +- tests/test_gui_commands.py | 2 +- tests/test_harbour.py | 2 +- tests/test_install.py | 6 +-- 31 files changed, 184 insertions(+), 199 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a295b5f4..a4480f9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,8 +75,8 @@ Guard tests for restructuring: It shows the results with pugwash's fifo dialogs (`pmtest/fifo.py`) and writes `pmtest/reports/--.zip`, scrubbed of MAC/private IP addresses, serials, Wi-Fi names and the hostname (`suite.scrub`). The tester's own `pugwash.txt`/`harbourmaster.txt`/`log.txt` are set aside in `pmtest/saved-logs/` for the run and put back afterwards (or by the next run after a crash), so a report only holds the run's logs. pugwash and the GUI tests run in the environment the installed launcher gives the GUI (`pm_launch.txt` with `PM_LAUNCH_ENV_ONLY=Y`). The build also writes `dist/pmtest.zip.md5`, for `harbourmaster install /pmtest.zip.md5`. `python3 -I tools/test_suite/matrix.py --out DIR` turns collected reports into `matrix.json` and a self-contained `matrix.html` coverage page, starting with a device × CFW release heat map. `tools/test_suite/devices.py` names the hardware behind each CFW's device string (`DEVICES`, `FAMILIES`) and reads a vendored copy of PortMaster-Info's `device_info.json` (`--refresh` updates it) for supported-but-untested cells; a report it can't place is printed and listed under Other; it and `receive.py` must keep running on Python 3.7 with only the standard library. A build made with `PM_PMTEST_UPLOAD=http://:8770/` carries `pmtest/upload.txt` and, if the tester agrees on the start screen, POSTs each finished report there, where `python3 -I tools/test_suite/receive.py ` saves it on the LAN. The public endpoint is the `pmtest-reports` Cloudflare Worker (its own repo, with `collect.py` to download reports); HTTPS uses PortMaster's certifi bundle. Builds without the variable send nothing. Tested in `tests/test_test_suite.py` and `tests/test_build.py`. **Theme preview server** (`PortMaster/pylibs/theme_server/`, run from a checkout with `tools/theme_server/serve.py`; documented in `THEME.md`): -- `serve.py` serves a page showing every screen rendered with a theme, at any resolution, colour scheme and language, and exports PNGs with `--export`. -- Each render process, `worker.py`, is a headless pugwash for one screen size (SDL allows one GUI per process, and the size is fixed at start). Its `load` command switches the theme, colour scheme and language in place with `reload_language()`/`reload_theme()`, about a second; `serve.py` sends it whenever a render needs another theme version (a saved file), scheme or language (or saved translations). `tests/test_theme_server.py` checks a switched renderer draws the same as a new one. +- `serve.py` serves a page showing every screen rendered with a theme, at any resolution, color scheme and language, and exports PNGs with `--export`. +- Each render process, `worker.py`, is a headless pugwash for one screen size (SDL allows one GUI per process, and the size is fixed at start). Its `load` command switches the theme, color scheme and language in place with `reload_language()`/`reload_theme()`, about a second; `serve.py` sends it whenever a render needs another theme version (a saved file), scheme or language (or saved translations). `tests/test_theme_server.py` checks a switched renderer draws the same as a new one. - Renderers are forked from a zygote (`worker.py --zygote`), which imports pugwash once: `serve.py` sends it a request with the renderer's end of a socket pair attached (`SCM_RIGHTS`), and the renderer talks on that socket. Each renderer's HarbourMaster has its own tools folder (`Renderer(tools_dir=...)`), the zygote's holds the theme links. Without a zygote (it failed to start) each renderer is a `worker.py` of its own. No threads in the zygote: it forks. - It pretends to be a device: the `device_info.txt` screen rules, and `PM_PRETEND_RESOLUTION`, which pugwash honours in `HM_TESTING` mode or headless. - It draws with no window (`PM_HEADLESS=Y`): SDL's software renderer on a surface, with no video or controller subsystem. So it needs no dummy video driver, and it runs on a device next to the GUI that owns the screen (muOS's SDL has only `mali`). @@ -127,7 +127,7 @@ Known bugs are pinned as `xfail(strict=True)`, so a test fails once its bug is f - `app.py`: `PortMasterGUI`, covering init, the main loop, drawing and the scene stack. It subclasses the mixins in `app_callbacks.py` (the thread-aware `harbourmaster.Callback` implementation and `run_task`), `app_commands.py` (install/uninstall/update front ends), `app_fifo.py` (fifo_control) and `app_ports.py` (port info/size for templates), plus `sdl.GUI` and `harbourmaster.Callback`. - `scenes/`: screens as `BaseScene` subclasses, one module per screen or group of screens. `scenes/base.py` imports no other scene, which keeps the modules free of import cycles. - `theme.py`: theme loading. Themes are JSON describing per-scene elements; `pylibs/default_theme/theme.json` is the built-in one. See `THEME.md` for the element/scene spec, inheritance order and text-template tags. - - Choosing a theme, colour scheme or language applies at once: `PortMasterGUI.reload_theme(then=...)` drops what the old theme loaded (images, fonts and text textures, sounds, animations, pallet, element rects, its resource path), loads the configured theme again and rebuilds the screens from the main menu up. `reload_language()` also swaps `lang.CURRENT_LANG`, the translations and the language capability (the theme's `font[ja_JP]` & co). Anything a theme loads into the GUI has to be undone there; `tests/test_reload.py` checks a screen is pixel-identical after switching away and back. + - Choosing a theme, color scheme or language applies at once: `PortMasterGUI.reload_theme(then=...)` drops what the old theme loaded (images, fonts and text textures, sounds, animations, pallet, element rects, its resource path), loads the configured theme again and rebuilds the screens from the main menu up. `reload_language()` also swaps `lang.CURRENT_LANG`, the translations and the language capability (the theme's `font[ja_JP]` & co). Anything a theme loads into the GUI has to be undone there; `tests/test_reload.py` checks a screen is pixel-identical after switching away and back. - `tasks.py`: threading helpers (dispatcher, `Task`, `DirectoryScanner`, `FifoReader`, `FrameStats`). It doesn't import SDL. - `sdl/`: the low-level SDL2 widget and rendering layer. It's pySDL2gui (port_gui), split by class group; `Region` in `sdl/region.py` is the themeable widget. diff --git a/PortMaster/pylibs/harbourmaster/hm_install.py b/PortMaster/pylibs/harbourmaster/hm_install.py index 306182cb..be24a9a4 100644 --- a/PortMaster/pylibs/harbourmaster/hm_install.py +++ b/PortMaster/pylibs/harbourmaster/hm_install.py @@ -104,7 +104,7 @@ def _install_portmaster(self, download_file, do_delete=False): logger.error(f"PortMaster.zip is damaged: {err}") self.last_install_error = _("PortMaster.zip is damaged, download it again.") self.callback.message(self.last_install_error) - self.callback.message_box(_("Port {download_name} installed failed.").format(download_name="PortMaster")) + self.callback.message_box(_("PortMaster failed to install.")) if do_delete: download_file.unlink() @@ -165,7 +165,7 @@ def _install_portmaster(self, download_file, do_delete=False): for bash_file in bash_files: self._fix_permissions(bash_file) - self.callback.message_box(_("Port {download_name!r} installed successfully.").format(download_name="PortMaster")) + self.callback.message_box(_("PortMaster installed successfully.")) finally: if do_delete: @@ -394,7 +394,7 @@ def _install_port(self, download_info, do_delete=False): self.callback.message(_("Installation failed: {reason}").format(reason=failure_reason)) self.last_install_error = failure_reason - self.callback.message_box(_("Port {download_name} installed failed.").format(download_name=port_nice_name)) + self.callback.message_box(_("Port {download_name} failed to install.").format(download_name=port_nice_name)) return 255 @@ -413,7 +413,7 @@ def _install_port(self, download_info, do_delete=False): runtime_name=runtime_name)) else: - self.callback.message_box(_("Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.").format( + self.callback.message_box(_("Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.").format( download_name=port_nice_name, runtime_name=runtime_name)) diff --git a/PortMaster/pylibs/harbourmaster/hm_runtimes.py b/PortMaster/pylibs/harbourmaster/hm_runtimes.py index 62828eec..6520000f 100644 --- a/PortMaster/pylibs/harbourmaster/hm_runtimes.py +++ b/PortMaster/pylibs/harbourmaster/hm_runtimes.py @@ -139,7 +139,7 @@ def check_runtime(self, runtime, port_name=None, in_install=False): return 0 cprint(f"Unable to download {runtime} when offline") - self.callback.message_box(_("Unable do download a runtime when in offline mode.")) + self.callback.message_box(_("Unable to download a runtime in offline mode.")) return 255 if runtime_file.is_file(): diff --git a/PortMaster/pylibs/locales/de_DE.json b/PortMaster/pylibs/locales/de_DE.json index 51ae4250..42538892 100644 --- a/PortMaster/pylibs/locales/de_DE.json +++ b/PortMaster/pylibs/locales/de_DE.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Installiere Theme {download_name}.", "Installing": "Wird installiert", "Theme {download_name!r} installed successfully.": "Theme {download_name!r} erfolgreich installiert.", - "Port {download_name} installed failed.": "Port {download_name} Installation fehlgeschlagen.", "Installing {download_name}.": "Installiere {download_name}.", - "Port {download_name!r} installed successfully.": "Port {download_name!r} erfolgreich installiert.", "Installation failed, removing files...": "Installation fehlgeschlagen, entferne Dateien...", "Okay": "Okay", "Cancel": "Abbrechen", + "Port {download_name} failed to install.": "Port {download_name} Installation fehlgeschlagen.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} und {runtime_name!r} erfolgreich installiert.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} wurde erfolgreich installiert, Installation von {runtime_name!r} ist fehlgeschlagen\nInstalliere den Port erneut um es nochmal zu versuchen oder besuche das Wiki für Hilfe.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} wurde erfolgreich installiert, aber die Installation von {runtime_name!r} ist fehlgeschlagen!!\n\nInstalliere den Port erneut, um es nochmal zu versuchen, oder besuche das Wiki für Hilfe.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} erfolgreich installiert.", "Unable to download in offline mode.": "Download im Offline-Modus nicht möglich.", "Unable to find a source for {port_name}": "Es konnte keine Quelle für {port_name} gefunden werden.", "Unknown port {port_name}": "Unbekannter Port {port_name}", @@ -28,7 +28,7 @@ "Verified": "Überprüft", "Unverified": "Nicht verifiziert", "Broken": "Fehlerhaft", - "Unable do download a runtime when in offline mode.": "Runtimes lassen sich nicht im Offline Modus herunterladen.", + "Unable to download a runtime in offline mode.": "Runtimes lassen sich nicht im Offline Modus herunterladen.", "Verifying runtime {runtime}": "Überprüfe Runtime {runtime}", "Verifying": "Überprüfe", "Unable to find a download for {runtime}.": "Es konnte kein Download für {runtime} gefunden werden.", @@ -47,7 +47,6 @@ "First check.": "Erste Überprüfung.", "Auto Update.": "Automatische Aktualisierung", "Updating": "Aktualisiere", - "Up to date already": "Neuste Version bereits installiert", "Fetching latest info": "Lade neuste Informationen", "Done.": "Fertig", "Fetching info": "Lade Informationen", @@ -163,12 +162,12 @@ "Select Theme": "Theme auswählen", "Select a theme for PortMaster.": "Theme auswählen.", "Select Color Scheme": "Farbschema auswählen", - "Select a colour scheme for PortMaster": "Farbschema auswählen", + "Select a color scheme for PortMaster.": "Farbschema auswählen.", "Audio": "Audio", "Music: ": "Musik: ", "Enable or Disable background music in PortMaster.": "Hintergrundmusik in PortMaster aktivieren oder deaktivieren.", "Sound FX: ": "Sound FX: ", - "Enable or Disable soundfx in PortMaster.": "Soundfx in PortMaster aktivieren oder deaktivieren.", + "Enable or Disable sound effects in PortMaster.": "Soundfx in PortMaster aktivieren oder deaktivieren.", "Port Metadata": "Port-Metadaten", "Metadata Update: ": "Metadaten Update: ", "Auto": "Automatisch", @@ -212,11 +211,11 @@ "PortMaster will now quit so you can run the restoration port.": "PortMaster wird nun beendet, damit du den Wiederherstellungs-Port ausführen kannst.", "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Bist du sicher, dass du zum Alpha-Release-Kanal wechseln möchtest?\n\nPortMaster wird entsprechend upgraden oder downgraden.\n\nWARNUNG: DIES KANN UND WIRD PROBLEME VERURSACHEN", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Bist du sicher, dass du den Release-Kanal von {current_channel} auf {new_channel} ändern möchtest?\n\nPortMaster wird entsprechend upgraden oder downgraden.", - "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Möchten sie wirklich gamelist.xml aktualisieren?\n\n\n\nDadurch werden gespeicherte Designs oder Port-Informationen überschrieben und es wird ein 50+ MB Download benötigt.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Sind Sie sicher, dass Sie Ports von \"{from_mode}\" nach \"{to_mode}\" verschieben möchten", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Möchten Sie wirklich gamelist.xml aktualisieren?\n\nDadurch werden gespeicherte Designs oder Port-Informationen überschrieben und es wird ein 50+ MB Download benötigt.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Sind Sie sicher, dass Sie Ports von \"{from_mode}\" nach \"{to_mode}\" verschieben möchten?", "SDCARD 1": "SDKARTE 1", "SDCARD 2": "SDKARTE 2", - "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Möchten sie wirklich Ports auf {to_loc} installieren und verwalten?\n\n\nBereits installiere Ports werden nicht verschoben.\n\nPortMaster wird neu starten um die Änderungen anzuwenden.", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Möchten Sie wirklich Ports auf {to_loc} installieren und verwalten?\n\nBereits installierte Ports werden nicht verschoben.\nPortMaster wird neu starten, um die Änderungen anzuwenden.", "Deleting Runtimes:": "Lösche Runtimes:", "Source Manager": "Quellen-Manager", "Port Info Popup": "Port-Info-Popup", @@ -243,8 +242,8 @@ "Accept": "Akzeptieren", "{theme_name} (Selected)": "{theme_name} (Ausgewählt)", "Download": "Herunterladen", - "Select Colour Scheme": "Farbschema auswählen", "{item_name} (Selected)": "{item_name} (Ausgewählt)", + "Up to date already": "Neuste Version bereits installiert", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Es wird von {old_release_channel} zur {new_release_channel} Version gewechselt.\n\nSoll das Downgrade durchgeführt werden?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Es gibt eine neue Version von PortMaster ({portmaster_version})\n\nSoll das Upgrade durchgeführt werden?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "PortMaster wieder installieren?\n\nDies wird vom {release_channel} Kanal zur Version {portmaster_version} installiert." diff --git a/PortMaster/pylibs/locales/es_ES.json b/PortMaster/pylibs/locales/es_ES.json index 632fe6a9..b3c5b495 100644 --- a/PortMaster/pylibs/locales/es_ES.json +++ b/PortMaster/pylibs/locales/es_ES.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Instalando Tema {download_name}.", "Installing": "Instalando", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", - "Port {download_name} installed failed.": "La instalación del Port {download_name} ha fallado.", "Installing {download_name}.": "Instalando {download_name}.", - "Port {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", "Installation failed, removing files...": "Instalación fallida, eliminando archivos...", "Okay": "Aceptar", "Cancel": "Cancelar", + "Port {download_name} failed to install.": "La instalación del Port {download_name} ha fallado.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} y {runtime_name!r} instalados correctamente.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "¡¡Port {download_name!r} instalado correctamente, pero la instalación de {runtime_name!r} ha fallado!!\n\nReintenta la instalación o revisa la wiki para obtener ayuda.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "¡¡Port {download_name!r} instalado correctamente, pero la instalación de {runtime_name!r} ha fallado!!\n\nReintenta la instalación o revisa la wiki para obtener ayuda.", + "Port {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", "Unable to download in offline mode.": "No se puede descargar en modo desconectado.", "Unable to find a source for {port_name}": "No se ha podido encontrar una fuente para {port_name}", "Unknown port {port_name}": "Port desconocido {port_name}", @@ -28,7 +28,7 @@ "Verified": "Verificado", "Unverified": "Sin verificar", "Broken": "Dañado", - "Unable do download a runtime when in offline mode.": "No se puede descargar un tiempo de ejecución en modo sin conexión.", + "Unable to download a runtime in offline mode.": "No se puede descargar un tiempo de ejecución en modo sin conexión.", "Verifying runtime {runtime}": "Verificando el runtime {runtime}", "Verifying": "Verificando", "Unable to find a download for {runtime}.": "No se ha podido encontrar una descarga para {runtime}.", @@ -47,7 +47,6 @@ "First check.": "Primera verificación.", "Auto Update.": "Actualización automática.", "Updating": "Actualizando", - "Up to date already": "Ya está actualizado", "Fetching latest info": "Obteniendo información más reciente", "Done.": "Hecho.", "Fetching info": "Obteniendo información", @@ -162,13 +161,13 @@ "Select the language PortMaster uses.": "Elige el idioma que PortMaster usa.", "Select Theme": "Seleccionar Tema", "Select a theme for PortMaster.": "Elige el tema de PortMaster.", - "Select Color Scheme": "Seleccione el Esquema de Color", - "Select a colour scheme for PortMaster": "Elige un esquema de color para PortMaster", + "Select Color Scheme": "Elige el Esquema de Color", + "Select a color scheme for PortMaster.": "Elige un esquema de color para PortMaster.", "Audio": "Audio", "Music: ": "Música: ", "Enable or Disable background music in PortMaster.": "Activa o Desactiva la música de fondo de PortMaster.", "Sound FX: ": "Efectos: ", - "Enable or Disable soundfx in PortMaster.": "Habilita o Deshabilita los efectos de sonido en PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Habilita o Deshabilita los efectos de sonido en PortMaster.", "Port Metadata": "Metadatos del Port", "Metadata Update: ": "Actualización de metadatos: ", "Auto": "Auto", @@ -213,7 +212,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "¿Estás seguro de que deseas cambiar al canal de lanzamiento alfa?\n\nPortMaster actualizará o descenderá de versión dependiendo de lo que sea necesario.\n\nESTO PUEDE ROMPER (Y ROMPERÁ) COSAS, HAS SIDO ADVERTIDO", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Estas seguro que quieres cambiar el canal de lanzamiento de {current_channel} a {new_channel}?\n\nPortMaster se actualizará o hará downgrade en consecuencia.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "¿Estás seguro de que quieres actualizar el gamelist.xml?\n\nEsto anulará cualquier artwork o información de ports obtenidos y requerirá una descarga de más de 50 MB.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "¿Estás seguro de que quieres mover los ports de {from_mode} a {to_mode} ?", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "¿Estás seguro de que quieres mover los ports de {from_mode} a {to_mode} ?", "SDCARD 1": "SDCARD 1", "SDCARD 2": "SDCARD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "¿Estás seguro de que quieres administrar e instalar ports en {to_loc}?\n\nLos ports ya instalados no se moverán.\nPortMaster se reiniciará para que surta efecto.", @@ -243,8 +242,8 @@ "Accept": "Aceptar", "{theme_name} (Selected)": "{theme_name} (seleccionado)", "Download": "Descargar", - "Select Colour Scheme": "Elige el Esquema de Color", "{item_name} (Selected)": "{item_name} (seleccionado)", + "Up to date already": "Ya está actualizado", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Estas cambiando de la version {old_release_channel} a la version {new_release_channel}.\n\nQuieres hacer el downgrade?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Hay una nueva version de PortMaster ({portmaster_version})\n\nTe gustaría actualizarla?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Te gustaría reinstalar PortMaster?\n\nEsto va a reinstalar desde el canal {release_channel} al canal {portmaster_version}." diff --git a/PortMaster/pylibs/locales/fr_FR.json b/PortMaster/pylibs/locales/fr_FR.json index 5576a42d..48fde918 100644 --- a/PortMaster/pylibs/locales/fr_FR.json +++ b/PortMaster/pylibs/locales/fr_FR.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Installation du thème {download_name}.", "Installing": "Installation en cours", "Theme {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", - "Port {download_name} installed failed.": "Échec de l'installation du port {download_name}.", "Installing {download_name}.": "Installation de {download_name}.", - "Port {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", "Installation failed, removing files...": "Échec de l'installation, suppression des fichiers en cours...", "Okay": "D'accord", "Cancel": "Annuler", + "Port {download_name} failed to install.": "Échec de l'installation du port {download_name}.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} et {runtime_name!r} installé avec succès.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Le port {download_name!r} a été installé avec succès, mais {runtime_name!r} n'a pas pu être installé!!\n\nRéinstallez pour essayer à nouveau, ou consultez le wiki pour obtenir de l'aide.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Le port {download_name!r} a été installé avec succès, mais {runtime_name!r} n'a pas pu être installé!!\n\nRéinstallez pour essayer à nouveau, ou consultez le wiki pour obtenir de l'aide.", + "Port {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", "Unable to download in offline mode.": "Impossible de télécharger un runtime en mode hors ligne.", "Unable to find a source for {port_name}": "Impossible de trouver une source pour {port_name}", "Unknown port {port_name}": "Port inconnu {port_name}", @@ -28,7 +28,7 @@ "Verified": "Vérifié", "Unverified": "Non vérifié", "Broken": "Cassé", - "Unable do download a runtime when in offline mode.": "Impossible de télécharger un runtime en mode hors ligne.", + "Unable to download a runtime in offline mode.": "Impossible de télécharger un runtime en mode hors ligne.", "Verifying runtime {runtime}": "Vérification du runtime {runtime}", "Verifying": "Vérification", "Unable to find a download for {runtime}.": "Impossible de trouver un téléchargement pour {runtime}.", @@ -47,7 +47,6 @@ "First check.": "Première vérification.", "Auto Update.": "Mise à jour automatique.", "Updating": "Mise à jour en cours", - "Up to date already": "Déjà à jour", "Fetching latest info": "Récupération des dernières informations", "Done.": "Terminé.", "Fetching info": "Récupération des informations", @@ -163,12 +162,12 @@ "Select Theme": "Sélection du thème", "Select a theme for PortMaster.": "Sélectionnez un thème pour PortMaster.", "Select Color Scheme": "Sélection de la palette de couleurs", - "Select a colour scheme for PortMaster": "Sélectionnez un schéma de couleurs pour PortMaster", + "Select a color scheme for PortMaster.": "Sélectionnez un schéma de couleurs pour PortMaster.", "Audio": "Audio", "Music: ": "Musique : ", "Enable or Disable background music in PortMaster.": "Activer ou désactiver la musique de fond dans PortMaster.", "Sound FX: ": "Effets sonores : ", - "Enable or Disable soundfx in PortMaster.": "Activer ou désactiver les effets sonores dans PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Activer ou désactiver les effets sonores dans PortMaster.", "Port Metadata": "Métadonnées du port", "Metadata Update: ": "Mise à jour des métadonnées : ", "Auto": "Auto", @@ -213,7 +212,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Êtes-vous sûr de vouloir changer le canal de publication de PortMaster en alpha ?\n\nPortMaster effectuera une mise à jour ou passera à une version antérieure en conséquence.\n\nAVERTISSEMENT : CETTE MANIPULATION PEUT ET VA CRÉER DES PROBLÈMES.", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Êtes-vous sûr de vouloir changer le canal de publication de {current_channel} à {new_channel}?\n\nPortMaster va être mis à jour ou rétrogradé en conséquence.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Êtes-vous sûr de vouloir mettre à jour votre gamelist.xml ?\n\nCeci remplacera toutes les informations ou images des ports et nécessitera un téléchargement de plus de 50 Mo.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Êtes-vous sûr de vouloir déplacer les ports de la {from_mode} vers la {to_mode}", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Êtes-vous sûr de vouloir déplacer les ports de la {from_mode} vers la {to_mode}?", "SDCARD 1": "Carte SD 1", "SDCARD 2": "Carte SD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Êtes-vous sûr de vouloir gérer et installer des ports sur {to_loc}?\n\nLes ports déjà installés ne seront pas déplacés.\nPortMaster redémarrera pour que cela prenne effet.", @@ -243,8 +242,8 @@ "Accept": "Accepter", "{theme_name} (Selected)": "{theme_name} (Sélectionné)", "Download": "Télécharger", - "Select Colour Scheme": "Sélection de la palette de couleurs", "{item_name} (Selected)": "{item_name} (Sélectionné)", + "Up to date already": "Déjà à jour", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Vous passez de l' {old_release_channel} à la version {new_release_channel} .\n\nVoulez-vous rétrograder ?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Il y a une nouvelle version de PortMaster ({portmaster_version})\n\nVoulez-vous mettre à jour ?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Voulez-vous réinstaller PortMaster ?\n\nCela va réinstaller depuis le canal {release_channel} vers {portmaster_version}." diff --git a/PortMaster/pylibs/locales/it_IT.json b/PortMaster/pylibs/locales/it_IT.json index 81f6a6e7..58b94db7 100644 --- a/PortMaster/pylibs/locales/it_IT.json +++ b/PortMaster/pylibs/locales/it_IT.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Installo Tema {download_name}.", "Installing": "Installo", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} installato con successo.", - "Port {download_name} installed failed.": "{download_name} fallisce l'installazione.", "Installing {download_name}.": "Installazione {download_name}.", - "Port {download_name!r} installed successfully.": "Port {download_name!r} installato con successo.", "Installation failed, removing files...": "Installazione fallita, rimozione file in corso...", "Okay": "Ok", "Cancel": "Annulla", + "Port {download_name} failed to install.": "{download_name} fallisce l'installazione.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "{download_name!r} e {runtime_name!r} sono stati installati con successo", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} e {runtime_name!r} installazione fallita!\n\nRiprova o controlla la Fiji per ulteriori informazioni.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} e {runtime_name!r} installazione fallita!\n\nRiprova o controlla la Fiji per ulteriori informazioni.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} installato con successo.", "Unable to find a source for {port_name}": "Impossibile trovare una fonte per {port_name}", "Unknown port {port_name}": "Port sconosciuto {port_name}", "Removing {port_name}": "Rimuovo {port_name}", @@ -26,7 +26,7 @@ "Verified": "Verificato", "Unverified": "Non verificato", "Broken": "Corrotto", - "Unable do download a runtime when in offline mode.": "Impossibile scaricare la runtime in modalità offline", + "Unable to download a runtime in offline mode.": "Impossibile scaricare la runtime in modalità offline", "Verifying runtime {runtime}": "Verifico runtime {runtime}", "Verifying": "Verifica in corso", "Unable to find a download for {runtime}.": "Impossibile trovare il download per {runtime}", @@ -43,7 +43,6 @@ "First check.": "Controllo d'avvio", "Auto Update.": "Aggiornamento automatico.", "Updating": "Aggiorno", - "Up to date already": "Già aggiornata", "Fetching latest info": "Cerco nuove informazioni", "Done.": "Fatto.", "Fetching info": "Cerco Informazioni", @@ -142,13 +141,13 @@ "Select the language PortMaster uses.": "Seleziona la lingua usata da PortMaster.", "Select Theme": "Seleziona Tema", "Select a theme for PortMaster.": "Cambia il tuo attuale tema o scaricane di nuovi.", - "Select Color Scheme": "Seleziona Modalità Tema", - "Select a colour scheme for PortMaster": "Cambia schema colori dell'attuale tema", + "Select Color Scheme": "Selezionare schema colori", + "Select a color scheme for PortMaster.": "Cambia schema colori dell'attuale tema.", "Audio": "Audio", "Music: ": "Musica: ", "Enable or Disable background music in PortMaster.": "Abilita o disabilita la musica di sottofondo in PortMaster.", "Sound FX: ": "Effetti Sonori: ", - "Enable or Disable soundfx in PortMaster.": "Abilita o disabilita gli effetti sonori in PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Abilita o disabilita gli effetti sonori in PortMaster.", "Auto": "Auto", "Manual": "Manuale", "Metadata Refresh": "Aggiorna Metadati", @@ -197,8 +196,8 @@ "Deleted runtime {runtime}": "Runtime {runtime} eliminata", "{theme_name} (Selected)": "{theme_name} (Attuale)", "Download": "Download", - "Select Colour Scheme": "Selezionare schema colori", "{item_name} (Selected)": "{item_name} (Attuale)", + "Up to date already": "Già aggiornata", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Stai passando dalla versione {old_release_channel} alla versione {new_release_channel} .\n\nVuoi tornare alla versione precedente?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "C'è una nuova versione di PortMaster ({portmaster_version})\n\nVuoi aggiornare?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Vuoi reinstallare PortMaster?\n\nProseguendo scaricherai da {release_channel} la versione {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ja_JP.json b/PortMaster/pylibs/locales/ja_JP.json index 16432bdc..5de5c101 100644 --- a/PortMaster/pylibs/locales/ja_JP.json +++ b/PortMaster/pylibs/locales/ja_JP.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "テーマ {download_name} のインストール中。", "Installing": "インストール中", "Theme {download_name!r} installed successfully.": "テーマ {download_name!r} のインストールに成功しました。", - "Port {download_name} installed failed.": "ポート {download_name} がインストールされませんでした。", "Installing {download_name}.": "{download_name} インストール中.", - "Port {download_name!r} installed successfully.": "ポート {download_name!r} が正常にインストールされました。", "Installation failed, removing files...": "インストールに失敗しました。ファイルの削除中...", "Okay": "了解", "Cancel": "キャンセル", + "Port {download_name} failed to install.": "ポート {download_name} がインストールされませんでした。", "Port {download_name!r} and {runtime_name!r} installed successfully.": "ポート {download_name!r} と {runtime_name!r} が正常にインストールされました。", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "ポート {download_name!r} は正常にインストールされましたが、{runtime_name!r} のインストールに失敗しました!\n\n再インストールして再試行するか、ウィキのヘルプを確認してください。", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "ポート {download_name!r} は正常にインストールされましたが、{runtime_name!r} のインストールに失敗しました!\n\n再インストールして再試行するか、ウィキのヘルプを確認してください。", + "Port {download_name!r} installed successfully.": "ポート {download_name!r} が正常にインストールされました。", "Unable to find a source for {port_name}": "{port_name} のソースが見つかりません", "Unknown port {port_name}": "不明なポート {port_name}", "Removing {port_name}": "{port_name} を削除中", @@ -26,7 +26,7 @@ "Verified": "認証済み", "Unverified": "未確認", "Broken": "損壊", - "Unable do download a runtime when in offline mode.": "オフラインモードではランタイムをダウンロードできません。", + "Unable to download a runtime in offline mode.": "オフラインモードではランタイムをダウンロードできません。", "Verifying runtime {runtime}": "ランタイム {runtime} を確認しています", "Verifying": "検証中", "Unable to find a download for {runtime}.": "{runtime} のダウンロードが見つかりません。", @@ -43,7 +43,6 @@ "First check.": "最初のチェック", "Auto Update.": "自動更新", "Updating": "更新中", - "Up to date already": "既に最新の状態です", "Fetching latest info": "最新情報を取得中", "Done.": "完了", "Fetching info": "情報を取得中", @@ -151,12 +150,12 @@ "Select Theme": "テーマを選択", "Select a theme for PortMaster.": "PortMasterのテーマを選択", "Select Color Scheme": "配色を選択", - "Select a colour scheme for PortMaster": "PortMasterの配色を選択", + "Select a color scheme for PortMaster.": "PortMasterの配色を選択。", "Audio": "音声", "Music: ": "音楽: ", "Enable or Disable background music in PortMaster.": "PortMasterでBGMを有効または無効にします。", "Sound FX: ": "サウンド効果: ", - "Enable or Disable soundfx in PortMaster.": "PortMasterサウンド効果を有効または無効にします。", + "Enable or Disable sound effects in PortMaster.": "PortMasterサウンド効果を有効または無効にします。", "System": "システム", "Runtime Manager": "ランタイムの管理", "Manage port runtimes.": "ポートのランタイムを管理", @@ -196,8 +195,8 @@ "Deleted runtime {runtime}": "ランタイム {runtime} を削除しました", "{theme_name} (Selected)": "{theme_name} (選択済み)", "Download": "ダウンロード", - "Select Colour Scheme": "配色を選択", "{item_name} (Selected)": "{item_name} (選択済み)", + "Up to date already": "既に最新の状態です", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel} から {new_release_channel} へ切り替えています。\n\nダウングレードしますか?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster ({portmaster_version})\n\nアップグレードしますか?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "PortMasterを再インストールしますか?\n\n {release_channel} チャンネルから {portmaster_version} に再インストールします。" diff --git a/PortMaster/pylibs/locales/ko_KR.json b/PortMaster/pylibs/locales/ko_KR.json index 4db7bd9d..b6caaaea 100644 --- a/PortMaster/pylibs/locales/ko_KR.json +++ b/PortMaster/pylibs/locales/ko_KR.json @@ -7,15 +7,15 @@ "Installing Theme {download_name}.": "{download_name} 테마를 설치 중", "Installing": "설치 중", "Theme {download_name!r} installed successfully.": "{download_name!r} 테마 설치에 성공했습니다.", - "Port {download_name} installed failed.": "{download_name} 포트 설치에 실패했습니다.", "Installing {download_name}.": "{download_name} 설치 중", - "Port {download_name!r} installed successfully.": "{download_name!r} 포트가 정상적으로 설치되었습니다.", "Installation failed, removing files...": "설치에 실패했습니다. 파일 삭제 중...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "안녕하세요.\n\n기기와 호환되지 않는 이식본을 수동으로 설치하고 계신 것 같습니다.\n\n문제가 발생하면 Discord에 보고하지 마세요. 조롱을 당할 수 있습니다.\n\n설치를 계속하시겠습니까?", "Okay": "확인", "Cancel": "취소", + "Port {download_name} failed to install.": "{download_name} 포트 설치에 실패했습니다.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "{download_name!r} 포트와 {runtime_name!r} 가 정상적으로 설치되었습니다.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} 포트는 정상적으로 설치되었으나, {runtime_name!r} 설치에 실패했습니다!\n\n재시도하려면 다시 설치하거나 위키에서 도움말을 확인하세요.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} 포트는 정상적으로 설치되었으나, {runtime_name!r} 설치에 실패했습니다!\n\n재시도하려면 다시 설치하거나 위키에서 도움말을 확인하세요.", + "Port {download_name!r} installed successfully.": "{download_name!r} 포트가 정상적으로 설치되었습니다.", "Unable to download in offline mode.": "오프라인 모드에서는 다운로드할 수 없습니다.", "Unable to find a source for {port_name}": "{port_name} 소스를 찾을 수 없습니다.", "Unknown port {port_name}": "알 수 없는 포트 {port_name}", @@ -30,7 +30,7 @@ "Verified": "검증 완료", "Unverified": "미 검증", "Broken": "파손", - "Unable do download a runtime when in offline mode.": "오프라인 모드에서는 런타임을 다운로드할 수 없습니다.", + "Unable to download a runtime in offline mode.": "오프라인 모드에서는 런타임을 다운로드할 수 없습니다.", "Verifying runtime {runtime}": "런타임 {runtime}를 검증 중", "Verifying": "검증 중", "Unable to find a download for {runtime}.": "{runtime} 다운로드를 찾을 수 없습니다.", @@ -49,7 +49,6 @@ "First check.": "최초 확인", "Auto Update.": "자동 업데이트", "Updating": "업데이트 중", - "Up to date already": "이미 최신 상태입니다.", "Fetching latest info": "최신 정보를 가져오는 중", "Done.": "완료.", "Fetching info": "정보 가져오는 중", @@ -165,13 +164,13 @@ "Select the language PortMaster uses.": "사용할 언어를 선택하세요.", "Select Theme": "테마 선택", "Select a theme for PortMaster.": "사용할 테마를 선택하세요.", - "Select Color Scheme": "색 구성표 선택", - "Select a colour scheme for PortMaster": "색상 구성표를 선택하세요.", + "Select Color Scheme": "색상 구성표 선택", + "Select a color scheme for PortMaster.": "색상 구성표를 선택하세요.", "Audio": "오디오", "Music: ": "음악: ", "Enable or Disable background music in PortMaster.": "배경 음악을 활성화 또는 비활성화합니다. ", "Sound FX: ": "효과음: ", - "Enable or Disable soundfx in PortMaster.": "효과음을 활성화 또는 비활성화합니다.", + "Enable or Disable sound effects in PortMaster.": "효과음을 활성화 또는 비활성화합니다.", "Port Metadata": "포트 메타데이터", "Metadata Update: ": "메타데이터 업데이트: ", "Auto": "자동", @@ -216,7 +215,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "알파 업데이트 채널로 변경하시겠습니까?\n\n포트마스터는 업그레이드 또는 다운그레이드됩니다.\n\n이것은 프로그램을 손상시킬 수 있습니다. 경고했습니다.", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "업데이트 채널을 {current_channel}에서 {new_channel}로 변경하시겠습니까?\n\n이에 따라 업그레이드 또는 다운그레이드됩니다.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "gamelist.xml을 업데이트하시겠습니까?\n\n이렇게 하면 스크래핑된 아트워크나 포트 정보가 무시되고 50MB 이상의 다운로드가 필요합니다.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "포트를 {from_mode}에서 {to_mode}로 이동하시겠습니까?", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "포트를 {from_mode}에서 {to_mode}로 이동하시겠습니까?", "SDCARD 1": "SD카드 1", "SDCARD 2": "SD카드 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "{to_loc}에서 포트를 관리하고 설치하시겠습니까?\n\n이미 설치된 포트는 이동하지 않습니다.\n이 기능을 적용하려면 포트마스터가 재시작됩니다.", @@ -246,8 +245,8 @@ "Accept": "수락", "{theme_name} (Selected)": "{theme_name} (선택됨)", "Download": "다운로드", - "Select Colour Scheme": "색상 구성표 선택", "{item_name} (Selected)": "{item_name} (선택됨)", + "Up to date already": "이미 최신 상태입니다.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel}에서 {new_release_channel}로 전환하고 있습니다.\n\n다운그레이드하시겠습니까?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "새로운 ({portmaster_version}) 버전이 있습니다.\n\n업그레이드하시겠습니까?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "포트마스터를 다시 설치 하겠습니까?\n\n {release_channel} 채널에서 {portmaster_version}로 다시 설치합니다." diff --git a/PortMaster/pylibs/locales/nl_NL.json b/PortMaster/pylibs/locales/nl_NL.json index 6a386662..2cf9d748 100644 --- a/PortMaster/pylibs/locales/nl_NL.json +++ b/PortMaster/pylibs/locales/nl_NL.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Thema {download_name} wordt geïnstalleerd.", "Installing": "Aan het installeren", "Theme {download_name!r} installed successfully.": "Thema {download_name!r} is succesvol geïnstalleerd.", - "Port {download_name} installed failed.": "Port {download_name} installatie mislukt.", "Installing {download_name}.": "{download_name} wordt geïnstalleerd.", - "Port {download_name!r} installed successfully.": "Port {download_name!r} is succesvol geïnstalleerd.", "Installation failed, removing files...": "Installatie mislukt, bestanden verwijderen...", "Okay": "Oké", "Cancel": "Annuleren", + "Port {download_name} failed to install.": "Port {download_name} installatie mislukt.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} en {runtime_name!r} succesvol geïnstalleerd.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} is succesvol geïnstalleerd, maar {runtime_name!r} installatie is mislukt!!!\n\nHerinstalleer om het opnieuw te proberen, of controleer de wiki voor hulp.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} is succesvol geïnstalleerd, maar {runtime_name!r} installatie is mislukt!!!\n\nHerinstalleer om het opnieuw te proberen, of controleer de wiki voor hulp.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} is succesvol geïnstalleerd.", "Unable to download in offline mode.": "Kan geen port downloaden in offline modus.", "Unable to find a source for {port_name}": "Kan geen bron vinden voor {port_name}", "Unknown port {port_name}": "Onbekende port {port_name}", @@ -28,7 +28,7 @@ "Verified": "Geverifiëerd", "Unverified": "Niet geverifiëerd", "Broken": "Defect", - "Unable do download a runtime when in offline mode.": "Kan geen runtime downloaden in offline modus.", + "Unable to download a runtime in offline mode.": "Kan geen runtime downloaden in offline modus.", "Verifying runtime {runtime}": "Runtime {runtime} verifiëren", "Verifying": "Verifiëren", "Unable to find a download for {runtime}.": "Kan geen download vinden voor {runtime}.", @@ -47,7 +47,6 @@ "First check.": "Eerste controle.", "Auto Update.": "Automatisch Bijwerken.", "Updating": "Bijwerken", - "Up to date already": "Al bijgewerkt", "Fetching latest info": "Nieuwste informatie ophalen", "Done.": "Klaar.", "Fetching info": "Informatie ophalen", @@ -163,12 +162,12 @@ "Select Theme": "Selecteer Thema", "Select a theme for PortMaster.": "Selecteer een thema voor PortMaster.", "Select Color Scheme": "Selecteer Kleurenschema", - "Select a colour scheme for PortMaster": "Selecteer een kleurenschema voor PortMaster", + "Select a color scheme for PortMaster.": "Selecteer een kleurenschema voor PortMaster.", "Audio": "Audio", "Music: ": "Muziek: ", "Enable or Disable background music in PortMaster.": "Achtergrondmuziek voor PortMaster in- of uitschakelen.", "Sound FX: ": "Geluidseffecten: ", - "Enable or Disable soundfx in PortMaster.": "Geluidseffecten voor PortMaster in- of uitschakelen.", + "Enable or Disable sound effects in PortMaster.": "Geluidseffecten voor PortMaster in- of uitschakelen.", "Port Metadata": "Port Metadata", "Metadata Update: ": "Metadata Update: ", "Auto": "Auto", @@ -213,7 +212,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Weet u zeker dat u wilt veranderen naar het alpha-release kanaal?\n\nPortMaster zal worden geüpgraded of gedegradeerd.\n\nDIT KAN EN ZAL DINGEN BREKEN, U BEN GEWAARSCHUWD", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Weet u zeker dat u het release-kanaal van {current_channel} naar {new_channel} wilt wijzigen?\n\nPortMaster zal worden geüpgraded of gedegradeerd naar toepassing.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Weet u zeker dat u uw gamelist.xml wilt updaten?\n\nDit overschrijft alle gescrapede bestands- of portinformatie en vereist een download van 50+ MB.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Weet u zeker dat u ports van {from_mode} naar {to_mode} wilt verplaatsen", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Weet u zeker dat u ports van {from_mode} naar {to_mode} wilt verplaatsen?", "SDCARD 1": "SD KAART 1", "SDCARD 2": "SD KAART 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Weet je zeker dat je poorten wilt beheren en installeren op {to_loc}?\n\nReeds geïnstalleerde poorten worden niet verplaatst.\nPortMaster zal opnieuw opstarten om dit door te voeren.", @@ -243,8 +242,8 @@ "Accept": "Accepteren", "{theme_name} (Selected)": "{theme_name} (Geselecteerd)", "Download": "Download", - "Select Colour Scheme": "Selecteer Kleurenschema", "{item_name} (Selected)": "{item_name} (Geselecteerd)", + "Up to date already": "Al bijgewerkt", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "U wisselt van {old_release_channel} naar {new_release_channel} versie.\n\nWilt u downgraden?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Er is een nieuwe versie van PortMaster ({portmaster_version})\n\nWilt u upgraden?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Wilt u PortMaster opnieuw installeren?\n\nDit zal opnieuw installeren van het {release_channel} kanaal naar {portmaster_version}." diff --git a/PortMaster/pylibs/locales/nl_NL.themes.json b/PortMaster/pylibs/locales/nl_NL.themes.json index 07e1de7e..6011cda7 100644 --- a/PortMaster/pylibs/locales/nl_NL.themes.json +++ b/PortMaster/pylibs/locales/nl_NL.themes.json @@ -8,7 +8,7 @@ "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Auteur: {theme_info.creator}\nStatus: {theme_info.status}", "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nRuimte: {system.free_space} beschikbaar", "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", - "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres} Download Grootte: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genres: {port_info.genres}\nDownload Grootte: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", "Time: {system.time_24hr} Battery: {system.battery_level}": "Tijd: {system.time_24hr} Batterij: {system.battery_level}", "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "Beschikbare ruimte: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Grootte: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nInstructie: {port_info.description}", "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Beschikbare ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}", diff --git a/PortMaster/pylibs/locales/pl_PL.json b/PortMaster/pylibs/locales/pl_PL.json index 32ee7ae1..af21360f 100644 --- a/PortMaster/pylibs/locales/pl_PL.json +++ b/PortMaster/pylibs/locales/pl_PL.json @@ -7,15 +7,15 @@ "Installing Theme {download_name}.": "Instalowanie motywu {download_name}.", "Installing": "Instalowanie", "Theme {download_name!r} installed successfully.": "Motyw {download_name!r} zainstalowany pomyślnie.", - "Port {download_name} installed failed.": "Port {download_name} nie został zainstalowany.", "Installing {download_name}.": "Instalacja {download_name}.", - "Port {download_name!r} installed successfully.": "Port {download_name!r} zainstalowany pomyślnie.", "Installation failed, removing files...": "Instalacja nie powiodła się, usuwanie plików...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "CZEŚĆ.\n\nWYGLĄDA NA TO, ŻE RĘCZNIE INSTALUJESZ PORT, KTÓRY JEST NIEZGODNY Z TWOIM URZĄDZENIEM.\n\nJEŚLI MASZ PROBLEMY, NIE ZGŁASZAJ ICH NA DISCORDZIE, GDYŻ NARADZISZ SIĘ.\n\nCZY NA PEWNO CHCESZ WZNOWIĆ INSTALACJĘ?", "Okay": "W porządku", "Cancel": "Anuluj", + "Port {download_name} failed to install.": "Port {download_name} nie został zainstalowany.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} i {runtime_name!r} zainstalowany pomyślnie.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} zainstalowany pomyślnie, ale {runtime_name!r} nie udało się zainstalować!\n\nZainstaluj ponownie, aby spróbować ponownie, lub sprawdź wiki po pomoc.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} zainstalowany pomyślnie, ale {runtime_name!r} nie udało się zainstalować!\n\nZainstaluj ponownie, aby spróbować ponownie, lub sprawdź wiki po pomoc.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} zainstalowany pomyślnie.", "Unable to download in offline mode.": "Nie można pobrać w trybie offline.", "Unable to find a source for {port_name}": "Nie można odnaleźć źródła dla {port_name}", "Unknown port {port_name}": "Nieznany port {port_name}", @@ -30,7 +30,7 @@ "Verified": "Zweryfikowane", "Unverified": "Niezweryfikowane", "Broken": "Uszkodzone", - "Unable do download a runtime when in offline mode.": "Nie można pobrać runtime w trybie offline.", + "Unable to download a runtime in offline mode.": "Nie można pobrać runtime w trybie offline.", "Verifying runtime {runtime}": "Weryfikowanie czasu pracy {runtime}", "Verifying": "Weryfikowanie", "Unable to find a download for {runtime}.": "Nie można znaleźć pobrania dla {runtime}.", @@ -49,7 +49,6 @@ "First check.": "Pierwsze sprawdzenie.", "Auto Update.": "Automatyczna aktualizacja.", "Updating": "Aktualizowanie", - "Up to date already": "Już aktualne", "Fetching latest info": "Pobieranie najnowszych informacji", "Done.": "Zrobione.", "Fetching info": "Pobieram informacje", @@ -166,12 +165,12 @@ "Select Theme": "Wybierz motyw", "Select a theme for PortMaster.": "Wybierz motyw dla PortMaster.", "Select Color Scheme": "Wybierz schemat kolorów", - "Select a colour scheme for PortMaster": "Wybierz schemat kolorów dla PortMaster", + "Select a color scheme for PortMaster.": "Wybierz schemat kolorów dla PortMaster.", "Audio": "Dźwięk", "Music: ": "Muzyka: ", "Enable or Disable background music in PortMaster.": "Włącz lub wyłącz muzykę w tle w PortMaster.", "Sound FX: ": "Efekty dźwiękowe: ", - "Enable or Disable soundfx in PortMaster.": "Włącz lub wyłącz efekty dźwiękowe w PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Włącz lub wyłącz efekty dźwiękowe w PortMaster.", "Port Metadata": "Metadane portu", "Metadata Update: ": "Aktualizacja metadanych: ", "Auto": "Auto", @@ -216,7 +215,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Czy na pewno chcesz przejść na kanał wersji alfa?\n\nPortMaster odpowiednio zaktualizuje lub obniży wersję.\n\nTO MOŻE ZEPSUĆ PEWNE RZECZY, ZOSTAŁEŚ OSTRZEŻONY", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Czy na pewno chcesz zmienić kanał wydania z {current_channel} na {new_channel}?\n\nPortMaster odpowiednio uaktualni lub obniży wersję.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Czy na pewno chcesz zaktualizować swoją listę gamelist.xml?\n\nZastąpi to wszelkie scrapowane informacje o grafice lub porcie.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Czy na pewno chcesz przenieść porty z {from_mode} do {to_mode}", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Czy na pewno chcesz przenieść porty z {from_mode} do {to_mode}?", "SDCARD 1": "SDCARD 1", "SDCARD 2": "SDCARD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Czy na pewno chcesz zarządzać i instalować porty na {to_loc}?\n\nZainstalowane porty nie zostaną przeniesione.\nPortMaster uruchomi się ponownie, aby to zadziałało.", @@ -246,8 +245,8 @@ "Accept": "Akceptuj", "{theme_name} (Selected)": "{theme_name} (Wybrany)", "Download": "Pobierz", - "Select Colour Scheme": "Wybierz schemat kolorów", "{item_name} (Selected)": "{item_name} (Wybrany)", + "Up to date already": "Już aktualne", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Przechodzisz z {old_release_channel} na wersję {new_release_channel}.\n\nCzy chcesz kontynuować?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Dostępna jest nowa wersja PortMaster ({portmaster_version})\n\nCzy chcesz zaktualizować?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Czy chcesz ponownie zainstalować PortMaster?\n\nZostanie to ponownie zainstalowane z kanału {release_channel} do {portmaster_version}." diff --git a/PortMaster/pylibs/locales/pt_BR.json b/PortMaster/pylibs/locales/pt_BR.json index 9b5d9f0d..4ee17d87 100644 --- a/PortMaster/pylibs/locales/pt_BR.json +++ b/PortMaster/pylibs/locales/pt_BR.json @@ -7,15 +7,15 @@ "Installing Theme {download_name}.": "Instalando tema {download_name}.", "Installing": "Instalando", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} instalado com sucesso.", - "Port {download_name} installed failed.": "Jogo {download_name} falhou na instalação.", "Installing {download_name}.": "Instalando {download_name}.", - "Port {download_name!r} installed successfully.": "Jogo {download_name!r} instalado com sucesso.", "Installation failed, removing files...": "Instalação falhou, removendo arquivos...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "OLÁ.\n\nPARECE QUE VOCÊ ESTÁ INSTALANDO MANUALMENTE UM JOGO QUE NÃO É COMPATÍVEL COM SEU APARELHO.\n\nSE VOCÊ TIVER PROBLEMAS, NÃO REPORTE ELES NO DISCORD, VOCÊ ESTARÁ SUJEITO A SER RIDICULARIZADO.\n\nVOCÊ TEM CERTEZA QUE QUER CONTINUAR INSTALANDO?", "Okay": "Ok", "Cancel": "Cancelar", + "Port {download_name} failed to install.": "Jogo {download_name} falhou na instalação.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Jogo {download_name!r} e {runtime_name!r} instalados com sucesso.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Jogo {download_name!r} instalado com sucesso, mas {runtime_name!r} falhou em instalar!\n\nReinstale para tentar novamente ou cheque o site para ajuda.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Jogo {download_name!r} instalado com sucesso, mas {runtime_name!r} falhou em instalar!\n\nReinstale para tentar novamente ou cheque o site para ajuda.", + "Port {download_name!r} installed successfully.": "Jogo {download_name!r} instalado com sucesso.", "Unable to download in offline mode.": "Impossível baixar no modo offline.", "Unable to find a source for {port_name}": "Impossível encontrar uma fonte para {port_name}", "Unknown port {port_name}": "Jogo desconhecido {port_name}", @@ -30,7 +30,7 @@ "Verified": "Verificado", "Unverified": "Não verificado", "Broken": "Corrompido", - "Unable do download a runtime when in offline mode.": "Não foi possível baixar o ambiente de execução no modo desconectado.", + "Unable to download a runtime in offline mode.": "Não foi possível baixar o ambiente de execução no modo desconectado.", "Verifying runtime {runtime}": "Verificando tempo de execução {runtime}", "Verifying": "Verificando", "Unable to find a download for {runtime}.": "Impossivel encontrar um download para {runtime}.", @@ -49,7 +49,6 @@ "First check.": "Primeira verificação.", "Auto Update.": "Atualização automática.", "Updating": "Atualizando", - "Up to date already": "Já está atualizado", "Fetching latest info": "Buscando informação mais recente", "Done.": "Concluído.", "Fetching info": "Buscando informação", @@ -166,12 +165,12 @@ "Select Theme": "Selecionar Tema", "Select a theme for PortMaster.": "Selecione um tema para PortMaster.", "Select Color Scheme": "Selecionar Esquema de Cores", - "Select a colour scheme for PortMaster": "Selecionar um esquema de cores para PortMaster", + "Select a color scheme for PortMaster.": "Selecionar um esquema de cores para PortMaster.", "Audio": "Áudio", "Music: ": "Música: ", "Enable or Disable background music in PortMaster.": "Ativar ou Desativar música de fundo em PortMaster.", "Sound FX: ": "Efeitos Sonoros: ", - "Enable or Disable soundfx in PortMaster.": "Ativar ou Desativar efeitos sonoros em PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Ativar ou Desativar efeitos sonoros em PortMaster.", "Port Metadata": "Metadados de Port", "Metadata Update: ": "Atualização de metadados: ", "Auto": "Auto", @@ -216,7 +215,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Você tem certeza que quer trocar para o canal alpha de lançamento?\n\nPortmaster vai atualizar ou reverter de acordo com ele.\n\nISSO PODE E VAI QUEBRAR COISAS, VOCÊ FOI AVISADO", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Você tem certeza que quer alterar o canal de lançamento de {current_channel} para {new_channel}?\n\nPortMaster irá atualizar ou regredir de acordo.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Você tem certeza que quer atualizar seu gamelist.xml?\n\nIsso vai substituir qualquer arte ou informação baixada e necessitará um download de 50+ MB.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Tem certeza que quer mover jogos de {from_mode} para {to_mode}", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Tem certeza que quer mover jogos de {from_mode} para {to_mode}?", "SDCARD 1": "Cartão SD 1", "SDCARD 2": "Cartão SD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Você tem certeza que quer gerenciar e instalar jogos em {to_loc}?\n\nJogos já instalados não serão movidos.\nPortMaster irá reiniciar para isso ter efeito.", @@ -246,8 +245,8 @@ "Accept": "Aceitar", "{theme_name} (Selected)": "{theme_name} (Selecionado)", "Download": "Baixar", - "Select Colour Scheme": "Selecionar Esquema de Cores", "{item_name} (Selected)": "{item_name} (Selecionado)", + "Up to date already": "Já está atualizado", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Você está trocando da versão {old_release_channel} para a versão {new_release_channel}.\n\nVocê deseja retroceder a versão?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Há uma nova versão do Portmaster ({portmaster_version})\n\nDeseja atualizar?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Você quer reinstalar PortMaster?\n\nIsso irá reinstalar do canal {release_channel} para {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ro_RO.json b/PortMaster/pylibs/locales/ro_RO.json index 1ca2ce4d..e691c922 100644 --- a/PortMaster/pylibs/locales/ro_RO.json +++ b/PortMaster/pylibs/locales/ro_RO.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Se instalează tema {download_name}.", "Installing": "Se instalează", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} a fost instalată cu succes.", - "Port {download_name} installed failed.": "Instalarea portului {download_name} a eșuat.", "Installing {download_name}.": "Se instalează {download_name}.", - "Port {download_name!r} installed successfully.": "Portul {download_name!r} a fost instalat cu succes.", "Installation failed, removing files...": "Instalarea a eșuat, se elimină fișierele...", "Okay": "OK", "Cancel": "Anulează", + "Port {download_name} failed to install.": "Instalarea portului {download_name} a eșuat.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Portul {download_name!r} și {runtime_name!r} au fost instalate cu succes.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Portul {download_name!r} a fost instalat cu succes, dar instalarea pentru {runtime_name!r} a eșuat!!\n\nPoți reîncerca instalarea sau verifica wiki-ul pentru ajutor.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Portul {download_name!r} a fost instalat cu succes, dar instalarea pentru {runtime_name!r} a eșuat!!\n\nPoți reîncerca instalarea sau verifica wiki-ul pentru ajutor.", + "Port {download_name!r} installed successfully.": "Portul {download_name!r} a fost instalat cu succes.", "Unable to download in offline mode.": "Imposibil de descărcat în modul offline.", "Unable to find a source for {port_name}": "Nu s-a găsit o sursă pentru {port_name}.", "Unknown port {port_name}": "Port necunoscut {port_name}.", @@ -28,7 +28,7 @@ "Verified": "Verificat", "Unverified": "Neverificat", "Broken": "Defect", - "Unable do download a runtime when in offline mode.": "Imposibil de descărcat un runtime în modul offline.", + "Unable to download a runtime in offline mode.": "Imposibil de descărcat un runtime în modul offline.", "Verifying runtime {runtime}": "Se verifică runtime-ul {runtime}", "Verifying": "Se verifică", "Unable to find a download for {runtime}.": "Nu s-a găsit un fișier de descărcare pentru {runtime}.", @@ -47,7 +47,6 @@ "First check.": "Prima verificare.", "Auto Update.": "Actualizare Automată.", "Updating": "Se actualizează", - "Up to date already": "Deja la zi", "Fetching latest info": "Se preiau cele mai recente informații", "Done.": "Gata.", "Fetching info": "Se preiau informațiile", @@ -159,11 +158,11 @@ "Select Theme": "Selectează Tema", "Select a theme for PortMaster.": "Selectează o temă pentru PortMaster.", "Select Color Scheme": "Selectează Schema de Culori", - "Select a colour scheme for PortMaster": "Selectează o schemă de culori pentru PortMaster", + "Select a color scheme for PortMaster.": "Selectează o schemă de culori pentru PortMaster.", "Music: ": "Muzică: ", "Enable or Disable background music in PortMaster.": "Activează sau dezactivează muzica de fundal în PortMaster.", "Sound FX: ": "Efecte Sonore: ", - "Enable or Disable soundfx in PortMaster.": "Activează sau dezactivează efectele sonore în PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Activează sau dezactivează efectele sonore în PortMaster.", "Port Metadata": "Metadate Port", "Metadata Update: ": "Actualizare Metadate: ", "Auto": "Automată", @@ -206,7 +205,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Ești sigur că vrei să schimbi canalul de distribuție la versiunea alpha?\n\nPortMaster va face upgrade sau downgrade în consecință.\n\nACEASTA POATE ȘI VA PROVOCA PROBLEME, AI FOST AVERTIZAT", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Ești sigur că vrei să schimbi canalul de distribuție de la {current_channel} la {new_channel}?\n\nPortMaster va face upgrade sau downgrade în consecință.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Ești sigur că vrei să actualizezi gamelist.xml?\n\nAceasta va suprascrie orice imagine sau informație despre port colectată automat și va necesita o descărcare de peste 50 MB.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Ești sigur că vrei să muți porturile din {from_mode} în {to_mode}?", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Ești sigur că vrei să muți porturile din {from_mode} în {to_mode}?", "SDCARD 1": "Card SD 1", "SDCARD 2": "Card SD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Ești sigur că vrei să gestionezi și să instalezi porturi pe {to_loc}?\n\nPorturile deja instalate nu vor fi mutate.\nPortMaster va reporni pentru ca această modificare să aibă efect.", @@ -236,8 +235,8 @@ "Accept": "Acceptă", "{theme_name} (Selected)": "{theme_name} (Selectat)", "Download": "Descarcă", - "Select Colour Scheme": "Selectează Schema de Culori", "{item_name} (Selected)": "{item_name} (Selectat)", + "Up to date already": "Deja la zi", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Schimbi de la versiunea {old_release_channel} la versiunea {new_release_channel}.\n\nDorești să faci un downgrade?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Există o nouă versiune a PortMaster ({portmaster_version})\n\nDorești să faci un upgrade?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Dorești să reinstalezi PortMaster?\n\nAceasta va reinstala din canalul {release_channel} la versiunea {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ro_RO.themes.json b/PortMaster/pylibs/locales/ro_RO.themes.json index 93ff45cc..0c472efa 100644 --- a/PortMaster/pylibs/locales/ro_RO.themes.json +++ b/PortMaster/pylibs/locales/ro_RO.themes.json @@ -1,6 +1,6 @@ { "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Timp: {system.time_24hr}\nBaterie: {system.battery_level}\nSpațiu: {system.free_space} liber", - "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genuri: {port_info.genres}\nDimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genuri: {port_info.genres} Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Porturi Disponibile: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltre: {ports_list.filters}{endif}", "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Spațiu Liber: {system.free_space}\n\nPorter: {port_info.porter}\nGenuri: {port_info.genres}\nDimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstrucțiuni: {port_info.instructions}", "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpațiu: {system.free_space} liber / {system.total_space} total\nTimp: {system.time_24hr}\nBaterie: {system.battery_level}", diff --git a/PortMaster/pylibs/locales/ru_RU.json b/PortMaster/pylibs/locales/ru_RU.json index 57a0d188..b1ae1167 100644 --- a/PortMaster/pylibs/locales/ru_RU.json +++ b/PortMaster/pylibs/locales/ru_RU.json @@ -7,15 +7,15 @@ "Installing Theme {download_name}.": "Установка темы {download_name}.", "Installing": "Установка", "Theme {download_name!r} installed successfully.": "Тема {download_name!r} успешно установлена.", - "Port {download_name} installed failed.": "Не удалось установить порт {download_name}.", "Installing {download_name}.": "Установка {download_name}.", - "Port {download_name!r} installed successfully.": "Порт {download_name!r} успешно установлен.", "Installation failed, removing files...": "Установка не удалась, удаление файлов...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "ПРИВЕТ.\n\nКАЖЕТСЯ, ВЫ ВРУЧНУЮ УСТАНАВЛИВАЕТЕ ПОРТ, НЕСОВМЕСТИМЫЙ С ВАШИМ УСТРОЙСТВОМ.\n\nЕСЛИ У ВАС ВОЗНИКЛИ ПРОБЛЕМЫ, НЕ СООБЩАЙТЕ О НИХ В DISCORD, ВЫ СТАНЕТЕ ОБЪЕКТОМ НАСМЕШЕК.\n\nВЫ УВЕРЕНЫ, ЧТО ХОТИТЕ ПРОДОЛЖИТЬ УСТАНОВКУ?", "Okay": "Ок", "Cancel": "Отменить", + "Port {download_name} failed to install.": "Не удалось установить порт {download_name}.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Порт {download_name!r} и {runtime_name!r} успешно установлены.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успешно установлен, но {runtime_name!r} не удалось установить!\n\nПопробуйте переустановить или обратитесь за помощью на вики.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успешно установлен, но {runtime_name!r} не удалось установить!\n\nПопробуйте переустановить или обратитесь за помощью на вики.", + "Port {download_name!r} installed successfully.": "Порт {download_name!r} успешно установлен.", "Unable to download in offline mode.": "Невозможно загрузить в автономном режиме.", "Unable to find a source for {port_name}": "Для {port_name} не удается найти источник", "Unknown port {port_name}": "Неизвестный порт {port_name}", @@ -30,7 +30,7 @@ "Verified": "Проверенный", "Unverified": "Непроверенный", "Broken": "Не рабочий", - "Unable do download a runtime when in offline mode.": "Невозможно загрузить среду исполнения в автономном режиме.", + "Unable to download a runtime in offline mode.": "Невозможно загрузить среду исполнения в автономном режиме.", "Verifying runtime {runtime}": "Проверка среды исполнения {runtime}", "Verifying": "Проверка", "Unable to find a download for {runtime}.": "Не удалось найти загрузку для {runtime}.", @@ -49,7 +49,6 @@ "First check.": "Первая проверка.", "Auto Update.": "Автоматическое обновление.", "Updating": "Обновление", - "Up to date already": "Актуальная версия уже существует", "Fetching latest info": "Получение последней информации", "Done.": "Готово.", "Fetching info": "Получение информации", @@ -63,7 +62,7 @@ "Unknown IP": "Неизвестный IP", "N/A": "Н/Д", "Fetching latest gameinfo.xml files and cover images.": "Получение последних файлов gameinfo.xml и обложек.", - "Downloading gameinfo.xml for {source_name}": "Скачивание gameinfo.xml для\n{source_name}", + "Downloading gameinfo.xml for {source_name}": "Скачивание gameinfo.xml для {source_name}", "Installing {port_name}": "Установка {port_name}", "Uninstalling {port_name}": "Удаление {port_name}", "Updating all port sources:": "Обновление всех источников портов:", @@ -166,12 +165,12 @@ "Select Theme": "Выбрать тему", "Select a theme for PortMaster.": "Изменить тему PortMaster.", "Select Color Scheme": "Выбрать цветовую схему", - "Select a colour scheme for PortMaster": "Изменить цветовую схему PortMaster", + "Select a color scheme for PortMaster.": "Изменить цветовую схему PortMaster.", "Audio": "Звук", "Music: ": "Музыка: ", "Enable or Disable background music in PortMaster.": "Включить или отключить фоновую музыку в PortMaster.", "Sound FX: ": "Звуковые эффекты: ", - "Enable or Disable soundfx in PortMaster.": "Включить или выключить звуковые эффекты в PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Включить или выключить звуковые эффекты в PortMaster.", "Port Metadata": "Метаданные порта", "Metadata Update: ": "Обновление метаданных: ", "Auto": "Авто", @@ -216,7 +215,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Вы уверены, что хотите изменить канал выпуска на альфа?\n\nPortMaster обновится или понизится соответствующим образом.\n\nЭТО МОЖЕТ СЛОМАТЬ НЕКОТОРЫЕ ФУНКЦИИ, ВАС ПРЕДУПРЕЖДАЛИ", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Вы уверены, что хотите изменить канал выпуска с {current_channel} на {new_channel}?\n\nPortMaster обновится или понизится соответствующим образом.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Вы уверены, что хотите обновить файл gamelist.xml?\n\nЭто переопределит любые изображения или информацию о порте, и потребуется загрузка объемом более 50 МБ.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Вы уверены, что хотите переместить порты из {from_mode} в {to_mode}", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Вы уверены, что хотите переместить порты из {from_mode} в {to_mode}?", "SDCARD 1": "SDCARD 1", "SDCARD 2": "SDCARD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Вы уверены, что хотите управлять и устанавливать порты на {to_loc}?\n\nУже установленные порты не будут перемещены.\nPortMaster перезагрузится чтобы изменения вступили в силу.", @@ -246,8 +245,8 @@ "Accept": "Принять", "{theme_name} (Selected)": "{theme_name} (Выбрана)", "Download": "Скачать", - "Select Colour Scheme": "Выбрать цветовую схему", "{item_name} (Selected)": "{item_name} (Выбранная)", + "Up to date already": "Актуальная версия уже существует", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Вы переключаетесь с версии {old_release_channel} на версию {new_release_channel}.\n\nВы желаете сменить версию?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна новая версия PortMaster ({portmaster_version})\n\nВы хотите обновиться?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Вы хотите переустановить PortMaster?\n\nЭто переустановит канал обновления {release_channel} на {portmaster_version}." diff --git a/PortMaster/pylibs/locales/ru_RU.themes.json b/PortMaster/pylibs/locales/ru_RU.themes.json index 3e33c017..e5bd8cdb 100644 --- a/PortMaster/pylibs/locales/ru_RU.themes.json +++ b/PortMaster/pylibs/locales/ru_RU.themes.json @@ -14,7 +14,7 @@ "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Доступные порты: {ports_list.filter_ports} / {ports_list.total_ports} \nФильтры: {ports_list.filters}{endif}", "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Жанры: {port_info.genres}\n{if:port_info.install_size}Установленный размер: {port_info.install_size}{else}Размер загрузки: {port_info.download_size}{endif}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}", "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Время: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nУстройство: {system.device_name}\nБатарея: {system.battery_level}", - "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}Накопитель: {system.free_space} свободно / {system.total_space} всего", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nНакопитель: {system.free_space} свободно / {system.total_space} всего", "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Создатель: {port_info.porter}\n• Жанры: {port_info.genres}\n• Описание: {port_info.description}", "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Жанры: {port_info.genres}\n• Скачать Size: {port_info.download_size}{if:port_info.runtime}\n• Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Описание: {port_info.description}\n• Инструкции:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json index 8dfb289e..98eaa0cc 100644 --- a/PortMaster/pylibs/locales/strings.json +++ b/PortMaster/pylibs/locales/strings.json @@ -68,18 +68,16 @@ "PortMaster.zip is damaged, download it again.": [ "pylibs/harbourmaster/hm_install.py:105" ], - "Port {download_name} installed failed.": [ - "pylibs/harbourmaster/hm_install.py:107", - "pylibs/harbourmaster/hm_install.py:397" + "PortMaster failed to install.": [ + "pylibs/harbourmaster/hm_install.py:107" ], "Installing {download_name}.": [ "pylibs/harbourmaster/hm_install.py:124", "pylibs/harbourmaster/hm_install.py:251", "pylibs/pugwash/autoinstall.py:126" ], - "Port {download_name!r} installed successfully.": [ - "pylibs/harbourmaster/hm_install.py:168", - "pylibs/harbourmaster/hm_install.py:421" + "PortMaster installed successfully.": [ + "pylibs/harbourmaster/hm_install.py:168" ], "Installation failed, removing files...": [ "pylibs/harbourmaster/hm_install.py:186" @@ -110,12 +108,18 @@ "Installation failed: {reason}": [ "pylibs/harbourmaster/hm_install.py:394" ], + "Port {download_name} failed to install.": [ + "pylibs/harbourmaster/hm_install.py:397" + ], "Port {download_name!r} and {runtime_name!r} installed successfully.": [ "pylibs/harbourmaster/hm_install.py:411" ], - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": [ + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": [ "pylibs/harbourmaster/hm_install.py:416" ], + "Port {download_name!r} installed successfully.": [ + "pylibs/harbourmaster/hm_install.py:421" + ], "Unable to download in offline mode.": [ "pylibs/harbourmaster/hm_install.py:432", "pylibs/harbourmaster/hm_install.py:512" @@ -167,7 +171,7 @@ "Broken": [ "pylibs/harbourmaster/hm_runtimes.py:131" ], - "Unable do download a runtime when in offline mode.": [ + "Unable to download a runtime in offline mode.": [ "pylibs/harbourmaster/hm_runtimes.py:142" ], "Verifying runtime {runtime}": [ @@ -225,19 +229,19 @@ ], "Updating": [ "pylibs/harbourmaster/source/portmaster.py:42", - "pylibs/pugwash/theme.py:562" + "pylibs/pugwash/theme.py:563" ], "Fetching latest info": [ "pylibs/harbourmaster/source/portmaster.py:46", - "pylibs/pugwash/theme.py:572" + "pylibs/pugwash/theme.py:573" ], "Done.": [ "pylibs/harbourmaster/source/portmaster.py:165", - "pylibs/pugwash/theme.py:595" + "pylibs/pugwash/theme.py:596" ], "Fetching info": [ "pylibs/harbourmaster/source/portmaster.py:197", - "pylibs/pugwash/theme.py:601" + "pylibs/pugwash/theme.py:602" ], "Unable to find {port_name}.": [ "pylibs/harbourmaster/source/portmaster.py:418" @@ -707,9 +711,10 @@ "pylibs/pugwash/scenes/options.py:37" ], "Select Color Scheme": [ - "pylibs/pugwash/scenes/options.py:43" + "pylibs/pugwash/scenes/options.py:43", + "pylibs/pugwash/scenes/themes.py:106" ], - "Select a colour scheme for PortMaster": [ + "Select a color scheme for PortMaster.": [ "pylibs/pugwash/scenes/options.py:44" ], "Theme Editor": [ @@ -733,7 +738,7 @@ "pylibs/pugwash/scenes/options.py:59", "pylibs/pugwash/scenes/options.py:329" ], - "Enable or Disable soundfx in PortMaster.": [ + "Enable or Disable sound effects in PortMaster.": [ "pylibs/pugwash/scenes/options.py:60" ], "Port Metadata": [ @@ -889,7 +894,7 @@ "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": [ "pylibs/pugwash/scenes/options.py:363" ], - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": [ + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": [ "pylibs/pugwash/scenes/options.py:384" ], "SDCARD 1": [ @@ -1048,14 +1053,11 @@ "Download": [ "pylibs/pugwash/scenes/themes.py:51" ], - "Select Colour Scheme": [ - "pylibs/pugwash/scenes/themes.py:106" - ], "{item_name} (Selected)": [ "pylibs/pugwash/scenes/themes.py:120" ], "Up to date already": [ - "pylibs/pugwash/theme.py:569" + "pylibs/pugwash/theme.py:570" ], "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": [ "pylibs/pugwash/update.py:79" diff --git a/PortMaster/pylibs/locales/strings.themes.json b/PortMaster/pylibs/locales/strings.themes.json index 4587c10c..f97cb05a 100644 --- a/PortMaster/pylibs/locales/strings.themes.json +++ b/PortMaster/pylibs/locales/strings.themes.json @@ -20,7 +20,7 @@ "ff_VII/theme.json:435", "Zelda/theme.json:485", "Stardew/theme.json:541", - "default_theme/theme.json:423" + "default_theme/theme.json:424" ], "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": [ "Fallout/theme.json:726" @@ -31,8 +31,8 @@ "ff_VII/theme.json:523", "Zelda/theme.json:551", "Stardew/theme.json:651", - "default_theme/theme.json:567", - "default_theme/theme.json:567" + "default_theme/theme.json:568", + "default_theme/theme.json:568" ], "Creator: {theme_info.creator}\nStatus: {theme_info.status}": [ "Fallout/theme.json:831", @@ -40,7 +40,7 @@ "ff_VII/theme.json:612", "Zelda/theme.json:633", "Stardew/theme.json:739", - "default_theme/theme.json:617" + "default_theme/theme.json:618" ], "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": [ "Stardew/theme.json:613" @@ -58,23 +58,23 @@ "basic_theme/theme.json:77" ], "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": [ - "default_theme/theme.json:326" + "default_theme/theme.json:327" ], "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": [ - "default_theme/theme.json:353", - "default_theme/theme.json:353" + "default_theme/theme.json:354", + "default_theme/theme.json:354" ], "{port_links.name}\n{port_links.url}": [ - "default_theme/theme.json:480" + "default_theme/theme.json:481" ], "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": [ "ff_VII/theme.json:480", "Stardew/theme.json:605", - "default_theme/theme.json:507" + "default_theme/theme.json:508" ], "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": [ "ff_VII/theme.json:488", - "default_theme/theme.json:515" + "default_theme/theme.json:516" ], "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": [ "hiviz/theme.json:267" @@ -90,7 +90,7 @@ ], "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": [ "hiviz/theme.json:526", - "default_theme/theme.json:710" + "default_theme/theme.json:711" ], "{ports_list.filter_ports} games": [ "jimmyhat999_grid_dark/theme.json:239", @@ -123,8 +123,5 @@ "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": [ "jimmyhat999_grid_dark/theme.json:492", "jimmyhat999_grid_wii/theme.json:492" - ], - "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: \nPlace file x in folder y then dance until tomorrow": [ - "unknown" ] } diff --git a/PortMaster/pylibs/locales/sv_SE.json b/PortMaster/pylibs/locales/sv_SE.json index e55e7235..e2b3c097 100644 --- a/PortMaster/pylibs/locales/sv_SE.json +++ b/PortMaster/pylibs/locales/sv_SE.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Installerar Tema {download_name}.", "Installing": "Installerar", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} har installerats.", - "Port {download_name} installed failed.": "Installation av {download_name} misslyckades.", "Installing {download_name}.": "Installerar {download_name}.", - "Port {download_name!r} installed successfully.": "Port {download_name!r} installerades.", "Installation failed, removing files...": "Installationen misslyckades, raderar filer...", "Okay": "Okej", "Cancel": "Avbryt", + "Port {download_name} failed to install.": "Installation av {download_name} misslyckades.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} och {runtime_name!r} har installerats.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} installerades, men {runtime_name!r} kunde inte installeras!!\n\nAntingen installera om för att försöka igen, eller kolla wikin för hjälp.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} installerades, men {runtime_name!r} kunde inte installeras!!\n\nAntingen installera om för att försöka igen, eller kolla wikin för hjälp.", + "Port {download_name!r} installed successfully.": "Port {download_name!r} installerades.", "Unable to find a source for {port_name}": "Kunde inte hitta en nedladdning för {port_name}", "Unknown port {port_name}": "Okänd port {port_name}", "Removing {port_name}": "Raderar {port_name}", @@ -27,7 +27,7 @@ "Verified": "Verifierad", "Unverified": "Overifierad", "Broken": "Trasig", - "Unable do download a runtime when in offline mode.": "Kan inte ladda ner en körtid i offlineläge.", + "Unable to download a runtime in offline mode.": "Kan inte ladda ner en körtid i offlineläge.", "Verifying runtime {runtime}": "Verifierar körtid {runtime}", "Verifying": "Verifierar", "Unable to find a download for {runtime}.": "Kunde inte hitta en nedladdning för {runtime}.", @@ -45,7 +45,6 @@ "First check.": "Första kontroll.", "Auto Update.": "Automatisk uppdatering.", "Updating": "Uppdaterar", - "Up to date already": "Redan uppdaterad", "Fetching latest info": "Hämtar senaste information", "Done.": "Färdig.", "Fetching info": "Hämtar information", @@ -151,12 +150,12 @@ "Select Theme": "Välj tema", "Select a theme for PortMaster.": "Välj ett tema för PortMaster.", "Select Color Scheme": "Välj färgschema", - "Select a colour scheme for PortMaster": "Välj färgschema för PortMaster", + "Select a color scheme for PortMaster.": "Välj färgschema för PortMaster.", "Audio": "Ljud", "Music: ": "Musik: ", "Enable or Disable background music in PortMaster.": "Aktivera eller inaktivera bakgrundsmusik i PortMaster.", "Sound FX: ": "Ljudeffekter: ", - "Enable or Disable soundfx in PortMaster.": "Aktivera eller inaktivera ljudeffekter i PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Aktivera eller inaktivera ljudeffekter i PortMaster.", "Port Metadata": "Port-metadata", "Metadata Update: ": "Uppdatera metadata: ", "Auto": "Automatisk", @@ -197,7 +196,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Är du säker på att du vill ändra till alfa-kanalen?\n\nPortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta.\n\nDETTA KAN OCH KOMMER ATT SABBA SAKER, DU HAR BLIVIT VARNAD", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Är du säker på att du vill ändra releasekanalen från {current_channel} till {new_channel}?\n\nPortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Är du säker på att du vill uppdatera din spellista.xml?\n\nDetta kommer att åsidosätta alla skrapade konstverk eller portinformation och kräver en nedladdning på 50+ MB.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Är du säker på att du vill flytta portar från {from_mode} till {to_mode}", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Är du säker på att du vill flytta portar från {from_mode} till {to_mode}?", "Deleting Runtimes:": "Raderar körtider:", "Source Manager": "Källhanterare", "Hide Info": "Dölj info", @@ -221,8 +220,8 @@ "Deleted runtime {runtime}": "Raderat körtid {runtime}", "{theme_name} (Selected)": "{theme_name} (Vald)", "Download": "Hämta", - "Select Colour Scheme": "Välj färgschema", "{item_name} (Selected)": "{item_name} (Vald)", + "Up to date already": "Redan uppdaterad", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Du kommer byta från {old_release_channel} till {new_release_channel}.\n\nVill du nedgradera?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Det finns en ny version av PortMaster ({portmaster_version})\n\nVill du uppgradera?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Vill du ominstallera PortMaster?\n\nDetta kommer ominstallera {portmaster_version} från {release_channel}-kanalen." diff --git a/PortMaster/pylibs/locales/uk_UA.json b/PortMaster/pylibs/locales/uk_UA.json index 6e6f2305..c8dce6ae 100644 --- a/PortMaster/pylibs/locales/uk_UA.json +++ b/PortMaster/pylibs/locales/uk_UA.json @@ -7,14 +7,14 @@ "Installing Theme {download_name}.": "Встановлення теми {download_name}.", "Installing": "Встановлення", "Theme {download_name!r} installed successfully.": "Тема {download_name!r} успішно встановлена.", - "Port {download_name} installed failed.": "Не вдалося встановити порт {download_name}.", "Installing {download_name}.": "Встановлення {download_name}.", - "Port {download_name!r} installed successfully.": "Порт {download_name!r} успішно встановлено.", "Installation failed, removing files...": "Помилка встановлення, видалення файлів...", "Okay": "Гаразд", "Cancel": "Скасувати", + "Port {download_name} failed to install.": "Не вдалося встановити порт {download_name}.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Порт {download_name!r} та {runtime_name!r} успішно встановлено.", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успішно встановлено, але встановити {runtime_name!r} не вдалося!\n\nСпробуйте перевстановити, або зверніться до вікі за допомогою.", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успішно встановлено, але встановити {runtime_name!r} не вдалося!\n\nСпробуйте перевстановити, або зверніться до вікі за допомогою.", + "Port {download_name!r} installed successfully.": "Порт {download_name!r} успішно встановлено.", "Unable to download in offline mode.": "Не вдається завантажити в офлайн режимі.", "Unable to find a source for {port_name}": "Не вдалося знайти джерело для {port_name}", "Unknown port {port_name}": "Невідомий порт {port_name}", @@ -28,7 +28,7 @@ "Verified": "Підтверджено", "Unverified": "Не підтверджено", "Broken": "Не працює", - "Unable do download a runtime when in offline mode.": "Неможливо завантажити Runtime в офлайн режимі.", + "Unable to download a runtime in offline mode.": "Неможливо завантажити Runtime в офлайн режимі.", "Verifying runtime {runtime}": "Verifying runtime {runtime}", "Verifying": "Підтвердження", "Unable to find a download for {runtime}.": "Не вдалося знайти завантаження для {runtime}.", @@ -47,7 +47,6 @@ "First check.": "Перша перевірка.", "Auto Update.": "Автоматичне оновлення.", "Updating": "Оновлення", - "Up to date already": "Вже оновлено", "Fetching latest info": "Отримання останньої інформації", "Done.": "Готово.", "Fetching info": "Отримання інформації", @@ -163,12 +162,12 @@ "Select Theme": "Оберіть тему", "Select a theme for PortMaster.": "Оберіть тему для PortMaster.", "Select Color Scheme": "Оберіть кольорову палітру", - "Select a colour scheme for PortMaster": "Оберіть кольорову палітру для PortMaster", + "Select a color scheme for PortMaster.": "Оберіть кольорову палітру для PortMaster.", "Audio": "Аудіо", "Music: ": "Музика: ", "Enable or Disable background music in PortMaster.": "Увімкнення чи вимкнення фонової музики в PortMaster.", "Sound FX: ": "Звукові ефекти: ", - "Enable or Disable soundfx in PortMaster.": "Увімкнення чи вимкнення звукових ефектів в PortMaster.", + "Enable or Disable sound effects in PortMaster.": "Увімкнення чи вимкнення звукових ефектів в PortMaster.", "Port Metadata": "Метадані портів", "Metadata Update: ": "Оновлення метаданих: ", "Auto": "Автоматично", @@ -213,7 +212,7 @@ "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Ви впевнені, що хочете перейти на канал альфа-релізів?\n\nВерсію PortMaster буде відповідно оновлено або знижено.\n\nЦЕ МОЖЕ І ПОШКОДИТЬ ФУНКЦІОНАЛ, ВИ БУЛИ ПОПЕРЕДЖЕНІ", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Ви впевнені, що хочете змінити канал релізу з {current_channel} на {new_channel}?\n\nВерсію PortMaster буде відповідно оновлено або знижено.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Ви дійсно бажаєте оновити свій gamelist.xml?\n\nЦе перезапише всі завантажені обкладинки або дані порту, і потребуватиме завантаження понад 50 МБ.", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "Ви впевнені, що хочете перемістити порти з {from_mode} до {to_mode}", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Ви впевнені, що хочете перемістити порти з {from_mode} до {to_mode}?", "SDCARD 1": "SDCARD 1", "SDCARD 2": "SDCARD 2", "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Ви впевнені, що хочете керувати портами та встановлювати їх у розташування {to_loc}?\n\nВже встановлені порти не будуть переміщені.\nPortMaster буде перезапущено, щоб зміни набули чинності.", @@ -243,8 +242,8 @@ "Accept": "Прийняти", "{theme_name} (Selected)": "{theme_name} (Обрано)", "Download": "Завантажити", - "Select Colour Scheme": "Оберіть кольорову палітру", "{item_name} (Selected)": "{item_name} (Обрано)", + "Up to date already": "Вже оновлено", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Ви переходите з {old_release_channel} на {new_release_channel} версію.\n\nХочете знизити версію?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна нова версія PortMaster ({portmaster_version})\n\nХочете оновити?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "Хочете перевстановити PortMaster?\n\nЦе перевстановить застосунок з {release_channel} каналу до {portmaster_version}." diff --git a/PortMaster/pylibs/locales/zh_CN.json b/PortMaster/pylibs/locales/zh_CN.json index a43783f3..ba926937 100644 --- a/PortMaster/pylibs/locales/zh_CN.json +++ b/PortMaster/pylibs/locales/zh_CN.json @@ -7,15 +7,15 @@ "Installing Theme {download_name}.": "正在安装 {download_name} 主题。", "Installing": "正在安装", "Theme {download_name!r} installed successfully.": "主题 {download_name!r} 安装成功。", - "Port {download_name} installed failed.": "移植作品 {download_name} 安装失败。", "Installing {download_name}.": "正在安装 {download_name}.", - "Port {download_name!r} installed successfully.": "已成功安装了移植作品 {download_name!r} 。", "Installation failed, removing files...": "安装失败,正在移除文件...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "你好!\n\n你正在人工下载一个与此设备不适配的移植软件!\n\n如果你遇到了问题,请勿将其报告到Discord,你会被狠狠笑话!!\n\n你确定要继续安装吗?", "Okay": "确定", "Cancel": "取消", + "Port {download_name} failed to install.": "移植作品 {download_name} 安装失败。", "Port {download_name!r} and {runtime_name!r} installed successfully.": "移植作品 {download_name!r} 和 {runtime_name!r} 安装成功。", - "Port {download_name!r} installed sucessfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "移植作品 {download_name!r} 安装成功,但 {runtime_name!r} 运行时安装失败!\n\n请尝试重新安装,或者查阅wiki以获得帮助。", + "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "移植作品 {download_name!r} 安装成功,但 {runtime_name!r} 运行时安装失败!\n\n请尝试重新安装,或者查阅wiki以获得帮助。", + "Port {download_name!r} installed successfully.": "已成功安装了移植作品 {download_name!r} 。", "Unable to download in offline mode.": "无法在离线模式下下载。", "Unable to find a source for {port_name}": "无法找到{port_name} 的下载资源。", "Unknown port {port_name}": "未知移植作品 {port_name}", @@ -30,7 +30,7 @@ "Verified": "已验证", "Unverified": "未验证", "Broken": "损坏", - "Unable do download a runtime when in offline mode.": "在离线模式下无法下载运行时。", + "Unable to download a runtime in offline mode.": "在离线模式下无法下载运行时。", "Verifying runtime {runtime}": "正在检查运行时 {runtime}", "Verifying": "正在验证", "Unable to find a download for {runtime}.": "无法找到 {runtime} 的下载链接。", @@ -48,7 +48,6 @@ "First check.": "首次检查", "Auto Update.": "自动更新", "Updating": "正在更新", - "Up to date already": "已经更新", "Fetching latest info": "正在获取最新信息。", "Done.": "完成。", "Fetching info": "正在获取信息", @@ -164,12 +163,12 @@ "Select Theme": "选择主题", "Select a theme for PortMaster.": "选择一个PortMaster主题。", "Select Color Scheme": "选择配色方案", - "Select a colour scheme for PortMaster": "选择一个PortMaster配色方案", + "Select a color scheme for PortMaster.": "选择一个PortMaster配色方案。", "Audio": "音频:", "Music: ": "音乐: ", "Enable or Disable background music in PortMaster.": "在 PortMaster中启用或禁用背景音乐", "Sound FX: ": "音效: ", - "Enable or Disable soundfx in PortMaster.": "在 PortMaster中启用或禁用音效", + "Enable or Disable sound effects in PortMaster.": "在 PortMaster中启用或禁用音效", "Port Metadata": "移植作品元数据", "Metadata Update: ": "元数据更新: ", "Auto": "自动", @@ -209,13 +208,13 @@ "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "您确定需要还原PortMaster吗?\n\n该操作需要安装一个特殊配置的“移植作品”文件。", "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "特殊“移植作品”已安装,名为“Restore PortMaster.sh”\n\n运行该文件以还原PortMaster。", "PortMaster will now quit so you can run the restoration port.": "PortMaster将退出以运行还原程序。", - "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "您确定要切换到Alpha通道吗?\nPortMaster会按需升级或降级。\n【现有设置会损坏,后果自负!!】", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "您确定要切换到Alpha通道吗?\n\nPortMaster会按需升级或降级。\n\n【现有设置会损坏,后果自负!!】", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "您确定要把发布通道从 {current_channel} 改为 {new_channel}?\n\nPortMaster会按需升级或者降级。", - "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "您确定希望更新gamelist.xml文件吗?\n将覆盖现有所有移植作品的封面和其他信息,且需要下载50MB以上的数据。", - "Are you sure you want to move ports from the {from_mode} to the {to_mode}": "您确定希望将移植作品从{from_mode} 移动到{to_mode} ?", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "您确定希望更新gamelist.xml文件吗?\n\n将覆盖现有所有移植作品的封面和其他信息,且需要下载50MB以上的数据。", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "您确定希望将移植作品从{from_mode} 移动到{to_mode} ?", "SDCARD 1": "SD卡1", "SDCARD 2": "SD卡2", - "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "您确定希望在{to_loc} 目录管理安装移植作品吗?\n已经安装的移植作品不会被移动。\nPortMaster需要重启以适用更改。", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "您确定希望在{to_loc} 目录管理安装移植作品吗?\n\n已经安装的移植作品不会被移动。\nPortMaster需要重启以适用更改。", "Deleting Runtimes:": "删除运行时:", "Source Manager": "源管理器", "Port Info Popup": "移植作品信息弹出", @@ -242,9 +241,9 @@ "Accept": "同意", "{theme_name} (Selected)": "{theme_name} (已选)", "Download": "下载", - "Select Colour Scheme": "选择配色方案", "{item_name} (Selected)": "{item_name} (已选)", - "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "正在从 {old_release_channel} 切换到 {new_release_channel} 版本。希望退回吗?", - "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster有新版本 ({portmaster_version}) 可用,希望升级吗?", + "Up to date already": "已经更新", + "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "正在从 {old_release_channel} 切换到 {new_release_channel} 版本。\n\n希望退回吗?", + "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster有新版本 ({portmaster_version}) 可用\n\n希望升级吗?", "Do you want to reinstall PortMaster?\n\nThis will reinstall from the {release_channel} channel to {portmaster_version}.": "您希望重装PortMaster吗?\n\n这会安装来自 {release_channel} 的 {portmaster_version} 版本。" } diff --git a/PortMaster/pylibs/pugwash/scenes/options.py b/PortMaster/pylibs/pugwash/scenes/options.py index 83852574..cc0681ca 100644 --- a/PortMaster/pylibs/pugwash/scenes/options.py +++ b/PortMaster/pylibs/pugwash/scenes/options.py @@ -41,7 +41,7 @@ def __init__(self, gui): self.tags['option_list'].add_option( 'select-scheme', _("Select Color Scheme"), - description=_("Select a colour scheme for PortMaster")) + description=_("Select a color scheme for PortMaster.")) self.tags['option_list'].add_option( 'theme-editor', @@ -57,7 +57,7 @@ def __init__(self, gui): self.tags['option_list'].add_option( 'toggle-sfx', _("Sound FX: ") + (self.gui.sounds.sound_is_disabled and _("Disabled") or _("Enabled")), - description=_("Enable or Disable soundfx in PortMaster.")) + description=_("Enable or Disable sound effects in PortMaster.")) if self.gui.hm.platform.gamelist_file() is not None: self.tags['option_list'].add_option(None, _("Port Metadata")) @@ -381,7 +381,7 @@ def do_update(self, events): from_mode = self.gui.hm.cfg_data.get('trimui-port-mode', 'roms') to_mode = change_map[from_mode] if self.gui.message_box( - _("Are you sure you want to move ports from the {from_mode} to the {to_mode}").format( + _("Are you sure you want to move ports from the {from_mode} to the {to_mode}?").format( from_mode=language_map[from_mode], to_mode=language_map[to_mode]), want_cancel=True): diff --git a/PortMaster/pylibs/pugwash/scenes/themes.py b/PortMaster/pylibs/pugwash/scenes/themes.py index 06cebc33..7ac7aa86 100644 --- a/PortMaster/pylibs/pugwash/scenes/themes.py +++ b/PortMaster/pylibs/pugwash/scenes/themes.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: MIT # -# Theme and colour scheme selection. +# Theme and color scheme selection. import harbourmaster from harbourmaster.i18n import _ @@ -103,7 +103,7 @@ def do_update(self, events): class ThemeSchemeScene(BaseScene): def __init__(self, gui): super().__init__(gui) - self.scene_title = _("Select Colour Scheme") + self.scene_title = _("Select Color Scheme") self.load_regions("option_menu", ['option_list', ]) diff --git a/PortMaster/pylibs/theme_server/static/index.html b/PortMaster/pylibs/theme_server/static/index.html index d1b30f88..830a1715 100644 --- a/PortMaster/pylibs/theme_server/static/index.html +++ b/PortMaster/pylibs/theme_server/static/index.html @@ -117,7 +117,7 @@

Theme Preview

- + diff --git a/README.md b/README.md index 9accbb51..a132a3a1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ PortMaster is a convenient script designed to facilitate the downloading and ins - *Zelda*: LTTP Inspired theme - *FF VII*: A beautifully done FF7 theme - *Basic Theme*: A super bare bones theme so you can get started designing your own! - - Colour Schemes, themes can support multiple colour schemes for darkmode / lightmode / different colour ways. + - Color Schemes, themes can support multiple color schemes for darkmode / lightmode / different color ways. - Builtin theme downloader! # Nerdy features diff --git a/THEME.md b/THEME.md index d1099722..91d45312 100644 --- a/THEME.md +++ b/THEME.md @@ -6,7 +6,7 @@ This is currently a WIP documenting the theme format. ## Previewing your theme -The theme preview server renders every PortMaster screen with your theme, at any screen size, colour scheme and language, and refreshes the page each time you save a file. It runs from a PortMaster-GUI checkout on Linux or macOS: +The theme preview server renders every PortMaster screen with your theme, at any screen size, color scheme and language, and refreshes the page each time you save a file. It runs from a PortMaster-GUI checkout on Linux or macOS: ```bash python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt # once, gets SDL2 @@ -17,14 +17,14 @@ Then open http://localhost:8090/. `--themes` is one theme folder (it has a `them - **The screens:** - main menu, all, ready-to-run and installed ports, port info (and its popup) - - filters, featured ports, options, runtimes, themes, colour schemes, language + - filters, featured ports, options, runtimes, themes, color schemes, language - on-screen keyboard, theme editor, messages with a progress bar, message box - the autoinstall and dialog screens, which always use the default theme on a device - the disclaimer and the unsupported-system warning, which are built in and look the same in every theme - They show the real ports list and screenshots, downloaded once into `~/.cache/portmaster-theme-server`. `--refresh-ports` gets the current ones. - **Region boxes:** draws each region's area with its name. In a screen's own view, hover over or click a box (or a name in the list) to see the region's final theme values, after `#base`, `#element` and `[capability]` overrides are applied. **Save PNG** saves the screen, with the boxes if they're on. - **One screen, every size:** one screen at every common resolution, to check your `[hires]`, `[4:3]` and other overrides. -- A screen your theme has no section for says so, rather than failing. Errors pugwash logged while drawing (a missing image, a bad colour) are listed on the screen. +- A screen your theme has no section for says so, rather than failing. Errors pugwash logged while drawing (a missing image, a bad color) are listed on the screen. To write the screens to files instead, for a theme's README or to compare before and after a change: @@ -40,7 +40,7 @@ That writes `out/////.png`, with a PortMaster carries the same server: **Options → Theme Editor** starts it on the device, and shows its address as text and as a QR code. Each device picks a random port the first time and keeps it, so the address stays the same. Open it on a computer or phone on the same network: no checkout or install needed. It previews the themes installed on the device, with the device's own ports list, and the translation editor saves into the device's PortMaster. Press B on the device to stop it. - Translations saved on a device are in `PortMaster/pylibs/locales/` there. Copy them off before updating PortMaster, which replaces that folder. -- On a handheld (an RG35XX-H) the first screen takes 2-3 seconds, a new screen size about 2, switching theme, colour scheme or language (or saving) about a second, and each other screen half a second. Each screen size has a render process; how many are kept at once depends on the device's free memory. +- On a handheld (an RG35XX-H) the first screen takes 2-3 seconds, a new screen size about 2, switching theme, color scheme or language (or saving) about a second, and each other screen half a second. Each screen size has a render process; how many are kept at once depends on the device's free memory. - Anyone on the same network can open it while it runs. ### Translating PortMaster @@ -69,7 +69,7 @@ The theme has a few different sections: - `#info`: Info about the theme - `#base`: The base element, which every gui element in the theme inherits from -- `#pallet`: Predefined colours for the theme. +- `#pallet`: Predefined colors for the theme. - `#resources`: Images the theme uses, can either be an atlas or individual images. - `#elements`: Reusable elements that different scenes in your theme can use. - **scenes**: Scenes that the gui uses @@ -315,7 +315,7 @@ Elements can be themed by setting a `fill`, `outline`, `thickness`, and `roundne - `outline`: the color of the outline for the elements rect - `thickness`: how thick the stroke of the outline is - `roundess`: if sdlGFX is available, it will make a roundrect with x pixels of roundness in the corners. -- `progress-fill`: this is a special fill colour, used for the progress bar. +- `progress-fill`: this is a special fill color, used for the progress bar. diff --git a/tests/test_fifo_install.py b/tests/test_fifo_install.py index 871c1a6e..a933db88 100644 --- a/tests/test_fifo_install.py +++ b/tests/test_fifo_install.py @@ -75,7 +75,7 @@ def test_install_failure_reason_is_shown(fifo, tmp_path): fifo.install(str(write_raw_zip(tmp_path / "testport.zip", b"garbage"))) assert any(m.startswith("Installation failed:") for m in fifo.shown), fifo.shown - assert any("installed failed" in m for m in fifo.shown), fifo.shown + assert any("failed to install" in m for m in fifo.shown), fifo.shown def test_install_missing_file(fifo, tmp_path): diff --git a/tests/test_gui_commands.py b/tests/test_gui_commands.py index 7b965a98..a8a3bec8 100644 --- a/tests/test_gui_commands.py +++ b/tests/test_gui_commands.py @@ -108,7 +108,7 @@ def test_do_install_bad_zip(app, hm_dirs, tmp_path): app.do_install("Bad", str(write_raw_zip(tmp_path / "testport.zip", b"garbage"))) assert list(hm_dirs['ports_dir'].iterdir()) == [] - assert app.test_boxes[-1] == "Port testport.zip installed failed." + assert app.test_boxes[-1] == "Port testport.zip failed to install." assert_idle(app) diff --git a/tests/test_harbour.py b/tests/test_harbour.py index 3061511b..6ce217e9 100644 --- a/tests/test_harbour.py +++ b/tests/test_harbour.py @@ -86,7 +86,7 @@ def test_install_bad_zip_rolls_back(hm, hm_dirs, tmp_path, callback): assert hm.install_port(str(zip_file)) == 255 assert list(hm_dirs['ports_dir'].iterdir()) == [] - assert "installed failed" in callback.message_boxes[-1] + assert "failed to install" in callback.message_boxes[-1] def test_install_incompatible_port_still_installs_from_cli(hm, tmp_path): diff --git a/tests/test_install.py b/tests/test_install.py index 42ccd862..fb50936c 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -59,7 +59,7 @@ def test_unusable_port_is_rejected(case, hm, hm_dirs, tmp_path, callback): assert hm.install_port(str(zip_file)) == 255 assert ports_left(hm_dirs) == [] - assert callback.message_boxes[-1] == "Port testport.zip installed failed." + assert callback.message_boxes[-1] == "Port testport.zip failed to install." hm.load_ports() assert hm.installed_ports == {} @@ -230,7 +230,7 @@ def test_failure_part_way_rolls_back(failing_extract, hm, hm_dirs, tmp_path, cal assert hm.install_port(str(zip_file)) == 255 assert len(failing_extract) == 3 assert ports_left(hm_dirs) == [] - assert callback.message_boxes[-1] == "Port testport.zip installed failed." + assert callback.message_boxes[-1] == "Port testport.zip failed to install." def test_interrupted_part_way_rolls_back_and_propagates(hm, hm_dirs, tmp_path, monkeypatch): @@ -598,7 +598,7 @@ def test_damaged_portmaster_zip_installs_nothing(hm, tmp_path, callback, damage) assert hm.install_port(str(zip_file)) == 255 assert not (hm.tools_dir / "PortMaster" / "first.txt").exists() assert hm.last_install_error == "PortMaster.zip is damaged, download it again." - assert callback.message_boxes[-1] == "Port PortMaster installed failed." + assert callback.message_boxes[-1] == "PortMaster failed to install." def test_portmaster_zip_replaces_running_launcher(hm, tmp_path): From a3ab4b33df3da8afed1762102235d38115232a91 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 20:01:27 +0800 Subject: [PATCH 26/28] Translations: fill every gap in all 14 languages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit About 1,080 translations, so every string in strings.json and strings.themes.json has one in each language (strings made only of {tags} need none). Most are the strings since the last Crowdin sync: the theme editor, port links, autoinstall's summary, the startup warnings, sorting, debug mode. Italian, Japanese, Swedish and Romanian also get the older ones they never had. Each language keeps the words it already used (ports are "jogos" in Brazilian Portuguese, the runtime is "Körtid" in Swedish, and so on). Every one passes tools/i18n.py check and safe_translation. They're machine-written: native speakers should look them over in the translation editor. The startup warnings screen used DejaVu Sans whatever the language, so Chinese, Japanese and Korean showed as boxes once translated: its hard-coded section now has the default theme's font for each language. Its "Wait N seconds" and "Please wait." are translatable. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/locales/de_DE.json | 63 +++++++++++ PortMaster/pylibs/locales/de_DE.themes.json | 8 +- PortMaster/pylibs/locales/es_ES.json | 63 +++++++++++ PortMaster/pylibs/locales/es_ES.themes.json | 8 +- PortMaster/pylibs/locales/fr_FR.json | 63 +++++++++++ PortMaster/pylibs/locales/fr_FR.themes.json | 8 +- PortMaster/pylibs/locales/it_IT.json | 109 +++++++++++++++++++ PortMaster/pylibs/locales/it_IT.themes.json | 15 ++- PortMaster/pylibs/locales/ja_JP.json | 110 ++++++++++++++++++++ PortMaster/pylibs/locales/ja_JP.themes.json | 15 ++- PortMaster/pylibs/locales/ko_KR.json | 60 +++++++++++ PortMaster/pylibs/locales/ko_KR.themes.json | 8 +- PortMaster/pylibs/locales/nl_NL.json | 63 +++++++++++ PortMaster/pylibs/locales/nl_NL.themes.json | 8 +- PortMaster/pylibs/locales/pl_PL.json | 60 +++++++++++ PortMaster/pylibs/locales/pl_PL.themes.json | 8 +- PortMaster/pylibs/locales/pt_BR.json | 60 +++++++++++ PortMaster/pylibs/locales/pt_BR.themes.json | 8 +- PortMaster/pylibs/locales/ro_RO.json | 70 +++++++++++++ PortMaster/pylibs/locales/ro_RO.themes.json | 8 +- PortMaster/pylibs/locales/ru_RU.json | 60 +++++++++++ PortMaster/pylibs/locales/ru_RU.themes.json | 8 +- PortMaster/pylibs/locales/strings.json | 17 ++- PortMaster/pylibs/locales/sv_SE.json | 85 +++++++++++++++ PortMaster/pylibs/locales/sv_SE.themes.json | 8 +- PortMaster/pylibs/locales/uk_UA.json | 63 +++++++++++ PortMaster/pylibs/locales/uk_UA.themes.json | 8 +- PortMaster/pylibs/locales/zh_CN.json | 64 ++++++++++++ PortMaster/pylibs/locales/zh_CN.themes.json | 8 +- PortMaster/pylibs/pugwash/scenes/startup.py | 4 +- PortMaster/pylibs/pugwash/theme.py | 19 +++- 31 files changed, 1137 insertions(+), 22 deletions(-) diff --git a/PortMaster/pylibs/locales/de_DE.json b/PortMaster/pylibs/locales/de_DE.json index 42538892..d2f0a557 100644 --- a/PortMaster/pylibs/locales/de_DE.json +++ b/PortMaster/pylibs/locales/de_DE.json @@ -1,24 +1,43 @@ { + "This is not a valid zip file.": "Dies ist keine gültige ZIP-Datei.", + "illegal file {file_name}": "unzulässige Datei {file_name}", + "the port has no directory": "der Port hat keinen Ordner", + "the port has no launch script": "der Port hat kein Startskript", + "unable to load {file_name}": "{file_name} konnte nicht geladen werden", "Loading...": "Lädt...", "Loading Sources.": "Lade Quellen.", "Download validation failed.": "Download Überprüfung fehlgeschlagen", + "Unable to download file.": "Datei konnte nicht heruntergeladen werden.", "Loading Info.": "Informationen werden geladen.", + "Fetching latest ports info data.": "Lade neueste Port-Informationen.", + "Fetching latest porters info.": "Lade neueste Porter-Informationen.", + "Fetching latest port stats.": "Lade neueste Port-Statistiken.", "Fetching latest featured ports.": "Lade neueste Ports.", + "Fetching latest featured port images zip.": "Lade neueste Bilder der empfohlenen Ports (ZIP).", + "Fetching featured port image {file_name}": "Lade Bild für empfohlenen Port {file_name}", "Installing Theme {download_name}.": "Installiere Theme {download_name}.", "Installing": "Wird installiert", "Theme {download_name!r} installed successfully.": "Theme {download_name!r} erfolgreich installiert.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip ist beschädigt, bitte erneut herunterladen.", + "PortMaster failed to install.": "Installation von PortMaster fehlgeschlagen.", "Installing {download_name}.": "Installiere {download_name}.", + "PortMaster installed successfully.": "PortMaster wurde erfolgreich installiert.", "Installation failed, removing files...": "Installation fehlgeschlagen, entferne Dateien...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "HALLO.\n\nES SIEHT SO AUS, ALS WÜRDEST DU MANUELL EINEN PORT INSTALLIEREN, DER MIT DEINEM GERÄT NICHT KOMPATIBEL IST.\n\nWENN DU PROBLEME HAST, MELDE SIE NICHT AUF DISCORD, SONST WIRST DU AUSGELACHT.\n\nMÖCHTEST DU DIE INSTALLATION WIRKLICH FORTSETZEN?", "Okay": "Okay", "Cancel": "Abbrechen", + "Not compatible with this device.": "Nicht kompatibel mit diesem Gerät.", + "Installation failed: {reason}": "Installation fehlgeschlagen: {reason}", "Port {download_name} failed to install.": "Port {download_name} Installation fehlgeschlagen.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} und {runtime_name!r} erfolgreich installiert.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} wurde erfolgreich installiert, aber die Installation von {runtime_name!r} ist fehlgeschlagen!!\n\nInstalliere den Port erneut, um es nochmal zu versuchen, oder besuche das Wiki für Hilfe.", "Port {download_name!r} installed successfully.": "Port {download_name!r} erfolgreich installiert.", "Unable to download in offline mode.": "Download im Offline-Modus nicht möglich.", + "File not found.": "Datei nicht gefunden.", "Unable to find a source for {port_name}": "Es konnte keine Quelle für {port_name} gefunden werden.", "Unknown port {port_name}": "Unbekannter Port {port_name}", "Removing {port_name}": "Entferne {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Fehler beim Deinstallieren von {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "Erfolgreich {port_name} deinstalliert", "Loading Ports.": "Lade Ports.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} enthält eine fehlerhafte Runtime, möglicherweise funktioniert das Spiel nicht korrekt.", @@ -65,16 +84,32 @@ "Uninstalling {port_name}": "Deinstallation von {port_name}", "Updating all port sources:": "Aktualisieren aller Port-Quellen:", "Checking {runtime_name}": "Prüfe {runtime_name}", + "Auto-installation": "Automatische Installation", + "PortMaster website": "PortMaster-Webseite", + "{store}: developer": "{store}: Entwickler", "** NO PORT **": "** KEIN PORT **", "Ready to Run": "Bereit zum Laufen", "Requires Files": "Benötigt Dateien", "Installed": "Installiert", "Missing": "Fehlt", + "Installation failed, see log.txt for details.": "Installation fehlgeschlagen, Details in log.txt.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Er ist für die falsche Architektur ({zip_arch}, dieses Gerät ist {device_arch}) und wurde gelöscht.", + "Installed {download_name}.": "{download_name} installiert.", + "- SUCCESS: {name}": "- ERFOLG: {name}", + "- FAILURE: {name}": "- FEHLER: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Ports wurden hinzugefügt oder entfernt, aktualisieren Sie Ihre Spiele-Liste, um die neuen Spiele zu sehen oder entfernen Sie die deinstallierten.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Einige Dateien konnten nicht installiert werden, wähle sie aus, um den Grund zu sehen. Sie sind im Autoinstall-Ordner geblieben.", + "Everything installed successfully.": "Alles wurde erfolgreich installiert.", + "{installed} installed, {failed} failed": "{installed} installiert, {failed} fehlgeschlagen", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} wurde erfolgreich installiert.", + "FAILED: {name}": "FEHLER: {name}", + "{name} failed to install:\n\n{reason}": "{name} konnte nicht installiert werden:\n\n{reason}", "Danish": "Dänisch", "German": "Deutsch", "English": "Englisch", "Spanish": "Spanisch", + "Esperanto": "Esperanto", "Finnish": "Finnisch", "French": "Französisch", "Italian": "Italienisch", @@ -84,6 +119,7 @@ "Polish": "Polnisch", "Portuguese (Portugal)": "Portugiesisch (Portugal)", "Portuguese (Brazil)": "Portugiesisch (Brasilien)", + "Romanian": "Rumänisch", "Russian": "Russisch", "Swedish": "Schwedisch", "Ukrainian": "Ukrainisch", @@ -98,6 +134,7 @@ "Alphabetical": "Alphabetisch", "Recently Added": "Zuletzt hinzugefügt", "Recently Updated": "Vor kurzem aktualisiert", + "Total Downloads": "Downloads gesamt", "Action": "Aktion", "Adventure": "Adventure", "Arcade": "Arcade", @@ -124,6 +161,8 @@ "ARM 32bit": "ARM 32Bit", "ARM 64bit": "ARM 64Bit", "x86 64bit": "x86 64Bit", + "Ascending": "Aufsteigend", + "Descending": "Absteigend", "Sort:": "Sortieren:", "Clear Filters": "Filter löschen", "Genres:": "Genres:", @@ -163,6 +202,8 @@ "Select a theme for PortMaster.": "Theme auswählen.", "Select Color Scheme": "Farbschema auswählen", "Select a color scheme for PortMaster.": "Farbschema auswählen.", + "Theme Editor": "Theme-Editor", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Themes ansehen und die Übersetzungen von PortMaster bearbeiten, von einem Computer oder Handy im selben Netzwerk.", "Audio": "Audio", "Music: ": "Musik: ", "Enable or Disable background music in PortMaster.": "Hintergrundmusik in PortMaster aktivieren oder deaktivieren.", @@ -203,6 +244,8 @@ "Show all ports, ignoring requirements.": "Zeige alle Ports, ignoriere Voraussetzungen.", "CWTBE Mode: ": "CWTBE Modus: ", "Enable gptokeyb2 by default.": "Aktiviere gptokeyb2 standardmäßig.", + "Debug Mode: ": "Debug-Modus: ", + "Enable debug logging all the time.": "Debug-Protokollierung immer aktivieren.", "Delete PortMaster Config": "PortMaster Konfiguration löschen", "This can break stuff, don't touch unless you know what you are doing.": "Griffel weg wenn du kein Plan hast, sonst machst du noch alles kaputt!", "Delete PortMaster Runtimes": "PortMaster Laufzeiten löschen", @@ -223,9 +266,11 @@ "Reinstall": "Neuinstallieren", "Uninstall": "Deinstallieren", "Install": "Installieren", + "Links": "Links", "Port Info": "Port Info", "Show Info": "Info anzeigen", "Are you sure you want to uninstall {port_name}?": "{port_name} wirklich deinstallieren?", + "No links for this port.": "Keine Links für diesen Port.", "NO PORTS": "KEINE PORTS", "** NO PORTS FOUND **": "** KEINE PORTS GEFUNDEN **", "Download ports first.": "Zuerst Ports herunterladen.", @@ -238,11 +283,29 @@ "Check": "Überprüfen", "Are you sure you want to download and verify all runtimes?": "Alle runtimes herunterladen und verifizieren?", "Deleted runtime {runtime}": "Runtime- {runtime} entfernt", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Für das beste Erlebnis wechsle zu einer unterstützten Firmware wie muOS oder KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS wird von PortMaster nicht unterstützt.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS wird von PortMaster nicht unterstützt.", + "NextUI is not supported by PortMaster.": "NextUI wird von PortMaster nicht unterstützt.", + "Your OS is not supported by PortMaster.": "Dein Betriebssystem wird von PortMaster nicht unterstützt.", + "Your device has less than 1GB of RAM.": "Dein Gerät hat weniger als 1 GB RAM.", "Disclaimer": "Haftungsausschluss", + "Wait {seconds} seconds": "Bitte {seconds} Sekunden warten", "Accept": "Akzeptieren", + "Warning": "Warnung", + "Many ports will not work.": "Viele Ports werden nicht funktionieren.", + "I Understand": "Verstanden", + "Starting the theme editor...": "Theme-Editor wird gestartet...", + "Stop": "Stopp", + "it stopped": "er wurde beendet", + "Unable to start the theme editor: {error}": "Der Theme-Editor konnte nicht gestartet werden: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "Der Theme-Editor läuft, aber dieses Gerät ist mit keinem Netzwerk verbunden. Verbinde es mit dem WLAN und starte den Theme-Editor erneut.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Scanne auf einem Computer oder Handy im selben Netzwerk den Code oder öffne:\n\n{url}\n\nSieh dir Themes in jeder Bildschirmgröße an und bearbeite die Übersetzungen von PortMaster: Sie werden auf diesem Gerät gespeichert.\n\nDrücke B, um den Theme-Editor zu beenden.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "Der Theme-Editor wurde beendet. Siehe theme_server.txt im PortMaster-Ordner.", "{theme_name} (Selected)": "{theme_name} (Ausgewählt)", "Download": "Herunterladen", "{item_name} (Selected)": "{item_name} (Ausgewählt)", + "Please wait.": "Bitte warten.", "Up to date already": "Neuste Version bereits installiert", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Es wird von {old_release_channel} zur {new_release_channel} Version gewechselt.\n\nSoll das Downgrade durchgeführt werden?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Es gibt eine neue Version von PortMaster ({portmaster_version})\n\nSoll das Upgrade durchgeführt werden?", diff --git a/PortMaster/pylibs/locales/de_DE.themes.json b/PortMaster/pylibs/locales/de_DE.themes.json index cd61552c..d72b3aab 100644 --- a/PortMaster/pylibs/locales/de_DE.themes.json +++ b/PortMaster/pylibs/locales/de_DE.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genres: {port_info.genres}\n• Downloadgröße: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Beschreibung: {port_info.description}\n• Anleitungen:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Ersteller: {theme_info.creator}\n• Status: {theme_info.status}\n• Beschreibung:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownloadgröße: {runtime_info.download_size}\nInstallationsgröße: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownloadgröße: {runtime_info.download_size}\nInstallationsgröße: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} Spiele", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Genre: {port_info.genres}\nGröße: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} frei", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}von {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Status: {runtime_info.status}\nVerwendet: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nAuf dem Gerät: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nVerwendet von: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/es_ES.json b/PortMaster/pylibs/locales/es_ES.json index b3c5b495..e43d2480 100644 --- a/PortMaster/pylibs/locales/es_ES.json +++ b/PortMaster/pylibs/locales/es_ES.json @@ -1,24 +1,43 @@ { + "This is not a valid zip file.": "Este no es un archivo zip válido.", + "illegal file {file_name}": "archivo no permitido {file_name}", + "the port has no directory": "el port no tiene carpeta", + "the port has no launch script": "el port no tiene script de inicio", + "unable to load {file_name}": "no se ha podido cargar {file_name}", "Loading...": "Cargando...", "Loading Sources.": "Cargando Fuentes.", "Download validation failed.": "Error al validar la descarga.", + "Unable to download file.": "No se ha podido descargar el archivo.", "Loading Info.": "Cargando información.", + "Fetching latest ports info data.": "Obteniendo la información más reciente de los ports.", + "Fetching latest porters info.": "Obteniendo la información más reciente de los porters.", + "Fetching latest port stats.": "Obteniendo las estadísticas más recientes de los ports.", "Fetching latest featured ports.": "Obteniendo los últimos ports destacados.", + "Fetching latest featured port images zip.": "Obteniendo el zip más reciente de imágenes de ports destacados.", + "Fetching featured port image {file_name}": "Obteniendo la imagen del port destacado {file_name}", "Installing Theme {download_name}.": "Instalando Tema {download_name}.", "Installing": "Instalando", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip está dañado, descárgalo de nuevo.", + "PortMaster failed to install.": "No se ha podido instalar PortMaster.", "Installing {download_name}.": "Instalando {download_name}.", + "PortMaster installed successfully.": "PortMaster se ha instalado correctamente.", "Installation failed, removing files...": "Instalación fallida, eliminando archivos...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "HOLA.\n\nPARECE QUE ESTÁS INSTALANDO MANUALMENTE UN PORT QUE NO ES COMPATIBLE CON TU DISPOSITIVO.\n\nSI TIENES PROBLEMAS, NO LOS REPORTES EN DISCORD O SERÁS OBJETO DE BURLAS.\n\n¿SEGURO QUE QUIERES CONTINUAR CON LA INSTALACIÓN?", "Okay": "Aceptar", "Cancel": "Cancelar", + "Not compatible with this device.": "No es compatible con este dispositivo.", + "Installation failed: {reason}": "Error en la instalación: {reason}", "Port {download_name} failed to install.": "La instalación del Port {download_name} ha fallado.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} y {runtime_name!r} instalados correctamente.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "¡¡Port {download_name!r} instalado correctamente, pero la instalación de {runtime_name!r} ha fallado!!\n\nReintenta la instalación o revisa la wiki para obtener ayuda.", "Port {download_name!r} installed successfully.": "Tema {download_name!r} instalado correctamente.", "Unable to download in offline mode.": "No se puede descargar en modo desconectado.", + "File not found.": "Archivo no encontrado.", "Unable to find a source for {port_name}": "No se ha podido encontrar una fuente para {port_name}", "Unknown port {port_name}": "Port desconocido {port_name}", "Removing {port_name}": "Eliminando {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Error al desinstalar {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "{port_name} Desinstalado correctamente", "Loading Ports.": "Cargando Ports.", "Port {runtime} contains a bad runtime, game may not run correctly.": "El Port {runtime} contiene un tiempo de ejecución defectuoso, el juego puede no ejecutarse correctamente.", @@ -65,16 +84,32 @@ "Uninstalling {port_name}": "Desinstalando {port_name}", "Updating all port sources:": "Actualizando todas las fuentes de ports:", "Checking {runtime_name}": "Checkeando {runtime_name}", + "Auto-installation": "Instalación automática", + "PortMaster website": "Web de PortMaster", + "{store}: developer": "{store}: desarrollador", "** NO PORT **": "** SIN PORT**", "Ready to Run": "Listo para Funcionar", "Requires Files": "Requiere Archivos", "Installed": "Instalado", "Missing": "No Disponible", + "Installation failed, see log.txt for details.": "Error en la instalación, consulta log.txt para más detalles.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Es para la arquitectura equivocada ({zip_arch}, este dispositivo es {device_arch}), se ha eliminado.", + "Installed {download_name}.": "{download_name} instalado.", + "- SUCCESS: {name}": "- ÉXITO: {name}", + "- FAILURE: {name}": "- FALLO: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Se han agregado o eliminado ports, actualiza la lista para ver los nuevos juegos o eliminar los juegos desinstalados.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Algunos archivos no se han podido instalar, selecciónalos para ver por qué. Se han dejado en la carpeta de instalación automática.", + "Everything installed successfully.": "Todo se ha instalado correctamente.", + "{installed} installed, {failed} failed": "{installed} instalados, {failed} fallidos", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} se ha instalado correctamente.", + "FAILED: {name}": "FALLO: {name}", + "{name} failed to install:\n\n{reason}": "No se ha podido instalar {name}:\n\n{reason}", "Danish": "Danés", "German": "Alemán", "English": "Inglés", "Spanish": "Español", + "Esperanto": "Esperanto", "Finnish": "Finlandés", "French": "Francés", "Italian": "Italiano", @@ -84,6 +119,7 @@ "Polish": "Polaco", "Portuguese (Portugal)": "Portugués (Portugal)", "Portuguese (Brazil)": "Portugués (Brasil)", + "Romanian": "Rumano", "Russian": "Ruso", "Swedish": "Sueco", "Ukrainian": "Ucraniano", @@ -98,6 +134,7 @@ "Alphabetical": "Alfabético", "Recently Added": "Recientemente Añadidos", "Recently Updated": "Recientemente Actualizados", + "Total Downloads": "Descargas totales", "Action": "Acción", "Adventure": "Aventura", "Arcade": "Arcade", @@ -124,6 +161,8 @@ "ARM 32bit": "ARM 32bit", "ARM 64bit": "ARM 64bit", "x86 64bit": "x86 64bit", + "Ascending": "Ascendente", + "Descending": "Descendente", "Sort:": "Ordenar:", "Clear Filters": "Limpiar Filtros", "Genres:": "Géneros:", @@ -163,6 +202,8 @@ "Select a theme for PortMaster.": "Elige el tema de PortMaster.", "Select Color Scheme": "Elige el Esquema de Color", "Select a color scheme for PortMaster.": "Elige un esquema de color para PortMaster.", + "Theme Editor": "Editor de temas", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Previsualiza temas y edita las traducciones de PortMaster desde un ordenador o móvil en la misma red.", "Audio": "Audio", "Music: ": "Música: ", "Enable or Disable background music in PortMaster.": "Activa o Desactiva la música de fondo de PortMaster.", @@ -203,6 +244,8 @@ "Show all ports, ignoring requirements.": "Muestra todos los ports, sin importar los requisitos.", "CWTBE Mode: ": "Modo CWTBE: ", "Enable gptokeyb2 by default.": "Activar gptokeyb2 por defecto.", + "Debug Mode: ": "Modo depuración: ", + "Enable debug logging all the time.": "Activar siempre el registro de depuración.", "Delete PortMaster Config": "Eliminar la Configuración de PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "Esto puede romper cosas, no lo toque a menos que sepas lo que estés haciendo.", "Delete PortMaster Runtimes": "Eliminar Runtimes de PortMaster", @@ -223,9 +266,11 @@ "Reinstall": "Reinstalar", "Uninstall": "Desinstalar", "Install": "Instalar", + "Links": "Enlaces", "Port Info": "Info del Port", "Show Info": "Mostrar información", "Are you sure you want to uninstall {port_name}?": "Estas seguro que quieres desinstalar el {port_name}?", + "No links for this port.": "Este port no tiene enlaces.", "NO PORTS": "SIN PORTS", "** NO PORTS FOUND **": "** NO SE HAN ENCONTRADO PORTS **", "Download ports first.": "Descarga ports primero.", @@ -238,11 +283,29 @@ "Check": "Comprobado", "Are you sure you want to download and verify all runtimes?": "Estás seguro que quieres descargar y verificar todos los runtimes?", "Deleted runtime {runtime}": "Se elimino el runtime {runtime}", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Para la mejor experiencia, cambia a un firmware compatible como muOS o KNULLI.", + "SpruceOS is not supported by PortMaster.": "PortMaster no es compatible con SpruceOS.", + "CrossMix-OS is not supported by PortMaster.": "PortMaster no es compatible con CrossMix-OS.", + "NextUI is not supported by PortMaster.": "PortMaster no es compatible con NextUI.", + "Your OS is not supported by PortMaster.": "PortMaster no es compatible con tu sistema operativo.", + "Your device has less than 1GB of RAM.": "Tu dispositivo tiene menos de 1 GB de RAM.", "Disclaimer": "Aviso legal", + "Wait {seconds} seconds": "Espera {seconds} segundos", "Accept": "Aceptar", + "Warning": "Advertencia", + "Many ports will not work.": "Muchos ports no funcionarán.", + "I Understand": "Entendido", + "Starting the theme editor...": "Iniciando el editor de temas...", + "Stop": "Detener", + "it stopped": "se ha detenido", + "Unable to start the theme editor: {error}": "No se ha podido iniciar el editor de temas: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "El editor de temas está en marcha, pero este dispositivo no está en ninguna red. Conéctalo a una red Wi-Fi y vuelve a iniciar el editor de temas.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "En un ordenador o móvil en la misma red, escanea el código o abre:\n\n{url}\n\nPrevisualiza temas con cualquier tamaño de pantalla y edita las traducciones de PortMaster: se guardan en este dispositivo.\n\nPulsa B para detener el editor de temas.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "El editor de temas se ha detenido. Consulta theme_server.txt en la carpeta de PortMaster.", "{theme_name} (Selected)": "{theme_name} (seleccionado)", "Download": "Descargar", "{item_name} (Selected)": "{item_name} (seleccionado)", + "Please wait.": "Espera, por favor.", "Up to date already": "Ya está actualizado", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Estas cambiando de la version {old_release_channel} a la version {new_release_channel}.\n\nQuieres hacer el downgrade?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Hay una nueva version de PortMaster ({portmaster_version})\n\nTe gustaría actualizarla?", diff --git a/PortMaster/pylibs/locales/es_ES.themes.json b/PortMaster/pylibs/locales/es_ES.themes.json index c8ddd2ec..e45d412d 100644 --- a/PortMaster/pylibs/locales/es_ES.themes.json +++ b/PortMaster/pylibs/locales/es_ES.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Géneros: {port_info.genres}\n• Tamaño descargado: {port_info.download_size}{if:port_info.runtime}\n• Tiempo de ejecución: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descripción: {port_info.description}\n• Instrucciones:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Creador: {theme_info.creator}\n• Estado: {theme_info.status}\n• Descripción:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nombre: {runtime_info.name}\nEstado: {runtime_info.status}\nPorts: {runtime_info.ports}\nTamaño de Descarga: {runtime_info.download_size}\nTamaño de Instalación: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nombre: {runtime_info.name}\nEstado: {runtime_info.status}\nPorts: {runtime_info.ports}\nTamaño de Descarga: {runtime_info.download_size}\nTamaño de Instalación: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} juegos", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Género: {port_info.genres}\nTamaño: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGénero: {port_info.genres}\nDescarga: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} libre", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}por {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Estado: {runtime_info.status}\nEn uso: {runtime_info.in_use}\nDescarga: {runtime_info.download_size}{if:runtime_info.disk_size}\nEn disco: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsado por: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/fr_FR.json b/PortMaster/pylibs/locales/fr_FR.json index 48fde918..f4c9252f 100644 --- a/PortMaster/pylibs/locales/fr_FR.json +++ b/PortMaster/pylibs/locales/fr_FR.json @@ -1,24 +1,43 @@ { + "This is not a valid zip file.": "Ce n'est pas un fichier zip valide.", + "illegal file {file_name}": "fichier non autorisé {file_name}", + "the port has no directory": "le port n'a pas de dossier", + "the port has no launch script": "le port n'a pas de script de lancement", + "unable to load {file_name}": "impossible de charger {file_name}", "Loading...": "Chargement...", "Loading Sources.": "Chargement des sources.", "Download validation failed.": "Échec de la validation du téléchargement.", + "Unable to download file.": "Impossible de télécharger le fichier.", "Loading Info.": "Chargement des informations.", + "Fetching latest ports info data.": "Récupération des dernières informations sur les ports.", + "Fetching latest porters info.": "Récupération des dernières informations sur les porteurs.", + "Fetching latest port stats.": "Récupération des dernières statistiques des ports.", "Fetching latest featured ports.": "Récupération des derniers ports recommandés.", + "Fetching latest featured port images zip.": "Récupération du dernier zip d'images des ports recommandés.", + "Fetching featured port image {file_name}": "Récupération de l'image du port recommandé {file_name}", "Installing Theme {download_name}.": "Installation du thème {download_name}.", "Installing": "Installation en cours", "Theme {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip est endommagé, téléchargez-le à nouveau.", + "PortMaster failed to install.": "Échec de l'installation de PortMaster.", "Installing {download_name}.": "Installation de {download_name}.", + "PortMaster installed successfully.": "PortMaster a été installé avec succès.", "Installation failed, removing files...": "Échec de l'installation, suppression des fichiers en cours...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "BONJOUR.\n\nIL SEMBLE QUE VOUS INSTALLEZ MANUELLEMENT UN PORT INCOMPATIBLE AVEC VOTRE APPAREIL.\n\nSI VOUS RENCONTREZ DES PROBLÈMES, NE LES SIGNALEZ PAS SUR DISCORD, VOUS SERIEZ LA RISÉE DE TOUS.\n\nVOULEZ-VOUS VRAIMENT POURSUIVRE L'INSTALLATION ?", "Okay": "D'accord", "Cancel": "Annuler", + "Not compatible with this device.": "Non compatible avec cet appareil.", + "Installation failed: {reason}": "Échec de l'installation : {reason}", "Port {download_name} failed to install.": "Échec de l'installation du port {download_name}.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} et {runtime_name!r} installé avec succès.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Le port {download_name!r} a été installé avec succès, mais {runtime_name!r} n'a pas pu être installé!!\n\nRéinstallez pour essayer à nouveau, ou consultez le wiki pour obtenir de l'aide.", "Port {download_name!r} installed successfully.": "Le thème {download_name!r} a été installé avec succès.", "Unable to download in offline mode.": "Impossible de télécharger un runtime en mode hors ligne.", + "File not found.": "Fichier introuvable.", "Unable to find a source for {port_name}": "Impossible de trouver une source pour {port_name}", "Unknown port {port_name}": "Port inconnu {port_name}", "Removing {port_name}": "Suppression de {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Erreur lors de la désinstallation de {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "{port_name} désinstallé avec succès", "Loading Ports.": "Chargement des ports.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Le port {runtime} contient un runtime corrompu, le jeu pourrait ne pas s'exécuter correctement.", @@ -65,16 +84,32 @@ "Uninstalling {port_name}": "Désinstallation de {port_name}", "Updating all port sources:": "Mise à jour de toutes les sources de ports :", "Checking {runtime_name}": "Vérification de {runtime_name}", + "Auto-installation": "Installation automatique", + "PortMaster website": "Site de PortMaster", + "{store}: developer": "{store} : développeur", "** NO PORT **": "** AUCUN RÉSULTAT **", "Ready to Run": "Prêt à Exécuter", "Requires Files": "Fichiers Requis", "Installed": "Installé", "Missing": "Manquant", + "Installation failed, see log.txt for details.": "Échec de l'installation, voir log.txt pour plus de détails.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Il est pour la mauvaise architecture ({zip_arch}, cet appareil est {device_arch}), il a été supprimé.", + "Installed {download_name}.": "{download_name} installé.", + "- SUCCESS: {name}": "- SUCCÈS : {name}", + "- FAILURE: {name}": "- ÉCHEC : {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Les ports ont été ajoutés ou supprimés, mettez à jour votre liste de jeux pour voir les nouveaux jeux ou supprimez ceux désinstallés.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Certains fichiers n'ont pas pu être installés, sélectionnez-les pour savoir pourquoi. Ils ont été laissés dans le dossier d'installation automatique.", + "Everything installed successfully.": "Tout a été installé avec succès.", + "{installed} installed, {failed} failed": "{installed} installés, {failed} en échec", + "OK: {name}": "OK : {name}", + "{name} installed successfully.": "{name} a été installé avec succès.", + "FAILED: {name}": "ÉCHEC : {name}", + "{name} failed to install:\n\n{reason}": "Échec de l'installation de {name} :\n\n{reason}", "Danish": "Danois", "German": "Allemand", "English": "Anglais", "Spanish": "Espagnol", + "Esperanto": "Espéranto", "Finnish": "Finnois", "French": "Français", "Italian": "Italien", @@ -84,6 +119,7 @@ "Polish": "Polonais", "Portuguese (Portugal)": "Portugais (Portugal)", "Portuguese (Brazil)": "Portugais (Brésil)", + "Romanian": "Roumain", "Russian": "Russe", "Swedish": "Suédois", "Ukrainian": "Ukrainien", @@ -98,6 +134,7 @@ "Alphabetical": "Ordre alphabétique", "Recently Added": "Ajouts récents", "Recently Updated": "Récemment mis à jour", + "Total Downloads": "Téléchargements totaux", "Action": "Action", "Adventure": "Aventure", "Arcade": "Arcade", @@ -124,6 +161,8 @@ "ARM 32bit": "ARM 32bits", "ARM 64bit": "ARM 64bits", "x86 64bit": "x86 64bits", + "Ascending": "Croissant", + "Descending": "Décroissant", "Sort:": "Ordre :", "Clear Filters": "Réinitialiser les filtres", "Genres:": "Genres :", @@ -163,6 +202,8 @@ "Select a theme for PortMaster.": "Sélectionnez un thème pour PortMaster.", "Select Color Scheme": "Sélection de la palette de couleurs", "Select a color scheme for PortMaster.": "Sélectionnez un schéma de couleurs pour PortMaster.", + "Theme Editor": "Éditeur de thèmes", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Prévisualisez les thèmes et modifiez les traductions de PortMaster depuis un ordinateur ou un téléphone sur le même réseau.", "Audio": "Audio", "Music: ": "Musique : ", "Enable or Disable background music in PortMaster.": "Activer ou désactiver la musique de fond dans PortMaster.", @@ -203,6 +244,8 @@ "Show all ports, ignoring requirements.": "Afficher tous les ports, ignorer les exigences.", "CWTBE Mode: ": "Mode CWTBE : ", "Enable gptokeyb2 by default.": "Activer gptokeyb2 par défaut.", + "Debug Mode: ": "Mode débogage : ", + "Enable debug logging all the time.": "Toujours activer les journaux de débogage.", "Delete PortMaster Config": "Supprimer la configuration de PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "Cela peut rendre votre installation inutilisable, n'y touchez pas à moins de savoir ce que vous faites.", "Delete PortMaster Runtimes": "Supprimer les runtimes de PortMaster", @@ -223,9 +266,11 @@ "Reinstall": "Réinstaller", "Uninstall": "Désinstaller", "Install": "Installer", + "Links": "Liens", "Port Info": "Informations sur le port", "Show Info": "Afficher les Infos", "Are you sure you want to uninstall {port_name}?": "Êtes-vous sûr de vouloir désinstaller {port_name}?", + "No links for this port.": "Aucun lien pour ce port.", "NO PORTS": "AUCUN RÉSULTAT", "** NO PORTS FOUND **": "** AUCUN RÉSULTAT **", "Download ports first.": "Téléchargez d'abord les ports.", @@ -238,11 +283,29 @@ "Check": "Vérifier", "Are you sure you want to download and verify all runtimes?": "Êtes-vous sûr de vouloir télécharger et vérifier tous les runtimes ?", "Deleted runtime {runtime}": "Runtime {runtime} supprimé", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Pour une meilleure expérience, passez à un firmware compatible comme muOS ou KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS n'est pas pris en charge par PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS n'est pas pris en charge par PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI n'est pas pris en charge par PortMaster.", + "Your OS is not supported by PortMaster.": "Votre système n'est pas pris en charge par PortMaster.", + "Your device has less than 1GB of RAM.": "Votre appareil a moins de 1 Go de RAM.", "Disclaimer": "Avertissement", + "Wait {seconds} seconds": "Patientez {seconds} secondes", "Accept": "Accepter", + "Warning": "Avertissement", + "Many ports will not work.": "Beaucoup de ports ne fonctionneront pas.", + "I Understand": "J'ai compris", + "Starting the theme editor...": "Démarrage de l'éditeur de thèmes...", + "Stop": "Arrêter", + "it stopped": "il s'est arrêté", + "Unable to start the theme editor: {error}": "Impossible de démarrer l'éditeur de thèmes : {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "L'éditeur de thèmes fonctionne, mais cet appareil n'est connecté à aucun réseau. Connectez-le au Wi-Fi, puis relancez l'éditeur de thèmes.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Sur un ordinateur ou un téléphone du même réseau, scannez le code ou ouvrez :\n\n{url}\n\nPrévisualisez les thèmes à toutes les tailles d'écran et modifiez les traductions de PortMaster : elles sont enregistrées sur cet appareil.\n\nAppuyez sur B pour arrêter l'éditeur de thèmes.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "L'éditeur de thèmes s'est arrêté. Voir theme_server.txt dans le dossier de PortMaster.", "{theme_name} (Selected)": "{theme_name} (Sélectionné)", "Download": "Télécharger", "{item_name} (Selected)": "{item_name} (Sélectionné)", + "Please wait.": "Veuillez patienter.", "Up to date already": "Déjà à jour", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Vous passez de l' {old_release_channel} à la version {new_release_channel} .\n\nVoulez-vous rétrograder ?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Il y a une nouvelle version de PortMaster ({portmaster_version})\n\nVoulez-vous mettre à jour ?", diff --git a/PortMaster/pylibs/locales/fr_FR.themes.json b/PortMaster/pylibs/locales/fr_FR.themes.json index e330bdef..fdcf79c8 100644 --- a/PortMaster/pylibs/locales/fr_FR.themes.json +++ b/PortMaster/pylibs/locales/fr_FR.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genres : {port_info.genres}\n• Taille du téléchargement : {port_info.download_size}{if:port_info.runtime}\n• Runtime : {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description : {port_info.description}\n• Instructions :\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM : {system.portmaster_version}, HM : {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Créateur : {theme_info.creator}\n• Statut : {theme_info.status}\n• Description :\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nom : {runtime_info.name}\nStatut: {runtime_info.status}\nPorts : {runtime_info.ports}\nTaille du téléchargement : {runtime_info.download_size}\nTaille de l'installation : {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nom : {runtime_info.name}\nStatut: {runtime_info.status}\nPorts : {runtime_info.ports}\nTaille du téléchargement : {runtime_info.download_size}\nTaille de l'installation : {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} jeux", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Genre : {port_info.genres}\nTaille : {port_info.download_size}{if:port_info.runtime}\nRuntime : {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorteur : {port_info.porter}\nGenre : {port_info.genres}\nTéléchargement : {port_info.download_size}{if:port_info.runtime}\nRuntime : {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} disponible", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}par {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Statut : {runtime_info.status}\nUtilisé : {runtime_info.in_use}\nTéléchargement : {runtime_info.download_size}{if:runtime_info.disk_size}\nSur le disque : {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUtilisé par : {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/it_IT.json b/PortMaster/pylibs/locales/it_IT.json index 58b94db7..4ae4100e 100644 --- a/PortMaster/pylibs/locales/it_IT.json +++ b/PortMaster/pylibs/locales/it_IT.json @@ -1,26 +1,47 @@ { + "This is not a valid zip file.": "Questo non è un file zip valido.", + "illegal file {file_name}": "file non consentito {file_name}", + "the port has no directory": "il port non ha una cartella", + "the port has no launch script": "il port non ha uno script di avvio", + "unable to load {file_name}": "impossibile caricare {file_name}", "Loading...": "Caricamento...", "Loading Sources.": "Caricamento Fonti.", "Download validation failed.": "Validazione del download fallita.", + "Unable to download file.": "Impossibile scaricare il file.", "Loading Info.": "Caricamento Informazioni", + "Fetching latest ports info data.": "Recupero delle ultime informazioni sui port.", + "Fetching latest porters info.": "Recupero delle ultime informazioni sui porter.", + "Fetching latest port stats.": "Recupero delle ultime statistiche dei port.", "Fetching latest featured ports.": "Recupero informazioni sugli ultimi ports Raccomandati dal Team.", + "Fetching latest featured port images zip.": "Recupero dell'ultimo zip di immagini dei port consigliati.", + "Fetching featured port image {file_name}": "Recupero dell'immagine del port consigliato {file_name}", "Installing Theme {download_name}.": "Installo Tema {download_name}.", "Installing": "Installo", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} installato con successo.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip è danneggiato, scaricalo di nuovo.", + "PortMaster failed to install.": "Installazione di PortMaster non riuscita.", "Installing {download_name}.": "Installazione {download_name}.", + "PortMaster installed successfully.": "PortMaster è stato installato correttamente.", "Installation failed, removing files...": "Installazione fallita, rimozione file in corso...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "CIAO.\n\nSEMBRA CHE TU STIA INSTALLANDO MANUALMENTE UN PORT NON COMPATIBILE CON IL TUO DISPOSITIVO.\n\nSE HAI PROBLEMI, NON SEGNALARLI SU DISCORD, ALTRIMENTI VERRAI DERISO.\n\nSEI SICURO DI VOLER CONTINUARE L'INSTALLAZIONE?", "Okay": "Ok", "Cancel": "Annulla", + "Not compatible with this device.": "Non compatibile con questo dispositivo.", + "Installation failed: {reason}": "Installazione non riuscita: {reason}", "Port {download_name} failed to install.": "{download_name} fallisce l'installazione.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "{download_name!r} e {runtime_name!r} sono stati installati con successo", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} e {runtime_name!r} installazione fallita!\n\nRiprova o controlla la Fiji per ulteriori informazioni.", "Port {download_name!r} installed successfully.": "Port {download_name!r} installato con successo.", + "Unable to download in offline mode.": "Impossibile scaricare in modalità offline.", + "File not found.": "File non trovato.", "Unable to find a source for {port_name}": "Impossibile trovare una fonte per {port_name}", "Unknown port {port_name}": "Port sconosciuto {port_name}", "Removing {port_name}": "Rimuovo {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Errore durante la disinstallazione di {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "{port_name} disinstallato con successo.", "Loading Ports.": "Caricamento Ports.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} sembra avere una runtime corrota, il gioco potrebbe avere problemi", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "Il port {runtime} contiene un runtime sconosciuto, il gioco potrebbe non funzionare correttamente.", "Not Installed": "Non Installato", "Update Available": "Aggiornamento Disponibile", "Verified": "Verificato", @@ -30,6 +51,7 @@ "Verifying runtime {runtime}": "Verifico runtime {runtime}", "Verifying": "Verifica in corso", "Unable to find a download for {runtime}.": "Impossibile trovare il download per {runtime}", + "Unable to download {runtime} in {device_arch}.": "Impossibile scaricare {runtime} per {device_arch}.", "Verified {runtime} successfully.": "{runtime} verificata con successo.", "Verified {runtime}.": "{runtime} Verificata.", "Updating {runtime}.": "Aggiornamento {runtime}.", @@ -37,6 +59,7 @@ "Downloading runtime {runtime}.": "Scarico {runtime}", "Unable to download {runtime}, game may not run correctly.": "Impossibile scaricare la runtime {runtime}, il gioco potrebbe presentare dei problemi.", "Successfully downloaded {runtime}.": "Scaricato con successo {runtime}", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nPer farlo:\nMENU -> UTILITIES -> Rescan Rom Directory", "Unable to download verification file.": "Non posso scaricare i file di verifica", "Unable to download non zip files.": "Non posso scaricare i file non zip", "Cache out of date.": "Cache Obsoleta.", @@ -55,19 +78,38 @@ "Unable to validate download.": "Impossibile validare il download.", "Unknown IP": "IP Ignoto", "N/A": "N/A", + "Fetching latest gameinfo.xml files and cover images.": "Recupero degli ultimi file gameinfo.xml e delle copertine.", + "Downloading gameinfo.xml for {source_name}": "Download di gameinfo.xml per {source_name}", "Installing {port_name}": "Installo {port_name}", "Uninstalling {port_name}": "Disinstallo {port_name}", "Updating all port sources:": "Aggiorno le fonti...", "Checking {runtime_name}": "Verifico {runtime_name}", + "Auto-installation": "Installazione automatica", + "PortMaster website": "Sito di PortMaster", + "{store}: developer": "{store}: sviluppatore", "** NO PORT **": "Nessun Risultato", "Ready to Run": "Ready to Run", "Requires Files": "File richiesti", "Installed": "Installato", "Missing": "Mancante", + "Installation failed, see log.txt for details.": "Installazione non riuscita, vedi log.txt per i dettagli.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "È per l'architettura sbagliata ({zip_arch}, questo dispositivo è {device_arch}), è stato eliminato.", + "Installed {download_name}.": "{download_name} installato.", + "- SUCCESS: {name}": "- RIUSCITO: {name}", + "- FAILURE: {name}": "- NON RIUSCITO: {name}", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Sono stati aggiunti o rimossi dei port, aggiorna l'elenco dei giochi per vedere i nuovi giochi o rimuovere quelli disinstallati.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Alcuni file non sono stati installati, selezionali per vedere il motivo. Sono rimasti nella cartella di installazione automatica.", + "Everything installed successfully.": "Tutto è stato installato correttamente.", + "{installed} installed, {failed} failed": "{installed} installati, {failed} non riusciti", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} è stato installato correttamente.", + "FAILED: {name}": "NON RIUSCITO: {name}", + "{name} failed to install:\n\n{reason}": "Impossibile installare {name}:\n\n{reason}", "Danish": "Danese", "German": "Tedesco", "English": "Inglese", "Spanish": "Spagnolo", + "Esperanto": "Esperanto", "Finnish": "Finlandese", "French": "Francese", "Italian": "Italiano", @@ -77,6 +119,7 @@ "Polish": "Polacco", "Portuguese (Portugal)": "Portoghese (Portogallo)", "Portuguese (Brazil)": "Portoghese (Brasile)", + "Romanian": "Rumeno", "Russian": "Russo", "Swedish": "Svedese", "Ukrainian": "Ucraino", @@ -86,10 +129,12 @@ "Featured Ports": "Raccomandati dal Team", "Select": "Seleziona", "Back": "Indietro", + "No featured ports found, internet connection is required.": "Nessun port consigliato trovato, è necessaria una connessione a internet.", "Filters Scene": "Filtri", "Alphabetical": "Alfabetico", "Recently Added": "Aggiunti di Recente", "Recently Updated": "Aggiornati di recente", + "Total Downloads": "Download totali", "Action": "Azione", "Adventure": "Avventura", "Arcade": "Arcade", @@ -105,12 +150,25 @@ "Strategy": "Strategia", "Visual Novel": "Visual Novel", "Other": "Altro", + "Files Required": "File richiesti", + "Experimental": "Sperimentale", "Broken Ports": "Ports Problematici", + "Free game, all files included.": "Gioco gratuito, tutti i file inclusi.", + "Demo files included.": "File demo inclusi.", + "Free external assets needed.": "Servono risorse esterne gratuite.", + "Paid external assets needed.": "Servono risorse esterne a pagamento.", "{runtime_name} Runtime": "{runtime_name} Runtime", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Ascending": "Crescente", + "Descending": "Decrescente", "Sort:": "Ordina per:", "Clear Filters": "Rimuovi Filtri", "Genres:": "Genere", "Attributes:": "Runtime", + "Architecture:": "Architettura:", + "Availability:": "Disponibilità:", "Porters:": "Porter", "Delete": "Elimina", "Upper Case": "Maiuscolo", @@ -135,6 +193,7 @@ "Secret Mode {secret_mode}": "Modalità Segreta {secret_mode}", "Enabled": "Attiva", "Disabled": "Disattiva", + "Are you sure you want to exit PortMaster?": "Sei sicuro di voler uscire da PortMaster?", "Options Menu": "Impostazioni", "Interface": "Interfaccia", "Choose Language": "Lingua", @@ -143,15 +202,24 @@ "Select a theme for PortMaster.": "Cambia il tuo attuale tema o scaricane di nuovi.", "Select Color Scheme": "Selezionare schema colori", "Select a color scheme for PortMaster.": "Cambia schema colori dell'attuale tema.", + "Theme Editor": "Editor dei temi", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Visualizza in anteprima i temi e modifica le traduzioni di PortMaster da un computer o telefono sulla stessa rete.", "Audio": "Audio", "Music: ": "Musica: ", "Enable or Disable background music in PortMaster.": "Abilita o disabilita la musica di sottofondo in PortMaster.", "Sound FX: ": "Effetti Sonori: ", "Enable or Disable sound effects in PortMaster.": "Abilita o disabilita gli effetti sonori in PortMaster.", + "Port Metadata": "Metadati dei port", + "Metadata Update: ": "Aggiornamento metadati: ", "Auto": "Auto", "Manual": "Manuale", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster aggiorna i metadati quando i port vengono installati, così mostra le immagini e le descrizioni dei port quando li sfogli sul tuo dispositivo.", "Metadata Refresh": "Aggiorna Metadati", + "Manually update port metadata with missing/updated information and artwork.": "Aggiorna manualmente i metadati dei port con le informazioni e le immagini mancanti o aggiornate.", + "Ports Location: ": "Posizione dei port: ", "Roms section": "Sezione Roms", + "Ports tab": "Scheda Port", + "Location where ports should be installed to.": "Posizione in cui installare i port.", "SD 1": "SD 1", "SD 2": "SD 2", "System": "Sistema", @@ -160,28 +228,49 @@ "Experimental Ports: ": "Porte Sperimentali: ", "Show or hide experimental ports.": "Mostra o nascondi le porte sperimentali.", "Update Ports": "Aggiorna Ports", + "Fetch latest ports information.": "Recupera le ultime informazioni sui port.", "Update PortMaster": "Aggiorna PortMaster", "Force check for a new PortMaster version.": "Forza il controllo per una nuova versione di PortMaster.", + "Restore PortMaster": "Ripristina PortMaster", + "This will restore PortMaster to the latest stable version of PortMaster.": "Ripristina PortMaster all'ultima versione stabile.", + "Release Channel: {channel}": "Canale di rilascio: {channel}", + "Change release channel of PortMaster, either beta or stable.": "Cambia il canale di rilascio di PortMaster, beta o stabile.", + "Enable Alpha Releases": "Abilita le versioni Alpha", + "Change release channel of PortMaster to alpha.": "Passa PortMaster al canale di rilascio alpha.", "Controller Mode: {controller_mode}": "Modalità Controller: {controller_mode}", "Toggle between various controller layouts.": "Cambia Schema comandi per la navigazione dei menu.", "Secret Options": "Opzioni Segrete", "All Ports: ": "Tutte Le Porte: ", "Show all ports, ignoring requirements.": "Mostra tutte le porte, ignorando i requisiti.", "CWTBE Mode: ": "Modo CWTBE ", + "Enable gptokeyb2 by default.": "Abilita gptokeyb2 come predefinito.", + "Debug Mode: ": "Modalità debug: ", + "Enable debug logging all the time.": "Abilita sempre i log di debug.", "Delete PortMaster Config": "Elimina Configurazione Attuale", "This can break stuff, don't touch unless you know what you are doing.": "Attenzione, non toccare a meno che tu non sappia cosa stai facendo.", "Delete PortMaster Runtimes": "Elimina Runtimes installate", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "Sei sicuro di voler ripristinare PortMaster?\n\nDovrai eseguire un port speciale che verrà installato.", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "È stato installato un port speciale chiamato \"Restore PortMaster.sh\"\n\nEseguilo per ripristinare la tua installazione di PortMaster.", + "PortMaster will now quit so you can run the restoration port.": "PortMaster ora si chiuderà, così potrai eseguire il port di ripristino.", "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "Sei sicuro di voler passare al canale di rilascio alpha?\n\nPortMaster verrà aggiornato o retrocesso di conseguenza.\n\nQUESTO PUÒ EFFETTIVAMENTE ROMPERE IL SISTEMA, SEI STATO AVVISATO", + "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Sei sicuro di voler cambiare il canale di rilascio da {current_channel} a {new_channel}?\n\nPortMaster verrà aggiornato o riportato a una versione precedente di conseguenza.", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Sei sicuro di voler aggiornare il tuo gamelist.xml?\n\nVerranno sovrascritte le immagini e le informazioni dei port già scaricate, e servirà un download di oltre 50 MB.", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Sei sicuro di voler spostare i port da {from_mode} a {to_mode}?", "SDCARD 1": "SDCARD 1", "SDCARD 2": "SDCARD 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Sei sicuro di voler gestire e installare i port su {to_loc}?\n\nI port già installati non verranno spostati.\nPortMaster verrà riavviato per applicare la modifica.", "Deleting Runtimes:": "Eliminazione Runtimes:", "Source Manager": "Gestione Fonti", + "Port Info Popup": "Popup informazioni port", + "Hide Info": "Nascondi info", "Reinstall": "Reinstalla", "Uninstall": "Disinstalla", "Install": "Installa", + "Links": "Link", "Port Info": "Port Info", "Show Info": "Mostra info", "Are you sure you want to uninstall {port_name}?": "Sei sicuro di voler disinstallare {port_name}?", + "No links for this port.": "Nessun link per questo port.", "NO PORTS": "Nessun Risultato", "** NO PORTS FOUND **": "** Nessun Risultato **", "Download ports first.": "Scarica qualcosa prima.", @@ -194,9 +283,29 @@ "Check": "Verifica", "Are you sure you want to download and verify all runtimes?": "Sei sicuro di voler scaricare tutte le runtimes?", "Deleted runtime {runtime}": "Runtime {runtime} eliminata", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Per un'esperienza migliore, passa a un firmware supportato come muOS o KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS non è supportato da PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS non è supportato da PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI non è supportato da PortMaster.", + "Your OS is not supported by PortMaster.": "Il tuo sistema operativo non è supportato da PortMaster.", + "Your device has less than 1GB of RAM.": "Il tuo dispositivo ha meno di 1 GB di RAM.", + "Disclaimer": "Avvertenze", + "Wait {seconds} seconds": "Attendi {seconds} secondi", + "Accept": "Accetto", + "Warning": "Attenzione", + "Many ports will not work.": "Molti port non funzioneranno.", + "I Understand": "Ho capito", + "Starting the theme editor...": "Avvio dell'editor dei temi...", + "Stop": "Ferma", + "it stopped": "si è fermato", + "Unable to start the theme editor: {error}": "Impossibile avviare l'editor dei temi: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "L'editor dei temi è in esecuzione, ma questo dispositivo non è connesso a una rete. Collegalo al Wi-Fi, poi avvia di nuovo l'editor dei temi.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Su un computer o telefono sulla stessa rete, scansiona il codice o apri:\n\n{url}\n\nVisualizza in anteprima i temi con qualsiasi dimensione dello schermo e modifica le traduzioni di PortMaster: vengono salvate su questo dispositivo.\n\nPremi B per fermare l'editor dei temi.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "L'editor dei temi si è fermato. Vedi theme_server.txt nella cartella di PortMaster.", "{theme_name} (Selected)": "{theme_name} (Attuale)", "Download": "Download", "{item_name} (Selected)": "{item_name} (Attuale)", + "Please wait.": "Attendi.", "Up to date already": "Già aggiornata", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Stai passando dalla versione {old_release_channel} alla versione {new_release_channel} .\n\nVuoi tornare alla versione precedente?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "C'è una nuova versione di PortMaster ({portmaster_version})\n\nVuoi aggiornare?", diff --git a/PortMaster/pylibs/locales/it_IT.themes.json b/PortMaster/pylibs/locales/it_IT.themes.json index 2b6a89d0..d0c19ad5 100644 --- a/PortMaster/pylibs/locales/it_IT.themes.json +++ b/PortMaster/pylibs/locales/it_IT.themes.json @@ -1,9 +1,12 @@ { + "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "Ora: {system.time_24hr}\nBatteria: {system.battery_level}\nSpazio: {system.free_space} liberi", "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genere: {port_info.genres} Dimensione Download: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}", "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}Ports disponibili: {ports_list.filter_ports} / {ports_list.total_ports} \nFiltri: {ports_list.filters}{endif}", "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "Spazio libero: {system.free_space}\n\nPorter: {port_info.porter}\nGenere: {port_info.genres}\nDimensione: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nIstruzioni: {port_info.instructions}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpazio: {system.free_space} liberi / {system.total_space} totali\nOra: {system.time_24hr}\nBatteria: {system.battery_level}", "IP: {system.ip_address}": "IP: {system.ip_address}", "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "Autore: {theme_info.creator}\nStato: {theme_info.status}", + "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpazio: {system.free_space} liberi", "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "Filtri: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Genere: {port_info.genres}\nDimensione: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", "Time: {system.time_24hr} Battery: {system.battery_level}": "Ora: {system.time_24hr} Batteria: {system.battery_level}", @@ -12,5 +15,15 @@ "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "Generi: {port_info.genres}\n{if:port_info.install_size}Dimensione Installazione: {port_info.install_size}{else}Dimensione Download: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "Ora: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDispositivo: {system.device_name}\nBatteria: {system.battery_level}", "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpazio: {system.free_space} liberi / {system.total_space} totali", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nome: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports} \nDimensione Download: {runtime_info.download_size}\nDimensione Installazione: {runtime_info.disk_size}" + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• Porter: {port_info.porter}\n• Genere: {port_info.genres}\n• Descrizione: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genere: {port_info.genres}\n• Dimensione: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descrizione: {port_info.description}\n• Istruzioni:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Autore: {theme_info.creator}\n• Stato: {theme_info.status}\n• Descrizione:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nome: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports} \nDimensione Download: {runtime_info.download_size}\nDimensione Installazione: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} giochi", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Genere: {port_info.genres}\nDimensione: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenere: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} liberi", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}di {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Stato: {runtime_info.status}\nIn uso: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nSu disco: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsato da: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/ja_JP.json b/PortMaster/pylibs/locales/ja_JP.json index 5de5c101..e00fef6f 100644 --- a/PortMaster/pylibs/locales/ja_JP.json +++ b/PortMaster/pylibs/locales/ja_JP.json @@ -1,26 +1,47 @@ { + "This is not a valid zip file.": "有効な zip ファイルではありません。", + "illegal file {file_name}": "許可されていないファイル {file_name}", + "the port has no directory": "ポートにフォルダーがありません", + "the port has no launch script": "ポートに起動スクリプトがありません", + "unable to load {file_name}": "{file_name} を読み込めません", "Loading...": "読み込み中...", "Loading Sources.": "ソースの読み込み中", "Download validation failed.": "ダウンロードの検証に失敗しました。", + "Unable to download file.": "ファイルをダウンロードできません。", "Loading Info.": "情報を読み込み中", + "Fetching latest ports info data.": "最新のポート情報を取得中。", + "Fetching latest porters info.": "最新のポーター情報を取得中。", + "Fetching latest port stats.": "最新のポート統計を取得中。", "Fetching latest featured ports.": "最新の注目ポートを取得しています", + "Fetching latest featured port images zip.": "注目のポートの最新画像 zip を取得中。", + "Fetching featured port image {file_name}": "注目のポートの画像を取得中 {file_name}", "Installing Theme {download_name}.": "テーマ {download_name} のインストール中。", "Installing": "インストール中", "Theme {download_name!r} installed successfully.": "テーマ {download_name!r} のインストールに成功しました。", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip が壊れています。もう一度ダウンロードしてください。", + "PortMaster failed to install.": "PortMaster のインストールに失敗しました。", "Installing {download_name}.": "{download_name} インストール中.", + "PortMaster installed successfully.": "PortMaster をインストールしました。", "Installation failed, removing files...": "インストールに失敗しました。ファイルの削除中...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "こんにちは。\n\nお使いのデバイスと互換性のないポートを手動でインストールしようとしているようです。\n\n問題が起きても DISCORD で報告しないでください。笑われます。\n\nインストールを続けてもよろしいですか?", "Okay": "了解", "Cancel": "キャンセル", + "Not compatible with this device.": "このデバイスと互換性がありません。", + "Installation failed: {reason}": "インストールに失敗しました: {reason}", "Port {download_name} failed to install.": "ポート {download_name} がインストールされませんでした。", "Port {download_name!r} and {runtime_name!r} installed successfully.": "ポート {download_name!r} と {runtime_name!r} が正常にインストールされました。", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "ポート {download_name!r} は正常にインストールされましたが、{runtime_name!r} のインストールに失敗しました!\n\n再インストールして再試行するか、ウィキのヘルプを確認してください。", "Port {download_name!r} installed successfully.": "ポート {download_name!r} が正常にインストールされました。", + "Unable to download in offline mode.": "オフラインモードではダウンロードできません。", + "File not found.": "ファイルが見つかりません。", "Unable to find a source for {port_name}": "{port_name} のソースが見つかりません", "Unknown port {port_name}": "不明なポート {port_name}", "Removing {port_name}": "{port_name} を削除中", + "Error uninstalling {port_name}\n\n{error}": "{port_name} のアンインストール中にエラーが発生しました\n\n{error}", "Successfully uninstalled {port_name}": "{port_name} のアンインストールに成功しました", "Loading Ports.": "ポートを読み込み中", "Port {runtime} contains a bad runtime, game may not run correctly.": "ポート {runtime} に不正なランタイムが含まれています。ゲームが正しく動作しない可能性があります。", + "Port {runtime} contains an unknown runtime, game may not run correctly.": "ポート {runtime} に不明なランタイムが含まれています。ゲームが正しく動作しない可能性があります。", "Not Installed": "インストールされていません", "Update Available": "アップデートが利用できます", "Verified": "認証済み", @@ -30,6 +51,7 @@ "Verifying runtime {runtime}": "ランタイム {runtime} を確認しています", "Verifying": "検証中", "Unable to find a download for {runtime}.": "{runtime} のダウンロードが見つかりません。", + "Unable to download {runtime} in {device_arch}.": "{device_arch} 用の {runtime} をダウンロードできません。", "Verified {runtime} successfully.": "{runtime} 認証された。", "Verified {runtime}.": "{runtime} 認証済み", "Updating {runtime}.": "{runtime} を更新中", @@ -37,6 +59,7 @@ "Downloading runtime {runtime}.": "ランタイム {runtime} をダウンロードしています。", "Unable to download {runtime}, game may not run correctly.": "{runtime} をダウンロードできません。ゲームが正しく実行されない可能性があります。", "Successfully downloaded {runtime}.": "{runtime} のダウンロードに成功しました。", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\n手順:\nMENU -> UTILITIES -> Rescan Rom Directory", "Unable to download verification file.": "確認情報が見つかりません。", "Unable to download non zip files.": "非 zip ファイルをダウンロードできません。", "Cache out of date.": "キャッシュが古いです。", @@ -55,19 +78,38 @@ "Unable to validate download.": "ダウンロードを検証できません。", "Unknown IP": "不明なIP", "N/A": "N/A", + "Fetching latest gameinfo.xml files and cover images.": "最新の gameinfo.xml ファイルとカバー画像を取得中。", + "Downloading gameinfo.xml for {source_name}": "{source_name} の gameinfo.xml をダウンロード中", "Installing {port_name}": "{port_name} インストール中", "Uninstalling {port_name}": "{port_name} をアンインストール中", "Updating all port sources:": "すべてのポートソースを更新しています:", "Checking {runtime_name}": "{runtime_name} を確認中", + "Auto-installation": "自動インストール", + "PortMaster website": "PortMaster ウェブサイト", + "{store}: developer": "{store}: 開発者", "** NO PORT **": "** ポートなし **", "Ready to Run": "実行準備完了", "Requires Files": "必要なファイル", "Installed": "インストール済み", "Missing": "欠落", + "Installation failed, see log.txt for details.": "インストールに失敗しました。詳しくは log.txt を確認してください。", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "別のアーキテクチャ用({zip_arch}、このデバイスは {device_arch})のため削除しました。", + "Installed {download_name}.": "{download_name} をインストールしました。", + "- SUCCESS: {name}": "- 成功: {name}", + "- FAILURE: {name}": "- 失敗: {name}", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "ポートが追加または削除されました。新しいゲームを表示したりアンインストールしたゲームを消したりするには、ゲームリストを更新してください。", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "一部のファイルをインストールできませんでした。選択すると理由が表示されます。ファイルは自動インストールフォルダーに残っています。", + "Everything installed successfully.": "すべてインストールしました。", + "{installed} installed, {failed} failed": "{installed} 件インストール、{failed} 件失敗", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} をインストールしました。", + "FAILED: {name}": "失敗: {name}", + "{name} failed to install:\n\n{reason}": "{name} をインストールできませんでした:\n\n{reason}", "Danish": "デンマーク語", "German": "ドイツ語", "English": "英語", "Spanish": "スペイン語", + "Esperanto": "エスペラント語", "Finnish": "フィンランド語", "French": "フランス語", "Italian": "イタリア語", @@ -77,6 +119,7 @@ "Polish": "ポーランド語", "Portuguese (Portugal)": "ポルトガル語 (ポルトガル)", "Portuguese (Brazil)": "ポルトガル語 (ブラジル)", + "Romanian": "ルーマニア語", "Russian": "ロシア語", "Swedish": "スウェーデン語", "Ukrainian": "ウクライナ語", @@ -91,6 +134,7 @@ "Alphabetical": "アルファベット順", "Recently Added": "最近の追加", "Recently Updated": "最近のアップデート", + "Total Downloads": "総ダウンロード数", "Action": "動作", "Adventure": "冒険", "Arcade": "アーケード", @@ -106,12 +150,19 @@ "Strategy": "戦略", "Visual Novel": "ビジュアルノベル", "Other": "その他", + "Files Required": "ファイルが必要", + "Experimental": "実験的", "Broken Ports": "壊れたポート", "Free game, all files included.": "無料ゲーム、すべてのファイルが含まれています。", "Demo files included.": "デモファイルが含まれています。", "Free external assets needed.": "無料の外部アセットが必要です。", "Paid external assets needed.": "有料の外部アセットが必要です。", "{runtime_name} Runtime": "{runtime_name} ランタイム", + "ARM 32bit": "ARM 32bit", + "ARM 64bit": "ARM 64bit", + "x86 64bit": "x86 64bit", + "Ascending": "昇順", + "Descending": "降順", "Sort:": "ソート:", "Clear Filters": "フィルターをクリアにする", "Genres:": "ジャンル:", @@ -151,26 +202,63 @@ "Select a theme for PortMaster.": "PortMasterのテーマを選択", "Select Color Scheme": "配色を選択", "Select a color scheme for PortMaster.": "PortMasterの配色を選択。", + "Theme Editor": "テーマエディター", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "同じネットワーク上のパソコンやスマートフォンから、テーマをプレビューして PortMaster の翻訳を編集します。", "Audio": "音声", "Music: ": "音楽: ", "Enable or Disable background music in PortMaster.": "PortMasterでBGMを有効または無効にします。", "Sound FX: ": "サウンド効果: ", "Enable or Disable sound effects in PortMaster.": "PortMasterサウンド効果を有効または無効にします。", + "Port Metadata": "ポートのメタデータ", + "Metadata Update: ": "メタデータの更新: ", + "Auto": "自動", + "Manual": "手動", + "PortMaster updates metadata when ports are installed so that it shows port images and descriptions when browsing ports on your device.": "PortMaster はポートのインストール時にメタデータを更新し、デバイスでポートを閲覧するときに画像や説明を表示できるようにします。", + "Metadata Refresh": "メタデータを再取得", + "Manually update port metadata with missing/updated information and artwork.": "不足している情報や更新された情報、アートワークでポートのメタデータを手動で更新します。", + "Ports Location: ": "ポートの場所: ", + "Roms section": "Roms セクション", + "Ports tab": "ポートタブ", + "Location where ports should be installed to.": "ポートのインストール先。", + "SD 1": "SD 1", + "SD 2": "SD 2", "System": "システム", "Runtime Manager": "ランタイムの管理", "Manage port runtimes.": "ポートのランタイムを管理", + "Experimental Ports: ": "実験的なポート: ", + "Show or hide experimental ports.": "実験的なポートの表示/非表示を切り替えます。", "Update Ports": "ポートを更新", + "Fetch latest ports information.": "最新のポート情報を取得します。", "Update PortMaster": "PortMasterを更新", "Force check for a new PortMaster version.": "新しいPortMasterバージョンを強制的にチェックします。", + "Restore PortMaster": "PortMaster を復元", + "This will restore PortMaster to the latest stable version of PortMaster.": "PortMaster を最新の安定版に復元します。", "Release Channel: {channel}": "リリースチャンネル: {channel}", "Change release channel of PortMaster, either beta or stable.": "PortMasterのリリースチャンネルをbetaまたはstableのいずれかに変更します。", + "Enable Alpha Releases": "アルファ版を有効にする", + "Change release channel of PortMaster to alpha.": "PortMaster のリリースチャンネルを alpha に変更します。", "Controller Mode: {controller_mode}": "コントローラーモード: {controller_mode}", "Toggle between various controller layouts.": "さまざまなコントローラーレイアウトを切り替えます。", "Secret Options": "シークレットオプション", + "All Ports: ": "すべてのポート: ", + "Show all ports, ignoring requirements.": "要件を無視してすべてのポートを表示します。", + "CWTBE Mode: ": "CWTBE モード: ", + "Enable gptokeyb2 by default.": "gptokeyb2 をデフォルトで有効にします。", + "Debug Mode: ": "デバッグモード: ", + "Enable debug logging all the time.": "デバッグログを常に有効にします。", "Delete PortMaster Config": "ポートマスター設定を削除", "This can break stuff, don't touch unless you know what you are doing.": "これは物事を壊すかもしれない、あなたがしていることを知っていない限り、触れないでください。", "Delete PortMaster Runtimes": "PortMasterランタイムを削除", + "Are you sure you want to restore PortMaster?\n\nThis will require you to run a specially crafted port that will be installed.": "PortMaster を復元しますか?\n\nインストールされる専用のポートを実行する必要があります。", + "A special port has been installed called \"Restore PortMaster.sh\"\n\nRun this port to restore your PortMaster installation.": "\"Restore PortMaster.sh\" という専用のポートをインストールしました。\n\nこのポートを実行して PortMaster を復元してください。", + "PortMaster will now quit so you can run the restoration port.": "復元用ポートを実行できるように、PortMaster を終了します。", + "Are you sure you want to change to the alpha release channel?\n\nPortMaster will upgrade or downgrade accordingly.\n\nTHIS CAN AND WILL BREAK STUFF, YOU HAVE BEEN WARNED": "alpha リリースチャンネルに変更しますか?\n\nPortMaster はそれに合わせてアップグレードまたはダウングレードします。\n\n不具合が起きる可能性が高いです。警告しましたよ", "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "リリースチャンネルを {current_channel} から {new_channel} に変更してもよろしいですか?\n\nPortMasterはそれに応じてアップグレードまたはダウングレードされます。", + "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "gamelist.xml を更新しますか?\n\n取得済みのアートワークやポート情報は上書きされ、50 MB 以上のダウンロードが必要です。", + "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "ポートを {from_mode} から {to_mode} に移動しますか?", + "SDCARD 1": "SDカード 1", + "SDCARD 2": "SDカード 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "{to_loc} でポートを管理・インストールしますか?\n\nインストール済みのポートは移動されません。\nこの変更を反映するために PortMaster を再起動します。", "Deleting Runtimes:": "ランタイムを削除中:", "Source Manager": "ソースマネージャー", "Port Info Popup": "ポート情報ポップアップ", @@ -178,9 +266,11 @@ "Reinstall": "再インストール", "Uninstall": "削除", "Install": "インストール", + "Links": "リンク", "Port Info": "ポート情報", "Show Info": "情報を表示", "Are you sure you want to uninstall {port_name}?": "{port_name} をアンインストールしてもよろしいですか?", + "No links for this port.": "このポートにはリンクがありません。", "NO PORTS": "ポートなし", "** NO PORTS FOUND **": "** 見つかりませんでした **", "Download ports first.": "ポートをダウンロードしてください。", @@ -193,9 +283,29 @@ "Check": "チェック", "Are you sure you want to download and verify all runtimes?": "すべてのランタイムをダウンロードして確認しますか?", "Deleted runtime {runtime}": "ランタイム {runtime} を削除しました", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "快適に使うには、muOS や KNULLI などの対応ファームウェアに切り替えてください。", + "SpruceOS is not supported by PortMaster.": "SpruceOS は PortMaster に対応していません。", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS は PortMaster に対応していません。", + "NextUI is not supported by PortMaster.": "NextUI は PortMaster に対応していません。", + "Your OS is not supported by PortMaster.": "お使いの OS は PortMaster に対応していません。", + "Your device has less than 1GB of RAM.": "お使いのデバイスの RAM は 1GB 未満です。", + "Disclaimer": "免責事項", + "Wait {seconds} seconds": "{seconds} 秒お待ちください", + "Accept": "同意する", + "Warning": "警告", + "Many ports will not work.": "多くのポートは動作しません。", + "I Understand": "了解しました", + "Starting the theme editor...": "テーマエディターを起動中...", + "Stop": "停止", + "it stopped": "停止しました", + "Unable to start the theme editor: {error}": "テーマエディターを起動できません: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "テーマエディターは動作していますが、このデバイスはネットワークに接続されていません。Wi-Fi に接続してから、テーマエディターをもう一度起動してください。", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "同じネットワーク上のパソコンやスマートフォンで、コードをスキャンするか次を開いてください:\n\n{url}\n\nどの画面サイズでもテーマをプレビューでき、PortMaster の翻訳を編集できます。翻訳はこのデバイスに保存されます。\n\nBを押すとテーマエディターを停止します。", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "テーマエディターが停止しました。PortMaster フォルダーの theme_server.txt を確認してください。", "{theme_name} (Selected)": "{theme_name} (選択済み)", "Download": "ダウンロード", "{item_name} (Selected)": "{item_name} (選択済み)", + "Please wait.": "お待ちください。", "Up to date already": "既に最新の状態です", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel} から {new_release_channel} へ切り替えています。\n\nダウングレードしますか?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster ({portmaster_version})\n\nアップグレードしますか?", diff --git a/PortMaster/pylibs/locales/ja_JP.themes.json b/PortMaster/pylibs/locales/ja_JP.themes.json index ac0bdff5..06d025c7 100644 --- a/PortMaster/pylibs/locales/ja_JP.themes.json +++ b/PortMaster/pylibs/locales/ja_JP.themes.json @@ -2,6 +2,7 @@ "Time: {system.time_24hr}\nBattery: {system.battery_level}\nSpace: {system.free_space} free": "時間: {system.time_24hr}\nバッテリー: {system.battery_level}\nスペース: {system.free_space} フリー", "Genres: {port_info.genres} Download Size: {port_info.download_size}{if:port_info.runtime} Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}": "ジャンル: {port_info.genres} ダウンロードサイズ: {port_info.download_size}{if:port_info.runtime} ランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}", "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}利用可能なポート: {ports_list.filter_ports} / {ports_list.total_ports} \nフィルター: {ports_list.filters}{endif}", + "Free Space: {system.free_space}\n\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\n\nInstruction: {port_info.instructions}": "空き容量: {system.free_space}\n\nポーター: {port_info.porter}\nジャンル: {port_info.genres}\nダウンロードサイズ: {port_info.download_size}{if:port_info.runtime}\nランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}\n\n説明: {port_info.instructions}", "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total\nTime: {system.time_24hr}\nBattery: {system.battery_level}": "PortMaster: {system.portmaster_version}\nHabourMaster: {system.harbourmaster_version}\nスペース: {system.free_space} フリー / {system.total_space} 合計\n時間: {system.time_24hr}\nバッテリー: {system.battery_level}", "IP: {system.ip_address}": "IP: {system.ip_address}", "Creator: {theme_info.creator}\nStatus: {theme_info.status}": "作成者: {theme_info.creator}\n状態: {theme_info.status}", @@ -9,8 +10,20 @@ "Filters: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}": "フィルター: {if:!ports_list.total_ports::ports_list.filter_ports} {ports_list.filters}{endif}", "Genres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "ジャンル: {port_info.genres}\nダウンロードサイズ: {port_info.download_size}{if:port_info.runtime}\nランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}", "Time: {system.time_24hr} Battery: {system.battery_level}": "時間: {system.time_24hr} バッテリー: {system.battery_level}", + "Title: {port_info.title}\nPorter: {port_info.porter}\nGenres: {port_info.genres}\nDownload Size: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}\nDescription: {port_info.description}": "タイトル: {port_info.title}\nポーター: {port_info.porter}\nジャンル: {port_info.genres}\nダウンロードサイズ: {port_info.download_size}{if:port_info.runtime}\nランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}\n説明: {port_info.description}", "{if:!ports_list.total_ports::ports_list.filter_ports}Available Ports: {ports_list.filter_ports} / {ports_list.total_ports} \nFilters: {ports_list.filters}{endif}": "{if:!ports_list.total_ports::ports_list.filter_ports}利用可能なポート: {ports_list.filter_ports} / {ports_list.total_ports} \nフィルター: {ports_list.filters}{endif}", + "Genres: {port_info.genres}\n{if:port_info.install_size}Installed Size: {port_info.install_size}{else}Download Size: {port_info.download_size}{endif}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "ジャンル: {port_info.genres}\n{if:port_info.install_size}インストールサイズ: {port_info.install_size}{else}ダウンロードサイズ: {port_info.download_size}{endif}{if:port_info.runtime}\nランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}", "Time: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nDevice: {system.device_name}\nBattery: {system.battery_level}": "時間: {system.time_24hr}\nCFW: {system.cfw_name} ({system.cfw_version})\nデバイス: {system.device_name}\nバッテリー: {system.battery_level}", "PM: {system.portmaster_version}\nHM: {system.harbourmaster_version}\nSpace: {system.free_space} free / {system.total_space} total": "PortMaster: {system.portmaster_version}\nHabourMaster: {system.harbourmaster_version}\nスペース: {system.free_space} フリー / {system.total_space} 合計", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "名前: {runtime_info.name}\nステータス: {runtime_info.status}\nポート: {runtime_info.ports}\nダウンロードサイズ: {runtime_info.download_size}\nインストールサイズ: {runtime_info.disk_size}" + "• Porter: {port_info.porter}\n• Genres: {port_info.genres}\n• Description: {port_info.description}": "• ポーター: {port_info.porter}\n• ジャンル: {port_info.genres}\n• 説明: {port_info.description}", + "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• ジャンル: {port_info.genres}\n• ダウンロードサイズ: {port_info.download_size}{if:port_info.runtime}\n• ランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}\n• 説明: {port_info.description}\n• 手順:\n{port_info.instructions}", + "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", + "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• 作成者: {theme_info.creator}\n• 状態: {theme_info.status}\n• 説明:\n{theme_info.description}", + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "名前: {runtime_info.name}\nステータス: {runtime_info.status}\nポート: {runtime_info.ports}\nダウンロードサイズ: {runtime_info.download_size}\nインストールサイズ: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} ゲーム", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "ジャンル: {port_info.genres}\nサイズ: {port_info.download_size}{if:port_info.runtime}\nランタイム: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nポーター: {port_info.porter}\nジャンル: {port_info.genres}\nダウンロード: {port_info.download_size}{if:port_info.runtime}\nランタイム: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "空き {system.free_space}", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}作成: {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}状態: {runtime_info.status}\n使用状況: {runtime_info.in_use}\nダウンロード: {runtime_info.download_size}{if:runtime_info.disk_size}\nディスク使用量: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\n使用しているポート: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/ko_KR.json b/PortMaster/pylibs/locales/ko_KR.json index b6caaaea..9cfc12db 100644 --- a/PortMaster/pylibs/locales/ko_KR.json +++ b/PortMaster/pylibs/locales/ko_KR.json @@ -1,22 +1,39 @@ { + "This is not a valid zip file.": "올바른 zip 파일이 아닙니다.", + "illegal file {file_name}": "허용되지 않는 파일 {file_name}", + "the port has no directory": "포트에 폴더가 없습니다", + "the port has no launch script": "포트에 실행 스크립트가 없습니다", + "unable to load {file_name}": "{file_name}을(를) 불러올 수 없습니다", "Loading...": "불러오는 중...", "Loading Sources.": "소스 불러오는 중", "Download validation failed.": "다운로드 검증에 실패했습니다.", + "Unable to download file.": "파일을 다운로드할 수 없습니다.", "Loading Info.": "정보 불러오는 중", + "Fetching latest ports info data.": "최신 포트 정보를 가져오는 중입니다.", + "Fetching latest porters info.": "최신 포터 정보를 가져오는 중입니다.", + "Fetching latest port stats.": "최신 포트 통계를 가져오는 중입니다.", "Fetching latest featured ports.": "최신 추천 포트를 가져오는 중", + "Fetching latest featured port images zip.": "최신 추천 포트 이미지 zip을 가져오는 중입니다.", + "Fetching featured port image {file_name}": "추천 포트 이미지를 가져오는 중 {file_name}", "Installing Theme {download_name}.": "{download_name} 테마를 설치 중", "Installing": "설치 중", "Theme {download_name!r} installed successfully.": "{download_name!r} 테마 설치에 성공했습니다.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip이 손상되었습니다. 다시 다운로드하세요.", + "PortMaster failed to install.": "PortMaster 설치에 실패했습니다.", "Installing {download_name}.": "{download_name} 설치 중", + "PortMaster installed successfully.": "PortMaster가 설치되었습니다.", "Installation failed, removing files...": "설치에 실패했습니다. 파일 삭제 중...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "안녕하세요.\n\n기기와 호환되지 않는 이식본을 수동으로 설치하고 계신 것 같습니다.\n\n문제가 발생하면 Discord에 보고하지 마세요. 조롱을 당할 수 있습니다.\n\n설치를 계속하시겠습니까?", "Okay": "확인", "Cancel": "취소", + "Not compatible with this device.": "이 기기와 호환되지 않습니다.", + "Installation failed: {reason}": "설치 실패: {reason}", "Port {download_name} failed to install.": "{download_name} 포트 설치에 실패했습니다.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "{download_name!r} 포트와 {runtime_name!r} 가 정상적으로 설치되었습니다.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "{download_name!r} 포트는 정상적으로 설치되었으나, {runtime_name!r} 설치에 실패했습니다!\n\n재시도하려면 다시 설치하거나 위키에서 도움말을 확인하세요.", "Port {download_name!r} installed successfully.": "{download_name!r} 포트가 정상적으로 설치되었습니다.", "Unable to download in offline mode.": "오프라인 모드에서는 다운로드할 수 없습니다.", + "File not found.": "파일을 찾을 수 없습니다.", "Unable to find a source for {port_name}": "{port_name} 소스를 찾을 수 없습니다.", "Unknown port {port_name}": "알 수 없는 포트 {port_name}", "Removing {port_name}": "{port_name} 제거 중", @@ -67,16 +84,32 @@ "Uninstalling {port_name}": "{port_name} 삭제 중", "Updating all port sources:": "모든 포트 소스 업데이트 중:", "Checking {runtime_name}": "{runtime_name} 확인 중", + "Auto-installation": "자동 설치", + "PortMaster website": "PortMaster 웹사이트", + "{store}: developer": "{store}: 개발자", "** NO PORT **": "** 포트 없음 **", "Ready to Run": "실행 준비 완료", "Requires Files": "필요한 파일", "Installed": "설치됨", "Missing": "누락", + "Installation failed, see log.txt for details.": "설치에 실패했습니다. 자세한 내용은 log.txt를 확인하세요.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "다른 아키텍처용({zip_arch}, 이 기기는 {device_arch})이라 삭제되었습니다.", + "Installed {download_name}.": "{download_name} 설치됨.", + "- SUCCESS: {name}": "- 성공: {name}", + "- FAILURE: {name}": "- 실패: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "포트가 추가 또는 제거되었습니다. 게임 목록을 업데이트하여 새로운 게임을 확인하거나 제거된 게임을 삭제하세요.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "일부 파일을 설치하지 못했습니다. 선택하면 이유를 볼 수 있습니다. 파일은 자동 설치 폴더에 남아 있습니다.", + "Everything installed successfully.": "모두 설치되었습니다.", + "{installed} installed, {failed} failed": "{installed}개 설치, {failed}개 실패", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name}이(가) 설치되었습니다.", + "FAILED: {name}": "실패: {name}", + "{name} failed to install:\n\n{reason}": "{name} 설치 실패:\n\n{reason}", "Danish": "덴마크어", "German": "독일어", "English": "영어", "Spanish": "스페인어", + "Esperanto": "에스페란토어", "Finnish": "핀란드어", "French": "프랑스어", "Italian": "이탈리아어", @@ -101,6 +134,7 @@ "Alphabetical": "알파벳순", "Recently Added": "최근에 주가된", "Recently Updated": "최근에 업데이트된", + "Total Downloads": "총 다운로드 수", "Action": "액션", "Adventure": "어드벤처", "Arcade": "아케이드", @@ -127,6 +161,8 @@ "ARM 32bit": "ARM 32비트", "ARM 64bit": "ARM 64비트", "x86 64bit": "x86 64비트", + "Ascending": "오름차순", + "Descending": "내림차순", "Sort:": "종류:", "Clear Filters": "필터 지우기", "Genres:": "장르:", @@ -166,6 +202,8 @@ "Select a theme for PortMaster.": "사용할 테마를 선택하세요.", "Select Color Scheme": "색상 구성표 선택", "Select a color scheme for PortMaster.": "색상 구성표를 선택하세요.", + "Theme Editor": "테마 편집기", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "같은 네트워크의 컴퓨터나 휴대폰에서 테마를 미리 보고 PortMaster의 번역을 편집합니다.", "Audio": "오디오", "Music: ": "음악: ", "Enable or Disable background music in PortMaster.": "배경 음악을 활성화 또는 비활성화합니다. ", @@ -206,6 +244,8 @@ "Show all ports, ignoring requirements.": "요구 사항을 무시하고 모든 포트를 표시합니다.", "CWTBE Mode: ": "CWTBE 방식: ", "Enable gptokeyb2 by default.": "기본적으로 gptokeyb2를 활성화합니다.", + "Debug Mode: ": "디버그 모드: ", + "Enable debug logging all the time.": "항상 디버그 로그를 기록합니다.", "Delete PortMaster Config": "구성 삭제", "This can break stuff, don't touch unless you know what you are doing.": "프로그램이 손상될 수 있으니, 잘 모르는 사람은 만지지 마세요.", "Delete PortMaster Runtimes": "런타임 삭제", @@ -226,9 +266,11 @@ "Reinstall": "다시 설치", "Uninstall": "제거", "Install": "설치", + "Links": "링크", "Port Info": "포트 정보", "Show Info": "정보 보기", "Are you sure you want to uninstall {port_name}?": "{port_name} 제거하시겠습니까?", + "No links for this port.": "이 포트의 링크가 없습니다.", "NO PORTS": "포트 없음", "** NO PORTS FOUND **": "** 포트를 찾을 수 없음 **", "Download ports first.": "포트를 먼저 다운로드합니다.", @@ -241,11 +283,29 @@ "Check": "확인", "Are you sure you want to download and verify all runtimes?": "모든 런타임을 다운로드하고 검증하시겠습니까?", "Deleted runtime {runtime}": "{runtime} 삭제된 런타임", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "최상의 경험을 위해 muOS나 KNULLI 같은 지원되는 펌웨어로 바꾸세요.", + "SpruceOS is not supported by PortMaster.": "PortMaster는 SpruceOS를 지원하지 않습니다.", + "CrossMix-OS is not supported by PortMaster.": "PortMaster는 CrossMix-OS를 지원하지 않습니다.", + "NextUI is not supported by PortMaster.": "PortMaster는 NextUI를 지원하지 않습니다.", + "Your OS is not supported by PortMaster.": "PortMaster는 사용 중인 OS를 지원하지 않습니다.", + "Your device has less than 1GB of RAM.": "기기의 RAM이 1GB 미만입니다.", "Disclaimer": "면책 조항", + "Wait {seconds} seconds": "{seconds}초 기다려 주세요", "Accept": "수락", + "Warning": "경고", + "Many ports will not work.": "많은 포트가 작동하지 않습니다.", + "I Understand": "알겠습니다", + "Starting the theme editor...": "테마 편집기를 시작하는 중...", + "Stop": "중지", + "it stopped": "중지되었습니다", + "Unable to start the theme editor: {error}": "테마 편집기를 시작할 수 없습니다: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "테마 편집기가 실행 중이지만 이 기기는 네트워크에 연결되어 있지 않습니다. Wi-Fi에 연결한 뒤 테마 편집기를 다시 시작하세요.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "같은 네트워크의 컴퓨터나 휴대폰에서 코드를 스캔하거나 다음 주소를 여세요:\n\n{url}\n\n모든 화면 크기에서 테마를 미리 보고 PortMaster의 번역을 편집할 수 있습니다. 번역은 이 기기에 저장됩니다.\n\nB를 누르면 테마 편집기가 중지됩니다.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "테마 편집기가 중지되었습니다. PortMaster 폴더의 theme_server.txt를 확인하세요.", "{theme_name} (Selected)": "{theme_name} (선택됨)", "Download": "다운로드", "{item_name} (Selected)": "{item_name} (선택됨)", + "Please wait.": "잠시 기다려 주세요.", "Up to date already": "이미 최신 상태입니다.", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "{old_release_channel}에서 {new_release_channel}로 전환하고 있습니다.\n\n다운그레이드하시겠습니까?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "새로운 ({portmaster_version}) 버전이 있습니다.\n\n업그레이드하시겠습니까?", diff --git a/PortMaster/pylibs/locales/ko_KR.themes.json b/PortMaster/pylibs/locales/ko_KR.themes.json index eab21b1b..679e488c 100644 --- a/PortMaster/pylibs/locales/ko_KR.themes.json +++ b/PortMaster/pylibs/locales/ko_KR.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• 장르: {port_info.genres}\n• 다운로드 크기: {port_info.download_size}{if:port_info.runtime}\n• 실행 시간: {port_info.runtime} ({port_info.runtime_status}){endif}\n• 설명: {port_info.description}\n• 사용 방법:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• 제작자: {theme_info.creator}\n• 상태: {theme_info.status}\n• 설명:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "이름: {runtime_info.name}\n상태: {runtime_info.status}\n포트: {runtime_info.ports}\n다운로드 크기: {runtime_info.download_size}\n설치 크기: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "이름: {runtime_info.name}\n상태: {runtime_info.status}\n포트: {runtime_info.ports}\n다운로드 크기: {runtime_info.download_size}\n설치 크기: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "게임 {ports_list.filter_ports}개", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "장르: {port_info.genres}\n크기: {port_info.download_size}{if:port_info.runtime}\n런타임: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\n포터: {port_info.porter}\n장르: {port_info.genres}\n다운로드: {port_info.download_size}{if:port_info.runtime}\n런타임: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} 여유 공간", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}제작: {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}상태: {runtime_info.status}\n사용 여부: {runtime_info.in_use}\n다운로드: {runtime_info.download_size}{if:runtime_info.disk_size}\n디스크 사용량: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\n사용하는 포트: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/nl_NL.json b/PortMaster/pylibs/locales/nl_NL.json index 2cf9d748..e0696366 100644 --- a/PortMaster/pylibs/locales/nl_NL.json +++ b/PortMaster/pylibs/locales/nl_NL.json @@ -1,24 +1,43 @@ { + "This is not a valid zip file.": "Dit is geen geldig zip-bestand.", + "illegal file {file_name}": "ongeldig bestand {file_name}", + "the port has no directory": "de port heeft geen map", + "the port has no launch script": "de port heeft geen startscript", + "unable to load {file_name}": "kan {file_name} niet laden", "Loading...": "Laden...", "Loading Sources.": "Bronnen worden geladen.", "Download validation failed.": "Download validatie mislukt.", + "Unable to download file.": "Kan bestand niet downloaden.", "Loading Info.": "Informatie laden.", + "Fetching latest ports info data.": "Nieuwste portinformatie ophalen.", + "Fetching latest porters info.": "Nieuwste porterinformatie ophalen.", + "Fetching latest port stats.": "Nieuwste portstatistieken ophalen.", "Fetching latest featured ports.": "Ophalen van de nieuwste ports.", + "Fetching latest featured port images zip.": "Nieuwste zip met afbeeldingen van aanbevolen ports ophalen.", + "Fetching featured port image {file_name}": "Afbeelding van aanbevolen port ophalen {file_name}", "Installing Theme {download_name}.": "Thema {download_name} wordt geïnstalleerd.", "Installing": "Aan het installeren", "Theme {download_name!r} installed successfully.": "Thema {download_name!r} is succesvol geïnstalleerd.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip is beschadigd, download het opnieuw.", + "PortMaster failed to install.": "Installatie van PortMaster mislukt.", "Installing {download_name}.": "{download_name} wordt geïnstalleerd.", + "PortMaster installed successfully.": "PortMaster is succesvol geïnstalleerd.", "Installation failed, removing files...": "Installatie mislukt, bestanden verwijderen...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "HALLO.\n\nHET LIJKT EROP DAT JE HANDMATIG EEN PORT INSTALLEERT DIE NIET COMPATIBEL IS MET JE APPARAAT.\n\nALS JE PROBLEMEN HEBT, MELD ZE DAN NIET OP DISCORD, ANDERS WORD JE UITGELACHEN.\n\nWEET JE ZEKER DAT JE DE INSTALLATIE WILT VOORTZETTEN?", "Okay": "Oké", "Cancel": "Annuleren", + "Not compatible with this device.": "Niet compatibel met dit apparaat.", + "Installation failed: {reason}": "Installatie mislukt: {reason}", "Port {download_name} failed to install.": "Port {download_name} installatie mislukt.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} en {runtime_name!r} succesvol geïnstalleerd.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} is succesvol geïnstalleerd, maar {runtime_name!r} installatie is mislukt!!!\n\nHerinstalleer om het opnieuw te proberen, of controleer de wiki voor hulp.", "Port {download_name!r} installed successfully.": "Port {download_name!r} is succesvol geïnstalleerd.", "Unable to download in offline mode.": "Kan geen port downloaden in offline modus.", + "File not found.": "Bestand niet gevonden.", "Unable to find a source for {port_name}": "Kan geen bron vinden voor {port_name}", "Unknown port {port_name}": "Onbekende port {port_name}", "Removing {port_name}": "Verwijderen van {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Fout bij het verwijderen van {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "{port_name} succesvol verwijderd", "Loading Ports.": "Ports worden geladen.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Poot {runtime} bevat een verkeerde runtime en het spel kan mogelijk niet correct worden uitgevoerd.", @@ -65,16 +84,32 @@ "Uninstalling {port_name}": "Deïnstalleert {port_name}", "Updating all port sources:": "Alle port bronnen aan het bijwerken:", "Checking {runtime_name}": "{runtime_name} controlerenIn order to do so:", + "Auto-installation": "Automatische installatie", + "PortMaster website": "PortMaster-website", + "{store}: developer": "{store}: ontwikkelaar", "** NO PORT **": "** GEEN PORT **", "Ready to Run": "Klaar om te Spelen", "Requires Files": "Bestanden Vereist", "Installed": "Geïnstalleerd", "Missing": "Ontbrekend", + "Installation failed, see log.txt for details.": "Installatie mislukt, zie log.txt voor details.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Het is voor de verkeerde architectuur ({zip_arch}, dit apparaat is {device_arch}), het is verwijderd.", + "Installed {download_name}.": "{download_name} geïnstalleerd.", + "- SUCCESS: {name}": "- GELUKT: {name}", + "- FAILURE: {name}": "- MISLUKT: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Ports zijn toegevoegd of verwijderd, werk jouw gamelijst bij om de nieuwe games te zien of de verwijderde games uit de lijst te halen.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Sommige bestanden konden niet worden geïnstalleerd, selecteer ze om te zien waarom. Ze zijn in de autoinstall-map gelaten.", + "Everything installed successfully.": "Alles is succesvol geïnstalleerd.", + "{installed} installed, {failed} failed": "{installed} geïnstalleerd, {failed} mislukt", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} is succesvol geïnstalleerd.", + "FAILED: {name}": "MISLUKT: {name}", + "{name} failed to install:\n\n{reason}": "{name} kon niet worden geïnstalleerd:\n\n{reason}", "Danish": "Deens", "German": "Duits", "English": "Engels", "Spanish": "Spaans", + "Esperanto": "Esperanto", "Finnish": "Fins", "French": "Frans", "Italian": "Italiaans", @@ -84,6 +119,7 @@ "Polish": "Pools", "Portuguese (Portugal)": "Portugees (Portugal)", "Portuguese (Brazil)": "Portugees (Brazilië)", + "Romanian": "Roemeens", "Russian": "Russisch", "Swedish": "Zweeds", "Ukrainian": "Oekraïens", @@ -98,6 +134,7 @@ "Alphabetical": "Alfabetisch", "Recently Added": "Recent Toegevoegd", "Recently Updated": "Recent Bijgewerkt", + "Total Downloads": "Totaal downloads", "Action": "Actie", "Adventure": "Avontuur", "Arcade": "Arcade", @@ -124,6 +161,8 @@ "ARM 32bit": "ARM 32bit", "ARM 64bit": "ARM 64bit", "x86 64bit": "x86 64bit", + "Ascending": "Oplopend", + "Descending": "Aflopend", "Sort:": "Sorteer:", "Clear Filters": "Filters Wissen", "Genres:": "Genres:", @@ -163,6 +202,8 @@ "Select a theme for PortMaster.": "Selecteer een thema voor PortMaster.", "Select Color Scheme": "Selecteer Kleurenschema", "Select a color scheme for PortMaster.": "Selecteer een kleurenschema voor PortMaster.", + "Theme Editor": "Thema-editor", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Bekijk thema's en bewerk de vertalingen van PortMaster vanaf een computer of telefoon op hetzelfde netwerk.", "Audio": "Audio", "Music: ": "Muziek: ", "Enable or Disable background music in PortMaster.": "Achtergrondmuziek voor PortMaster in- of uitschakelen.", @@ -203,6 +244,8 @@ "Show all ports, ignoring requirements.": "Toon alle ports, negeer vereisten.", "CWTBE Mode: ": "CWTBE Modus: ", "Enable gptokeyb2 by default.": "Gptokeyb2 als standaard inschakelen.", + "Debug Mode: ": "Debugmodus: ", + "Enable debug logging all the time.": "Debuglogging altijd inschakelen.", "Delete PortMaster Config": "Verwijder PortMaster Configuratie", "This can break stuff, don't touch unless you know what you are doing.": "Dit kan dingen breken, raak niet aan tenzij je weet wat je doet.", "Delete PortMaster Runtimes": "Verwijder PortMaster Runtimes", @@ -223,9 +266,11 @@ "Reinstall": "Herinstalleer", "Uninstall": "Verwijder", "Install": "Installeer", + "Links": "Links", "Port Info": "Port Info", "Show Info": "Toon info", "Are you sure you want to uninstall {port_name}?": "Weet u zeker dat u {port_name} wilt verwijderen?", + "No links for this port.": "Geen links voor deze port.", "NO PORTS": "GEEN PORTS", "** NO PORTS FOUND **": "** GEEN PORTS GEVONDEN **", "Download ports first.": "Download eerst ports.", @@ -238,11 +283,29 @@ "Check": "Controleer", "Are you sure you want to download and verify all runtimes?": "Weet je zeker dat je alle runtimes wilt downloaden en verifiëren?", "Deleted runtime {runtime}": "Runtime {runtime} verwijderd", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Stap voor de beste ervaring over op een ondersteunde firmware zoals muOS of KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS wordt niet ondersteund door PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS wordt niet ondersteund door PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI wordt niet ondersteund door PortMaster.", + "Your OS is not supported by PortMaster.": "Je besturingssysteem wordt niet ondersteund door PortMaster.", + "Your device has less than 1GB of RAM.": "Je apparaat heeft minder dan 1 GB RAM.", "Disclaimer": "Disclaimer", + "Wait {seconds} seconds": "Wacht {seconds} seconden", "Accept": "Accepteren", + "Warning": "Waarschuwing", + "Many ports will not work.": "Veel ports zullen niet werken.", + "I Understand": "Begrepen", + "Starting the theme editor...": "Thema-editor wordt gestart...", + "Stop": "Stoppen", + "it stopped": "hij is gestopt", + "Unable to start the theme editor: {error}": "Kan de thema-editor niet starten: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "De thema-editor draait, maar dit apparaat is niet met een netwerk verbonden. Verbind het met wifi en start de thema-editor opnieuw.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Scan op een computer of telefoon op hetzelfde netwerk de code of open:\n\n{url}\n\nBekijk thema's op elk schermformaat en bewerk de vertalingen van PortMaster: ze worden op dit apparaat opgeslagen.\n\nDruk op B om de thema-editor te stoppen.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "De thema-editor is gestopt. Zie theme_server.txt in de PortMaster-map.", "{theme_name} (Selected)": "{theme_name} (Geselecteerd)", "Download": "Download", "{item_name} (Selected)": "{item_name} (Geselecteerd)", + "Please wait.": "Even geduld.", "Up to date already": "Al bijgewerkt", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "U wisselt van {old_release_channel} naar {new_release_channel} versie.\n\nWilt u downgraden?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Er is een nieuwe versie van PortMaster ({portmaster_version})\n\nWilt u upgraden?", diff --git a/PortMaster/pylibs/locales/nl_NL.themes.json b/PortMaster/pylibs/locales/nl_NL.themes.json index 6011cda7..33a08387 100644 --- a/PortMaster/pylibs/locales/nl_NL.themes.json +++ b/PortMaster/pylibs/locales/nl_NL.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genres: {port_info.genres}\n• Download grootte: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Beschrijving: {port_info.description}\n• Instructies:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Auteur: {theme_info.creator}\n• Status: {theme_info.status}\n• Beschrijving:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Naam: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownloadgrootte: {runtime_info.download_size}\nInstallatiegrootte: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Naam: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownloadgrootte: {runtime_info.download_size}\nInstallatiegrootte: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} games", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Genre: {port_info.genres}\nGrootte: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} vrij", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}door {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Status: {runtime_info.status}\nIn gebruik: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOp schijf: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nGebruikt door: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/pl_PL.json b/PortMaster/pylibs/locales/pl_PL.json index af21360f..490d1275 100644 --- a/PortMaster/pylibs/locales/pl_PL.json +++ b/PortMaster/pylibs/locales/pl_PL.json @@ -1,22 +1,39 @@ { + "This is not a valid zip file.": "To nie jest prawidłowy plik zip.", + "illegal file {file_name}": "niedozwolony plik {file_name}", + "the port has no directory": "port nie ma folderu", + "the port has no launch script": "port nie ma skryptu uruchomieniowego", + "unable to load {file_name}": "nie można wczytać {file_name}", "Loading...": "Ładowanie...", "Loading Sources.": "Ładowanie źródeł.", "Download validation failed.": "Sprawdzanie poprawności pliku nie powiodło się.", + "Unable to download file.": "Nie można pobrać pliku.", "Loading Info.": "Wczytywanie Informacji.", + "Fetching latest ports info data.": "Pobieranie najnowszych informacji o portach.", + "Fetching latest porters info.": "Pobieranie najnowszych informacji o porterach.", + "Fetching latest port stats.": "Pobieranie najnowszych statystyk portów.", "Fetching latest featured ports.": "Pobieranie najnowszych polecanych portów.", + "Fetching latest featured port images zip.": "Pobieranie najnowszego pliku zip z obrazami polecanych portów.", + "Fetching featured port image {file_name}": "Pobieranie obrazu polecanego portu {file_name}", "Installing Theme {download_name}.": "Instalowanie motywu {download_name}.", "Installing": "Instalowanie", "Theme {download_name!r} installed successfully.": "Motyw {download_name!r} zainstalowany pomyślnie.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip jest uszkodzony, pobierz go ponownie.", + "PortMaster failed to install.": "Instalacja PortMastera nie powiodła się.", "Installing {download_name}.": "Instalacja {download_name}.", + "PortMaster installed successfully.": "PortMaster został pomyślnie zainstalowany.", "Installation failed, removing files...": "Instalacja nie powiodła się, usuwanie plików...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "CZEŚĆ.\n\nWYGLĄDA NA TO, ŻE RĘCZNIE INSTALUJESZ PORT, KTÓRY JEST NIEZGODNY Z TWOIM URZĄDZENIEM.\n\nJEŚLI MASZ PROBLEMY, NIE ZGŁASZAJ ICH NA DISCORDZIE, GDYŻ NARADZISZ SIĘ.\n\nCZY NA PEWNO CHCESZ WZNOWIĆ INSTALACJĘ?", "Okay": "W porządku", "Cancel": "Anuluj", + "Not compatible with this device.": "Niezgodny z tym urządzeniem.", + "Installation failed: {reason}": "Instalacja nie powiodła się: {reason}", "Port {download_name} failed to install.": "Port {download_name} nie został zainstalowany.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} i {runtime_name!r} zainstalowany pomyślnie.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} zainstalowany pomyślnie, ale {runtime_name!r} nie udało się zainstalować!\n\nZainstaluj ponownie, aby spróbować ponownie, lub sprawdź wiki po pomoc.", "Port {download_name!r} installed successfully.": "Port {download_name!r} zainstalowany pomyślnie.", "Unable to download in offline mode.": "Nie można pobrać w trybie offline.", + "File not found.": "Nie znaleziono pliku.", "Unable to find a source for {port_name}": "Nie można odnaleźć źródła dla {port_name}", "Unknown port {port_name}": "Nieznany port {port_name}", "Removing {port_name}": "Usuwanie {port_name}", @@ -67,16 +84,32 @@ "Uninstalling {port_name}": "Odinstalowywanie {port_name}", "Updating all port sources:": "Aktualizowanie wszystkich źródeł portów:", "Checking {runtime_name}": "Sprawdzanie {runtime_name}", + "Auto-installation": "Automatyczna instalacja", + "PortMaster website": "Strona PortMastera", + "{store}: developer": "{store}: twórca", "** NO PORT **": "** BRAK PORTU **", "Ready to Run": "Gotowe do uruchomienia", "Requires Files": "Wymagane pliki", "Installed": "Zainstalowane", "Missing": "Brakujące", + "Installation failed, see log.txt for details.": "Instalacja nie powiodła się, szczegóły w log.txt.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Jest dla niewłaściwej architektury ({zip_arch}, to urządzenie to {device_arch}), został usunięty.", + "Installed {download_name}.": "Zainstalowano {download_name}.", + "- SUCCESS: {name}": "- SUKCES: {name}", + "- FAILURE: {name}": "- BŁĄD: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Porty zostały dodane lub usunięte, zaktualizuj listę gier, aby zobaczyć nowe gry lub usunąć odinstalowane.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Niektórych plików nie udało się zainstalować, wybierz je, aby zobaczyć dlaczego. Zostały w folderze autoinstalacji.", + "Everything installed successfully.": "Wszystko zostało pomyślnie zainstalowane.", + "{installed} installed, {failed} failed": "Zainstalowano: {installed}, błędy: {failed}", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} został pomyślnie zainstalowany.", + "FAILED: {name}": "BŁĄD: {name}", + "{name} failed to install:\n\n{reason}": "Nie udało się zainstalować {name}:\n\n{reason}", "Danish": "Duński", "German": "Niemiecki", "English": "Angielski", "Spanish": "Hiszpański", + "Esperanto": "Esperanto", "Finnish": "Fiński", "French": "Francuski", "Italian": "Włoski", @@ -101,6 +134,7 @@ "Alphabetical": "Alfabetycznie", "Recently Added": "Ostatnio dodane", "Recently Updated": "Ostatnio zaktualizowane", + "Total Downloads": "Łączna liczba pobrań", "Action": "Akcja", "Adventure": "Przygodowe", "Arcade": "Arcade", @@ -127,6 +161,8 @@ "ARM 32bit": "ARM 32bit", "ARM 64bit": "ARM 64bit", "x86 64bit": "x86 64bit", + "Ascending": "Rosnąco", + "Descending": "Malejąco", "Sort:": "Sortuj:", "Clear Filters": "Wyczyść filtry", "Genres:": "Gatunek:", @@ -166,6 +202,8 @@ "Select a theme for PortMaster.": "Wybierz motyw dla PortMaster.", "Select Color Scheme": "Wybierz schemat kolorów", "Select a color scheme for PortMaster.": "Wybierz schemat kolorów dla PortMaster.", + "Theme Editor": "Edytor motywów", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Podglądaj motywy i edytuj tłumaczenia PortMastera z komputera lub telefonu w tej samej sieci.", "Audio": "Dźwięk", "Music: ": "Muzyka: ", "Enable or Disable background music in PortMaster.": "Włącz lub wyłącz muzykę w tle w PortMaster.", @@ -206,6 +244,8 @@ "Show all ports, ignoring requirements.": "Pokaż wszystkie porty, ignorując wymagania.", "CWTBE Mode: ": "Tryb CWTBE: ", "Enable gptokeyb2 by default.": "Domyślnie włącz gptokeyb2.", + "Debug Mode: ": "Tryb debugowania: ", + "Enable debug logging all the time.": "Zawsze włączaj logowanie debugowania.", "Delete PortMaster Config": "Usuń konfigurację PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "To może zniszczyć rzeczy, nie dotykaj, chyba że wiesz co robisz.", "Delete PortMaster Runtimes": "Usuń runtime PortMastera", @@ -226,9 +266,11 @@ "Reinstall": "Reinstalacja", "Uninstall": "Odinstaluj", "Install": "Zainstaluj", + "Links": "Linki", "Port Info": "Informacje o porcie", "Show Info": "Pokaż info", "Are you sure you want to uninstall {port_name}?": "Jesteś pewien, że chcesz odinstalować {port_name}?", + "No links for this port.": "Brak linków dla tego portu.", "NO PORTS": "BRAK PORTÓW", "** NO PORTS FOUND **": "** NIE ZNALEZIONO PORTÓW **", "Download ports first.": "Najpierw pobierz porty.", @@ -241,11 +283,29 @@ "Check": "Sprawdź", "Are you sure you want to download and verify all runtimes?": "Czy na pewno chcesz pobrać i zweryfikować wszystkie runtimes?", "Deleted runtime {runtime}": "Usunięto runtime {runtime}", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Aby uzyskać najlepsze wrażenia, przejdź na obsługiwane oprogramowanie, takie jak muOS lub KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS nie jest obsługiwany przez PortMastera.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS nie jest obsługiwany przez PortMastera.", + "NextUI is not supported by PortMaster.": "NextUI nie jest obsługiwany przez PortMastera.", + "Your OS is not supported by PortMaster.": "Twój system nie jest obsługiwany przez PortMastera.", + "Your device has less than 1GB of RAM.": "Twoje urządzenie ma mniej niż 1 GB RAM.", "Disclaimer": "Uwaga", + "Wait {seconds} seconds": "Poczekaj {seconds} s", "Accept": "Akceptuj", + "Warning": "Ostrzeżenie", + "Many ports will not work.": "Wiele portów nie będzie działać.", + "I Understand": "Rozumiem", + "Starting the theme editor...": "Uruchamianie edytora motywów...", + "Stop": "Zatrzymaj", + "it stopped": "zatrzymał się", + "Unable to start the theme editor: {error}": "Nie można uruchomić edytora motywów: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "Edytor motywów działa, ale to urządzenie nie jest połączone z siecią. Połącz je z Wi-Fi i ponownie uruchom edytor motywów.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Na komputerze lub telefonie w tej samej sieci zeskanuj kod lub otwórz:\n\n{url}\n\nPodglądaj motywy w dowolnym rozmiarze ekranu i edytuj tłumaczenia PortMastera: są zapisywane na tym urządzeniu.\n\nNaciśnij B, aby zatrzymać edytor motywów.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "Edytor motywów się zatrzymał. Zobacz theme_server.txt w folderze PortMastera.", "{theme_name} (Selected)": "{theme_name} (Wybrany)", "Download": "Pobierz", "{item_name} (Selected)": "{item_name} (Wybrany)", + "Please wait.": "Proszę czekać.", "Up to date already": "Już aktualne", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Przechodzisz z {old_release_channel} na wersję {new_release_channel}.\n\nCzy chcesz kontynuować?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Dostępna jest nowa wersja PortMaster ({portmaster_version})\n\nCzy chcesz zaktualizować?", diff --git a/PortMaster/pylibs/locales/pl_PL.themes.json b/PortMaster/pylibs/locales/pl_PL.themes.json index cfb9245c..9559587b 100644 --- a/PortMaster/pylibs/locales/pl_PL.themes.json +++ b/PortMaster/pylibs/locales/pl_PL.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Gatunki {port_info.genres}\n• Rozmiar pobierania: {port_info.download_size}{if:port_info.runtime}\n• Czas uruchomienia: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Opis: {port_info.description}\n• Instrukcje:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Twórca: {theme_info.creator}\n• Status: {theme_info.status}\n• Opis:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nazwa: {runtime_info.name}\nStatus: {runtime_info.status}\nPorty: {runtime_info.ports}\nRozmiar pobierania: {runtime_info.download_size}\nRozmiar instalacji: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nazwa: {runtime_info.name}\nStatus: {runtime_info.status}\nPorty: {runtime_info.ports}\nRozmiar pobierania: {runtime_info.download_size}\nRozmiar instalacji: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "Gry: {ports_list.filter_ports}", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Gatunek: {port_info.genres}\nRozmiar: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGatunek: {port_info.genres}\nPobieranie: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} wolnych", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}autor: {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Status: {runtime_info.status}\nUżywany: {runtime_info.in_use}\nPobieranie: {runtime_info.download_size}{if:runtime_info.disk_size}\nNa dysku: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUżywany przez: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/pt_BR.json b/PortMaster/pylibs/locales/pt_BR.json index 4ee17d87..ceb90901 100644 --- a/PortMaster/pylibs/locales/pt_BR.json +++ b/PortMaster/pylibs/locales/pt_BR.json @@ -1,22 +1,39 @@ { + "This is not a valid zip file.": "Este não é um arquivo zip válido.", + "illegal file {file_name}": "arquivo não permitido {file_name}", + "the port has no directory": "o jogo não tem pasta", + "the port has no launch script": "o jogo não tem script de inicialização", + "unable to load {file_name}": "não foi possível carregar {file_name}", "Loading...": "Carregando...", "Loading Sources.": "Carregando fontes.", "Download validation failed.": "Validação do download falhou.", + "Unable to download file.": "Não foi possível baixar o arquivo.", "Loading Info.": "Carregando informação.", + "Fetching latest ports info data.": "Buscando as informações mais recentes dos jogos.", + "Fetching latest porters info.": "Buscando as informações mais recentes dos porters.", + "Fetching latest port stats.": "Buscando as estatísticas mais recentes dos jogos.", "Fetching latest featured ports.": "Buscando jogos em destaque mais recentes.", + "Fetching latest featured port images zip.": "Buscando o zip mais recente de imagens dos jogos em destaque.", + "Fetching featured port image {file_name}": "Buscando a imagem do jogo em destaque {file_name}", "Installing Theme {download_name}.": "Instalando tema {download_name}.", "Installing": "Instalando", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} instalado com sucesso.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip está danificado, baixe-o novamente.", + "PortMaster failed to install.": "Falha ao instalar o PortMaster.", "Installing {download_name}.": "Instalando {download_name}.", + "PortMaster installed successfully.": "PortMaster instalado com sucesso.", "Installation failed, removing files...": "Instalação falhou, removendo arquivos...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "OLÁ.\n\nPARECE QUE VOCÊ ESTÁ INSTALANDO MANUALMENTE UM JOGO QUE NÃO É COMPATÍVEL COM SEU APARELHO.\n\nSE VOCÊ TIVER PROBLEMAS, NÃO REPORTE ELES NO DISCORD, VOCÊ ESTARÁ SUJEITO A SER RIDICULARIZADO.\n\nVOCÊ TEM CERTEZA QUE QUER CONTINUAR INSTALANDO?", "Okay": "Ok", "Cancel": "Cancelar", + "Not compatible with this device.": "Não é compatível com este dispositivo.", + "Installation failed: {reason}": "Falha na instalação: {reason}", "Port {download_name} failed to install.": "Jogo {download_name} falhou na instalação.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Jogo {download_name!r} e {runtime_name!r} instalados com sucesso.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Jogo {download_name!r} instalado com sucesso, mas {runtime_name!r} falhou em instalar!\n\nReinstale para tentar novamente ou cheque o site para ajuda.", "Port {download_name!r} installed successfully.": "Jogo {download_name!r} instalado com sucesso.", "Unable to download in offline mode.": "Impossível baixar no modo offline.", + "File not found.": "Arquivo não encontrado.", "Unable to find a source for {port_name}": "Impossível encontrar uma fonte para {port_name}", "Unknown port {port_name}": "Jogo desconhecido {port_name}", "Removing {port_name}": "Removendo {port_name}", @@ -67,16 +84,32 @@ "Uninstalling {port_name}": "Desinstalando {port_name}", "Updating all port sources:": "Atualizando todas as fontes de jogos:", "Checking {runtime_name}": "Verificando {runtime_name}", + "Auto-installation": "Instalação automática", + "PortMaster website": "Site do PortMaster", + "{store}: developer": "{store}: desenvolvedor", "** NO PORT **": "** SEM JOGO **", "Ready to Run": "Pronto para executar", "Requires Files": "Arquivos necessários", "Installed": "Instalado", "Missing": "Faltando", + "Installation failed, see log.txt for details.": "Falha na instalação, veja log.txt para detalhes.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "É para a arquitetura errada ({zip_arch}, este dispositivo é {device_arch}), foi excluído.", + "Installed {download_name}.": "{download_name} instalado.", + "- SUCCESS: {name}": "- SUCESSO: {name}", + "- FAILURE: {name}": "- FALHA: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Jogos foram adicionados ou removidos, atualize sua lista para ver os novos jogos ou remover os que não estão mais instalados.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Alguns arquivos não puderam ser instalados, selecione-os para ver o motivo. Eles foram deixados na pasta de instalação automática.", + "Everything installed successfully.": "Tudo foi instalado com sucesso.", + "{installed} installed, {failed} failed": "{installed} instalados, {failed} com falha", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} instalado com sucesso.", + "FAILED: {name}": "FALHA: {name}", + "{name} failed to install:\n\n{reason}": "Falha ao instalar {name}:\n\n{reason}", "Danish": "Dinamarquês", "German": "Alemão", "English": "Inglês", "Spanish": "Espanhol", + "Esperanto": "Esperanto", "Finnish": "Finlandês", "French": "Francês", "Italian": "Italiano", @@ -101,6 +134,7 @@ "Alphabetical": "Alfabético", "Recently Added": "Adicionado Recentemente", "Recently Updated": "Atualizado Recentemente", + "Total Downloads": "Total de downloads", "Action": "Ação", "Adventure": "Aventura", "Arcade": "Arcade", @@ -127,6 +161,8 @@ "ARM 32bit": "ARM 32bit", "ARM 64bit": "ARM 64bit", "x86 64bit": "x86 64bit", + "Ascending": "Crescente", + "Descending": "Decrescente", "Sort:": "Organizar:", "Clear Filters": "Remover Filtros", "Genres:": "Gêneros:", @@ -166,6 +202,8 @@ "Select a theme for PortMaster.": "Selecione um tema para PortMaster.", "Select Color Scheme": "Selecionar Esquema de Cores", "Select a color scheme for PortMaster.": "Selecionar um esquema de cores para PortMaster.", + "Theme Editor": "Editor de temas", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Visualize temas e edite as traduções do PortMaster de um computador ou celular na mesma rede.", "Audio": "Áudio", "Music: ": "Música: ", "Enable or Disable background music in PortMaster.": "Ativar ou Desativar música de fundo em PortMaster.", @@ -206,6 +244,8 @@ "Show all ports, ignoring requirements.": "Exibir todos os jogos, ignorando requisitos.", "CWTBE Mode: ": "Modo CWTBE: ", "Enable gptokeyb2 by default.": "Habilitar gptokeyb2 por padrão.", + "Debug Mode: ": "Modo de depuração: ", + "Enable debug logging all the time.": "Sempre ativar o registro de depuração.", "Delete PortMaster Config": "Deletar Configuração de PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "Isso pode causar problemas, não mexa a menos que saiba o que está fazendo.", "Delete PortMaster Runtimes": "Deletar Tempos de Execução de PortMaster", @@ -226,9 +266,11 @@ "Reinstall": "Reinstalar", "Uninstall": "Desinstalar", "Install": "Instalar", + "Links": "Links", "Port Info": "Informações de Jogo", "Show Info": "Mostrar informação", "Are you sure you want to uninstall {port_name}?": "Você tem certeza que quer desinstalar {port_name}?", + "No links for this port.": "Nenhum link para este jogo.", "NO PORTS": "SEM JOGOS", "** NO PORTS FOUND **": "** NENHUM JOGO ENCONTRADO **", "Download ports first.": "Baixe jogos primeiro.", @@ -241,11 +283,29 @@ "Check": "Verificar", "Are you sure you want to download and verify all runtimes?": "Você tem certeza que quer baixar e verificar todos os tempos de execução?", "Deleted runtime {runtime}": "Tempo de execução {runtime} deletado", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Para a melhor experiência, mude para um firmware compatível como muOS ou KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS não é compatível com o PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS não é compatível com o PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI não é compatível com o PortMaster.", + "Your OS is not supported by PortMaster.": "Seu sistema operacional não é compatível com o PortMaster.", + "Your device has less than 1GB of RAM.": "Seu dispositivo tem menos de 1 GB de RAM.", "Disclaimer": "Aviso", + "Wait {seconds} seconds": "Aguarde {seconds} segundos", "Accept": "Aceitar", + "Warning": "Aviso", + "Many ports will not work.": "Muitos jogos não vão funcionar.", + "I Understand": "Entendi", + "Starting the theme editor...": "Iniciando o editor de temas...", + "Stop": "Parar", + "it stopped": "ele parou", + "Unable to start the theme editor: {error}": "Não foi possível iniciar o editor de temas: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "O editor de temas está rodando, mas este dispositivo não está em uma rede. Conecte-o ao Wi-Fi e inicie o editor de temas novamente.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Em um computador ou celular na mesma rede, escaneie o código ou abra:\n\n{url}\n\nVisualize temas em qualquer tamanho de tela e edite as traduções do PortMaster: elas são salvas neste dispositivo.\n\nPressione B para parar o editor de temas.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "O editor de temas parou. Veja theme_server.txt na pasta do PortMaster.", "{theme_name} (Selected)": "{theme_name} (Selecionado)", "Download": "Baixar", "{item_name} (Selected)": "{item_name} (Selecionado)", + "Please wait.": "Aguarde.", "Up to date already": "Já está atualizado", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Você está trocando da versão {old_release_channel} para a versão {new_release_channel}.\n\nVocê deseja retroceder a versão?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Há uma nova versão do Portmaster ({portmaster_version})\n\nDeseja atualizar?", diff --git a/PortMaster/pylibs/locales/pt_BR.themes.json b/PortMaster/pylibs/locales/pt_BR.themes.json index f190dcc7..8d5cd584 100644 --- a/PortMaster/pylibs/locales/pt_BR.themes.json +++ b/PortMaster/pylibs/locales/pt_BR.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Gêneros: {port_info.genres}\n• Tamanho de download: {port_info.download_size}{if:port_info.runtime}\n• Tempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descrição: {port_info.description}\n• Instruções:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Criador: {theme_info.creator}\n• Estado: {theme_info.status}\n• Descrição:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nome: {runtime_info.name}\nEstado: {runtime_info.status}\nJogos: {runtime_info.ports}\nTamanho do Download: {runtime_info.download_size}\nTamanho para Instalar: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nome: {runtime_info.name}\nEstado: {runtime_info.status}\nJogos: {runtime_info.ports}\nTamanho do Download: {runtime_info.download_size}\nTamanho para Instalar: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} jogos", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Gênero: {port_info.genres}\nTamanho: {port_info.download_size}{if:port_info.runtime}\nTempo de execução: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGênero: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nTempo de execução: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} livre", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}por {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Estado: {runtime_info.status}\nEm uso: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nNo disco: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsado por: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/ro_RO.json b/PortMaster/pylibs/locales/ro_RO.json index e691c922..f6c31463 100644 --- a/PortMaster/pylibs/locales/ro_RO.json +++ b/PortMaster/pylibs/locales/ro_RO.json @@ -1,24 +1,43 @@ { + "This is not a valid zip file.": "Acesta nu este un fișier zip valid.", + "illegal file {file_name}": "fișier nepermis {file_name}", + "the port has no directory": "portul nu are folder", + "the port has no launch script": "portul nu are script de pornire", + "unable to load {file_name}": "nu s-a putut încărca {file_name}", "Loading...": "Se încarcă...", "Loading Sources.": "Se încarcă sursele.", "Download validation failed.": "Validarea descărcării a eșuat.", + "Unable to download file.": "Nu s-a putut descărca fișierul.", "Loading Info.": "Se încarcă informațiile.", + "Fetching latest ports info data.": "Se obțin cele mai noi informații despre porturi.", + "Fetching latest porters info.": "Se obțin cele mai noi informații despre porteri.", + "Fetching latest port stats.": "Se obțin cele mai noi statistici ale porturilor.", "Fetching latest featured ports.": "Se preiau cele mai recente porturi recomandate.", + "Fetching latest featured port images zip.": "Se obține cel mai nou zip cu imaginile porturilor recomandate.", + "Fetching featured port image {file_name}": "Se obține imaginea portului recomandat {file_name}", "Installing Theme {download_name}.": "Se instalează tema {download_name}.", "Installing": "Se instalează", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} a fost instalată cu succes.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip este deteriorat, descarcă-l din nou.", + "PortMaster failed to install.": "Instalarea PortMaster a eșuat.", "Installing {download_name}.": "Se instalează {download_name}.", + "PortMaster installed successfully.": "PortMaster a fost instalat cu succes.", "Installation failed, removing files...": "Instalarea a eșuat, se elimină fișierele...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "SALUT.\n\nSE PARE CĂ INSTALEZI MANUAL UN PORT CARE NU ESTE COMPATIBIL CU DISPOZITIVUL TĂU.\n\nDACĂ AI PROBLEME, NU LE RAPORTA PE DISCORD, VEI FI LUAT ÎN RÂS.\n\nSIGUR VREI SĂ CONTINUI INSTALAREA?", "Okay": "OK", "Cancel": "Anulează", + "Not compatible with this device.": "Nu este compatibil cu acest dispozitiv.", + "Installation failed: {reason}": "Instalarea a eșuat: {reason}", "Port {download_name} failed to install.": "Instalarea portului {download_name} a eșuat.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Portul {download_name!r} și {runtime_name!r} au fost instalate cu succes.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Portul {download_name!r} a fost instalat cu succes, dar instalarea pentru {runtime_name!r} a eșuat!!\n\nPoți reîncerca instalarea sau verifica wiki-ul pentru ajutor.", "Port {download_name!r} installed successfully.": "Portul {download_name!r} a fost instalat cu succes.", "Unable to download in offline mode.": "Imposibil de descărcat în modul offline.", + "File not found.": "Fișierul nu a fost găsit.", "Unable to find a source for {port_name}": "Nu s-a găsit o sursă pentru {port_name}.", "Unknown port {port_name}": "Port necunoscut {port_name}.", "Removing {port_name}": "Se elimină {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Eroare la dezinstalarea {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "{port_name} a fost dezinstalat cu succes.", "Loading Ports.": "Se încarcă porturile.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Portul {runtime} conține un runtime defect, jocul ar putea să nu ruleze corect.", @@ -58,22 +77,39 @@ "Passed file validation.": "Fișierul a trecut validarea.", "Unable to validate download.": "Nu s-a putut valida descărcarea.", "Unknown IP": "IP Necunoscut", + "N/A": "N/A", "Fetching latest gameinfo.xml files and cover images.": "Se preiau cele mai recente fișiere gameinfo.xml și imagini de copertă.", "Downloading gameinfo.xml for {source_name}": "Se descarcă gameinfo.xml pentru {source_name}", "Installing {port_name}": "Se instalează {port_name}", "Uninstalling {port_name}": "Se dezinstalează {port_name}", "Updating all port sources:": "Se actualizează toate sursele porturilor:", "Checking {runtime_name}": "Se verifică {runtime_name}", + "Auto-installation": "Instalare automată", + "PortMaster website": "Site-ul PortMaster", + "{store}: developer": "{store}: dezvoltator", "** NO PORT **": "** FĂRĂ PORT **", "Ready to Run": "Gata de Rulare", "Requires Files": "Necesită Fișiere", "Installed": "Instalat", "Missing": "Lipsă", + "Installation failed, see log.txt for details.": "Instalarea a eșuat, vezi log.txt pentru detalii.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Este pentru altă arhitectură ({zip_arch}, acest dispozitiv este {device_arch}), a fost șters.", + "Installed {download_name}.": "{download_name} instalat.", + "- SUCCESS: {name}": "- SUCCES: {name}", + "- FAILURE: {name}": "- EȘEC: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Porturile au fost adăugate sau eliminate, actualizează lista de jocuri pentru a vedea noile jocuri sau pentru a elimina cele dezinstalate.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Unele fișiere nu au putut fi instalate, selectează-le ca să vezi de ce. Au rămas în folderul de instalare automată.", + "Everything installed successfully.": "Totul a fost instalat cu succes.", + "{installed} installed, {failed} failed": "{installed} instalate, {failed} eșuate", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} a fost instalat cu succes.", + "FAILED: {name}": "EȘEC: {name}", + "{name} failed to install:\n\n{reason}": "{name} nu a putut fi instalat:\n\n{reason}", "Danish": "Daneză", "German": "Germană", "English": "Engleză", "Spanish": "Spaniolă", + "Esperanto": "Esperanto", "Finnish": "Finlandeză", "French": "Franceză", "Italian": "Italiană", @@ -83,6 +119,7 @@ "Polish": "Poloneză", "Portuguese (Portugal)": "Portugheză (Portugalia)", "Portuguese (Brazil)": "Portugheză (Brazilia)", + "Romanian": "Română", "Russian": "Rusă", "Swedish": "Suedeză", "Ukrainian": "Ucraineană", @@ -97,11 +134,14 @@ "Alphabetical": "Alfabetic", "Recently Added": "Adăugate Recent", "Recently Updated": "Actualizate Recent", + "Total Downloads": "Total descărcări", "Action": "Acțiune", "Adventure": "Aventură", + "Arcade": "Arcade", "Casino/Card": "Casino/Cărți", "First Person Shooter": "Shooter la persoana întâi", "Platformer": "Platformer", + "Puzzle": "Puzzle", "Racing": "Curse", "Rhythm": "Ritm", "Role Playing Game": "Joc de rol (RPG)", @@ -111,6 +151,7 @@ "Visual Novel": "Roman Vizual", "Other": "Altele", "Files Required": "Fișiere Necesare", + "Experimental": "Experimental", "Broken Ports": "Porturi Defecte", "Free game, all files included.": "Joc gratuit, toate fișierele incluse.", "Demo files included.": "Fișiere demo incluse.", @@ -120,6 +161,8 @@ "ARM 32bit": "ARM 32biți", "ARM 64bit": "ARM 64biți", "x86 64bit": "x86 64biți", + "Ascending": "Crescător", + "Descending": "Descrescător", "Sort:": "Sortare:", "Clear Filters": "Șterge Filtrele", "Genres:": "Genuri:", @@ -159,6 +202,9 @@ "Select a theme for PortMaster.": "Selectează o temă pentru PortMaster.", "Select Color Scheme": "Selectează Schema de Culori", "Select a color scheme for PortMaster.": "Selectează o schemă de culori pentru PortMaster.", + "Theme Editor": "Editor de teme", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Previzualizează teme și editează traducerile PortMaster de pe un computer sau telefon din aceeași rețea.", + "Audio": "Audio", "Music: ": "Muzică: ", "Enable or Disable background music in PortMaster.": "Activează sau dezactivează muzica de fundal în PortMaster.", "Sound FX: ": "Efecte Sonore: ", @@ -174,6 +220,8 @@ "Roms section": "Secțiunea ROM-uri", "Ports tab": "Fila porturi", "Location where ports should be installed to.": "Locația unde ar trebui instalate porturile.", + "SD 1": "SD 1", + "SD 2": "SD 2", "System": "Sistem", "Runtime Manager": "Manager Runtime", "Manage port runtimes.": "Gestionează runtime-urile porturilor.", @@ -196,6 +244,8 @@ "Show all ports, ignoring requirements.": "Afișează toate porturile, ignorând cerințele.", "CWTBE Mode: ": "Mod CWTBE: ", "Enable gptokeyb2 by default.": "Activează gptokeyb2 în mod implicit.", + "Debug Mode: ": "Mod depanare: ", + "Enable debug logging all the time.": "Activează mereu jurnalul de depanare.", "Delete PortMaster Config": "Șterge configurația PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "Aceasta poate cauza probleme, nu modifica dacă nu știi ce faci.", "Delete PortMaster Runtimes": "Șterge Runtime-urile PortMaster", @@ -216,9 +266,11 @@ "Reinstall": "Reinstalează", "Uninstall": "Dezinstalează", "Install": "Instalează", + "Links": "Linkuri", "Port Info": "Informații Port", "Show Info": "Afișează Informații", "Are you sure you want to uninstall {port_name}?": "Ești sigur că vrei să dezinstalezi {port_name}?", + "No links for this port.": "Acest port nu are linkuri.", "NO PORTS": "NICIUN PORT", "** NO PORTS FOUND **": "** NU S-AU GĂSIT PORTURI **", "Download ports first.": "Descarcă mai întâi porturile.", @@ -231,11 +283,29 @@ "Check": "Verifică", "Are you sure you want to download and verify all runtimes?": "Ești sigur că vrei să descarci și să verifici toate runtime-urile?", "Deleted runtime {runtime}": "Runtime-ul {runtime} a fost șters", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Pentru cea mai bună experiență, treci la un firmware compatibil, precum muOS sau KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS nu este suportat de PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS nu este suportat de PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI nu este suportat de PortMaster.", + "Your OS is not supported by PortMaster.": "Sistemul tău de operare nu este suportat de PortMaster.", + "Your device has less than 1GB of RAM.": "Dispozitivul tău are mai puțin de 1 GB de RAM.", "Disclaimer": "Declinarea responsabilității", + "Wait {seconds} seconds": "Așteaptă {seconds} secunde", "Accept": "Acceptă", + "Warning": "Avertisment", + "Many ports will not work.": "Multe porturi nu vor funcționa.", + "I Understand": "Am înțeles", + "Starting the theme editor...": "Se pornește editorul de teme...", + "Stop": "Oprește", + "it stopped": "s-a oprit", + "Unable to start the theme editor: {error}": "Nu s-a putut porni editorul de teme: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "Editorul de teme rulează, dar acest dispozitiv nu este conectat la o rețea. Conectează-l la Wi-Fi, apoi pornește din nou editorul de teme.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "Pe un computer sau telefon din aceeași rețea, scanează codul sau deschide:\n\n{url}\n\nPrevizualizează teme la orice dimensiune de ecran și editează traducerile PortMaster: sunt salvate pe acest dispozitiv.\n\nApasă B pentru a opri editorul de teme.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "Editorul de teme s-a oprit. Vezi theme_server.txt în folderul PortMaster.", "{theme_name} (Selected)": "{theme_name} (Selectat)", "Download": "Descarcă", "{item_name} (Selected)": "{item_name} (Selectat)", + "Please wait.": "Te rugăm să aștepți.", "Up to date already": "Deja la zi", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Schimbi de la versiunea {old_release_channel} la versiunea {new_release_channel}.\n\nDorești să faci un downgrade?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Există o nouă versiune a PortMaster ({portmaster_version})\n\nDorești să faci un upgrade?", diff --git a/PortMaster/pylibs/locales/ro_RO.themes.json b/PortMaster/pylibs/locales/ro_RO.themes.json index 0c472efa..40204c9c 100644 --- a/PortMaster/pylibs/locales/ro_RO.themes.json +++ b/PortMaster/pylibs/locales/ro_RO.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genuri: {port_info.genres}\n• Dimensiune Descărcare: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Descriere: {port_info.description}\n• Instrucțiuni:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Descriere:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nume: {runtime_info.name}\nStatus: {runtime_info.status}\nPorturi: {runtime_info.ports}\nDimensiune Descărcare: {runtime_info.download_size}\nDimensiune Instalare: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Nume: {runtime_info.name}\nStatus: {runtime_info.status}\nPorturi: {runtime_info.ports}\nDimensiune Descărcare: {runtime_info.download_size}\nDimensiune Instalare: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} jocuri", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Gen: {port_info.genres}\nDimensiune: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGen: {port_info.genres}\nDescărcare: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} liber", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}de {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Stare: {runtime_info.status}\nÎn uz: {runtime_info.in_use}\nDescărcare: {runtime_info.download_size}{if:runtime_info.disk_size}\nPe disc: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nFolosit de: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/ru_RU.json b/PortMaster/pylibs/locales/ru_RU.json index b1ae1167..1b9a8af4 100644 --- a/PortMaster/pylibs/locales/ru_RU.json +++ b/PortMaster/pylibs/locales/ru_RU.json @@ -1,22 +1,39 @@ { + "This is not a valid zip file.": "Это не допустимый zip-файл.", + "illegal file {file_name}": "недопустимый файл {file_name}", + "the port has no directory": "у порта нет папки", + "the port has no launch script": "у порта нет скрипта запуска", + "unable to load {file_name}": "не удалось загрузить {file_name}", "Loading...": "Загрузка...", "Loading Sources.": "Загрузка источников.", "Download validation failed.": "Ошибка проверки загрузки.", + "Unable to download file.": "Невозможно загрузить файл.", "Loading Info.": "Загрузка информации.", + "Fetching latest ports info data.": "Получение последней информации о портах.", + "Fetching latest porters info.": "Получение последней информации о портерах.", + "Fetching latest port stats.": "Получение последней статистики портов.", "Fetching latest featured ports.": "Получение последних портов.", + "Fetching latest featured port images zip.": "Получение последнего zip с изображениями рекомендуемых портов.", + "Fetching featured port image {file_name}": "Получение изображения рекомендуемого порта {file_name}", "Installing Theme {download_name}.": "Установка темы {download_name}.", "Installing": "Установка", "Theme {download_name!r} installed successfully.": "Тема {download_name!r} успешно установлена.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip повреждён, загрузите его заново.", + "PortMaster failed to install.": "Не удалось установить PortMaster.", "Installing {download_name}.": "Установка {download_name}.", + "PortMaster installed successfully.": "PortMaster успешно установлен.", "Installation failed, removing files...": "Установка не удалась, удаление файлов...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "ПРИВЕТ.\n\nКАЖЕТСЯ, ВЫ ВРУЧНУЮ УСТАНАВЛИВАЕТЕ ПОРТ, НЕСОВМЕСТИМЫЙ С ВАШИМ УСТРОЙСТВОМ.\n\nЕСЛИ У ВАС ВОЗНИКЛИ ПРОБЛЕМЫ, НЕ СООБЩАЙТЕ О НИХ В DISCORD, ВЫ СТАНЕТЕ ОБЪЕКТОМ НАСМЕШЕК.\n\nВЫ УВЕРЕНЫ, ЧТО ХОТИТЕ ПРОДОЛЖИТЬ УСТАНОВКУ?", "Okay": "Ок", "Cancel": "Отменить", + "Not compatible with this device.": "Несовместимо с этим устройством.", + "Installation failed: {reason}": "Ошибка установки: {reason}", "Port {download_name} failed to install.": "Не удалось установить порт {download_name}.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Порт {download_name!r} и {runtime_name!r} успешно установлены.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успешно установлен, но {runtime_name!r} не удалось установить!\n\nПопробуйте переустановить или обратитесь за помощью на вики.", "Port {download_name!r} installed successfully.": "Порт {download_name!r} успешно установлен.", "Unable to download in offline mode.": "Невозможно загрузить в автономном режиме.", + "File not found.": "Файл не найден.", "Unable to find a source for {port_name}": "Для {port_name} не удается найти источник", "Unknown port {port_name}": "Неизвестный порт {port_name}", "Removing {port_name}": "Удаление {port_name}", @@ -67,16 +84,32 @@ "Uninstalling {port_name}": "Удаление {port_name}", "Updating all port sources:": "Обновление всех источников портов:", "Checking {runtime_name}": "Проверяем {runtime_name}", + "Auto-installation": "Автоматическая установка", + "PortMaster website": "Сайт PortMaster", + "{store}: developer": "{store}: разработчик", "** NO PORT **": "** НЕТ ПОРТА **", "Ready to Run": "Готов к запуску", "Requires Files": "Требуются файлы", "Installed": "Установлено", "Missing": "Отсутствует", + "Installation failed, see log.txt for details.": "Ошибка установки, подробности в log.txt.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Он для другой архитектуры ({zip_arch}, это устройство {device_arch}), он удалён.", + "Installed {download_name}.": "{download_name} установлен.", + "- SUCCESS: {name}": "- УСПЕХ: {name}", + "- FAILURE: {name}": "- ОШИБКА: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Порты были добавлены или удалены, обновите список игр, чтобы увидеть новые игры или удалить неустановленные.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Некоторые файлы не удалось установить, выберите их, чтобы узнать причину. Они остались в папке автоустановки.", + "Everything installed successfully.": "Всё успешно установлено.", + "{installed} installed, {failed} failed": "Установлено: {installed}, ошибок: {failed}", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} успешно установлен.", + "FAILED: {name}": "ОШИБКА: {name}", + "{name} failed to install:\n\n{reason}": "Не удалось установить {name}:\n\n{reason}", "Danish": "Датский", "German": "Немецкий", "English": "Английский", "Spanish": "Испанский", + "Esperanto": "Эсперанто", "Finnish": "Финский", "French": "Французский", "Italian": "Итальянский", @@ -101,6 +134,7 @@ "Alphabetical": "По алфавиту", "Recently Added": "Недавно добавленные", "Recently Updated": "Недавно обновленные", + "Total Downloads": "Всего загрузок", "Action": "Экшен", "Adventure": "Приключение", "Arcade": "Аркада", @@ -127,6 +161,8 @@ "ARM 32bit": "ARM 32bit", "ARM 64bit": "ARM 64bit", "x86 64bit": "x86 64bit", + "Ascending": "По возрастанию", + "Descending": "По убыванию", "Sort:": "Сортировка:", "Clear Filters": "Очистить фильтры", "Genres:": "Жанры:", @@ -166,6 +202,8 @@ "Select a theme for PortMaster.": "Изменить тему PortMaster.", "Select Color Scheme": "Выбрать цветовую схему", "Select a color scheme for PortMaster.": "Изменить цветовую схему PortMaster.", + "Theme Editor": "Редактор тем", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Просматривайте темы и редактируйте переводы PortMaster с компьютера или телефона в той же сети.", "Audio": "Звук", "Music: ": "Музыка: ", "Enable or Disable background music in PortMaster.": "Включить или отключить фоновую музыку в PortMaster.", @@ -206,6 +244,8 @@ "Show all ports, ignoring requirements.": "Показать все порты, игнорировать требования.", "CWTBE Mode: ": "CWTBE режим: ", "Enable gptokeyb2 by default.": "По умолчанию включить gptokeyb2.", + "Debug Mode: ": "Режим отладки: ", + "Enable debug logging all the time.": "Всегда включать журнал отладки.", "Delete PortMaster Config": "Удалить конфигурацию PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "Это может сломать некоторые функции, не трогайте, если вы не знаете, что делаете.", "Delete PortMaster Runtimes": "Удалить среды выполнения PortMaster", @@ -226,9 +266,11 @@ "Reinstall": "Переустановить", "Uninstall": "Удалить", "Install": "Установить", + "Links": "Ссылки", "Port Info": "Информация о порте", "Show Info": "Показать информацию", "Are you sure you want to uninstall {port_name}?": "Вы уверены, что хотите удалить {port_name}?", + "No links for this port.": "Для этого порта нет ссылок.", "NO PORTS": "НЕТ ПОРТОВ", "** NO PORTS FOUND **": "** ПОРТОВ НЕ НАЙДЕНО **", "Download ports first.": "Сначала загрузите порты.", @@ -241,11 +283,29 @@ "Check": "Проверить", "Are you sure you want to download and verify all runtimes?": "Вы уверены, что хотите загрузить и проверить все среды выполнения?", "Deleted runtime {runtime}": "Удаление сред выполнения {runtime}", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Для лучшей работы перейдите на поддерживаемую прошивку, например muOS или KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS не поддерживается PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS не поддерживается PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI не поддерживается PortMaster.", + "Your OS is not supported by PortMaster.": "Ваша ОС не поддерживается PortMaster.", + "Your device has less than 1GB of RAM.": "На вашем устройстве меньше 1 ГБ оперативной памяти.", "Disclaimer": "Отказ от ответственности", + "Wait {seconds} seconds": "Подождите {seconds} с", "Accept": "Принять", + "Warning": "Предупреждение", + "Many ports will not work.": "Многие порты не будут работать.", + "I Understand": "Понятно", + "Starting the theme editor...": "Запуск редактора тем...", + "Stop": "Остановить", + "it stopped": "он остановился", + "Unable to start the theme editor: {error}": "Не удалось запустить редактор тем: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "Редактор тем запущен, но это устройство не подключено к сети. Подключите его к Wi-Fi и снова запустите редактор тем.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "На компьютере или телефоне в той же сети отсканируйте код или откройте:\n\n{url}\n\nПросматривайте темы при любом размере экрана и редактируйте переводы PortMaster: они сохраняются на этом устройстве.\n\nНажмите B, чтобы остановить редактор тем.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "Редактор тем остановился. См. theme_server.txt в папке PortMaster.", "{theme_name} (Selected)": "{theme_name} (Выбрана)", "Download": "Скачать", "{item_name} (Selected)": "{item_name} (Выбранная)", + "Please wait.": "Пожалуйста, подождите.", "Up to date already": "Актуальная версия уже существует", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Вы переключаетесь с версии {old_release_channel} на версию {new_release_channel}.\n\nВы желаете сменить версию?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна новая версия PortMaster ({portmaster_version})\n\nВы хотите обновиться?", diff --git a/PortMaster/pylibs/locales/ru_RU.themes.json b/PortMaster/pylibs/locales/ru_RU.themes.json index e5bd8cdb..58ed9297 100644 --- a/PortMaster/pylibs/locales/ru_RU.themes.json +++ b/PortMaster/pylibs/locales/ru_RU.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Жанры: {port_info.genres}\n• Скачать Size: {port_info.download_size}{if:port_info.runtime}\n• Среда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Описание: {port_info.description}\n• Инструкции:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Создатель: {theme_info.creator}\n• Статус: {theme_info.status}\n• Описание:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Имя: {runtime_info.name}\nСтатус: {runtime_info.status}\nПорты: {runtime_info.ports}\nРазмер загрузки: {runtime_info.download_size}\nРазмер установки: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Имя: {runtime_info.name}\nСтатус: {runtime_info.status}\nПорты: {runtime_info.ports}\nРазмер загрузки: {runtime_info.download_size}\nРазмер установки: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "Игр: {ports_list.filter_ports}", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Жанр: {port_info.genres}\nРазмер: {port_info.download_size}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nПортер: {port_info.porter}\nЖанр: {port_info.genres}\nЗагрузка: {port_info.download_size}{if:port_info.runtime}\nСреда выполнения: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} свободно", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}автор {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Статус: {runtime_info.status}\nИспользуется: {runtime_info.in_use}\nЗагрузка: {runtime_info.download_size}{if:runtime_info.disk_size}\nНа диске: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nИспользуют: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/strings.json b/PortMaster/pylibs/locales/strings.json index 98eaa0cc..c07a93a9 100644 --- a/PortMaster/pylibs/locales/strings.json +++ b/PortMaster/pylibs/locales/strings.json @@ -229,19 +229,19 @@ ], "Updating": [ "pylibs/harbourmaster/source/portmaster.py:42", - "pylibs/pugwash/theme.py:563" + "pylibs/pugwash/theme.py:581" ], "Fetching latest info": [ "pylibs/harbourmaster/source/portmaster.py:46", - "pylibs/pugwash/theme.py:573" + "pylibs/pugwash/theme.py:591" ], "Done.": [ "pylibs/harbourmaster/source/portmaster.py:165", - "pylibs/pugwash/theme.py:596" + "pylibs/pugwash/theme.py:614" ], "Fetching info": [ "pylibs/harbourmaster/source/portmaster.py:197", - "pylibs/pugwash/theme.py:602" + "pylibs/pugwash/theme.py:620" ], "Unable to find {port_name}.": [ "pylibs/harbourmaster/source/portmaster.py:418" @@ -1014,6 +1014,10 @@ "Disclaimer": [ "pylibs/pugwash/scenes/startup.py:103" ], + "Wait {seconds} seconds": [ + "pylibs/pugwash/scenes/startup.py:127", + "pylibs/pugwash/scenes/startup.py:210" + ], "Accept": [ "pylibs/pugwash/scenes/startup.py:129" ], @@ -1056,8 +1060,11 @@ "{item_name} (Selected)": [ "pylibs/pugwash/scenes/themes.py:120" ], + "Please wait.": [ + "pylibs/pugwash/theme.py:358" + ], "Up to date already": [ - "pylibs/pugwash/theme.py:570" + "pylibs/pugwash/theme.py:588" ], "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": [ "pylibs/pugwash/update.py:79" diff --git a/PortMaster/pylibs/locales/sv_SE.json b/PortMaster/pylibs/locales/sv_SE.json index e2b3c097..0989db44 100644 --- a/PortMaster/pylibs/locales/sv_SE.json +++ b/PortMaster/pylibs/locales/sv_SE.json @@ -1,23 +1,43 @@ { + "This is not a valid zip file.": "Det här är inte en giltig zip-fil.", + "illegal file {file_name}": "otillåten fil {file_name}", + "the port has no directory": "porten har ingen mapp", + "the port has no launch script": "porten har inget startskript", + "unable to load {file_name}": "kunde inte läsa in {file_name}", "Loading...": "Laddar...", "Loading Sources.": "Laddar Källor.", "Download validation failed.": "Validering av nedladdning misslyckades.", + "Unable to download file.": "Kunde inte hämta filen.", "Loading Info.": "Laddar Info.", + "Fetching latest ports info data.": "Hämtar den senaste informationen om portar.", + "Fetching latest porters info.": "Hämtar den senaste informationen om portutvecklare.", + "Fetching latest port stats.": "Hämtar den senaste portstatistiken.", "Fetching latest featured ports.": "Hämtar senaste utvalda portar.", + "Fetching latest featured port images zip.": "Hämtar den senaste zip-filen med bilder på utvalda portar.", + "Fetching featured port image {file_name}": "Hämtar bild för utvald port {file_name}", "Installing Theme {download_name}.": "Installerar Tema {download_name}.", "Installing": "Installerar", "Theme {download_name!r} installed successfully.": "Tema {download_name!r} har installerats.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip är skadad, hämta den igen.", + "PortMaster failed to install.": "Installationen av PortMaster misslyckades.", "Installing {download_name}.": "Installerar {download_name}.", + "PortMaster installed successfully.": "PortMaster installerades.", "Installation failed, removing files...": "Installationen misslyckades, raderar filer...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "HEJ.\n\nDET SER UT SOM ATT DU INSTALLERAR EN PORT MANUELLT SOM INTE ÄR KOMPATIBEL MED DIN ENHET.\n\nOM DU FÅR PROBLEM, RAPPORTERA DEM INTE PÅ DISCORD, DÅ KOMMER DU ATT BLI UTSKRATTAD.\n\nÄR DU SÄKER PÅ ATT DU VILL FORTSÄTTA INSTALLATIONEN?", "Okay": "Okej", "Cancel": "Avbryt", + "Not compatible with this device.": "Inte kompatibel med den här enheten.", + "Installation failed: {reason}": "Installationen misslyckades: {reason}", "Port {download_name} failed to install.": "Installation av {download_name} misslyckades.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Port {download_name!r} och {runtime_name!r} har installerats.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Port {download_name!r} installerades, men {runtime_name!r} kunde inte installeras!!\n\nAntingen installera om för att försöka igen, eller kolla wikin för hjälp.", "Port {download_name!r} installed successfully.": "Port {download_name!r} installerades.", + "Unable to download in offline mode.": "Kan inte hämta i offlineläge.", + "File not found.": "Filen hittades inte.", "Unable to find a source for {port_name}": "Kunde inte hitta en nedladdning för {port_name}", "Unknown port {port_name}": "Okänd port {port_name}", "Removing {port_name}": "Raderar {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Fel vid avinstallation av {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "Avinstallerat {port_name}", "Loading Ports.": "Laddar Portar.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Port {runtime} innehåller en dålig körtid, spelet kanske inte körs korrekt.", @@ -39,6 +59,7 @@ "Downloading runtime {runtime}.": "Hämtar körtid {runtime}.", "Unable to download {runtime}, game may not run correctly.": "Kunde inte ladda ner {runtime}, spelet kanske inte körs korrekt.", "Successfully downloaded {runtime}.": "Hämtat {runtime}.", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\nSå här gör du:\nMENU -> UTILITIES -> Rescan Rom Directory", "Unable to download verification file.": "Kunde inte hämta verifieringsfilen.", "Unable to download non zip files.": "Kan inte hämta icke-zip-filer.", "Cache out of date.": "Cache är föråldrad.", @@ -63,15 +84,32 @@ "Uninstalling {port_name}": "Avinstallerar {port_name}", "Updating all port sources:": "Uppdaterar alla portkällor:", "Checking {runtime_name}": "Kontrollerar {runtime_name}", + "Auto-installation": "Automatisk installation", + "PortMaster website": "PortMasters webbplats", + "{store}: developer": "{store}: utvecklare", "** NO PORT **": "** INGEN PORT **", "Ready to Run": "Redo att köras", "Requires Files": "Kräver filer", "Installed": "Installerad", "Missing": "Saknas", + "Installation failed, see log.txt for details.": "Installationen misslyckades, se log.txt för detaljer.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Den är för fel arkitektur ({zip_arch}, den här enheten är {device_arch}), den har raderats.", + "Installed {download_name}.": "{download_name} installerad.", + "- SUCCESS: {name}": "- KLART: {name}", + "- FAILURE: {name}": "- MISSLYCKADES: {name}", + "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Portar har lagts till eller tagits bort, uppdatera din spellista för att se de nya spelen eller ta bort de avinstallerade.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Några filer kunde inte installeras, välj dem för att se varför. De lämnades kvar i autoinstallationsmappen.", + "Everything installed successfully.": "Allt installerades.", + "{installed} installed, {failed} failed": "{installed} installerade, {failed} misslyckades", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} installerades.", + "FAILED: {name}": "MISSLYCKADES: {name}", + "{name} failed to install:\n\n{reason}": "{name} kunde inte installeras:\n\n{reason}", "Danish": "Danska", "German": "Tyska", "English": "Engelska", "Spanish": "Spanska", + "Esperanto": "Esperanto", "Finnish": "Finska", "French": "Franska", "Italian": "Italienska", @@ -81,7 +119,9 @@ "Polish": "Polska", "Portuguese (Portugal)": "Portugisiska (Portugal)", "Portuguese (Brazil)": "Portugisiska (Brasilien)", + "Romanian": "Rumänska", "Russian": "Ryska", + "Swedish": "Svenska", "Ukrainian": "Ukrainska", "Chinese Simplified": "Kinesiska, förenklad", "Messages": "Meddelanden", @@ -90,13 +130,16 @@ "Select": "Välj", "Back": "Tillbaka", "No featured ports found, internet connection is required.": "Inga utvalda portar hittades, internetanslutning krävs.", + "Filters Scene": "Filter", "Alphabetical": "Alfabetisk", "Recently Added": "Nyligen tillagda", "Recently Updated": "Senast uppdaterade", + "Total Downloads": "Totalt antal hämtningar", "Action": "Action", "Adventure": "Äventyr", "Arcade": "Arkad", "Casino/Card": "Kasino/Kortspel", + "First Person Shooter": "Förstapersonsskjutare", "Platformer": "Platformer", "Puzzle": "Pussel", "Racing": "Racing", @@ -107,16 +150,24 @@ "Strategy": "Strategi", "Visual Novel": "Visuell roman", "Other": "Övrigt", + "Files Required": "Filer krävs", + "Experimental": "Experimentell", "Broken Ports": "Trasiga Portar", "Free game, all files included.": "Gratis spel, alla filer ingår.", "Demo files included.": "Demofiler ingår.", "Free external assets needed.": "Gratis externa tillgångar krävs.", "Paid external assets needed.": "Betalda externa tillgångar krävs.", "{runtime_name} Runtime": "{runtime_name}", + "ARM 32bit": "ARM 32-bit", + "ARM 64bit": "ARM 64-bit", + "x86 64bit": "x86 64-bit", + "Ascending": "Stigande", + "Descending": "Fallande", "Sort:": "Sortera:", "Clear Filters": "Rensa Filter", "Genres:": "Genrer:", "Attributes:": "Attribut:", + "Architecture:": "Arkitektur:", "Availability:": "Tillgänglighet:", "Porters:": "Portutvecklare:", "Delete": "Radera", @@ -151,6 +202,8 @@ "Select a theme for PortMaster.": "Välj ett tema för PortMaster.", "Select Color Scheme": "Välj färgschema", "Select a color scheme for PortMaster.": "Välj färgschema för PortMaster.", + "Theme Editor": "Temaredigerare", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Förhandsgranska teman och redigera PortMasters översättningar från en dator eller telefon i samma nätverk.", "Audio": "Ljud", "Music: ": "Musik: ", "Enable or Disable background music in PortMaster.": "Aktivera eller inaktivera bakgrundsmusik i PortMaster.", @@ -164,14 +217,18 @@ "Metadata Refresh": "Uppdatera metadata", "Manually update port metadata with missing/updated information and artwork.": "Uppdatera portmetadata manuellt med saknad/uppdaterad information och illustrationer.", "Ports Location: ": "Lagringsplats för Portar: ", + "Roms section": "Roms-sektionen", "Ports tab": "Portfliken", "Location where ports should be installed to.": "Plats där portar ska installeras till.", + "SD 1": "SD 1", + "SD 2": "SD 2", "System": "System", "Runtime Manager": "Körtidshanterare", "Manage port runtimes.": "Hantera portkörtider.", "Experimental Ports: ": "Experimentella portar: ", "Show or hide experimental ports.": "Visa eller dölj experimentella portar.", "Update Ports": "Uppdatera Portar", + "Fetch latest ports information.": "Hämta den senaste portinformationen.", "Update PortMaster": "Uppdatera PortMaster", "Force check for a new PortMaster version.": "Tvinga sökning av en ny PortMaster-version.", "Restore PortMaster": "Återställ PortMaster", @@ -187,6 +244,8 @@ "Show all ports, ignoring requirements.": "Visa alla portar, ignorera krav.", "CWTBE Mode: ": "CWTBE Läge: ", "Enable gptokeyb2 by default.": "Aktivera gptokeyb2 som standard.", + "Debug Mode: ": "Felsökningsläge: ", + "Enable debug logging all the time.": "Aktivera alltid felsökningsloggning.", "Delete PortMaster Config": "Radera PortMaster-konfiguration", "This can break stuff, don't touch unless you know what you are doing.": "Detta kan sabba saker, rör ej om inte du vet vad du gör.", "Delete PortMaster Runtimes": "Radera PortMaster-körtider", @@ -197,15 +256,21 @@ "Are you sure you want to change the release channel from {current_channel} to {new_channel}?\n\nPortMaster will upgrade or downgrade accordingly.": "Är du säker på att du vill ändra releasekanalen från {current_channel} till {new_channel}?\n\nPortMaster kommer att uppgraderas eller nedgraderas i enlighet med detta.", "Are you sure you want to update your gamelist.xml?\n\nThis will override any scraped artwork or port information and will require a 50+ MB download.": "Är du säker på att du vill uppdatera din spellista.xml?\n\nDetta kommer att åsidosätta alla skrapade konstverk eller portinformation och kräver en nedladdning på 50+ MB.", "Are you sure you want to move ports from the {from_mode} to the {to_mode}?": "Är du säker på att du vill flytta portar från {from_mode} till {to_mode}?", + "SDCARD 1": "SD-KORT 1", + "SDCARD 2": "SD-KORT 2", + "Are you sure you want to manage and install ports on {to_loc}?\n\nAlready installed ports will not be moved.\nPortMaster will restart for this to take effect.": "Är du säker på att du vill hantera och installera portar på {to_loc}?\n\nRedan installerade portar flyttas inte.\nPortMaster startar om för att ändringen ska gälla.", "Deleting Runtimes:": "Raderar körtider:", "Source Manager": "Källhanterare", + "Port Info Popup": "Popup med portinformation", "Hide Info": "Dölj info", "Reinstall": "Ominstallera", "Uninstall": "Avinstallera", "Install": "Installera", + "Links": "Länkar", "Port Info": "Portinformation", "Show Info": "Visa info", "Are you sure you want to uninstall {port_name}?": "Är du säker på att du vill avinstallera {port_name}?", + "No links for this port.": "Inga länkar för den här porten.", "NO PORTS": "INGA PORTAR", "** NO PORTS FOUND **": "** INGA PORTER HITTADES **", "Download ports first.": "Ladda ner portar först.", @@ -218,9 +283,29 @@ "Check": "Kontrollera", "Are you sure you want to download and verify all runtimes?": "Är du säker på att du vill ladda ner och verifiera alla körtider?", "Deleted runtime {runtime}": "Raderat körtid {runtime}", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "För bästa upplevelse, byt till en firmware som stöds, till exempel muOS eller KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS stöds inte av PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS stöds inte av PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI stöds inte av PortMaster.", + "Your OS is not supported by PortMaster.": "Ditt operativsystem stöds inte av PortMaster.", + "Your device has less than 1GB of RAM.": "Din enhet har mindre än 1 GB RAM.", + "Disclaimer": "Ansvarsfriskrivning", + "Wait {seconds} seconds": "Vänta {seconds} sekunder", + "Accept": "Godkänn", + "Warning": "Varning", + "Many ports will not work.": "Många portar kommer inte att fungera.", + "I Understand": "Jag förstår", + "Starting the theme editor...": "Startar temaredigeraren...", + "Stop": "Stoppa", + "it stopped": "den stoppades", + "Unable to start the theme editor: {error}": "Kunde inte starta temaredigeraren: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "Temaredigeraren körs, men den här enheten är inte ansluten till något nätverk. Anslut den till wifi och starta temaredigeraren igen.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "På en dator eller telefon i samma nätverk, skanna koden eller öppna:\n\n{url}\n\nFörhandsgranska teman i alla skärmstorlekar och redigera PortMasters översättningar: de sparas på den här enheten.\n\nTryck på B för att stoppa temaredigeraren.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "Temaredigeraren stoppades. Se theme_server.txt i PortMaster-mappen.", "{theme_name} (Selected)": "{theme_name} (Vald)", "Download": "Hämta", "{item_name} (Selected)": "{item_name} (Vald)", + "Please wait.": "Vänta.", "Up to date already": "Redan uppdaterad", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Du kommer byta från {old_release_channel} till {new_release_channel}.\n\nVill du nedgradera?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Det finns en ny version av PortMaster ({portmaster_version})\n\nVill du uppgradera?", diff --git a/PortMaster/pylibs/locales/sv_SE.themes.json b/PortMaster/pylibs/locales/sv_SE.themes.json index 96af26a6..bb9e3a24 100644 --- a/PortMaster/pylibs/locales/sv_SE.themes.json +++ b/PortMaster/pylibs/locales/sv_SE.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Genrer: {port_info.genres}\n• Hämtningsstorlek: {port_info.download_size}{if:port_info.runtime}\n• Körtid: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Beskrivning: {port_info.description}\n• Instruktioner:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Skapare: {theme_info.creator}\n• Status: {theme_info.status}\n• Beskrivning:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Namn: {runtime_info.name}\nStatus: {runtime_info.status}\nPortar: {runtime_info.ports}\nHämtningsstorlek: {runtime_info.download_size}\nInstalleringsstorlek: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Namn: {runtime_info.name}\nStatus: {runtime_info.status}\nPortar: {runtime_info.ports}\nHämtningsstorlek: {runtime_info.download_size}\nInstalleringsstorlek: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} spel", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Genre: {port_info.genres}\nStorlek: {port_info.download_size}{if:port_info.runtime}\nKörtid: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nPortutvecklare: {port_info.porter}\nGenre: {port_info.genres}\nHämtning: {port_info.download_size}{if:port_info.runtime}\nKörtid: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} ledigt", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}av {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Status: {runtime_info.status}\nAnvänds: {runtime_info.in_use}\nHämtning: {runtime_info.download_size}{if:runtime_info.disk_size}\nPå disken: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nAnvänds av: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/uk_UA.json b/PortMaster/pylibs/locales/uk_UA.json index c8dce6ae..9f9b3a7e 100644 --- a/PortMaster/pylibs/locales/uk_UA.json +++ b/PortMaster/pylibs/locales/uk_UA.json @@ -1,24 +1,43 @@ { + "This is not a valid zip file.": "Це не дійсний zip-файл.", + "illegal file {file_name}": "недозволений файл {file_name}", + "the port has no directory": "порт не має теки", + "the port has no launch script": "порт не має скрипта запуску", + "unable to load {file_name}": "не вдалося завантажити {file_name}", "Loading...": "Завантаження...", "Loading Sources.": "Завантаження джерел.", "Download validation failed.": "Помилка при перевірці завантаження.", + "Unable to download file.": "Не вдалося завантажити файл.", "Loading Info.": "Завантаження інформації.", + "Fetching latest ports info data.": "Отримання найновішої інформації про порти.", + "Fetching latest porters info.": "Отримання найновішої інформації про портувальників.", + "Fetching latest port stats.": "Отримання найновішої статистики портів.", "Fetching latest featured ports.": "Отримання останніх рекомендованих портів.", + "Fetching latest featured port images zip.": "Отримання найновішого zip із зображеннями рекомендованих портів.", + "Fetching featured port image {file_name}": "Отримання зображення рекомендованого порту {file_name}", "Installing Theme {download_name}.": "Встановлення теми {download_name}.", "Installing": "Встановлення", "Theme {download_name!r} installed successfully.": "Тема {download_name!r} успішно встановлена.", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip пошкоджено, завантажте його ще раз.", + "PortMaster failed to install.": "Не вдалося встановити PortMaster.", "Installing {download_name}.": "Встановлення {download_name}.", + "PortMaster installed successfully.": "PortMaster успішно встановлено.", "Installation failed, removing files...": "Помилка встановлення, видалення файлів...", + "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "ПРИВІТ.\n\nСХОЖЕ, ВИ ВРУЧНУ ВСТАНОВЛЮЄТЕ ПОРТ, НЕСУМІСНИЙ З ВАШИМ ПРИСТРОЄМ.\n\nЯКЩО ВИНИКНУТЬ ПРОБЛЕМИ, НЕ ПОВІДОМЛЯЙТЕ ПРО НИХ У DISCORD, ІНАКШЕ З ВАС ПОСМІЮТЬСЯ.\n\nВИ ВПЕВНЕНІ, ЩО ХОЧЕТЕ ПРОДОВЖИТИ ВСТАНОВЛЕННЯ?", "Okay": "Гаразд", "Cancel": "Скасувати", + "Not compatible with this device.": "Несумісно з цим пристроєм.", + "Installation failed: {reason}": "Помилка встановлення: {reason}", "Port {download_name} failed to install.": "Не вдалося встановити порт {download_name}.", "Port {download_name!r} and {runtime_name!r} installed successfully.": "Порт {download_name!r} та {runtime_name!r} успішно встановлено.", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "Порт {download_name!r} успішно встановлено, але встановити {runtime_name!r} не вдалося!\n\nСпробуйте перевстановити, або зверніться до вікі за допомогою.", "Port {download_name!r} installed successfully.": "Порт {download_name!r} успішно встановлено.", "Unable to download in offline mode.": "Не вдається завантажити в офлайн режимі.", + "File not found.": "Файл не знайдено.", "Unable to find a source for {port_name}": "Не вдалося знайти джерело для {port_name}", "Unknown port {port_name}": "Невідомий порт {port_name}", "Removing {port_name}": "Видалення {port_name}", + "Error uninstalling {port_name}\n\n{error}": "Помилка під час видалення {port_name}\n\n{error}", "Successfully uninstalled {port_name}": "Успішно видалено {port_name}", "Loading Ports.": "Завантаження портів.", "Port {runtime} contains a bad runtime, game may not run correctly.": "Порт {runtime} містить неправильний Runtime, гра може працювати некоректно.", @@ -65,16 +84,32 @@ "Uninstalling {port_name}": "Видалення {port_name}", "Updating all port sources:": "Оновлення всіх джерел порту:", "Checking {runtime_name}": "Перевірка {runtime_name}", + "Auto-installation": "Автоматичне встановлення", + "PortMaster website": "Сайт PortMaster", + "{store}: developer": "{store}: розробник", "** NO PORT **": "** НЕМАЄ ПОРТУ **", "Ready to Run": "Готовий до запуску", "Requires Files": "Необхідні файли", "Installed": "Встановлено", "Missing": "Відсутній", + "Installation failed, see log.txt for details.": "Помилка встановлення, подробиці в log.txt.", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "Він для іншої архітектури ({zip_arch}, цей пристрій {device_arch}), його видалено.", + "Installed {download_name}.": "{download_name} встановлено.", + "- SUCCESS: {name}": "- УСПІХ: {name}", + "- FAILURE: {name}": "- ПОМИЛКА: {name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "Порти було додано або видалено, оновіть список ігор, щоб побачити нові ігри або видалити деінстальовані.", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "Деякі файли не вдалося встановити, виберіть їх, щоб побачити причину. Вони залишилися в теці автовстановлення.", + "Everything installed successfully.": "Усе успішно встановлено.", + "{installed} installed, {failed} failed": "{installed} встановлено, {failed} з помилками", + "OK: {name}": "OK: {name}", + "{name} installed successfully.": "{name} успішно встановлено.", + "FAILED: {name}": "ПОМИЛКА: {name}", + "{name} failed to install:\n\n{reason}": "Не вдалося встановити {name}:\n\n{reason}", "Danish": "Данська", "German": "Німецька", "English": "Англійська", "Spanish": "Іспанська", + "Esperanto": "Есперанто", "Finnish": "Фінська", "French": "Французька", "Italian": "Італійська", @@ -84,6 +119,7 @@ "Polish": "Польська", "Portuguese (Portugal)": "Португальська (Португалія)", "Portuguese (Brazil)": "Португальська (Бразилія)", + "Romanian": "Румунська", "Russian": "російська", "Swedish": "Шведська", "Ukrainian": "Українська", @@ -98,6 +134,7 @@ "Alphabetical": "За абеткою", "Recently Added": "Нещодавно додані", "Recently Updated": "Нещодавно оновлені", + "Total Downloads": "Усього завантажень", "Action": "Бойовик", "Adventure": "Пригоди", "Arcade": "Аркада", @@ -124,6 +161,8 @@ "ARM 32bit": "ARM 32-біт", "ARM 64bit": "ARM 64-біт", "x86 64bit": "x86 64-біт", + "Ascending": "За зростанням", + "Descending": "За спаданням", "Sort:": "Сортувати:", "Clear Filters": "Скинути фільтри", "Genres:": "Жанри:", @@ -163,6 +202,8 @@ "Select a theme for PortMaster.": "Оберіть тему для PortMaster.", "Select Color Scheme": "Оберіть кольорову палітру", "Select a color scheme for PortMaster.": "Оберіть кольорову палітру для PortMaster.", + "Theme Editor": "Редактор тем", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "Переглядайте теми та редагуйте переклади PortMaster з комп'ютера або телефона в тій самій мережі.", "Audio": "Аудіо", "Music: ": "Музика: ", "Enable or Disable background music in PortMaster.": "Увімкнення чи вимкнення фонової музики в PortMaster.", @@ -203,6 +244,8 @@ "Show all ports, ignoring requirements.": "Показати всі порти, ігноруючи вимоги.", "CWTBE Mode: ": "Режим CWTBE: ", "Enable gptokeyb2 by default.": "Увімкнути gptokeyb2 за замовчуванням.", + "Debug Mode: ": "Режим налагодження: ", + "Enable debug logging all the time.": "Завжди вмикати журнал налагодження.", "Delete PortMaster Config": "Видалити конфігурацію PortMaster", "This can break stuff, don't touch unless you know what you are doing.": "Це може щось зламати, не чіпайте, якщо ви не знаєте, що робите.", "Delete PortMaster Runtimes": "Видалити Runtime PortMaster", @@ -223,9 +266,11 @@ "Reinstall": "Перевстановити", "Uninstall": "Видалити", "Install": "Встановити", + "Links": "Посилання", "Port Info": "Відомості про порт", "Show Info": "Показати відомості", "Are you sure you want to uninstall {port_name}?": "Ви впевнені, що бажаєте видалити {port_name}?", + "No links for this port.": "Для цього порту немає посилань.", "NO PORTS": "НЕМАЄ ПОРТІВ", "** NO PORTS FOUND **": "** ПОРТИ НЕ ЗНАЙДЕНО **", "Download ports first.": "Спершу звантажте порти.", @@ -238,11 +283,29 @@ "Check": "Перевірити", "Are you sure you want to download and verify all runtimes?": "Ви впевнені, що хочете завантажити та перевірити всі Runtime?", "Deleted runtime {runtime}": "Видалено Runtime {runtime}", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "Для найкращої роботи перейдіть на підтримувану прошивку, наприклад muOS або KNULLI.", + "SpruceOS is not supported by PortMaster.": "SpruceOS не підтримується PortMaster.", + "CrossMix-OS is not supported by PortMaster.": "CrossMix-OS не підтримується PortMaster.", + "NextUI is not supported by PortMaster.": "NextUI не підтримується PortMaster.", + "Your OS is not supported by PortMaster.": "Ваша ОС не підтримується PortMaster.", + "Your device has less than 1GB of RAM.": "Ваш пристрій має менше 1 ГБ оперативної пам'яті.", "Disclaimer": "Відмова від відповідальності", + "Wait {seconds} seconds": "Зачекайте {seconds} с", "Accept": "Прийняти", + "Warning": "Попередження", + "Many ports will not work.": "Багато портів не працюватимуть.", + "I Understand": "Зрозуміло", + "Starting the theme editor...": "Запуск редактора тем...", + "Stop": "Зупинити", + "it stopped": "він зупинився", + "Unable to start the theme editor: {error}": "Не вдалося запустити редактор тем: {error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "Редактор тем працює, але цей пристрій не підключено до мережі. Підключіть його до Wi-Fi і знову запустіть редактор тем.", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "На комп'ютері або телефоні в тій самій мережі відскануйте код або відкрийте:\n\n{url}\n\nПереглядайте теми з будь-яким розміром екрана та редагуйте переклади PortMaster: їх буде збережено на цьому пристрої.\n\nНатисніть B, щоб зупинити редактор тем.", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "Редактор тем зупинився. Див. theme_server.txt у теці PortMaster.", "{theme_name} (Selected)": "{theme_name} (Обрано)", "Download": "Завантажити", "{item_name} (Selected)": "{item_name} (Обрано)", + "Please wait.": "Будь ласка, зачекайте.", "Up to date already": "Вже оновлено", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "Ви переходите з {old_release_channel} на {new_release_channel} версію.\n\nХочете знизити версію?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "Доступна нова версія PortMaster ({portmaster_version})\n\nХочете оновити?", diff --git a/PortMaster/pylibs/locales/uk_UA.themes.json b/PortMaster/pylibs/locales/uk_UA.themes.json index 747e567e..1c0a4a50 100644 --- a/PortMaster/pylibs/locales/uk_UA.themes.json +++ b/PortMaster/pylibs/locales/uk_UA.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• Жанри: {port_info.genres}\n• Розмір завантаження: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Опис: {port_info.description}\n• Інструкції:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• Автор: {theme_info.creator}\n• Статус: {theme_info.status}\n• Опис:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Назва: {runtime_info.name}\nСтатус: {runtime_info.status}\nПорти: {runtime_info.ports}\nРозмір завантаження: {runtime_info.download_size}\nРозмір інсталяції: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "Назва: {runtime_info.name}\nСтатус: {runtime_info.status}\nПорти: {runtime_info.ports}\nРозмір завантаження: {runtime_info.download_size}\nРозмір інсталяції: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} ігор", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "Жанр: {port_info.genres}\nРозмір: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\nПортувальник: {port_info.porter}\nЖанр: {port_info.genres}\nЗавантаження: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "{system.free_space} доступно", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}автор {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}Статус: {runtime_info.status}\nВикористовується: {runtime_info.in_use}\nЗавантаження: {runtime_info.download_size}{if:runtime_info.disk_size}\nНа диску: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nВикористовують: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/locales/zh_CN.json b/PortMaster/pylibs/locales/zh_CN.json index ba926937..ac8e566f 100644 --- a/PortMaster/pylibs/locales/zh_CN.json +++ b/PortMaster/pylibs/locales/zh_CN.json @@ -1,22 +1,39 @@ { + "This is not a valid zip file.": "这不是有效的 zip 文件。", + "illegal file {file_name}": "不允许的文件 {file_name}", + "the port has no directory": "移植作品没有目录", + "the port has no launch script": "移植作品没有启动脚本", + "unable to load {file_name}": "无法加载 {file_name}", "Loading...": "加载中…", "Loading Sources.": "正在加载资源。", "Download validation failed.": "下载验证失败。", + "Unable to download file.": "无法下载文件。", "Loading Info.": "正在载入信息...", + "Fetching latest ports info data.": "正在获取最新的移植作品信息。", + "Fetching latest porters info.": "正在获取最新的移植人员信息。", + "Fetching latest port stats.": "正在获取最新的移植作品统计。", "Fetching latest featured ports.": "正在获取最新的优秀移植作品。", + "Fetching latest featured port images zip.": "正在获取最新的精选移植作品图片压缩包。", + "Fetching featured port image {file_name}": "正在获取精选移植作品图片 {file_name}", "Installing Theme {download_name}.": "正在安装 {download_name} 主题。", "Installing": "正在安装", "Theme {download_name!r} installed successfully.": "主题 {download_name!r} 安装成功。", + "PortMaster.zip is damaged, download it again.": "PortMaster.zip 已损坏,请重新下载。", + "PortMaster failed to install.": "PortMaster 安装失败。", "Installing {download_name}.": "正在安装 {download_name}.", + "PortMaster installed successfully.": "PortMaster 安装成功。", "Installation failed, removing files...": "安装失败,正在移除文件...", "HELLO.\n\nIT LOOKS LIKE YOU'RE MANUALLY INSTALLING A PORT THAT IS INCOMPATIBLE WITH YOUR DEVICE.\n\nIF YOU HAVE ISSUES, DO NOT REPORT THEM ON DISCORD, YOU WILL BE SUBJECT TO RIDICULE.\n\nARE YOU SURE YOU WANT TO RESUME INSTALLING?": "你好!\n\n你正在人工下载一个与此设备不适配的移植软件!\n\n如果你遇到了问题,请勿将其报告到Discord,你会被狠狠笑话!!\n\n你确定要继续安装吗?", "Okay": "确定", "Cancel": "取消", + "Not compatible with this device.": "与此设备不兼容。", + "Installation failed: {reason}": "安装失败:{reason}", "Port {download_name} failed to install.": "移植作品 {download_name} 安装失败。", "Port {download_name!r} and {runtime_name!r} installed successfully.": "移植作品 {download_name!r} 和 {runtime_name!r} 安装成功。", "Port {download_name!r} installed successfully, but {runtime_name!r} failed to install!!\n\nEither reinstall to try again, or check the wiki for help.": "移植作品 {download_name!r} 安装成功,但 {runtime_name!r} 运行时安装失败!\n\n请尝试重新安装,或者查阅wiki以获得帮助。", "Port {download_name!r} installed successfully.": "已成功安装了移植作品 {download_name!r} 。", "Unable to download in offline mode.": "无法在离线模式下下载。", + "File not found.": "未找到文件。", "Unable to find a source for {port_name}": "无法找到{port_name} 的下载资源。", "Unknown port {port_name}": "未知移植作品 {port_name}", "Removing {port_name}": "正在移除{port_name}", @@ -42,6 +59,7 @@ "Downloading runtime {runtime}.": "正在下载运行时{runtime}", "Unable to download {runtime}, game may not run correctly.": "无法下载 {runtime},游戏可能无法正常运行。", "Successfully downloaded {runtime}.": "成功下载 {runtime}。", + "\n\nIn order to do so:\nMENU -> UTILITIES -> Rescan Rom Directory": "\n\n操作方法:\nMENU -> UTILITIES -> Rescan Rom Directory", "Unable to download verification file.": "无法下载验证信息。", "Unable to download non zip files.": "无法下载非zip文件。", "Cache out of date.": "缓存过期。", @@ -66,16 +84,32 @@ "Uninstalling {port_name}": "正在卸载 {port_name}", "Updating all port sources:": "正在更新所有移植作品源:", "Checking {runtime_name}": "正在检查 {runtime_name}", + "Auto-installation": "自动安装", + "PortMaster website": "PortMaster 网站", + "{store}: developer": "{store}:开发者", "** NO PORT **": "** 无移植作品 **", "Ready to Run": "可直接运行", "Requires Files": "需要用户提供文件", "Installed": "已安装", "Missing": "缺失", + "Installation failed, see log.txt for details.": "安装失败,详情请查看 log.txt。", + "It is for the wrong architecture ({zip_arch}, this device is {device_arch}), it was deleted.": "它适用于其他架构({zip_arch},此设备为 {device_arch}),已被删除。", + "Installed {download_name}.": "已安装 {download_name}。", + "- SUCCESS: {name}": "- 成功:{name}", + "- FAILURE: {name}": "- 失败:{name}", "Ports have been added or removed, update your games list to see the new games or remove the uninstalled ones.": "有移植作品被增加或移除了,请刷新您的游戏列表以查看新增的游戏或移除已删除的游戏。", + "Some files failed to install, select them to see why. They were left in the autoinstall directory.": "部分文件安装失败,选择它们可查看原因。它们留在了自动安装目录中。", + "Everything installed successfully.": "全部安装成功。", + "{installed} installed, {failed} failed": "{installed} 个已安装,{failed} 个失败", + "OK: {name}": "成功:{name}", + "{name} installed successfully.": "{name} 安装成功。", + "FAILED: {name}": "失败:{name}", + "{name} failed to install:\n\n{reason}": "{name} 安装失败:\n\n{reason}", "Danish": "丹麦语", "German": "德语", "English": "英语", "Spanish": "西班牙语", + "Esperanto": "世界语", "Finnish": "芬兰语", "French": "法语", "Italian": "意大利语", @@ -100,6 +134,7 @@ "Alphabetical": "按字母顺序", "Recently Added": "最近添加", "Recently Updated": "最近更新", + "Total Downloads": "总下载量", "Action": "动作", "Adventure": "冒险", "Arcade": "街机", @@ -126,10 +161,13 @@ "ARM 32bit": "ARM 32位", "ARM 64bit": "ARM 64位", "x86 64bit": "x86 64位", + "Ascending": "升序", + "Descending": "降序", "Sort:": "排序:", "Clear Filters": "重置筛选", "Genres:": "类型:", "Attributes:": "属性", + "Architecture:": "架构:", "Availability:": "可用性:", "Porters:": "移植人员:", "Delete": "删除", @@ -164,6 +202,8 @@ "Select a theme for PortMaster.": "选择一个PortMaster主题。", "Select Color Scheme": "选择配色方案", "Select a color scheme for PortMaster.": "选择一个PortMaster配色方案。", + "Theme Editor": "主题编辑器", + "Preview themes and edit PortMaster's translations from a computer or phone on the same network.": "在同一网络的电脑或手机上预览主题并编辑 PortMaster 的翻译。", "Audio": "音频:", "Music: ": "音乐: ", "Enable or Disable background music in PortMaster.": "在 PortMaster中启用或禁用背景音乐", @@ -177,6 +217,8 @@ "Metadata Refresh": "元数据重置", "Manually update port metadata with missing/updated information and artwork.": "手动更新缺失或需要更新的移植作品元数据", "Ports Location: ": "移植作品位置: ", + "Roms section": "Roms 分区", + "Ports tab": "移植作品标签页", "Location where ports should be installed to.": "移植作品将安装的位置目录。", "SD 1": "SD 1", "SD 2": "SD 2", @@ -202,6 +244,8 @@ "Show all ports, ignoring requirements.": "显示所有移植作品,忽略系统需求。", "CWTBE Mode: ": "CWTBE 模式: ", "Enable gptokeyb2 by default.": "默认启用gptokeyb2。", + "Debug Mode: ": "调试模式: ", + "Enable debug logging all the time.": "始终启用调试日志。", "Delete PortMaster Config": "删除 PortMaster 配置", "This can break stuff, don't touch unless you know what you are doing.": "如果您不知道此选择的后果,请勿继续,损失自负!", "Delete PortMaster Runtimes": "删除 PortMaster 运行时", @@ -222,9 +266,11 @@ "Reinstall": "重新安装", "Uninstall": "卸载", "Install": "安装", + "Links": "链接", "Port Info": "移植作品信息", "Show Info": "显示详情", "Are you sure you want to uninstall {port_name}?": "您确定要卸载 {port_name} 吗?", + "No links for this port.": "此移植作品没有链接。", "NO PORTS": "无移植作品", "** NO PORTS FOUND **": "** 没有发现移植作品 **", "Download ports first.": "请先下载移植作品。", @@ -237,11 +283,29 @@ "Check": "检查", "Are you sure you want to download and verify all runtimes?": "您确定要下载并验证所有运行时吗?", "Deleted runtime {runtime}": "删除运行时 {runtime}", + "For the best experience, switch to a supported firmware such as muOS or KNULLI.": "为获得最佳体验,请换用受支持的固件,例如 muOS 或 KNULLI。", + "SpruceOS is not supported by PortMaster.": "PortMaster 不支持 SpruceOS。", + "CrossMix-OS is not supported by PortMaster.": "PortMaster 不支持 CrossMix-OS。", + "NextUI is not supported by PortMaster.": "PortMaster 不支持 NextUI。", + "Your OS is not supported by PortMaster.": "PortMaster 不支持你的操作系统。", + "Your device has less than 1GB of RAM.": "你的设备内存少于 1GB。", "Disclaimer": "声明", + "Wait {seconds} seconds": "请等待 {seconds} 秒", "Accept": "同意", + "Warning": "警告", + "Many ports will not work.": "许多移植作品将无法运行。", + "I Understand": "我明白了", + "Starting the theme editor...": "正在启动主题编辑器...", + "Stop": "停止", + "it stopped": "它已停止", + "Unable to start the theme editor: {error}": "无法启动主题编辑器:{error}", + "The theme editor is running, but this device isn't on a network. Connect it to Wi-Fi, then start the theme editor again.": "主题编辑器正在运行,但此设备未连接网络。请连接 Wi-Fi,然后重新启动主题编辑器。", + "On a computer or phone on the same network, scan the code or open:\n\n{url}\n\nPreview themes at any screen size, and edit PortMaster's translations: they are saved on this device.\n\nPress B to stop the theme editor.": "在同一网络的电脑或手机上扫描二维码或打开:\n\n{url}\n\n可在任意屏幕尺寸下预览主题,并编辑 PortMaster 的翻译:它们会保存在此设备上。\n\n按 B 停止主题编辑器。", + "The theme editor stopped. See theme_server.txt in the PortMaster folder.": "主题编辑器已停止。请查看 PortMaster 文件夹中的 theme_server.txt。", "{theme_name} (Selected)": "{theme_name} (已选)", "Download": "下载", "{item_name} (Selected)": "{item_name} (已选)", + "Please wait.": "请稍候。", "Up to date already": "已经更新", "You are switching from the {old_release_channel} to the {new_release_channel} version.\n\nDo you want to downgrade?": "正在从 {old_release_channel} 切换到 {new_release_channel} 版本。\n\n希望退回吗?", "There is a new version of PortMaster ({portmaster_version})\n\nDo you want to upgrade?": "PortMaster有新版本 ({portmaster_version}) 可用\n\n希望升级吗?", diff --git a/PortMaster/pylibs/locales/zh_CN.themes.json b/PortMaster/pylibs/locales/zh_CN.themes.json index 62187b35..3b9faa04 100644 --- a/PortMaster/pylibs/locales/zh_CN.themes.json +++ b/PortMaster/pylibs/locales/zh_CN.themes.json @@ -19,5 +19,11 @@ "• Genres: {port_info.genres}\n• Download Size: {port_info.download_size}{if:port_info.runtime}\n• Runtime: {port_info.runtime} ({port_info.runtime_status}){endif}\n• Description: {port_info.description}\n• Instructions:\n{port_info.instructions}": "• 作品类型: {port_info.genres}\n• 下载大小: {port_info.download_size}{if:port_info.runtime}\n• 运行时: {port_info.runtime} ({port_info.runtime_status}){endif}\n• 描述: {port_info.description}\n• 安装说明:\n{port_info.instructions}", "PM: {system.portmaster_version}, HM: {system.harbourmaster_version}": "PM版本: {system.portmaster_version}, HM版本: {system.harbourmaster_version}", "• Creator: {theme_info.creator}\n• Status: {theme_info.status}\n• Description:\n{theme_info.description}": "• 创作者: {theme_info.creator}\n• 状态: {theme_info.status}\n• 描述:\n{theme_info.description}", - "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "运行时名称: {runtime_info.name}\n状态: {runtime_info.status}\n移植作品: {runtime_info.ports}\n下载大小: {runtime_info.download_size}\n安装大小: {runtime_info.disk_size}" + "Name: {runtime_info.name}\nStatus: {runtime_info.status}\nPorts: {runtime_info.ports}\nDownload Size: {runtime_info.download_size}\nInstall Size: {runtime_info.disk_size}": "运行时名称: {runtime_info.name}\n状态: {runtime_info.status}\n移植作品: {runtime_info.ports}\n下载大小: {runtime_info.download_size}\n安装大小: {runtime_info.disk_size}", + "{ports_list.filter_ports} games": "{ports_list.filter_ports} 个游戏", + "Genre: {port_info.genres}\nSize: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime}{endif}\n\n{port_info.description}": "类型: {port_info.genres}\n大小: {port_info.download_size}{if:port_info.runtime}\n运行时: {port_info.runtime}{endif}\n\n{port_info.description}", + "{port_info.ready_to_run}\n\nPorter: {port_info.porter}\nGenre: {port_info.genres}\nDownload: {port_info.download_size}{if:port_info.runtime}\nRuntime: {port_info.runtime} ({port_info.runtime_status}){endif}": "{port_info.ready_to_run}\n\n移植人员: {port_info.porter}\n类型: {port_info.genres}\n下载: {port_info.download_size}{if:port_info.runtime}\n运行时: {port_info.runtime} ({port_info.runtime_status}){endif}", + "{system.free_space} free": "剩余 {system.free_space}", + "{if:theme_info.creator}by {theme_info.creator} - {endif}{theme_info.status}": "{if:theme_info.creator}作者 {theme_info.creator} - {endif}{theme_info.status}", + "{if:runtime_info.name}Status: {runtime_info.status}\nIn use: {runtime_info.in_use}\nDownload: {runtime_info.download_size}{if:runtime_info.disk_size}\nOn disk: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\nUsed by: {runtime_info.ports}{endif}{endif}": "{if:runtime_info.name}状态: {runtime_info.status}\n使用中: {runtime_info.in_use}\n下载: {runtime_info.download_size}{if:runtime_info.disk_size}\n磁盘占用: {runtime_info.disk_size}{endif}{if:runtime_info.ports}\n\n使用者: {runtime_info.ports}{endif}{endif}" } diff --git a/PortMaster/pylibs/pugwash/scenes/startup.py b/PortMaster/pylibs/pugwash/scenes/startup.py index 63a8fcce..d9e00bc2 100644 --- a/PortMaster/pylibs/pugwash/scenes/startup.py +++ b/PortMaster/pylibs/pugwash/scenes/startup.py @@ -124,7 +124,7 @@ def do_draw(self): if elapsed != self.last_elapsed: if elapsed < self.disclaimer_wait: - self.tags['button_bar'].bar = [f'Wait {self.disclaimer_wait - elapsed} seconds'] + self.tags['button_bar'].bar = [_('Wait {seconds} seconds').format(seconds=self.disclaimer_wait - elapsed)] else: self.set_buttons({'A': _('Accept'), 'B': _('Quit')}) @@ -207,7 +207,7 @@ def do_draw(self): if elapsed != self.last_elapsed: if elapsed < self.warning_wait: - self.tags['button_bar'].bar = [f'Wait {self.warning_wait - elapsed} seconds'] + self.tags['button_bar'].bar = [_('Wait {seconds} seconds').format(seconds=self.warning_wait - elapsed)] else: self.set_buttons({'A': _('I Understand'), 'B': _('Quit')}) diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 8c671899..33d2e57f 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -282,9 +282,16 @@ def theme_load(gui, theme_file, color_scheme=None): theme_data[section_name] = theme_merge(base_scheme.get(section_name, {}), section_data) ## vvvvvvvv HARD CODED - DO NOT TOUCH vvvvvvvv + ## (The same in every theme. Its fonts are the default theme's for each language, as + ## the warnings are translated: DejaVu Sans has no Chinese, Japanese or Korean.) theme_data['disclaimer'] = { "background": { "font": "DejaVuSans.ttf", + "font[zh_TW]": "NotoSansTC-Regular.ttf", + "font[zh_CN]": "NotoSansSC-Regular.ttf", + "font[zh_HK]": "NotoSansHK-Regular.ttf", + "font[ko_KR]": "NotoSansKR-Regular.ttf", + "font[ja_JP]": "NotoSansJP-Regular.ttf", "area": [0.0, 0.0, 1.0, 1.0], "fill": [ 200, 200, 200, 255 ], "border": 8, @@ -293,6 +300,11 @@ def theme_load(gui, theme_file, color_scheme=None): }, "disclaimer_text": { "font": "DejaVuSans.ttf", + "font[zh_TW]": "NotoSansTC-Regular.ttf", + "font[zh_CN]": "NotoSansSC-Regular.ttf", + "font[zh_HK]": "NotoSansHK-Regular.ttf", + "font[ko_KR]": "NotoSansKR-Regular.ttf", + "font[ja_JP]": "NotoSansJP-Regular.ttf", "area": [0.05, 0.05, 0.95, 0.95], "fill": [ 230, 230, 230 ], "thickness": 6, @@ -321,6 +333,11 @@ def theme_load(gui, theme_file, color_scheme=None): }, "button_bar": { "font": "DejaVuSans.ttf", + "font[zh_TW]": "NotoSansTC-Regular.ttf", + "font[zh_CN]": "NotoSansSC-Regular.ttf", + "font[zh_HK]": "NotoSansHK-Regular.ttf", + "font[ko_KR]": "NotoSansKR-Regular.ttf", + "font[ja_JP]": "NotoSansJP-Regular.ttf", "area": [0.0, 0.93, 0.95, -5], "font-scale[1920x1152]": 3.0, "font-scale[1920x1080]": 3.0, @@ -337,7 +354,7 @@ def theme_load(gui, theme_file, color_scheme=None): "font-size": 16, "font-color": [ 0, 0, 0 ], "align": "midright", - "bar": ["Please wait."], + "bar": [_("Please wait.")], }, } ## ^^^^^^^^ HARD CODED - DO NOT TOUCH ^^^^^^^^ From 32d1c61c34a14a1c8e5866e96fda5465885877c0 Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 20:12:59 +0800 Subject: [PATCH 27/28] Tests for theme loading and validation, languages and tools/i18n.py New: test_region_validation.py (every Region._verify_* check), test_theme_loading.py (#base, elements, requirement tags, colour schemes, resources, a theme's #translations, what PortMaster adds), test_lang.py (picking and switching the language, the language list, i18n lookups) and test_i18n_tool.py (extract, check, problems). What they found: - check_lang("") was German: every language "starts with" "", so an empty LANG (or ".UTF-8", "_US") made DEFAULT_LANG German while the screen was English, and choosing German "reset" to the system's. - "font-scale": 2 or "line-height": 1 (whole numbers) were "not an float"; a theme's "text": 5 raised a TypeError (it raised a str) instead of a theme error. - theme_load read #base in its loop, so a #base after the sections (a colour scheme's is added last) never applied; an unknown colour scheme fell back to the first one, not the theme's default-scheme; and a section with a requirement ("main_menu[640x480]") merged each region with the whole old section and dropped the rest. No shipped theme hit these: every render of the device's 10 themes is the same. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- PortMaster/pylibs/pugwash/lang.py | 7 +- PortMaster/pylibs/pugwash/sdl/region.py | 13 +- PortMaster/pylibs/pugwash/theme.py | 16 +- tests/test_i18n_tool.py | 219 +++++++++++++++ tests/test_lang.py | 232 ++++++++++++++++ tests/test_region_validation.py | 216 +++++++++++++++ tests/test_theme_loading.py | 346 ++++++++++++++++++++++++ 7 files changed, 1038 insertions(+), 11 deletions(-) create mode 100644 tests/test_i18n_tool.py create mode 100644 tests/test_lang.py create mode 100644 tests/test_region_validation.py create mode 100644 tests/test_theme_loading.py diff --git a/PortMaster/pylibs/pugwash/lang.py b/PortMaster/pylibs/pugwash/lang.py index 5615dd21..bc2a081e 100644 --- a/PortMaster/pylibs/pugwash/lang.py +++ b/PortMaster/pylibs/pugwash/lang.py @@ -52,9 +52,11 @@ def check_lang(lang): We then check all languages on the list of languages. """ - if lang is None: + ## Nothing left of it ("", ".UTF-8", "_US") would match every language: the first one won. + if not isinstance(lang, str) or not lang.strip(): return None + lang = lang.strip() if i18n.language_file(LANG_DIR, lang).is_file(): return lang @@ -67,6 +69,9 @@ def check_lang(lang): if '_' in lang: lang = lang.split('_', 1)[0] + if not lang: + return None + for lang_opt in ALL_LANGUAGES: if i18n.language_file(LANG_DIR, lang_opt).is_file() and lang_opt.startswith(lang): return lang_opt diff --git a/PortMaster/pylibs/pugwash/sdl/region.py b/PortMaster/pylibs/pugwash/sdl/region.py index c6fe63ba..d9eeeea1 100644 --- a/PortMaster/pylibs/pugwash/sdl/region.py +++ b/PortMaster/pylibs/pugwash/sdl/region.py @@ -1469,9 +1469,11 @@ def _verify_float(self, name, default=0, optional=False, minimum=None, maximum=N if val is None and optional: return None - if not isinstance(val, float): - raise GUIThemeError(f'{name} is not an float') - #print(f'{name}: {val}') + ## A whole number is a float too: "font-scale": 2 is as good as 2.0. + if isinstance(val, bool) or not isinstance(val, (int, float)): + raise GUIThemeError(f'{name} is not a number') + + val = float(val) if minimum is not None and val < minimum: val = minimum @@ -1541,10 +1543,9 @@ def _verify_text(self, name, default=None, optional=False): if val is None and optional: return None if isinstance(val, str): - #print(f'{name}: {val}') return val - else: - raise(f'{name} is not text') + + raise GUIThemeError(f'{name} is not text') def _verify_list(self, name, default=None, optional=False, allow_null=False): 'verify that value of self._dict[name] is valid list' diff --git a/PortMaster/pylibs/pugwash/theme.py b/PortMaster/pylibs/pugwash/theme.py index 33d2e57f..24d29fc2 100644 --- a/PortMaster/pylibs/pugwash/theme.py +++ b/PortMaster/pylibs/pugwash/theme.py @@ -257,7 +257,8 @@ def theme_load(gui, theme_file, color_scheme=None): for scheme_name in theme_data.get("#schemes", {}) if not scheme_name.startswith("#")] - if color_scheme is None: + ## One it doesn't have (renamed in an update, say): its default, then its first. + if color_scheme not in all_schemes: color_scheme = theme_data.get("#info", {}).get("default-scheme", None) if color_scheme not in all_schemes: @@ -360,11 +361,13 @@ def theme_load(gui, theme_file, color_scheme=None): ## ^^^^^^^^ HARD CODED - DO NOT TOUCH ^^^^^^^^ + ## Before any section, wherever it is in the file (a colour scheme's #base is added last). + base_data = theme_data.get("#base", {}) + for section_name, section_data in theme_data.items(): if section_name.startswith('#'): if section_name == "#base": - logger.debug("- loading base_data") - base_data = section_data + pass elif section_name == "#resources": logger.debug("- loading resources:") @@ -436,8 +439,13 @@ def theme_load(gui, theme_file, color_scheme=None): logger.error(f"{section_name} not yet defined, skipping.") continue + ## Changes the regions it has, the section keeps the rest ("main_menu[640x480]"). logger.debug(f" - loading section {section_name}") - sections[section_name] = theme_apply(gui, section_data, sections[section_name], elements) + merged = dict(sections[section_name]) + for region_name, region_data in theme_apply(gui, section_data, {}, elements).items(): + merged[region_name] = theme_merge(merged.get(region_name, {} if region_name.startswith('#') else base_data), region_data) + + sections[section_name] = merged else: logger.debug(f" - loading section {section_name}") sections[section_name] = theme_apply(gui, section_data, base_data, elements) diff --git a/tests/test_i18n_tool.py b/tests/test_i18n_tool.py new file mode 100644 index 00000000..b74d0d98 --- /dev/null +++ b/tests/test_i18n_tool.py @@ -0,0 +1,219 @@ +# SPDX-License-Identifier: MIT +# +# tools/i18n.py (theme_server/translations.py): finding the translatable strings in the code +# and the themes, keeping strings.json up to date, and checking the language files. + +import json +import shutil + +import pytest + +from conftest import PM_DIR + +from theme_server import translations as tool + + +@pytest.fixture +def locales(tmp_path, monkeypatch): + """A copy of the locales folder the tool works on: the checkout's is never written.""" + locales = tmp_path / "locales" + shutil.copytree(str(PM_DIR / "pylibs" / "locales"), str(locales)) + monkeypatch.setattr(tool, "LOCALES_DIR", locales) + return locales + + +def write_code(path, text): + path.write_text(text, encoding="utf-8") + return path + + +################################################################################ +## Finding strings in code +def test_code_strings(tmp_path): + code = write_code(tmp_path / "a.py", '\n'.join([ + 'from harbourmaster.i18n import _', + 'x = _("Hello")', + 'y = _("Hello")', + 'z = _("Port {name}").format(name="x")', + 'w = _(f"no {x}")', ## f-strings can't be looked up: skipped + 'v = _(name)', ## a variable: skipped + 'u = other("Not this")', + 't = i18n._("Not this either")', + 's = _("")', ## empty: nothing to translate + 'r = _("Two\\nlines")', + ])) + + found = tool.code_strings([code]) + + assert list(found) == ["Hello", "Port {name}", "Two\nlines"] + assert [where.rsplit(":", 1)[1] for where in found["Hello"]] == ["2", "3"] + + +def test_code_strings_in_file_order(tmp_path): + a = write_code(tmp_path / "a.py", '_("B")\n_("A")\n') + b = write_code(tmp_path / "b.py", '_("C")\n_("A")\n') + + found = tool.code_strings([a, b]) + + assert list(found) == ["B", "A", "C"] + assert len(found["A"]) == 2 + + +def test_every_code_file_is_scanned(): + files = tool.code_files() + names = {path.name for path in files} + + assert {"pugwash", "harbourmaster", "options.py", "hm_install.py", "i18n.py"} <= names + assert all(path.is_file() for path in files) + + +################################################################################ +## Finding strings in themes +def test_theme_strings(tmp_path): + theme = tmp_path / "my_theme" + theme.mkdir() + (theme / "theme.json").write_text(json.dumps({ + "main_menu": { + "title": {"text": "Welcome"}, + "status": {"text": "{system.battery_level}"}, ## only a tag + "colour": {"text": "#ff00aa"}, ## a colour + "blank": {"text": " "}, + "info": {"text[640x480]": "Space: {system.free_space}", "text": "Space"}, + "list": {"options": [{"text": "In a list"}]}, + "not_text": {"title": "Not a text key"}, + }, + }, indent=2), encoding="utf-8") + + found = tool.theme_strings(theme) + + assert set(found) == {"Welcome", "Space: {system.free_space}", "Space", "In a list"} + ## Where: the theme's name and the line. + assert found["Welcome"][0].startswith("my_theme/theme.json:") and int(found["Welcome"][0].rsplit(":", 1)[1]) > 1 + + +def test_default_theme_strings_are_in_strings_themes_json(): + found = tool.theme_strings(PM_DIR / "pylibs" / "default_theme", "default_theme") + assert set(found) <= set(tool.read_strings()["themes"]) + + +################################################################################ +## extract: strings.json from the code and the themes +def test_extract_keeps_themes_not_scanned(locales, tmp_path): + strings = tool.read_strings() + strings["themes"]["Only in an old theme"] = ["old_theme/theme.json:3"] + tool.write_json(tool.domain_file("strings", "themes"), strings["themes"]) + + extracted = tool.extract() + + assert extracted["themes"]["Only in an old theme"] == ["old_theme/theme.json:3"] + assert extracted["messages"] == tool.code_strings() + + +def test_extract_rescans_the_themes_it_is_given(locales, tmp_path): + themes = tmp_path / "themes" + (themes / "old_theme").mkdir(parents=True) + (themes / "old_theme" / "theme.json").write_text(json.dumps({"s": {"r": {"text": "New text"}}})) + strings = tool.read_strings() + strings["themes"]["Gone from old_theme"] = ["old_theme/theme.json:3"] + tool.write_json(tool.domain_file("strings", "themes"), strings["themes"]) + + extracted = tool.extract([themes]) + + assert "Gone from old_theme" not in extracted["themes"] + assert extracted["themes"]["New text"] == ["old_theme/theme.json:1"] + + +def test_main_extract_writes_strings_json(locales, capsys): + (locales / "strings.json").write_text("{}") + + assert tool.main(["extract"]) == 0 + + assert json.loads((locales / "strings.json").read_text(encoding="utf-8")) == tool.code_strings() + assert "code strings" in capsys.readouterr().out + + +def test_strings_json_is_up_to_date(): + ## tools/i18n.py extract has been run: the checkout's strings.json is the code's. + assert list(tool.read_strings()["messages"]) == list(tool.code_strings()) + + +################################################################################ +## check: the language files +def test_check_is_clean(): + ## Every shipped translation keeps the English's placeholders, line breaks and spaces. + assert tool.check() == 0 + + +def test_check_reports(locales, capsys): + de = tool.read_translations("de_DE")["messages"] + de["Not a string any more"] = "Nicht mehr" + de["Port {download_name} failed to install."] = "Port konnte nicht installiert werden." + de["Music: "] = "Musik:" + tool.write_json(tool.domain_file("de_DE", "messages"), de) + + assert tool.check() == 1 + + out = capsys.readouterr().out + assert "de_DE.json: not used any more: 'Not a string any more'" in out + assert "missing {download_name}" in out + assert "English ends with a space" in out + + +def test_main_check_and_help(locales, capsys): + assert tool.main(["check"]) == 0 + assert tool.main([]) == 2 + assert "extract" in capsys.readouterr().out + + +################################################################################ +## problems: what a translation lost +@pytest.mark.parametrize("source, translation, expected", [ + ("Hello", "Hallo", []), + ("Port {name}", "{name} Port", []), + ("Port {name}", "Port", ["missing {name}"]), + ("Port", "Port {name}", ["unknown {name}"]), + ("Port {a} {b}", "{b} {c}", ["missing {a}", "unknown {c}"]), + ("Music: ", "Musik:", ["English ends with a space, this doesn't"]), + ("Music: ", "Musik: ", []), + (" x", "x", ["English starts with a space, this doesn't"]), + ("\n\nIn order", "Um", ["English starts with a space, this doesn't", "2 line breaks in English, 0 here"]), + ("A\nB", "A B", ["1 line breaks in English, 0 here"]), + ("A B", "A\nB", ["0 line breaks in English, 1 here"]), + ("{if:x}Yes{endif}", "{if:x}Ja{endif}", []), + ("{if:x}Yes{endif}", "Ja", ["missing {endif}, {if:x}"]), + ]) +def test_problems(source, translation, expected): + assert tool.problems(source, translation) == expected + + +################################################################################ +## The files +def test_languages(locales): + (locales / "notalang.json").write_text("{}") + (locales / "xx_XXX.json").write_text("{}") + (locales / "qq_QQ.themes.json").write_text("{}") + + langs = tool.languages() + + assert "notalang" not in langs and "xx_XXX" not in langs + ## A language with only its themes file is still a language. + assert "qq_QQ" in langs and "de_DE" in langs and "en_US" in langs + + +def test_domain_files(): + assert tool.domain_file("strings", "messages").name == "strings.json" + assert tool.domain_file("strings", "themes").name == "strings.themes.json" + assert tool.domain_file("de_DE", "themes").name == "de_DE.themes.json" + + +@pytest.mark.parametrize("contents", ["", "[1]", "{ bad", "null"]) +def test_read_json_bad(tmp_path, contents): + (tmp_path / "x.json").write_text(contents) + assert tool.read_json(tmp_path / "x.json") == {} + assert tool.read_json(tmp_path / "missing.json") == {} + + +def test_write_json_keeps_unicode(tmp_path): + tool.write_json(tmp_path / "x.json", {"Options": "オプション"}) + text = (tmp_path / "x.json").read_text(encoding="utf-8") + assert "オプション" in text and text.endswith("\n") diff --git a/tests/test_lang.py b/tests/test_lang.py new file mode 100644 index 00000000..564df174 --- /dev/null +++ b/tests/test_lang.py @@ -0,0 +1,232 @@ +# SPDX-License-Identifier: MIT +# +# Picking the language (pugwash.lang): the system's from the environment, the one chosen in +# Options from config.json, switching at runtime, and the list of languages to choose from. +# Plus harbourmaster.i18n's lookups: code strings, theme text and a theme's own translations. + +import json +import os + +import pytest + +from harbourmaster import i18n + + +@pytest.fixture +def lang(pugwash, monkeypatch, tmp_path): + """pugwash.lang, put back as it was (module state, the environment, the translations).""" + from pugwash import lang + + for key in ('LANG', 'LANGUAGE', 'LC_ALL', 'LC_MESSAGES'): + monkeypatch.delenv(key, raising=False) + for name in ('DEFAULT_LANG', 'CURRENT_LANG', 'ENVIRON_KEY'): + monkeypatch.setattr(lang, name, getattr(lang, name)) + + ## load_lang() reads the language chosen in Options from here. + import harbourmaster + tools = tmp_path / "tools" + (tools / "PortMaster" / "config").mkdir(parents=True) + monkeypatch.setattr(harbourmaster, "HM_TOOLS_DIR", tools) + monkeypatch.setattr(lang, "config_file", tools / "PortMaster" / "config" / "config.json", raising=False) + + yield lang + + i18n.load_language(lang.LANG_DIR, "en_US") + i18n.set_theme_translations({}) + + +def load(lang, config=None, **environ): + if config is not None: + lang.config_file.write_text(json.dumps(config) if not isinstance(config, str) else config) + for key, value in environ.items(): + os.environ[key] = value + lang.load_lang() + return lang.DEFAULT_LANG, lang.CURRENT_LANG + + +################################################################################ +## check_lang: a language PortMaster has, from whatever the system says +@pytest.mark.parametrize("value, expected", [ + ("de_DE", "de_DE"), + ("de_DE.UTF-8", "de_DE"), + ("de_DE.utf8", "de_DE"), + (" de_DE ", "de_DE"), + ("de", "de_DE"), + ("de_AT", "de_DE"), + ("en_AU.UTF-8", "en_US"), + ("pt", "pt_BR"), ## pt_PT has no translation, so the next pt_* + ("zh_CN", "zh_CN"), + ("ja_JP.eucJP", "ja_JP"), + ]) +def test_check_lang(lang, value, expected): + assert lang.check_lang(value) == expected + + +@pytest.mark.parametrize("value", [ + None, "", " ", ".UTF-8", "_US", "_", ".", "C", "C.UTF-8", "POSIX", "zz_ZZ", "xx", 5, ["de_DE"]]) +def test_check_lang_nothing(lang, value): + ## "" (some CFWs set LANG to it) matched the first language there is: German. + assert lang.check_lang(value) is None + + +################################################################################ +## load_lang: config.json's language, else the system's, else English +def test_system_language(lang): + assert load(lang, LANG="fr_FR.UTF-8") == ("fr_FR", "fr_FR") + assert os.environ['LANG'] == "fr_FR" + + +def test_chosen_language_wins(lang): + assert load(lang, {"language": "ja_JP"}, LANG="fr_FR.UTF-8") == ("fr_FR", "ja_JP") + + +@pytest.mark.parametrize("config", [{"language": "zz_ZZ"}, {"language": ""}, {"language": None}, {}, "{ not json", "null"]) +def test_bad_chosen_language_is_the_systems(lang, config): + assert load(lang, config, LANG="de_DE") == ("de_DE", "de_DE") + + +@pytest.mark.parametrize("value", ["", "C", "POSIX", "zz_ZZ"]) +def test_no_usable_system_language_is_english(lang, value): + ## DEFAULT_LANG too: with LANG="" it was German, so choosing German "reset" to English. + assert load(lang, LANG=value) == ("en_US", "en_US") + + +def test_no_language_variable_at_all(lang): + assert load(lang) == ("en_US", "en_US") + assert lang.ENVIRON_KEY == "LANG" and os.environ["LANG"] == "en_US" + + +def test_first_language_variable_is_used(lang): + ## LANG first: the others are dropped, so ports PortMaster starts see one language. + assert load(lang, LANG="it_IT", LANGUAGE="de_DE", LC_ALL="fr_FR") == ("it_IT", "it_IT") + assert "LANGUAGE" not in os.environ and "LC_ALL" not in os.environ + + +def test_other_language_variable(lang): + assert load(lang, LC_ALL="ru_RU.UTF-8") == ("ru_RU", "ru_RU") + assert lang.ENVIRON_KEY == "LC_ALL" and os.environ["LC_ALL"] == "ru_RU" + + +################################################################################ +## set_language: switching while PortMaster runs +def test_set_language(lang): + load(lang, LANG="en_US") + + assert lang.set_language("de_DE") == "de_DE" + assert lang.CURRENT_LANG == "de_DE" and os.environ[lang.ENVIRON_KEY] == "de_DE" + assert i18n._("Options") == "Optionen" + + ## None, or one PortMaster hasn't: the system's. + for value in (None, "zz_ZZ", ""): + assert lang.set_language(value) == "en_US" + assert i18n._("Options") == "Options" + + +def test_set_language_simplifies(lang): + load(lang, LANG="en_US") + assert lang.set_language("ja_JP.UTF-8") == "ja_JP" + assert i18n._("Options") == "オプション" + + +################################################################################ +## The language list +def test_lang_list_has_the_languages_with_a_translation(lang): + load(lang, LANG="en_US") + listed = lang.lang_list() + + files = {path.name[:-5] for path in lang.LANG_DIR.glob("??_??.json")} + assert set(listed) == files + ## Sorted by name, in English. + assert list(listed.values()) == sorted(listed.values()) + assert listed["de_DE"] == "German" and listed["ja_JP"] == "Japanese" + + +def test_lang_list_in_the_current_language(lang): + load(lang, LANG="en_US") + lang.set_language("de_DE") + listed = lang.lang_list() + + assert listed["de_DE"] == "Deutsch" and listed["en_US"] == "Englisch" + assert list(listed.values()) == sorted(listed.values()) + + +def test_every_translation_is_a_listed_language(lang): + ## A language file without an ALL_LANGUAGES entry could never be chosen. + files = {path.name[:-5] for path in lang.LANG_DIR.glob("??_??.json")} + assert files <= set(lang.ALL_LANGUAGES) + + +def test_every_language_names_every_language(lang): + ## The list is in the current language: each one has a name for all of them. + load(lang, LANG="en_US") + files = sorted(path.name[:-5] for path in lang.LANG_DIR.glob("??_??.json")) + english = {code: name for code, name in lang.ALL_LANGUAGES.items() if code in files} + + for code in files: + if code == "en_US": + continue + lang.set_language(code) + names = lang.lang_list() + untranslated = [name for listed, name in names.items() if name == english[listed]] + assert untranslated == [], f"{code}: {untranslated}" + + +################################################################################ +## harbourmaster.i18n lookups +@pytest.fixture +def locales(tmp_path): + (tmp_path / "xx_XX.json").write_text(json.dumps({"Options": "Opzioni", "Empty": "", "Bad": 5})) + (tmp_path / "xx_XX.themes.json").write_text(json.dumps({"Genres:": "Generi:"})) + yield tmp_path + i18n.load_language(tmp_path.parent, "en_US") + i18n.set_theme_translations({}) + + +def test_i18n_lookups(locales): + i18n.load_language(locales, "xx_XX") + + assert i18n.LANGUAGE == "xx_XX" + assert i18n._("Options") == "Opzioni" + ## Empty or not a string: the English. + assert i18n._("Empty") == "Empty" and i18n._("Bad") == "Bad" + assert i18n.theme_text("Genres:") == "Generi:" + ## Code and theme strings are separate. + assert i18n.theme_text("Options") == "Options" and i18n._("Genres:") == "Genres:" + + +def test_theme_translations_come_first(locales): + i18n.load_language(locales, "xx_XX") + i18n.set_theme_translations({"xx_XX": {"Genres:": "Tipi:", "Empty": ""}, "yy_YY": {"Genres:": "Nope"}}) + + assert i18n.theme_text("Genres:") == "Tipi:" + assert i18n.theme_text("Empty") == "Empty" + + ## A language switch keeps the theme's, for the new language. + i18n.load_language(locales, "yy_YY") + i18n.set_theme_translations({"xx_XX": {"Genres:": "Tipi:"}, "yy_YY": {"Genres:": "Nope"}}) + assert i18n.theme_text("Genres:") == "Nope" + + +@pytest.mark.parametrize("translations", [None, [], "x", {"xx_XX": "not a dict"}, {"xx_XX": {"a": 5, 5: "b"}}]) +def test_bad_theme_translations(locales, translations): + ## From a theme's "#translations": whatever is in there. + i18n.load_language(locales, "xx_XX") + i18n.set_theme_translations(translations) + assert i18n.theme_text("Genres:") == "Generi:" and i18n.theme_text("a") == "a" + + +@pytest.mark.parametrize("contents", ["[]", '"x"', "null", "{ not json", "\xff\xfe"]) +def test_bad_language_file(locales, contents): + (locales / "zz_ZZ.json").write_bytes(contents.encode("latin-1")) + i18n.load_language(locales, "zz_ZZ") + assert i18n._("Options") == "Options" + + +def test_missing_language_file(locales): + i18n.load_language(locales, "qq_QQ") + assert i18n.LANGUAGE == "qq_QQ" and i18n._("Options") == "Options" + + +def test_language_file_names(tmp_path): + assert i18n.language_file(tmp_path, "de_DE").name == "de_DE.json" + assert i18n.language_file(tmp_path, "de_DE", "themes").name == "de_DE.themes.json" diff --git a/tests/test_region_validation.py b/tests/test_region_validation.py new file mode 100644 index 00000000..231abfa2 --- /dev/null +++ b/tests/test_region_validation.py @@ -0,0 +1,216 @@ +# SPDX-License-Identifier: MIT +# +# Theme validation: the Region._verify_* checks every value a theme's JSON gives a region +# goes through. Run on a bare Region (no SDL window): a theme author's typo must be a +# GUIThemeError (the theme then falls back), never a crash of some other kind. + +from types import SimpleNamespace + +import pytest + + +pytestmark = pytest.mark.sdl + + +@pytest.fixture +def region(pugwash): + """make(data, pallet=None, files=()) -> a Region with only what the checks use.""" + from pugwash.sdl.region import Region + from pugwash.sdl import geometry + + def make(data, pallet=None, files=()): + region = Region.__new__(Region) + region._dict = dict(data) + region.pallet = pallet or {} + region.gui = SimpleNamespace(resources=SimpleNamespace(find=lambda name: name if name in files else None)) + region.parent = 'root' + region.name = 'test' + region._rects = geometry.NamedRects((0, 0, 640, 480)) + return region + + return make + + +@pytest.fixture +def ThemeError(pugwash): + from pugwash.sdl.errors import GUIThemeError + return GUIThemeError + + +################################################################################ +## Numbers +@pytest.mark.parametrize("value, expected", [(0, 0), (18, 18), (-4, -4)]) +def test_int(region, value, expected): + assert region({'v': value})._verify_int('v') == expected + + +@pytest.mark.parametrize("value", ["18", 18.5, [18], None, {"a": 1}]) +def test_int_bad(region, ThemeError, value): + with pytest.raises(ThemeError): + region({'v': value})._verify_int('v') + + +def test_int_default_optional_and_clamped(region): + assert region({})._verify_int('v', default=7) == 7 + assert region({})._verify_int('v', default=None, optional=True) is None + assert region({'v': 500})._verify_int('v', maximum=128) == 128 + assert region({'v': -3})._verify_int('v', minimum=0) == 0 + assert region({'v': 3})._verify_int('v', minimum=1, maximum=6) == 3 + + +@pytest.mark.parametrize("value, expected", [(1.5, 1.5), (2, 2.0), (0, 0.0), (-0.5, -0.5)]) +def test_float(region, value, expected): + ## "font-scale": 2 is as good as 2.0 (it was "is not an float"). + result = region({'v': value})._verify_float('v') + assert result == expected and isinstance(result, float) + + +@pytest.mark.parametrize("value", ["1.5", True, False, [1.0], None]) +def test_float_bad(region, ThemeError, value): + with pytest.raises(ThemeError): + region({'v': value})._verify_float('v') + + +def test_float_clamped(region): + assert region({'v': 9})._verify_float('v', maximum=2.5) == 2.5 + assert region({'v': -9})._verify_float('v', minimum=0.1) == 0.1 + assert region({})._verify_float('v', default=None, optional=True) is None + + +@pytest.mark.parametrize("value, count", [([1, 2], 2), ([0, 0, 0, 0], 4), ((5, 6), 2)]) +def test_ints(region, value, count): + assert list(region({'v': value})._verify_ints('v', count)) == list(value) + + +@pytest.mark.parametrize("value, count", [([1], 2), ([1, 2, 3], 2), ([1, "2"], 2), ([1, 2.5], 2), ("12", 2), (12, 2)]) +def test_ints_bad(region, ThemeError, value, count): + with pytest.raises(ThemeError): + region({'v': value})._verify_ints('v', count) + + +################################################################################ +## Booleans, text, options, lists +@pytest.mark.parametrize("value, expected", [(True, True), (False, False), (1, True), (0, False)]) +def test_bool(region, value, expected): + assert region({'v': value})._verify_bool('v') is expected + + +@pytest.mark.parametrize("value", ["true", 2, None, [], "yes"]) +def test_bool_bad(region, ThemeError, value): + with pytest.raises(ThemeError): + region({'v': value})._verify_bool('v', default=None) + + +def test_text(region, ThemeError): + assert region({'v': "Hello {port_info.title}"})._verify_text('v') == "Hello {port_info.title}" + assert region({})._verify_text('v', optional=True) is None + ## A number where text goes: a theme error (it raised a str, so a TypeError). + for value in (5, ["a"], {"a": 1}): + with pytest.raises(ThemeError): + region({'v': value})._verify_text('v') + + +@pytest.mark.parametrize("value, expected", [ + ("center", "center"), + ("sideways", "left"), + (["top", "left"], ["top", "left"]), + (["top", "sideways"], "left"), + (["top"], "left"), + ]) +def test_option(region, value, expected): + ## Anything unknown is the default, not an error. + options = ("left", "center", "right", "top") + assert region({'v': value})._verify_option('v', options, default="left", length=2 if isinstance(value, list) else None) == expected + + +def test_list(region, ThemeError): + assert region({'v': ["a", "b"]})._verify_list('v') == ["a", "b"] + assert region({'v': ["a", None]})._verify_list('v', allow_null=True) == ["a", None] + for value in ("a", 5, ["a", 5], ["a", None]): + with pytest.raises(ThemeError): + region({'v': value})._verify_list('v') + + +################################################################################ +## Colours +@pytest.mark.parametrize("value, expected", [ + ([1, 2, 3], (1, 2, 3, 255)), + ([1, 2, 3, 4], (1, 2, 3, 4)), + ((255, 255, 255), (255, 255, 255, 255)), + ("#ff0000", (255, 0, 0, 255)), + ("#0f08", (0, 255, 0, 136)), + ("accent", (10, 20, 30, 255)), + ("dim", (1, 2, 3, 4)), + ]) +def test_color(region, value, expected): + pallet = {"accent": [10, 20, 30], "dim": "#01020304"} + assert tuple(region({'v': value}, pallet=pallet)._verify_color('v')) == expected + + +@pytest.mark.parametrize("value", [ + "red", ## not in the pallet, not a #code + [1, 2], ## too short + [1, 2, 3, 4, 5], ## too long + [256, 0, 0], ## out of range + [-1, 0, 0], + [1.5, 0, 0], ## not ints + ["1", 0, 0], + ]) +def test_color_bad(region, ThemeError, value): + with pytest.raises(ThemeError): + region({'v': value})._verify_color('v') + + +def test_color_optional(region): + assert region({})._verify_color('v', optional=True) is None + assert tuple(region({})._verify_color('v', default=(1, 2, 3))) == (1, 2, 3, 255) + + +def test_outline(region, ThemeError): + assert region({'v': [[0, 0, 0], 2]})._verify_outline('v', None, True) == ((0, 0, 0, 255), 2) + assert region({})._verify_outline('v', None, True) is None + for value in ([[0, 0, 0]], "black", [[0, 0, 0], "2"], [["x"], 2]): + with pytest.raises(ThemeError): + region({'v': value})._verify_outline('v', None, True) + + +################################################################################ +## Files and areas +def test_file(region, ThemeError): + assert region({'v': "font.ttf"}, files=("font.ttf",))._verify_file('v') == "font.ttf" + assert region({})._verify_file('v', optional=True) is None + with pytest.raises(ThemeError): + region({'v': "missing.ttf"})._verify_file('v') + with pytest.raises(ThemeError): + region({'v': 5}, files=(5,))._verify_file('v') + + +@pytest.mark.parametrize("area, expected", [ + ([0.0, 0.0, 1.0, 1.0], (0, 0, 640, 480)), + ([0.5, 0.5, 1.0, 1.0], (320, 240, 320, 240)), + ([10, 20, -10, -20], (10, 20, 620, 440)), + ]) +def test_rect(region, area, expected): + assert region({'area': area})._verify_rect('area').tuple() == expected + + +@pytest.mark.parametrize("area", [[0, 0, 1], [0, 0, 1, 1, 1], [0, "a", 1, 1]]) +def test_rect_bad(region, ThemeError, area): + with pytest.raises(ThemeError): + region({'area': area})._verify_rect('area') + + +def test_rect_not_a_list(region): + with pytest.raises(TypeError): + region({'area': 5})._verify_rect('area') + + +################################################################################ +## Bars (the button bar) +def test_bar(region, ThemeError): + assert region({})._verify_bar('bar') is None + for value in ("A: Okay", 5): + with pytest.raises(ThemeError): + region({'bar': value})._verify_bar('bar') + with pytest.raises(ThemeError): + region({'bar': ["a", "None", "b", "None"]})._verify_bar('bar') diff --git a/tests/test_theme_loading.py b/tests/test_theme_loading.py new file mode 100644 index 00000000..6b3ed078 --- /dev/null +++ b/tests/test_theme_loading.py @@ -0,0 +1,346 @@ +# SPDX-License-Identifier: MIT +# +# pugwash.theme.theme_load / theme_apply: a theme's JSON to the sections the scenes draw: +# "#base", "#elements", "#pallet", "#resources", colour schemes, requirement tags on +# sections, regions and elements ("[640x480]", "[ja_JP|zh_CN]", "[!opengl]"), a theme's own +# "#translations", and the sections PortMaster adds itself. A fake GUI records what's loaded. + +import json +from types import SimpleNamespace + +import pytest + +import harbourmaster +from harbourmaster import i18n + + +pytestmark = pytest.mark.sdl + + +class FakeGUI: + def __init__(self): + from pugwash.sdl import geometry + self.loaded_images, self.loaded_sounds, self.animations_added = [], [], [] + self.images = SimpleNamespace( + load_data=lambda name, data: self.loaded_images.append((name, data)) or object(), + load_data_lazy=lambda name, data: self.loaded_images.append((name, None)) or object()) + self.sounds = SimpleNamespace(load=lambda name, set_name: self.loaded_sounds.append((name, set_name)) or object()) + self.animations = SimpleNamespace(add_animation=lambda name, options: self.animations_added.append(name)) + self.default_rects = geometry.NamedRects((0, 0, 640, 480)) + self.pallet = {} + self.override = {} + + +@pytest.fixture +def load(pugwash, tmp_path): + """load(theme_dict, scheme=None) -> (sections, gui)""" + from pugwash import theme + + def _load(data, scheme=None): + base = { + "option_menu": {"option_list": {"area": [0, 0, 1.0, 1.0]}}, + "ports_list": {"ports_list": {"area": [0, 0, 1.0, 1.0]}}, + } + base.update(data) + path = tmp_path / "theme.json" + path.write_text(json.dumps(base), encoding="utf-8") + gui = FakeGUI() + return theme.theme_load(gui, path, scheme), gui + + yield _load + i18n.set_theme_translations({}) + + +@pytest.fixture +def capabilities(monkeypatch): + """add(*caps): the test device's capabilities (aarch64 640x480 power opengl) and these.""" + caps = list(harbourmaster.device_info()['capabilities']) + + def add(*extra): + monkeypatch.setattr(harbourmaster, "device_info", lambda *a, **k: {'capabilities': caps + list(extra)}) + + add() + return add + + +################################################################################ +## Sections, #base and region requirements +def test_base_applies_to_every_region(load, capabilities): + sections, _ = load({ + "#base": {"font": "Base.ttf", "font-size": 20}, + "main_menu": {"title": {"area": [0, 0, 1.0, 0.1], "font-size": 30}, "list": {"area": [0, 0.1, 1.0, 1.0]}}, + }) + + assert sections["main_menu"]["title"] == {"font": "Base.ttf", "font-size": 30, "area": [0, 0, 1.0, 0.1]} + assert sections["main_menu"]["list"]["font"] == "Base.ttf" + + +def test_section_base_overrides_the_themes(load, capabilities): + sections, _ = load({ + "#base": {"font": "Base.ttf"}, + "main_menu": {"#base": {"font": "Menu.ttf"}, "title": {"area": [0, 0, 1, 1]}}, + "options": {"title": {"area": [0, 0, 1, 1]}}, + }) + + assert sections["main_menu"]["title"]["font"] == "Menu.ttf" + assert sections["options"]["title"]["font"] == "Base.ttf" + assert "#base" not in sections["main_menu"] + + +def test_key_requirements(load, capabilities): + sections, _ = load({"main_menu": {"title": { + "font-size": 10, "font-size[640x480]": 20, "font-size[1280x720]": 30, + "fill[!opengl]": [1, 2, 3], "fill[opengl]": [4, 5, 6], + "image[power|battery]": "a.png", "image[nothing|else]": "b.png", + "border[aarch64,640x480]": 3, "border[aarch64,1280x720]": 9, + }}}) + + assert sections["main_menu"]["title"] == {"font-size": 20, "fill": [4, 5, 6], "image": "a.png", "border": 3} + + +def test_language_requirements(load, capabilities): + ## The GUI adds the current language to the capabilities: font[ja_JP] & co. + capabilities("ja_JP") + sections, _ = load({"#base": { + "font": "DejaVuSans.ttf", "font[ja_JP]": "NotoSansJP.ttf", + "line-height[ja_JP|zh_CN|ko_KR]": 0.8}, "main_menu": {"title": {}}}) + + assert sections["main_menu"]["title"] == {"font": "NotoSansJP.ttf", "line-height": 0.8} + + +def test_region_requirements(load, capabilities): + sections, _ = load({"main_menu": { + "title": {"area": [0, 0, 1, 1]}, + "title[1280x720]": {"area": [0, 0, 0.5, 0.5]}, + "title[640x480]": {"font-size": 40}, + "extra[!power]": {"area": [0, 0, 1, 1]}, + }}) + + assert sections["main_menu"]["title"] == {"area": [0, 0, 1, 1], "font-size": 40} + assert "extra" not in sections["main_menu"] + + +def test_section_requirements(load, capabilities): + sections, _ = load({ + "main_menu": {"title": {"font-size": 10}, "list": {"font-size": 12}}, + "main_menu[640x480]": {"title": {"font-size": 20}}, + "main_menu[1280x720]": {"title": {"font-size": 30}}, + ## A section that isn't defined yet can't be added to. + "options[640x480]": {"title": {"font-size": 99}}, + }) + + assert sections["main_menu"]["title"]["font-size"] == 20 + assert sections["main_menu"]["list"]["font-size"] == 12 + assert "options" not in sections + + +def test_config_section_is_kept_apart(load, capabilities): + sections, _ = load({"main_menu": {"#config": {"buttons": {"A": "select"}}, "title": {}}}) + assert sections["main_menu"]["#config"] == {"buttons": {"A": "select"}} + + +def test_images_in_regions_are_preloaded(load, capabilities): + _, gui = load({"main_menu": { + "logo": {"image": "logo.png"}, + "shot": {"image": "{port_info.image}"}, ## a template: loaded when drawn + "bg": {"pimage": "bg.png"}, + "n": {"image": 5}, + }}) + + names = [name for name, _ in gui.loaded_images] + assert "logo.png" in names and "bg.png" in names + assert not any("{" in str(name) for name in names) + + +################################################################################ +## Elements +def test_elements(load, capabilities): + sections, gui = load({ + "#elements": { + "panel": {"parent": "root", "area": [0.0, 0.0, 0.5, 1.0], "fill": [1, 1, 1]}, + "panel_wide[1280x720]": {"area": [0, 0, 1, 1]}, + }, + "main_menu": { + "#element:panel": {"border": 4}, + "#element:panel:left_panel": {"border": 8}, + "#element:panel:wide_only[1280x720]": {}, + "#element:missing": {"border": 1}, + }, + }) + + assert sections["main_menu"]["panel"] == {"parent": "root", "area": [0.0, 0.0, 0.5, 1.0], "fill": [1, 1, 1], "border": 4} + assert sections["main_menu"]["left_panel"]["border"] == 8 + assert "wide_only" not in sections["main_menu"] and "missing" not in sections["main_menu"] + ## An element's area is a named area other regions can use as a parent. + assert gui.default_rects._rects["panel"].tuple() == (0, 0, 320, 480) + assert "panel_wide" not in gui.default_rects._rects + + +def test_element_area_requirements(load, capabilities): + _, gui = load({"#elements": {"panel": {"area": [0, 0, 1.0, 1.0], "area[640x480]": [0, 0, 0.5, 0.5], "area[1280x720]": [0, 0, 0.25, 0.25]}}}) + assert gui.default_rects._rects["panel"].tuple() == (0, 0, 320, 240) + + +################################################################################ +## Resources, pallet, overrides, animations +def test_resources(load, capabilities): + _, gui = load({"#resources": { + "bg.png": {"name": "BACKGROUND"}, "logo.jpg": {}, "pic.svg": {}, + "click.wav": {"name": "click"}, "music.ogg": {}, "song.mp3": {}, "tune.mod": {}, + "notes.txt": {}, + }}) + + assert [name for name, _ in gui.loaded_images[:3]] == ["bg.png", "logo.jpg", "pic.svg"] + assert gui.loaded_sounds == [("click.wav", "click"), ("music.ogg", "music.ogg"), ("song.mp3", "song.mp3"), ("tune.mod", "tune.mod")] + + +def test_pallet_override_and_animations(load, capabilities): + _, gui = load({ + "#pallet": {"accent": [1, 2, 3]}, + "#override": {"main_menu.title": "Hi"}, + "#animations": {"fade": {"duration": 100}}, + }) + + assert gui.pallet == {"accent": [1, 2, 3]} + assert gui.override == {"main_menu.title": "Hi"} + assert gui.animations_added == ["fade"] + + +################################################################################ +## Colour schemes +SCHEMES = { + "#info": {"default-scheme": "Dark"}, + "#schemes": { + "#base": {"#pallet": {"text": [0, 0, 0], "background": [255, 255, 255]}}, + "Light": {"#pallet": {"background": [250, 250, 250]}}, + "Dark": {"#pallet": {"text": [255, 255, 255], "background": [0, 0, 0]}, "#base": {"font-size": 18}}, + }, + "main_menu": {"title": {}}, + } + + +def test_default_scheme(load, capabilities): + sections, gui = load(SCHEMES) + + assert gui.pallet == {"text": [255, 255, 255], "background": [0, 0, 0]} + assert sections["main_menu"]["title"] == {"font-size": 18} + + +def test_chosen_scheme_merges_with_the_scheme_base(load, capabilities): + _, gui = load(SCHEMES, "Light") + assert gui.pallet == {"text": [0, 0, 0], "background": [250, 250, 250]} + + +@pytest.mark.parametrize("scheme", ["Nope", "#base"]) +def test_unknown_scheme_is_the_default(load, capabilities, scheme): + _, gui = load(SCHEMES, scheme) + assert gui.pallet["text"] == [255, 255, 255] + + +def test_no_default_scheme_is_the_first(load, capabilities): + data = json.loads(json.dumps(SCHEMES)) + del data["#info"] + _, gui = load(data) + assert gui.pallet == {"text": [0, 0, 0], "background": [250, 250, 250]} + + +def test_no_schemes(load, capabilities): + _, gui = load({"#pallet": {"a": [1, 1, 1]}}, "Dark") + assert gui.pallet == {"a": [1, 1, 1]} + + +################################################################################ +## A theme's own translations +def test_theme_translations(load, capabilities, monkeypatch): + monkeypatch.setattr(i18n, "LANGUAGE", "de_DE") + load({"#translations": {"de_DE": {"Hello": "Hallo"}, "fr_FR": {"Hello": "Bonjour"}}}) + assert i18n.theme_text("Hello") == "Hallo" + + ## The next theme's (none) replace them. + load({}) + assert i18n.theme_text("Hello") == "Hello" + + +################################################################################ +## What PortMaster adds +def test_disclaimer_is_always_the_same(load, capabilities): + sections, _ = load({"disclaimer": {"background": {"fill": [255, 0, 0]}}}) + + disclaimer = sections["disclaimer"] + assert set(disclaimer) == {"background", "disclaimer_text", "button_bar"} + assert disclaimer["background"]["fill"] == [200, 200, 200, 255] + + +@pytest.mark.parametrize("lang, font", [ + ("en_US", "DejaVuSans.ttf"), ("ja_JP", "NotoSansJP-Regular.ttf"), ("zh_CN", "NotoSansSC-Regular.ttf"), + ("ko_KR", "NotoSansKR-Regular.ttf"), ("zh_TW", "NotoSansTC-Regular.ttf")]) +def test_disclaimer_font_for_the_language(load, capabilities, lang, font): + ## The startup warnings are translated: DejaVu Sans would draw CJK as boxes. + capabilities(lang) + sections, _ = load({}) + assert {region["font"] for region in sections["disclaimer"].values()} == {font} + + +def test_disclaimer_bar_is_translated(load, capabilities, monkeypatch): + from pugwash import theme + monkeypatch.setattr(theme, "_", lambda text: "Bitte warten." if text == "Please wait." else text) + sections, _ = load({}) + assert sections["disclaimer"]["button_bar"]["bar"] == ["Bitte warten."] + + +def test_sections_made_for_older_themes(load, capabilities): + sections, _ = load({ + "option_menu": {"option_list": {"area": [0, 0, 1, 1], "font-size": 11}}, + "ports_list": {"ports_list": {"area": [0, 0, 1, 1], "font-size": 12}}, + }) + + assert sections["themes_list"]["themes_list"]["font-size"] == 11 and "option_list" not in sections["themes_list"] + assert sections["runtime_list"]["runtime_list"]["font-size"] == 11 + assert sections["featured_ports_list"]["option_list"]["font-size"] == 11 + assert sections["featured_ports"]["ports_list"]["font-size"] == 12 + + +def test_port_links_and_editor_from_the_themes_list(load, capabilities): + sections, _ = load({"themes_list": { + "themes_list": {"area": [0, 0, 0.5, 1], "border-x": 12, "list": []}, + "theme_image": {"area": [0.5, 0, 1, 0.5], "image": "{theme_info.image}"}, + "theme_desc": {"area": [0.5, 0.5, 1, 1], "text": "{theme_info.description}"}, + }}) + + assert sections["port_links"]["link_qr"]["image"] == "qr:{port_links.url}" + assert sections["theme_server"]["server_info"]["border-x"] == 12 + + +def test_own_port_links_and_editor_sections_are_kept(load, capabilities): + sections, _ = load({ + "themes_list": {"themes_list": {"list": []}, "theme_image": {"image": "{theme_info.image}"}}, + "port_links": {"mine": {"area": [0, 0, 1, 1]}}, + }) + + assert set(sections["port_links"]) == {"mine"} + assert "server_qr" in sections["theme_server"] + + +def test_no_screenshot_no_made_sections(load, capabilities): + ## Nowhere for the QR code: the scenes show their own plain screens instead. + sections, _ = load({"themes_list": {"themes_list": {"list": []}}}) + assert "port_links" not in sections and "theme_server" not in sections + + +################################################################################ +## Broken themes raise (the theme engine falls back to the default theme) +@pytest.mark.parametrize("data", [ + {"main_menu": {"title": "not a region"}}, + {"main_menu": ["not", "a", "section"]}, + {"#elements": {"panel": "not an element"}}, + ]) +def test_broken_theme_raises(load, capabilities, data): + with pytest.raises(Exception): + load(data) + + +def test_null_theme_raises(pugwash, tmp_path): + from pugwash import theme + (tmp_path / "theme.json").write_text("null") + with pytest.raises(ValueError): + theme.theme_load(FakeGUI(), tmp_path / "theme.json") From e1f417699a8d06509b2dbd06e91d470c8ffae78f Mon Sep 17 00:00:00 2001 From: kloptops Date: Sat, 24 Oct 2026 20:20:33 +0800 Subject: [PATCH 28/28] Tests for the theme server's web API and the theme editor screen test_theme_server_http.py runs the web API against a fake render pool (no SDL): every route, render parameters and cache headers, a missing section (404) and a renderer error (500), bad sizes, languages, screens and themes, versions changing with the theme and the translations, translation saves (problems, removal, theme strings, ten at once, the ones refused and the requests forbidden), the themes listed and their info, the languages, and --export. test_theme_editor_scene.py drives Options -> Theme Editor with a fake server process: the command it runs, the port it keeps, and what the screen says when the server can't start, refuses, dies before answering, stops by itself or the device has no network; B stopping it, and a server that won't stop being killed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01NxABmR1rUUgi4iicVLZchY --- tests/test_theme_editor_scene.py | 214 +++++++++++++++ tests/test_theme_server_http.py | 440 +++++++++++++++++++++++++++++++ 2 files changed, 654 insertions(+) create mode 100644 tests/test_theme_editor_scene.py create mode 100644 tests/test_theme_server_http.py diff --git a/tests/test_theme_editor_scene.py b/tests/test_theme_editor_scene.py new file mode 100644 index 00000000..f9cca9be --- /dev/null +++ b/tests/test_theme_editor_scene.py @@ -0,0 +1,214 @@ +# SPDX-License-Identifier: MIT +# +# Options -> Theme Editor (pugwash/scenes/theme_server.py) with a fake server process: +# what it starts, and what the screen says when it can't start, the device has no network, +# or the server stops by itself. test_theme_server.py runs the real server. + +import io +import json +import subprocess +import time + +import pytest + +import harbourmaster + + +pytestmark = pytest.mark.sdl + + +class FakeProc: + """A theme server process: answers `line` on stdout, then exits with `returncode` when asked.""" + made = [] + + def __init__(self, command, line='{"ok": true, "ready": true, "port": 51234}\n', stays_up=True, slow_to_stop=False, **kwargs): + self.command = command + self.kwargs = kwargs + self.stdout = io.StringIO(line) + self.stdin = io.StringIO() + self.returncode = None if stays_up else 3 + self.slow_to_stop = slow_to_stop + self.killed = False + FakeProc.made.append(self) + + def poll(self): + return self.returncode + + def wait(self, timeout=None): + if self.slow_to_stop: + raise subprocess.TimeoutExpired("theme_server", timeout) + self.returncode = 0 + return 0 + + def kill(self): + self.killed = True + self.returncode = -9 + + +@pytest.fixture +def editor(gui, hm_dirs, monkeypatch): + """start(**fake_proc_options) -> the scene, once the fake server has answered.""" + from pugwash.scenes import theme_server as scene_module + + FakeProc.made = [] + gui.hm = harbourmaster.HarbourMaster({'offline': True, 'no-check': True}, callback=gui, **hm_dirs) + saved_scenes = list(gui.scenes) + monkeypatch.setattr(scene_module, "get_ip_address", lambda force_update=False: "192.168.1.50") + + def start(**options): + def popen(command, **kwargs): + if options.get('fail'): + raise OSError(2, "No such file or directory") + return FakeProc(command, **{key: value for key, value in options.items() if key != 'fail'}, **kwargs) + + monkeypatch.setattr(scene_module.subprocess, "Popen", popen) + scene = scene_module.ThemeServerScene(gui) + gui.push_scene('theme-editor', scene) + end = time.monotonic() + 10 + while gui.text_data.get('theme_server.status') == scene_module._("Starting the theme editor...") and time.monotonic() < end: + gui.do_loop(no_delay=True) + gui.do_loop(no_delay=True) + return scene + + start.module = scene_module + yield start + + gui.scenes = saved_scenes + gui.hm = None + + +def status(gui): + return gui.text_data.get('theme_server.status', "") + + +################################################################################ +def test_command(editor, gui, hm_dirs): + scene = editor() + command = FakeProc.made[0].command + + assert command[1:4] == ["-m", "theme_server.serve", "--managed"] + port = int(command[command.index("--port") + 1]) + assert editor.module.PORT_RANGE[0] <= port <= editor.module.PORT_RANGE[1] + assert "--any-port" in command and command[command.index("--try-ports") + 1] == "20" + assert command[command.index("--themes") + 1] == str(gui.hm.themes_dir) + assert command[command.index("--ports-config") + 1] == str(gui.hm.cfg_dir) + assert 1 <= int(command[command.index("--max-workers") + 1]) <= editor.module.MAX_WORKERS + ## pylibs and exlibs for the server's imports; its log next to pugwash.txt. + assert "pylibs" in FakeProc.made[0].kwargs['env']['PYTHONPATH'] + assert (gui.hm.cfg_dir.parent / "theme_server.txt").is_file() + + assert scene.url == "http://192.168.1.50:51234/" + assert gui.text_data['theme_server.url'] == scene.url and scene.url in status(gui) + assert gui.hm.cfg_data['theme_server_port'] == 51234 + + +def test_cannot_start(editor, gui): + scene = editor(fail=True) + assert "Unable to start the theme editor" in status(gui) and "No such file" in status(gui) + assert scene.proc is None and gui.text_data['theme_server.url'] == "" + + +def test_server_says_no(editor, gui): + scene = editor(line=json.dumps({"ok": False, "error": "port 51234 is busy"}) + "\n") + assert "port 51234 is busy" in status(gui) + assert scene.proc is None and scene.stopped + + +@pytest.mark.parametrize("line, shown", [("", "it stopped"), ("Traceback: boom\n", "Traceback: boom")]) +def test_server_dies_before_it_answers(editor, gui, line, shown): + editor(line=line) + assert "Unable to start the theme editor" in status(gui) and shown in status(gui) + + +def test_no_network(editor, gui, monkeypatch): + monkeypatch.setattr(editor.module, "get_ip_address", lambda force_update=False: None) + scene = editor() + assert "isn't on a network" in status(gui) + assert scene.url is None and gui.text_data['theme_server.url'] == "" + + +def test_server_stops_by_itself(editor, gui): + scene = editor() + FakeProc.made[0].returncode = 1 + gui.do_loop(no_delay=True) + + assert "theme_server.txt" in status(gui) + assert scene.proc is None and scene.url is None and gui.text_data['theme_server.url'] == "" + + +def test_b_stops_it(editor, gui, monkeypatch): + scene = editor() + proc = FakeProc.made[0] + monkeypatch.setattr(gui.events, "was_pressed", lambda button: button == 'B') + gui.do_loop(no_delay=True) + + end = time.monotonic() + 5 + while proc.returncode is None and time.monotonic() < end: + time.sleep(0.01) + assert proc.stdin.closed and proc.returncode == 0 and not proc.killed + assert scene.stopped and gui.scenes[-1][1][-1] is not scene + + +def test_a_server_that_wont_stop_is_killed(editor, gui): + scene = editor(slow_to_stop=True) + proc = FakeProc.made[0] + scene.stop() + + end = time.monotonic() + 5 + while not proc.killed and time.monotonic() < end: + time.sleep(0.01) + assert proc.killed + + +def test_stopping_twice(editor, gui): + scene = editor() + scene.stop() + scene.stop() + ## A late answer after it was stopped changes nothing. + scene.started({'ok': True, 'port': 1}) + assert scene.url == "http://192.168.1.50:51234/" + + +def test_kept_port_is_used_again(editor, gui): + gui.hm.cfg_data['theme_server_port'] = 55555 + editor(line='{"ok": true, "port": 55555}\n') + command = FakeProc.made[0].command + assert command[command.index("--port") + 1] == "55555" + + +################################################################################ +## Its screen +def test_screen_only(editor, gui): + ## start=False: the theme server's own preview of this screen. + scene = editor.module.ThemeServerScene(gui, start=False) + assert scene.proc is None and FakeProc.made == [] + + +def test_plain_screen_for_themes_without_one(editor, gui, monkeypatch): + monkeypatch.delitem(gui.theme_data, "theme_server", raising=False) + editor() + section = gui.theme_data["theme_server"] + assert section["server_qr"]["image"] == "qr:{theme_server.url}" + assert section["server_info"]["text"] == "{theme_server.status}" + + +@pytest.mark.parametrize("lang, font", [("en_US", "DejaVuSans.ttf"), ("ja_JP", "NotoSansJP-Regular.ttf"), ("ko_KR", "NotoSansKR-Regular.ttf")]) +def test_plain_screen_font_for_the_language(editor, monkeypatch, lang, font): + from pugwash import lang as pug_lang + monkeypatch.setattr(pug_lang, "CURRENT_LANG", lang) + section = editor.module.fallback_section() + assert {region.get("font") for region in section.values() if "font" in region} == {font} + + +@pytest.mark.parametrize("available_mb, expected", [(100, 1), (300, 1), (350, 2), (550, 4), (8000, 4)]) +def test_worker_count(editor, tmp_path, available_mb, expected): + meminfo = tmp_path / "meminfo" + meminfo.write_text(f"MemTotal: 999999 kB\nMemFree: 1 kB\nMemAvailable: {available_mb * 1024} kB\n") + assert editor.module.worker_count(str(meminfo)) == expected + + +@pytest.mark.parametrize("contents", ["", "MemAvailable: lots kB\n", "MemTotal: 5 kB\n"]) +def test_worker_count_unreadable(editor, tmp_path, contents): + meminfo = tmp_path / "meminfo" + meminfo.write_text(contents) + assert editor.module.worker_count(str(meminfo)) == 2 diff --git a/tests/test_theme_server_http.py b/tests/test_theme_server_http.py new file mode 100644 index 00000000..43c3caa6 --- /dev/null +++ b/tests/test_theme_server_http.py @@ -0,0 +1,440 @@ +# SPDX-License-Identifier: MIT +# +# The theme server without renderers: its web API (pylibs/theme_server/serve.py's Handler) +# against a fake render pool, the themes it lists and their versions, the translation +# saves, the languages, and --export. No SDL: test_theme_server.py renders for real. + +import json +import os +import shutil +import threading +import time +import urllib.error +import urllib.request + +import pytest + +from conftest import PM_DIR + +from theme_server import serve + + +class FakePool: + """What the Handler uses of a RenderPool: render(), status(), translations.""" + def __init__(self): + self.calls = [] + self.missing = None + self.error = None + self.translations = serve.Translations() + + def render(self, theme_id, scheme, resolution, lang, shot): + self.calls.append((theme_id, scheme, resolution, lang, shot)) + if self.error: + raise serve.WorkerError(self.error) + if self.missing: + return None, {'missing': self.missing, 'errors': []} + return b"\x89PNG fake", {'shot': shot, 'theme': theme_id, 'resolution': resolution, 'errors': []} + + def status(self): + return {'starting': [], 'loading': [], 'rendering': [], 'running': ["640x480"]} + + def shots(self): + return [["main_menu", "Main menu"], ["options", "Options"]] + + +@pytest.fixture +def locales(tmp_path, monkeypatch): + locales = tmp_path / "locales" + shutil.copytree(str(PM_DIR / "pylibs" / "locales"), str(locales)) + monkeypatch.setattr(serve.i18n_tool, "LOCALES_DIR", locales) + return locales + + +@pytest.fixture +def themes_dir(tmp_path): + themes = tmp_path / "themes" + (themes / "good").mkdir(parents=True) + (themes / "good" / "theme.json").write_text(json.dumps({ + "#info": {"name": "Good Theme", "creator": "me", "default-scheme": "Dark"}, + "#schemes": {"#base": {}, "Light": {}, "Dark": {}}, + })) + (themes / "broken").mkdir() + (themes / "broken" / "theme.json").write_text("{ not json") + (themes / "not_a_theme").mkdir() + return themes + + +@pytest.fixture +def server(locales, themes_dir): + """(base url, the fake pool) of a server on a free local port.""" + pool = FakePool() + server = serve.Server(("127.0.0.1", 0), serve.Handler) + server.themes, server.pool, server.shots, server.verbose = serve.Themes(themes_dir), pool, pool.shots(), False + ## (Polled often, so shutting it down after each test is quick.) + threading.Thread(target=server.serve_forever, kwargs={'poll_interval': 0.02}, daemon=True).start() + yield f"http://127.0.0.1:{server.server_address[1]}", pool + server.shutdown() + server.server_close() + + +def get(base, path): + try: + with urllib.request.urlopen(base + path, timeout=30) as response: + return response.status, response.read(), dict(response.headers) + except urllib.error.HTTPError as error: + return error.code, error.read(), dict(error.headers) + + +def post(base, path, body=None, headers=None, raw=None): + data = raw if raw is not None else json.dumps(body).encode("utf-8") + request = urllib.request.Request(base + path, data=data, method="POST", headers=headers if headers is not None else { + "Content-Type": "application/json", "X-Theme-Server": "1"}) + try: + with urllib.request.urlopen(request, timeout=30) as response: + return response.status, json.loads(response.read()) + except urllib.error.HTTPError as error: + return error.code, json.loads(error.read() or b"{}") + + +################################################################################ +## Pages and info +@pytest.mark.parametrize("path", ["/", "/index.html"]) +def test_page(server, path): + status, body, headers = get(server[0], path) + assert status == 200 and headers["Content-Type"].startswith("text/html") + assert b"