diff --git a/.gitea/workflows/build-nightlly.yml b/.gitea/workflows/build-nightlly.yml index 55c388f..314df27 100644 --- a/.gitea/workflows/build-nightlly.yml +++ b/.gitea/workflows/build-nightlly.yml @@ -2,8 +2,14 @@ name: Nightly Build - AppImage, Arch, Fedora on: workflow_dispatch: + inputs: + branch: + description: 'Branch to build' + required: false + default: 'main' env: + BUILD_BRANCH: ${{ inputs.branch || 'main' }} PKGDEST: "/tmp/portprotonqt" PACKAGE: "portprotonqt" @@ -22,12 +28,14 @@ jobs: pacman -Syu --noconfirm --disable-download-timeout --needed git wget gnupg nodejs npm xorg-server-xvfb zsync - uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + ref: ${{ env.BUILD_BRANCH }} - name: Install appimage dependencies run: | cd build-aux/AppImage chmod +x get-dependencies.sh portprotonqt-appimage.sh - ./get-dependencies.sh --git + ./get-dependencies.sh --git "${{ env.BUILD_BRANCH }}" - name: Build AppImage run: | @@ -55,9 +63,8 @@ jobs: steps: - name: Install build dependencies run: | - dnf install -y git rpmdevtools python3-devel python3-wheel python3-pip \ - python3-build pyproject-rpm-macros systemd-rpm-macros python3-setuptools \ - redhat-rpm-config nodejs npm + dnf install -y git rpmdevtools meson ninja-build python3-devel \ + systemd-rpm-macros redhat-rpm-config nodejs npm - name: Setup rpmbuild environment run: | @@ -68,10 +75,13 @@ jobs: - name: Checkout repo uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + ref: ${{ env.BUILD_BRANCH }} - name: Copy fedora.spec run: | - cp build-aux/fedora-git.spec /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec + sed "s|git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git|git clone -b ${{ env.BUILD_BRANCH }} https://git.linux-gaming.ru/Boria138/PortProtonQt.git|" \ + build-aux/fedora-git.spec > /home/rpmbuild/SPECS/${{ env.PACKAGE }}.spec chown -R rpmbuild:users /home/rpmbuild - name: Build RPM @@ -118,13 +128,16 @@ jobs: - name: Build run: | cd /__w/portproton-repo - git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git + git clone -b ${{ env.BUILD_BRANCH }} https://git.linux-gaming.ru/Boria138/PortProtonQt.git cd /__w/portproton-repo/PortProtonQt/build-aux + sed -i "s|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git\")|source=(\"git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git#branch=${{ env.BUILD_BRANCH }}\")|" PKGBUILD-git chown user -R .. su user -c "yes '' | makepkg --noconfirm -s -p PKGBUILD-git" - name: Checkout uses: https://gitea.com/actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + ref: ${{ env.BUILD_BRANCH }} - name: Upload Arch package uses: https://gitea.com/actions/gitea-upload-artifact@v4 diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index f7d73c0..6e73280 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -109,9 +109,8 @@ jobs: steps: - name: Install build dependencies run: | - dnf install -y git rpmdevtools python3-devel python3-wheel python3-pip \ - python3-build pyproject-rpm-macros systemd-rpm-macros python3-setuptools \ - redhat-rpm-config nodejs npm + dnf install -y git rpmdevtools meson ninja-build python3-devel \ + systemd-rpm-macros redhat-rpm-config nodejs npm - name: Setup rpmbuild environment run: | diff --git a/.gitea/workflows/check-spell.yml b/.gitea/workflows/check-spell.yml index 3c340ca..6c7ac95 100644 --- a/.gitea/workflows/check-spell.yml +++ b/.gitea/workflows/check-spell.yml @@ -1,4 +1,4 @@ -name: Check Translations (disabled until yaspeller is fixed) +name: Check Translations run-name: Check spelling in translation files on: push: diff --git a/.gitea/workflows/code-build.yml b/.gitea/workflows/code-build.yml index a7694cf..375589d 100644 --- a/.gitea/workflows/code-build.yml +++ b/.gitea/workflows/code-build.yml @@ -108,9 +108,8 @@ jobs: steps: - name: Install build dependencies run: | - dnf install -y git rpmdevtools python3-devel python3-wheel python3-pip \ - python3-build pyproject-rpm-macros python3-setuptools \ - redhat-rpm-config nodejs npm + dnf install -y git rpmdevtools meson ninja-build python3-devel \ + systemd-rpm-macros redhat-rpm-config nodejs npm - name: Setup rpmbuild environment run: | diff --git a/build-aux/AppImage/get-dependencies.sh b/build-aux/AppImage/get-dependencies.sh index 190a2cd..a770221 100755 --- a/build-aux/AppImage/get-dependencies.sh +++ b/build-aux/AppImage/get-dependencies.sh @@ -9,15 +9,23 @@ else GIT_MODE=false fi +# Get the branch name from the second argument, default to 'main' if not provided +# Only use custom branch for git builds, stable builds always use main +if [ "$GIT_MODE" = true ]; then + BRANCH="${2:-main}" +else + BRANCH="main" +fi + ARCH="$(uname -m)" PACKAGE_BUILDER="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/make-aur-package.sh" EXTRA_PACKAGES="https://raw.githubusercontent.com/pkgforge-dev/Anylinux-AppImages/refs/heads/main/useful-tools/get-debloated-pkgs.sh" if [ "$GIT_MODE" = true ]; then - echo "Using git version of PortProtonQt..." - PPQT_PKGBUILD="https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/build-aux/PKGBUILD-git" + echo "Using git version of PortProtonQt from branch: $BRANCH..." + PPQT_PKGBUILD="https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/$BRANCH/build-aux/PKGBUILD-git" else - echo "Using stable version of PortProtonQt..." + echo "Using stable version of PortProtonQt from main branch..." PPQT_PKGBUILD="https://git.linux-gaming.ru/Boria138/PortProtonQt/raw/branch/main/build-aux/PKGBUILD" fi diff --git a/build-aux/PKGBUILD b/build-aux/PKGBUILD index 8ba1850..b548b7b 100644 --- a/build-aux/PKGBUILD +++ b/build-aux/PKGBUILD @@ -7,18 +7,15 @@ url="https://git.linux-gaming.ru/Boria138/PortProtonQt" license=('GPL-3.0') depends=('python-requests' 'python-babel' 'python-evdev' 'python-pyudev' 'python-orjson' 'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'python-rapidfuzz' 'icoextract' 'python-pillow' 'perl-image-exiftool' 'xdg-utils' 'python-beautifulsoup4' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'unrar' 'qt6-svg') -makedepends=('python-'{'build','installer','setuptools','wheel'}) +makedepends=('meson' 'ninja') source=("git+https://git.linux-gaming.ru/Boria138/PortProtonQt#tag=v$pkgver") sha256sums=('SKIP') build() { - cd "$srcdir/PortProtonQt" - python -m build --wheel --no-isolation + arch-meson PortProtonQt build + meson compile -C build } package() { - cd "$srcdir/PortProtonQt" - python -m installer --destdir="$pkgdir" dist/*.whl - cp -r build-aux/share "$pkgdir/usr/" - cp -r build-aux/lib "$pkgdir/usr/" + meson install -C build --destdir "$pkgdir" } diff --git a/build-aux/PKGBUILD-git b/build-aux/PKGBUILD-git index 9567815..ba88a16 100644 --- a/build-aux/PKGBUILD-git +++ b/build-aux/PKGBUILD-git @@ -7,7 +7,7 @@ url="https://git.linux-gaming.ru/Boria138/PortProtonQt" license=('GPL-3.0') depends=('python-requests' 'python-babel' 'python-evdev' 'python-pyudev' 'python-orjson' 'python-psutil' 'python-tqdm' 'python-vdf' 'python-libarchive-c' 'pyside6' 'icoextract' 'python-pillow' 'python-rapidfuzz' 'perl-image-exiftool' 'xdg-utils' 'python-beautifulsoup4' 'python-websocket-client' 'cabextract' 'unzip' 'curl' 'unrar' 'qt6-svg') -makedepends=('python-'{'build','installer','setuptools','wheel'}) +makedepends=('meson' 'ninja') source=("git+https://git.linux-gaming.ru/Boria138/PortProtonQt.git") sha256sums=('SKIP') @@ -17,13 +17,10 @@ pkgver() { } build() { - cd "$srcdir/PortProtonQt" - python -m build --wheel --no-isolation + arch-meson PortProtonQt build + meson compile -C build } package() { - cd "$srcdir/PortProtonQt" - python -m installer --destdir="$pkgdir" dist/*.whl - cp -r build-aux/share "$pkgdir/usr/" - cp -r build-aux/lib "$pkgdir/usr/" + meson install -C build --destdir "$pkgdir" } diff --git a/build-aux/fedora-git.spec b/build-aux/fedora-git.spec index 315be96..4c072e7 100644 --- a/build-aux/fedora-git.spec +++ b/build-aux/fedora-git.spec @@ -1,12 +1,12 @@ %global pypi_name portprotonqt -%global pypi_version 0.1.1 +%global pypi_version 0.1.10 %global oname PortProtonQt %global build_timestamp %(date +"%Y%m%d") %global _python_no_extras_requires 1 %global rel_build 1.git.%{build_timestamp}%{?dist} -Name: python-%{pypi_name}-git +Name: %{pypi_name}-git Version: %{pypi_version} Release: %{rel_build} Summary: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store (development build) @@ -15,21 +15,15 @@ License: GPL-3.0 URL: https://git.linux-gaming.ru/Boria138/PortProtonQt BuildArch: noarch +BuildRequires: meson >= 0.61.2 +BuildRequires: ninja-build BuildRequires: python3-devel -BuildRequires: python3-wheel -BuildRequires: python3-pip -BuildRequires: python3-build -BuildRequires: pyproject-rpm-macros -BuildRequires: python3dist(setuptools) BuildRequires: git BuildRequires: systemd-rpm-macros -%description -%{summary} +Obsoletes: python3-%{pypi_name}-git < %{version}-%{release} +Provides: python3-%{pypi_name}-git = %{version}-%{release} -%package -n python3-%{pypi_name}-git -Summary: %{summary} -%{?python_provide:%python_provide python3-%{pypi_name}} Requires: python3-babel Requires: python3-evdev Requires: python3-icoextract @@ -55,7 +49,7 @@ Requires: unzip Requires: curl Requires: unrar -%description -n python3-%{pypi_name}-git +%description This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store. It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization. Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers. Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup. %{?python_disable_dependency_generator} @@ -65,17 +59,17 @@ git clone https://git.linux-gaming.ru/Boria138/PortProtonQt.git %build cd %{oname} -%pyproject_wheel +%meson -Dpython_libdir=%{python3_sitelib} -Dudevdir=%{_udevrulesdir} +%meson_build %install cd %{oname} -%pyproject_install -%pyproject_save_files %{pypi_name} -cp -r build-aux/share %{buildroot}/usr/ -cp -r build-aux/lib %{buildroot}/usr/ +%meson_install +%find_lang %{pypi_name} -%files -n python3-%{pypi_name}-git -f %{pyproject_files} +%files -f %{oname}/%{pypi_name}.lang %{_bindir}/%{pypi_name} +%{python3_sitelib}/%{pypi_name}/ %{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg %{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml %{_udevrulesdir}/60-portprotonqt.rules diff --git a/build-aux/fedora.spec b/build-aux/fedora.spec index dfde695..36bd95c 100644 --- a/build-aux/fedora.spec +++ b/build-aux/fedora.spec @@ -3,7 +3,7 @@ %global oname PortProtonQt %global _python_no_extras_requires 1 -Name: python-%{pypi_name} +Name: %{pypi_name} Version: %{pypi_version} Release: 1%{?dist} Summary: Modern GUI for managing and launching games from PortProton, Steam, and Epic Games Store @@ -12,21 +12,15 @@ License: GPL-3.0 URL: https://git.linux-gaming.ru/Boria138/PortProtonQt BuildArch: noarch +BuildRequires: meson >= 0.61.2 +BuildRequires: ninja-build BuildRequires: python3-devel -BuildRequires: python3-wheel -BuildRequires: python3-pip -BuildRequires: python3-build -BuildRequires: pyproject-rpm-macros -BuildRequires: python3dist(setuptools) BuildRequires: git BuildRequires: systemd-rpm-macros -%description -%{summary} +Obsoletes: python3-%{pypi_name} < %{version}-%{release} +Provides: python3-%{pypi_name} = %{version}-%{release} -%package -n python3-%{pypi_name} -Summary: %{summary} -%{?python_provide:%python_provide python3-%{pypi_name}} Requires: python3-babel Requires: python3-evdev Requires: python3-icoextract @@ -52,7 +46,7 @@ Requires: unzip Requires: curl Requires: unrar -%description -n python3-%{pypi_name} +%description This application provides a sleek, intuitive graphical interface for managing and launching games from PortProton, Steam, and Epic Games Store. It consolidates your game libraries into a single, user-friendly hub for seamless navigation and organization. Its lightweight structure and cross-platform support deliver a cohesive gaming experience, eliminating the need for multiple launchers. Unique PortProton integration enhances Linux gaming, enabling effortless play of Windows-based titles with minimal setup. %{?python_disable_dependency_generator} @@ -64,17 +58,17 @@ git checkout v%{pypi_version} %build cd %{oname} -%pyproject_wheel +%meson -Dpython_libdir=%{python3_sitelib} -Dudevdir=%{_udevrulesdir} +%meson_build %install cd %{oname} -%pyproject_install -%pyproject_save_files %{pypi_name} -cp -r build-aux/share %{buildroot}/usr/ -cp -r build-aux/lib %{buildroot}/usr/ +%meson_install +%find_lang %{pypi_name} -%files -n python3-%{pypi_name} -f %{pyproject_files} +%files -f %{oname}/%{pypi_name}.lang %{_bindir}/%{pypi_name} +%{python3_sitelib}/%{pypi_name}/ %{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg %{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml %{_udevrulesdir}/60-portprotonqt.rules diff --git a/build-aux/portprotonqt b/build-aux/portprotonqt new file mode 100644 index 0000000..9ef140b --- /dev/null +++ b/build-aux/portprotonqt @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import sys + +from portprotonqt.app import main + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/dev-scripts/bump_ver.py b/dev-scripts/bump_ver.py index 46fc256..cbd7016 100755 --- a/dev-scripts/bump_ver.py +++ b/dev-scripts/bump_ver.py @@ -12,7 +12,9 @@ BASE_DIR = Path(__file__).parent.parent APPIMAGE_RECIPE = BASE_DIR / "build-aux" / "AppImageBuilder.yml" ARCH_PKGBUILD = BASE_DIR / "build-aux" / "PKGBUILD" FEDORA_SPEC = BASE_DIR / "build-aux" / "fedora.spec" +FEDORA_GIT_SPEC = BASE_DIR / "build-aux" / "fedora-git.spec" PYPROJECT = BASE_DIR / "pyproject.toml" +MESON_BUILD = BASE_DIR / "meson.build" APP_PY = BASE_DIR / "portprotonqt" / "app.py" GITEA_WORKFLOW = BASE_DIR / ".gitea" / "workflows" / "build.yml" CHANGELOG = BASE_DIR / "CHANGELOG.md" @@ -56,6 +58,7 @@ def bump_fedora(path: Path, old: str, new: str) -> bool: path.write_text(new_text, encoding='utf-8') return bool(count) + def bump_pyproject(path: Path, old: str, new: str) -> bool: """ Update version in pyproject.toml under [project] @@ -69,6 +72,19 @@ def bump_pyproject(path: Path, old: str, new: str) -> bool: path.write_text(new_text, encoding='utf-8') return bool(count) +def bump_meson(path: Path, old: str, new: str) -> bool: + """ + Update version in meson.build + """ + if not path.exists(): + return False + text = path.read_text(encoding='utf-8') + pattern = re.compile(r"(version:\s*)'" + re.escape(old) + r"'") + new_text, count = pattern.subn(lambda m: m.group(1) + f"'{new}'", text) + if count: + path.write_text(new_text, encoding='utf-8') + return bool(count) + def bump_app_py(path: Path, old: str, new: str) -> bool: """ Update __app_version__ in app.py @@ -120,7 +136,9 @@ def main(): (APPIMAGE_RECIPE, bump_appimage), (ARCH_PKGBUILD, bump_arch), (FEDORA_SPEC, bump_fedora), + (FEDORA_GIT_SPEC, bump_fedora), (PYPROJECT, bump_pyproject), + (MESON_BUILD, bump_meson), (APP_PY, bump_app_py), (GITEA_WORKFLOW, bump_workflow), (CHANGELOG, bump_changelog) diff --git a/dev-scripts/l10n.py b/dev-scripts/l10n.py index 6a9ff5c..d6eb972 100755 --- a/dev-scripts/l10n.py +++ b/dev-scripts/l10n.py @@ -17,8 +17,9 @@ README_EN = GUIDE_DIR / "README.md" README_RU = GUIDE_DIR / "README.ru.md" LOCALES_PATH = Path(__file__).parent.parent / "portprotonqt" / "locales" THEMES_PATH = Path(__file__).parent.parent / "portprotonqt" / "themes" +MESON_BUILD = Path(__file__).parent.parent / "portprotonqt" / "meson.build" README_FILES = [README_EN, README_RU] -POT_FILE = LOCALES_PATH / "messages.pot" +POT_FILE = LOCALES_PATH / "portprotonqt.pot" # ---------- Версия проекта ---------- def _get_version() -> str: @@ -27,16 +28,16 @@ def _get_version() -> str: # ---------- Обновление README ---------- def _update_coverage(lines: list[str]) -> None: # Парсим статистику из вывода pybabel --statistics - locales_stats = [line for line in lines if line.endswith(".po")] + locales_stats = [line for line in lines if "portprotonqt.po" in line] # Извлекаем (count, pct, locale) и сортируем rows = sorted( - (m := re.search( + row for stat in locales_stats + if (m := re.search( r"""(\d+\ of\ \d+).* # message counts \((\d+\%)\).* # message percentage - locales\/(.*)\/LC_MESSAGES # locale name""", + locales\/([^/]+)\/LC_MESSAGES # locale name""", stat, re.VERBOSE - )) and m.groups() - for stat in locales_stats + )) and (row := m.groups()) ) for md_file in README_FILES: @@ -59,14 +60,14 @@ def _update_coverage(lines: list[str]) -> None: "| Локаль | Прогресс | Переведено |\n" "| :----- | -------: | ---------: |\n" ) - fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/messages.po) | {pct} | {count.replace(' of ', ' из ')} |" + fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/portprotonqt.po) | {pct} | {count.replace(' of ', ' из ')} |" else: table_header = ( "\n\n" "| Locale | Progress | Translated |\n" "| :----- | -------: | ---------: |\n" ) - fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/messages.po) | {pct} | {count} |" + fmt = lambda count, pct, loc: f"| [{loc}](./{loc}/LC_MESSAGES/portprotonqt.po) | {pct} | {count} |" # Собираем строки и добавляем '---' в конце coverage_table = ( @@ -100,7 +101,7 @@ def _update_coverage(lines: list[str]) -> None: def compile_locales() -> None: CommandLineInterface().run([ "pybabel", "compile", "--use-fuzzy", "--directory", - f"{LOCALES_PATH.resolve()}", "--statistics" + f"{LOCALES_PATH.resolve()}", "--domain=portprotonqt", "--statistics" ]) def extract_strings() -> None: @@ -121,10 +122,39 @@ def update_locales() -> None: "pybabel", "update", f"--input-file={POT_FILE.resolve()}", f"--output-dir={LOCALES_PATH.resolve()}", + "--domain=portprotonqt", "--ignore-obsolete", "--update-header-comment", ]) +def _update_meson_locales(new_locales: list[str]) -> None: + """Обновляет список языков в meson.build.""" + if not MESON_BUILD.exists(): + return + + text = MESON_BUILD.read_text(encoding="utf-8") + + # Ищем foreach lang : ['de', 'es', 'pt', 'ru'] + pattern = r"(foreach\s+lang\s*:\s*\[)([^\]]+)(\])" + match = re.search(pattern, text) + if not match: + return + + # Парсим текущий список языков + current_langs_str = match.group(2) + current_langs = re.findall(r"'([^']+)'", current_langs_str) + + # Добавляем новые языки и сортируем + all_langs = sorted(set(current_langs) | set(new_locales)) + + # Формируем новый список + new_langs_str = ", ".join(f"'{lang}'" for lang in all_langs) + new_text = text[:match.start()] + match.group(1) + new_langs_str + match.group(3) + text[match.end():] + + if new_text != text: + MESON_BUILD.write_text(new_text, encoding="utf-8") + print(f"Updated meson.build with locales: {all_langs}") + def create_new(locales: list[str]) -> None: if not POT_FILE.exists(): extract_strings() @@ -133,8 +163,11 @@ def create_new(locales: list[str]) -> None: "pybabel", "init", f"--input-file={POT_FILE.resolve()}", f"--output-dir={LOCALES_PATH.resolve()}", + "--domain=portprotonqt", f"--locale={locale}" ]) + # Обновляем meson.build с новыми локалями + _update_meson_locales(locales) # ---------- Игнорируемые префиксы для spellcheck ---------- IGNORED_PREFIXES = () @@ -148,6 +181,41 @@ def load_ignored_prefixes(ignore_file=".spellignore"): IGNORED_PREFIXES = load_ignored_prefixes() + ("PortProton", "flatpak") +# ---------- Проверка fuzzy строк ---------- +def find_fuzzy_entries(filepath: Path) -> list[tuple[int, str, str]]: + """Находит fuzzy записи в .po файле. Возвращает список (номер_строки, msgid, флаги).""" + fuzzy_entries = [] + lines = filepath.read_text(encoding='utf-8').splitlines() + i = 0 + while i < len(lines): + line = lines[i].strip() + # Ищем комментарий с флагами, содержащий fuzzy + if line.startswith('#,') and 'fuzzy' in line: + flags = line[2:].strip() + line_num = i + 1 + # Ищем следующий msgid + msgid = "" + i += 1 + while i < len(lines): + next_line = lines[i].strip() + if next_line.startswith('msgid '): + match = re.match(r'^msgid\s+"(.*)"', next_line) + if match: + msgid = match.group(1) + i += 1 + # Собираем многострочный msgid + while i < len(lines) and lines[i].strip().startswith('"'): + msgid += lines[i].strip()[1:-1] + i += 1 + break + i += 1 + # Пропускаем пустой msgid (заголовок PO файла) + if msgid: + fuzzy_entries.append((line_num, msgid, flags)) + else: + i += 1 + return fuzzy_entries + # ---------- Проверка орфографии с параллелизмом ---------- speller = YandexSpeller() MSGID_RE = re.compile(r'^msgid\s+"(.*)"') @@ -208,17 +276,36 @@ def main(args) -> int: if args.create_new: create_new(args.create_new) if args.spellcheck: - files = list(LOCALES_PATH.glob("**/*.po")) + [POT_FILE] + files = list(LOCALES_PATH.glob("**/portprotonqt.po")) + [POT_FILE] seen = set(); has_err = False issues_summary = defaultdict(list) + fuzzy_summary = defaultdict(list) for f in files: if not f.exists() or f in seen: continue seen.add(f) + # Проверка fuzzy строк (только для .po файлов) + if f.suffix == '.po': + fuzzy_entries = find_fuzzy_entries(f) + if fuzzy_entries: + fuzzy_summary[f] = fuzzy_entries + has_err = True if check_file(f, issues_summary): has_err = True else: - print(f"✅ {f} — no errors found.") - if has_err: + if f not in fuzzy_summary: + print(f"✅ {f} — no errors found.") + # Вывод fuzzy строк + if fuzzy_summary: + print("\n⚠️ Fuzzy Entries (require review before release):") + for file, entries in fuzzy_summary.items(): + print(f"\n⚠ {file}") + print("-----") + for idx, (line_num, msgid, flags) in enumerate(entries, 1): + print(f"{idx}. Line {line_num}: [{flags}]") + print(f" msgid: \"{msgid[:80]}{'...' if len(msgid) > 80 else ''}\"") + print("-----") + # Вывод орфографических ошибок + if issues_summary: print("\n📋 Summary of Spelling Errors:") for file, errs in issues_summary.items(): print(f"\n✗ {file}") diff --git a/documentation/localization_guide/README.md b/documentation/localization_guide/README.md index 6208d50..1434e8c 100644 --- a/documentation/localization_guide/README.md +++ b/documentation/localization_guide/README.md @@ -13,7 +13,7 @@ ## 📖 Overview -Localization in `PortProtonQT` is powered by `Babel` using `.po/.mo` files stored under `LC_MESSAGES/messages.po` for each language. +Localization in `PortProtonQT` is powered by `Babel` using `.po/.mo` files stored under `LC_MESSAGES/portprotonqt.po` for each language. Current translation status: @@ -21,9 +21,10 @@ Current translation status: | Locale | Progress | Translated | | :----- | -------: | ---------: | -| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 of 376 | -| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 of 376 | -| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 376 of 376 | +| [de](./de/LC_MESSAGES/portprotonqt.po) | 0% | 0 of 376 | +| [es](./es/LC_MESSAGES/portprotonqt.po) | 0% | 0 of 376 | +| [pt](./pt/LC_MESSAGES/portprotonqt.po) | 0% | 0 of 376 | +| [ru](./ru/LC_MESSAGES/portprotonqt.po) | 100% | 376 of 376 | --- @@ -39,7 +40,7 @@ source .venv/bin/activate python dev-scripts/l10n.py --create-new ``` -2. Edit the file `portprotonqt/locales//LC_MESSAGES/messages.po` in Poedit or any text editor. +2. Edit the file `portprotonqt/locales//LC_MESSAGES/portprotonqt.po` in Poedit or any text editor. --- diff --git a/documentation/localization_guide/README.ru.md b/documentation/localization_guide/README.ru.md index 3bd4df0..bdd769e 100644 --- a/documentation/localization_guide/README.ru.md +++ b/documentation/localization_guide/README.ru.md @@ -13,7 +13,7 @@ ## 📖 Обзор -Локализация в `PortProtonQT` осуществляется через систему `.po/.mo` файлов и управляется утилитой `Babel`. Все переводы находятся в подкаталогах вида `LC_MESSAGES/messages.po` для каждой поддерживаемой локали. +Локализация в `PortProtonQT` осуществляется через систему `.po/.mo` файлов и управляется утилитой `Babel`. Все переводы находятся в подкаталогах вида `LC_MESSAGES/portprotonqt.po` для каждой поддерживаемой локали. Текущий статус перевода: @@ -21,9 +21,10 @@ | Локаль | Прогресс | Переведено | | :----- | -------: | ---------: | -| [de_DE](./de_DE/LC_MESSAGES/messages.po) | 0% | 0 из 376 | -| [es_ES](./es_ES/LC_MESSAGES/messages.po) | 0% | 0 из 376 | -| [ru_RU](./ru_RU/LC_MESSAGES/messages.po) | 100% | 376 из 376 | +| [de](./de/LC_MESSAGES/portprotonqt.po) | 0% | 0 из 376 | +| [es](./es/LC_MESSAGES/portprotonqt.po) | 0% | 0 из 376 | +| [pt](./pt/LC_MESSAGES/portprotonqt.po) | 0% | 0 из 376 | +| [ru](./ru/LC_MESSAGES/portprotonqt.po) | 100% | 376 из 376 | --- @@ -39,7 +40,7 @@ source .venv/bin/activate python dev-scripts/l10n.py --create-new <код_локали> ``` -2. Отредактируйте файл `portprotonqt/locales/<локаль>/LC_MESSAGES/messages.po` в Poedit или любом текстовом редакторе. +2. Отредактируйте файл `portprotonqt/locales/<локаль>/LC_MESSAGES/portprotonqt.po` в Poedit или любом текстовом редакторе. --- diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..715a13a --- /dev/null +++ b/meson.build @@ -0,0 +1,83 @@ +project('portprotonqt', + version: '0.1.10', + meson_version: '>= 0.61.2', + license: 'GPL-3.0', +) + +# Project directories +prefix = get_option('prefix') +datadir = prefix / get_option('datadir') +bindir = prefix / get_option('bindir') + +# Python module for site-packages path +python = import('python') +python3 = python.find_installation('python3') +pythondir = join_paths(prefix, python3.get_path('purelib')) + +python_libdir = get_option('python_libdir') +if python_libdir == '' + python_installdir = pythondir +else + python_installdir = python_libdir +endif + +pkgdatadir = python_installdir / meson.project_name() + +conf = configuration_data() +conf.set('PYTHON', python.find_installation('python3').full_path()) + +# Install Python package +subdir('portprotonqt') + +# Install entry point script with proper name to avoid conflict in build directory +configured_portprotonqt = configure_file( + input: 'build-aux/portprotonqt', + output: 'portprotonqt-script', + configuration: conf, +) + +# Install the configured script with the correct name +install_data(configured_portprotonqt, + install_dir: bindir, + install_mode: 'rwxr-xr-x', + rename: 'portprotonqt') + +# Install desktop file +install_data( + 'build-aux/share/applications/ru.linux_gaming.PortProtonQt.desktop', + install_dir: datadir / 'applications', +) + +# Install icon +install_data( + 'build-aux/share/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg', + install_dir: datadir / 'icons/hicolor/scalable/apps', +) + +# Install metainfo +install_data( + 'build-aux/share/metainfo/ru.linux_gaming.PortProtonQt.metainfo.xml', + install_dir: datadir / 'metainfo', +) + +# Install bash completion +install_data( + 'build-aux/share/bash-completion/completions/portprotonqt', + install_dir: datadir / 'bash-completion/completions', +) + +# Install udev rules +udevdir = get_option('udevdir') +if udevdir == '' + udev = dependency('udev', required: false) + if udev.found() + udevdir = udev.get_variable(pkgconfig: 'udevdir') / 'rules.d' + else + udevdir = prefix / get_option('libdir') / 'udev/rules.d' + endif +endif + +install_data( + 'build-aux/lib/udev/rules.d/60-portprotonqt.rules', + install_dir: udevdir, +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..ff8c92e --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,11 @@ +option('udevdir', + type: 'string', + value: '', + description: 'Directory for udev rules (auto-detected if empty)', +) + +option('python_libdir', + type: 'string', + value: '', + description: 'Python site-packages directory (auto-detected if empty)', +) diff --git a/portprotonqt/locales/de_DE/LC_MESSAGES/messages.mo b/portprotonqt/locales/de/LC_MESSAGES/portprotonqt.mo similarity index 100% rename from portprotonqt/locales/de_DE/LC_MESSAGES/messages.mo rename to portprotonqt/locales/de/LC_MESSAGES/portprotonqt.mo diff --git a/portprotonqt/locales/de_DE/LC_MESSAGES/messages.po b/portprotonqt/locales/de/LC_MESSAGES/portprotonqt.po similarity index 100% rename from portprotonqt/locales/de_DE/LC_MESSAGES/messages.po rename to portprotonqt/locales/de/LC_MESSAGES/portprotonqt.po diff --git a/portprotonqt/locales/es_ES/LC_MESSAGES/messages.mo b/portprotonqt/locales/es/LC_MESSAGES/portprotonqt.mo similarity index 100% rename from portprotonqt/locales/es_ES/LC_MESSAGES/messages.mo rename to portprotonqt/locales/es/LC_MESSAGES/portprotonqt.mo diff --git a/portprotonqt/locales/es_ES/LC_MESSAGES/messages.po b/portprotonqt/locales/es/LC_MESSAGES/portprotonqt.po similarity index 100% rename from portprotonqt/locales/es_ES/LC_MESSAGES/messages.po rename to portprotonqt/locales/es/LC_MESSAGES/portprotonqt.po diff --git a/portprotonqt/locales/messages.pot b/portprotonqt/locales/portprotonqt.pot similarity index 99% rename from portprotonqt/locales/messages.pot rename to portprotonqt/locales/portprotonqt.pot index f071cc2..e86eef0 100644 --- a/portprotonqt/locales/messages.pot +++ b/portprotonqt/locales/portprotonqt.pot @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: PortProtonQt 0.1.1\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2026-01-12 20:59+0500\n" +"POT-Creation-Date: 2026-01-18 11:43+0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" diff --git a/portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.mo b/portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.mo new file mode 100644 index 0000000..27ff4bc Binary files /dev/null and b/portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.mo differ diff --git a/portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.po b/portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.po new file mode 100644 index 0000000..3ae7ce3 --- /dev/null +++ b/portprotonqt/locales/pt/LC_MESSAGES/portprotonqt.po @@ -0,0 +1,1286 @@ +# Portuguese translations for PortProtonQt. +# Copyright (C) 2026 boria138 +# This file is distributed under the same license as the PortProtonQt +# project. +# FIRST AUTHOR , 2026. +# +msgid "" +msgstr "" +"Project-Id-Version: PortProtonQt 0.1.1\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2026-01-12 20:59+0500\n" +"PO-Revision-Date: 2026-01-18 11:09+0500\n" +"Last-Translator: FULL NAME \n" +"Language: pt\n" +"Language-Team: pt \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 2.17.0\n" + +msgid "Error" +msgstr "" + +msgid "PortProton directory not found" +msgstr "" + +msgid "Remove from Favorites" +msgstr "" + +msgid "Add to Favorites" +msgstr "" + +msgid "Delete from PortProton" +msgstr "" + +msgid "Stop Game" +msgstr "" + +msgid "Launch Game" +msgstr "" + +msgid "Import to Legendary" +msgstr "" + +msgid "Remove from Steam" +msgstr "" + +msgid "Add to Steam" +msgstr "" + +msgid "Open Game Folder" +msgstr "" + +msgid "Remove from Desktop" +msgstr "" + +msgid "Add to Desktop" +msgstr "" + +msgid "Remove from Menu" +msgstr "" + +msgid "Add to Menu" +msgstr "" + +msgid "Edit Shortcut" +msgstr "" + +#, python-brace-format +msgid "Stopped '{game_name}'" +msgstr "" + +#, python-brace-format +msgid "Legendary executable not found at {path}" +msgstr "" + +msgid "Success" +msgstr "" + +#, python-brace-format +msgid "" +"'{game_name}' was added to Steam. Please restart Steam for changes to " +"take effect." +msgstr "" + +#, python-brace-format +msgid "Executable not found for game: {game_name}" +msgstr "" + +#, python-brace-format +msgid "Opened folder for '{game_name}'" +msgstr "" + +#, python-brace-format +msgid "Failed to open folder: {error}" +msgstr "" + +msgid "No folder selected" +msgstr "" + +#, python-brace-format +msgid "Imported '{game_name}' to Legendary" +msgstr "" + +#, python-brace-format +msgid "Failed to import '{game_name}' to Legendary: {error}" +msgstr "" + +#, python-brace-format +msgid "Importing '{game_name}' to Legendary..." +msgstr "" + +#, python-brace-format +msgid "Added '{game_name}' to favorites" +msgstr "" + +#, python-brace-format +msgid "Removed '{game_name}' from favorites" +msgstr "" + +#, python-brace-format +msgid "start.sh not found at {path}" +msgstr "" + +#, python-brace-format +msgid "Launch game \"{name}\" with PortProton" +msgstr "" + +#, python-brace-format +msgid "Failed to create .desktop file: {error}" +msgstr "" + +#, python-brace-format +msgid "Added '{game_name}' to {location}" +msgstr "" + +msgid "Desktop" +msgstr "" + +#, python-brace-format +msgid "Failed to add '{game_name}' to {location}: {error}" +msgstr "" + +#, python-brace-format +msgid "Failed to remove '{game_name}' from {location}: {error}" +msgstr "" + +#, python-brace-format +msgid "Removed '{game_name}' from {location}" +msgstr "" + +msgid "Menu" +msgstr "" + +#, python-brace-format +msgid "No executable command found in .desktop file for '{game_name}'" +msgstr "" + +#, python-brace-format +msgid "Failed to parse .desktop file for '{game_name}'" +msgstr "" + +#, python-brace-format +msgid "Error reading .desktop file: {error}" +msgstr "" + +#, python-brace-format +msgid "No .desktop file found for '{game_name}'" +msgstr "" + +msgid "Confirm Deletion" +msgstr "" + +#, python-brace-format +msgid "" +"Are you sure you want to delete '{game_name}'? This will remove the " +".desktop file and custom data." +msgstr "" + +#, python-brace-format +msgid "Failed to delete .desktop file: {error}" +msgstr "" + +#, python-brace-format +msgid "Deleted '{game_name}' successfully" +msgstr "" + +#, python-brace-format +msgid "Failed to delete custom data: {error}" +msgstr "" + +#, python-brace-format +msgid "Added '{game_name}' successfully" +msgstr "" + +msgid "Game name and executable path are required" +msgstr "" + +msgid "Failed to generate .desktop file data" +msgstr "" + +#, python-brace-format +msgid "Failed to delete old .desktop file: {error}" +msgstr "" + +#, python-brace-format +msgid "Removed old .desktop file for '{game_name}'" +msgstr "" + +#, python-brace-format +msgid "Failed to save .desktop file: {error}" +msgstr "" + +#, python-brace-format +msgid "Failed to copy cover image: {error}" +msgstr "" + +#, python-brace-format +msgid "Unsupported image format: {extension}" +msgstr "" + +#, python-brace-format +msgid "Failed to add '{game_name}' to Steam: {error}" +msgstr "" + +#, python-brace-format +msgid "" +"'{game_name}' was removed from Steam. Please restart Steam for changes to" +" take effect." +msgstr "" + +#, python-brace-format +msgid "Failed to remove game '{game_name}' from Steam: {error}" +msgstr "" + +msgid "Undo" +msgstr "" + +msgid "Redo" +msgstr "" + +msgid "Cut" +msgstr "" + +msgid "Copy" +msgstr "" + +msgid "Paste" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Select All" +msgstr "" + +msgid "Back" +msgstr "" + +msgid "LAST LAUNCH" +msgstr "" + +msgid "PLAY TIME" +msgstr "" + +msgid "MAIN STORY" +msgstr "" + +msgid "MAIN + SIDES" +msgstr "" + +msgid "COMPLETIONIST" +msgstr "" + +msgid "full" +msgstr "" + +msgid "partial" +msgstr "" + +msgid "none" +msgstr "" + +#, python-brace-format +msgid "Gamepad Support: {0}" +msgstr "" + +msgid "Stop" +msgstr "" + +msgid "Play" +msgstr "" + +msgid "Settings" +msgstr "" + +msgid "Reinstall" +msgstr "" + +msgid "Install" +msgstr "" + +msgid "Open" +msgstr "" + +msgid "Select Dir" +msgstr "" + +msgid "Prev Dir" +msgstr "" + +msgid "Cancel" +msgstr "" + +msgid "Toggle" +msgstr "" + +msgid "Force Install" +msgstr "" + +msgid "Prev Tab" +msgstr "" + +msgid "Next Tab" +msgstr "" + +msgid "Save" +msgstr "" + +msgid "Search" +msgstr "" + +msgid "Apply" +msgstr "" + +msgid "Clear All" +msgstr "" + +#, python-brace-format +msgid "Launching {0}" +msgstr "" + +msgid "File Explorer" +msgstr "" + +msgid "Select" +msgstr "" + +msgid "Path: " +msgstr "" + +#, python-format +msgid "Access denied: %s" +msgstr "" + +msgid "Edit Game" +msgstr "" + +msgid "Add Game" +msgstr "" + +msgid "Game Name:" +msgstr "" + +msgid "Path to Executable:" +msgstr "" + +msgid "Browse..." +msgstr "" + +msgid "Custom Cover:" +msgstr "" + +msgid "Enter local path or URL for cover image" +msgstr "" + +msgid "Cover Preview:" +msgstr "" + +msgid "Invalid image" +msgstr "" + +msgid "Failed to download cover" +msgstr "" + +msgid "Downloading cover..." +msgstr "" + +msgid "No cover selected" +msgstr "" + +msgid "Prefix Manager" +msgstr "" + +msgid "Set" +msgstr "" + +msgid "Libraries" +msgstr "" + +msgid "Information" +msgstr "" + +msgid "Fonts" +msgstr "" + +msgid "Winetricks not found. Please try again." +msgstr "" + +msgid "Warning" +msgstr "" + +msgid "No components selected." +msgstr "" + +msgid "Installation failed. Check logs." +msgstr "" + +msgid "Components installed successfully." +msgstr "" + +msgid "Exe Settings" +msgstr "" + +msgid "Search:" +msgstr "" + +msgid "Search settings..." +msgstr "" + +msgid "Main" +msgstr "" + +msgid "Advanced" +msgstr "" + +msgid "Setting" +msgstr "" + +msgid "Value" +msgstr "" + +msgid "Description" +msgstr "" + +msgid "disabled" +msgstr "" + +msgid "Info" +msgstr "" + +msgid "No changes to apply." +msgstr "" + +msgid "Failed to apply changes. Check logs." +msgstr "" + +msgid "Settings updated successfully." +msgstr "" + +msgid "Loading Epic Games Store games..." +msgstr "" + +msgid "Never" +msgstr "" + +msgid "No description available" +msgstr "" + +msgid "Supported" +msgstr "" + +msgid "Running" +msgstr "" + +msgid "Planned" +msgstr "" + +msgid "Broken" +msgstr "" + +msgid "Denied" +msgstr "" + +msgid "Platinum" +msgstr "" + +msgid "Gold" +msgstr "" + +msgid "Silver" +msgstr "" + +msgid "Bronze" +msgstr "" + +msgid "Pending" +msgstr "" + +msgid "Manage Wine versions" +msgstr "" + +msgid "Selected assets:" +msgstr "" + +msgid "No assets selected" +msgstr "" + +msgid "Downloading: " +msgstr "" + +msgid "Download Selected" +msgstr "" + +msgid "Installed" +msgstr "" + +#, python-brace-format +msgid "Error loading wine data: {error}" +msgstr "" + +msgid "Version Name" +msgstr "" + +msgid "Size" +msgstr "" + +msgid "Unknown" +msgstr "" + +#, python-brace-format +msgid "{display_name} (installed)" +msgstr "" + +msgid "No Wine/Proton versions installed" +msgstr "" + +msgid "Select to remove this version" +msgstr "" + +#, python-brace-format +msgid "Selected {} assets:\n" +msgstr "" + +#, python-brace-format +msgid "" +"\n" +"Total size to delete: {}\n" +msgstr "" + +msgid "Delete Selected" +msgstr "" + +#, python-brace-format +msgid "" +"\n" +"Total size to download: {}\n" +msgstr "" + +msgid "Downloading in Progress" +msgstr "" + +msgid "Cannot clear selection while extraction is in progress." +msgstr "" + +msgid "No Selection" +msgstr "" + +msgid "Please select at least one archive to download." +msgstr "" + +msgid "Please wait for current downloading to complete." +msgstr "" + +msgid "Please select at least one version to delete." +msgstr "" + +#, python-brace-format +msgid "" +"Are you sure you want to delete {} selected version(s)?\n" +"\n" +"This action cannot be undone." +msgstr "" + +#, python-brace-format +msgid "Failed to remove version at {}: {}" +msgstr "" + +#, python-brace-format +msgid "Successfully removed {} version(s)." +msgstr "" + +msgid "Downloading Complete" +msgstr "" + +msgid "All selected archives have been downloaded!" +msgstr "" + +#, python-brace-format +msgid "Downloading: {0} ({1}%)" +msgstr "" + +#, python-brace-format +msgid "Extracting: {0}" +msgstr "" + +#, python-brace-format +msgid ", ETA: {}s" +msgstr "" + +#, python-brace-format +msgid ", Speed: {:.1f}MB/s" +msgstr "" + +#, python-brace-format +msgid "Extracting: {0}{1}{2}" +msgstr "" + +msgid "Extraction Error" +msgstr "" + +#, python-brace-format +msgid "Failed to extract archive: {0}" +msgstr "" + +msgid "Operation Cancelled" +msgstr "" + +msgid "Download or extraction has been cancelled." +msgstr "" + +msgid "Unknown Game" +msgstr "" + +msgid "Starting PortProton..." +msgstr "" + +msgid "Library" +msgstr "" + +msgid "Auto Install" +msgstr "" + +msgid "Wine Settings" +msgstr "" + +msgid "PortProton Settings" +msgstr "" + +msgid "Themes" +msgstr "" + +msgid "Fullscreen" +msgstr "" + +msgid "Refresh Grid" +msgstr "" + +msgid "Installation already in progress." +msgstr "" + +msgid "Failed to start installation." +msgstr "" + +#, python-brace-format +msgid "Processed {} installation..." +msgstr "" + +msgid "Installation completed successfully." +msgstr "" + +msgid "Installation failed." +msgstr "" + +msgid "Installation error." +msgstr "" + +msgid "Game library refreshed" +msgstr "" + +msgid "Loading Steam games..." +msgstr "" + +msgid "Loading PortProton games..." +msgstr "" + +msgid "Game Library" +msgstr "" + +msgid "Find Games ..." +msgstr "" + +msgid "A refresh is already in progress..." +msgstr "" + +msgid "Refreshing..." +msgstr "" + +msgid "Refreshing game library..." +msgstr "" + +#, python-brace-format +msgid "Added '{name}'" +msgstr "" + +msgid "Compatibility tool:" +msgstr "" + +msgid "Prefix:" +msgstr "" + +msgid "Wine Configuration" +msgstr "" + +msgid "Registry Editor" +msgstr "" + +msgid "Command Prompt" +msgstr "" + +msgid "Uninstaller" +msgstr "" + +msgid "Create Prefix Backup" +msgstr "" + +msgid "Load Prefix Backup" +msgstr "" + +msgid "Delete Prefix" +msgstr "" + +msgid "Clear Prefix" +msgstr "" + +msgid "Manage WINE versions" +msgstr "" + +msgid "Launching tool..." +msgstr "" + +msgid "Failed to start process." +msgstr "" + +msgid "Confirm Clear" +msgstr "" + +#, python-brace-format +msgid "Are you sure you want to clear prefix '{}'?" +msgstr "" + +msgid "Clearing prefix..." +msgstr "" + +msgid "Failed to start prefix clear process." +msgstr "" + +msgid "Prefix cleared successfully." +msgstr "" + +#, python-brace-format +msgid "Prefix clear failed with exit code {}." +msgstr "" + +#, python-brace-format +msgid "Failed to run clear prefix command: {}" +msgstr "" + +msgid "Failed to start backup process." +msgstr "" + +msgid "Failed to start restore process." +msgstr "" + +msgid "Prefix backup completed." +msgstr "" + +msgid "Prefix backup failed." +msgstr "" + +msgid "Prefix restore completed." +msgstr "" + +msgid "Prefix restore failed." +msgstr "" + +#, python-brace-format +msgid "Are you sure you want to delete prefix '{}'?" +msgstr "" + +#, python-brace-format +msgid "Prefix '{}' deleted." +msgstr "" + +#, python-brace-format +msgid "Failed to delete prefix: {}" +msgstr "" + +msgid "Main PortProton parameters..." +msgstr "" + +msgid "detailed" +msgstr "" + +msgid "brief" +msgstr "" + +msgid "Time Detail Level:" +msgstr "" + +msgid "last launch" +msgstr "" + +msgid "playtime" +msgstr "" + +msgid "alphabetical" +msgstr "" + +msgid "favorites" +msgstr "" + +msgid "Games Sort Method:" +msgstr "" + +msgid "all" +msgstr "" + +msgid "Games Display Filter:" +msgstr "" + +msgid "Gamepad Type:" +msgstr "" + +msgid "Proxy URL" +msgstr "" + +msgid "Proxy URL:" +msgstr "" + +msgid "Proxy Username" +msgstr "" + +msgid "Proxy Username:" +msgstr "" + +msgid "Proxy Password" +msgstr "" + +msgid "Proxy Password:" +msgstr "" + +msgid "Launch Application in Fullscreen" +msgstr "" + +msgid "Application Fullscreen Mode:" +msgstr "" + +msgid "Minimize to tray on close" +msgstr "" + +msgid "Application Close Mode:" +msgstr "" + +msgid "Auto Fullscreen on Gamepad connected" +msgstr "" + +msgid "Auto Fullscreen on Gamepad connected:" +msgstr "" + +msgid "Gamepad haptic feedback" +msgstr "" + +msgid "Gamepad haptic feedback:" +msgstr "" + +msgid "Save Settings" +msgstr "" + +msgid "Reset Settings" +msgstr "" + +msgid "Clear Cache" +msgstr "" + +msgid "Confirm Reset" +msgstr "" + +msgid "Are you sure you want to reset all settings? This action cannot be undone." +msgstr "" + +msgid "Settings reset. Restarting..." +msgstr "" + +msgid "Confirm Clear Cache" +msgstr "" + +msgid "Are you sure you want to clear the cache? This action cannot be undone." +msgstr "" + +msgid "Cache cleared" +msgstr "" + +msgid "Settings saved" +msgstr "" + +msgid "Select Theme:" +msgstr "" + +msgid "Apply Theme" +msgstr "" + +msgid "No link" +msgstr "" + +msgid "Name:" +msgstr "" + +msgid "Description:" +msgstr "" + +msgid "Author:" +msgstr "" + +msgid "Link:" +msgstr "" + +#, python-brace-format +msgid "Theme '{0}' applied successfully" +msgstr "" + +#, python-brace-format +msgid "Error applying theme '{0}'" +msgstr "" + +#, python-brace-format +msgid "Executable not found: {0}" +msgstr "" + +#, python-brace-format +msgid "Executable not found for EGS game: {0}" +msgstr "" + +msgid "Cannot launch game while another game is running" +msgstr "" + +msgid "Launching" +msgstr "" + +#, python-brace-format +msgid "Failed to launch game: {0}" +msgstr "" + +msgid "Invalid command format (native)" +msgstr "" + +msgid "Invalid command format (flatpak)" +msgstr "" + +#, python-brace-format +msgid "File not found: {0}" +msgstr "" + +msgid "" +"Using FPS and system load monitoring (Turns on and off by the key " +"combination - right Shift + F12)" +msgstr "" + +msgid "Forced use of MANGOHUD system settings (GOverlay, etc.)" +msgstr "" + +msgid "" +"Enable vkBasalt by default to improve graphics in games running on " +"Vulkan. (The HOME hotkey disables vkbasalt)" +msgstr "" + +msgid "Forced use of VKBASALT system settings (GOverlay, etc.)" +msgstr "" + +msgid "" +"Enable dgVoodoo2. Forced use all dgVoodoo2 libs (Glide 2.11-3.1, " +"DirectDraw 1-7, Direct3D 2-9) on all 3D API." +msgstr "" + +msgid "" +"Super + F : Toggle fullscreen\n" +"Super + N : Toggle nearest neighbour filtering\n" +"Super + U : Toggle FSR upscaling\n" +"Super + Y : Toggle NIS upscaling\n" +"Super + I : Increase FSR sharpness by 1\n" +"Super + O : Decrease FSR sharpness by 1\n" +"Super + S : Take screenshot (currently goes to /tmp/gamescope_DATE.png)\n" +"Super + G : Toggle keyboard grab\n" +"Super + C : Update clipboard" +msgstr "" + +msgid "Enable in-process synchronization primitives based on eventfd." +msgstr "" + +msgid "Enable futex-based in-process synchronization primitives." +msgstr "" + +msgid "Enable in-process synchronization via the Linux ntsync driver." +msgstr "" + +msgid "Enable vkd3d support - Ray Tracing" +msgstr "" + +msgid "Enable DLSS on supported NVIDIA graphics cards" +msgstr "" + +msgid "Enable OptiScaler (replacement upscaler / frame generator)" +msgstr "" + +msgid "Enable Lossless Scaling frame generation (experimental)" +msgstr "" + +msgid "FSR upscaling in fullscreen with ProtonGE below native resolution" +msgstr "" + +msgid "Disguise all NVIDIA GPU features" +msgstr "" + +msgid "Run the application in WINE virtual desktop" +msgstr "" + +msgid "Run the application in a terminal" +msgstr "" + +msgid "Use system GameMode for performance optimization" +msgstr "" + +msgid "Enable forced use of third-party DirectX libraries" +msgstr "" + +msgid "Fix pink-tinted video playback in some games" +msgstr "" + +msgid "Reduce PulseAudio latency to fix intermittent sound" +msgstr "" + +msgid "Force US keyboard layout" +msgstr "" + +msgid "Use GStreamer for in-game clips (WMF support)" +msgstr "" + +msgid "Use WINE shader caching" +msgstr "" + +msgid "Force use of built-in DXGI library" +msgstr "" + +msgid "Enable Easy Anti-Cheat and BattlEye runtimes" +msgstr "" + +msgid "Use system Vulkan layers (MangoHud, vkBasalt, OBS, etc.)" +msgstr "" + +msgid "Enable OBS Studio capture via obs-vkcapture" +msgstr "" + +msgid "Disable desktop compositing for performance" +msgstr "" + +msgid "Use container launch mode (recommended default)" +msgstr "" + +msgid "Force DirectInput protocol instead of XInput" +msgstr "" + +msgid "Enable experimental native Wayland support" +msgstr "" + +msgid "Enable HDR settings under native Wayland" +msgstr "" + +msgid "Use Gallium Zink (OpenGL via Vulkan)" +msgstr "" + +msgid "Use Gallium Nine (native DirectX 9 for Mesa)" +msgstr "" + +msgid "Use WineD3D Vulkan backend (Damavand)" +msgstr "" + +msgid "Use bundled dxvk/vkd3d from Wine/Proton" +msgstr "" + +msgid "Use async dxvk-sarek (experimental)" +msgstr "" + +msgid "Wine Version" +msgstr "" + +msgid "Select the Wine or Proton version to use for this executable." +msgstr "" + +msgid "Prefix Name" +msgstr "" + +msgid "Specify the Wine prefix to run this game with" +msgstr "" + +msgid "Newest" +msgstr "" + +msgid "Stable" +msgstr "" + +msgid "Vulkan Backend" +msgstr "" + +msgid "" +"Select the DirectX → Vulkan/OpenGL backend:\n" +"\n" +"• Newest – latest DXVK + VKD3D (best compatibility/performance, requires " +"modern drivers: AMD Mesa 25+, NVIDIA 550.54.14+, Intel Mesa 24.2+)\n" +"• Stable – older, well-tested DXVK + VKD3D (works on any Vulkan 1.3+ " +"driver)\n" +"• Sarek – experimental DXVK-Sarek + VKD3D-Sarek (supports older drivers, " +"Vulkan 1.1+)\n" +"• WINED3D – OpenGL fallback (lowest performance, use only if others fail)" +msgstr "" + +msgid "Windows version" +msgstr "" + +msgid "" +"Changing the WINDOWS emulation version may be required to run older " +"games. WINDOWS versions below 10 do not support new games with DirectX 12" +msgstr "" + +msgid "DLL Overrides" +msgstr "" + +msgid "" +"Forced to use/disable the library only for the given application.\n" +"\n" +"A brief instruction:\n" +"* libraries are written WITHOUT the .dll file extension\n" +"* libraries are separated by semicolons - ;\n" +"* library=n - use the WINDOWS (third-party) library\n" +"* library=b - use WINE (built-in) library\n" +"* library=n,b - use WINDOWS library and then WINE\n" +"* library=b,n - use WINE library and then WINDOWS\n" +"* library= - disable the use of this library\n" +"\n" +"Example: libglesv2=;d3dx9_36,d3dx9_42=n,b;mfc120=b,n" +msgstr "" + +msgid "Launch Arguments" +msgstr "" + +msgid "" +"Adding an argument after the .exe file, just like you would add an " +"argument in a shortcut on a WINDOWS system.\n" +"\n" +"Example: -dx11 -skipintro 1" +msgstr "" + +msgid "CPU Cores Limit" +msgstr "" + +msgid "" +"Limiting the number of CPU cores is useful for Unity games (It is " +"recommended to set the value equal to 8)" +msgstr "" + +msgid "OpenGL Version" +msgstr "" + +msgid "" +"You can select the required OpenGL version, some games require a forced " +"Compatibility Profile (COMP)." +msgstr "" + +msgid "VKD3D Feature Level" +msgstr "" + +msgid "You can set a forced feature level VKD3D for games on DirectX12" +msgstr "" + +msgid "Locale" +msgstr "" + +msgid "Force certain locale for an app. Fixes encoding issues in legacy software" +msgstr "" + +msgid "Window Mode" +msgstr "" + +msgid "" +"Window mode (for Vulkan and OpenGL):\n" +"fifo - First in, first out. Limits the frame rate + no tearing. (VSync)\n" +"immediate - Unlimited frame rate + tearing.\n" +"mailbox - Triple buffering. Unlimited frame rate + no tearing.\n" +"relaxed - Same as fifo but allows tearing when below the monitors refresh" +" rate." +msgstr "" + +msgid "AMD Vulkan Driver" +msgstr "" + +msgid "" +"Select needed AMD vulkan implementation. Choosing which implementation of" +" vulkan will be used to run the game" +msgstr "" + +msgid "NUMA Node" +msgstr "" + +msgid "" +"NUMA node for CPU affinity. In multi-core systems, CPUs are split into " +"NUMA nodes, each with its own local memory and cores. Binding a game to a" +" single node reduces memory-access latency and limits costly core-to-core" +" switches." +msgstr "" + +msgid "Reboot" +msgstr "" + +msgid "Shutdown" +msgstr "" + +msgid "Suspend" +msgstr "" + +msgid "Exit Application" +msgstr "" + +msgid "Return to Desktop" +msgstr "" + +msgid "portprotonqt-session-select file not found at /usr/bin/" +msgstr "" + +msgid "Failed to reboot the system" +msgstr "" + +msgid "Failed to shutdown the system" +msgstr "" + +msgid "Failed to suspend the system" +msgstr "" + +msgid "Failed to return to desktop" +msgstr "" + +msgid "just now" +msgstr "" + +msgid "d." +msgstr "" + +msgid "h." +msgstr "" + +msgid "min." +msgstr "" + +msgid "sec." +msgstr "" + +msgid "Show" +msgstr "" + +msgid "Favorites" +msgstr "" + +msgid "Recent Games" +msgstr "" + +msgid "Exit" +msgstr "" + +msgid "Hide" +msgstr "" + +msgid "No favorites" +msgstr "" + +msgid "No recent games" +msgstr "" + diff --git a/portprotonqt/locales/ru_RU/LC_MESSAGES/messages.mo b/portprotonqt/locales/ru/LC_MESSAGES/portprotonqt.mo similarity index 100% rename from portprotonqt/locales/ru_RU/LC_MESSAGES/messages.mo rename to portprotonqt/locales/ru/LC_MESSAGES/portprotonqt.mo diff --git a/portprotonqt/locales/ru_RU/LC_MESSAGES/messages.po b/portprotonqt/locales/ru/LC_MESSAGES/portprotonqt.po similarity index 100% rename from portprotonqt/locales/ru_RU/LC_MESSAGES/messages.po rename to portprotonqt/locales/ru/LC_MESSAGES/portprotonqt.po diff --git a/portprotonqt/localization.py b/portprotonqt/localization.py index 2f4f9c7..ddeeb19 100644 --- a/portprotonqt/localization.py +++ b/portprotonqt/localization.py @@ -33,11 +33,21 @@ LOCALE_MAP = { 'el': 'greek', } -translate = gettext.translation( - domain="messages", - localedir = Path(__file__).parent / "locales", - fallback=True, -) +# Try system locale directory first, fallback to local for development +_system_localedir = Path("/usr/share/locale") +_local_localedir = Path(__file__).parent / "locales" + +try: + translate = gettext.translation( + domain="portprotonqt", + localedir=_system_localedir, + ) +except FileNotFoundError: + translate = gettext.translation( + domain="portprotonqt", + localedir=_local_localedir, + fallback=True, + ) _ = translate.gettext def get_system_locale(): diff --git a/portprotonqt/meson.build b/portprotonqt/meson.build new file mode 100644 index 0000000..4d66a2f --- /dev/null +++ b/portprotonqt/meson.build @@ -0,0 +1,54 @@ +# Install Python source files +install_data( + '__init__.py', + 'animations.py', + 'app.py', + 'cli.py', + 'config_utils.py', + 'context_menu_manager.py', + 'custom_widgets.py', + 'detail_pages.py', + 'dialogs.py', + 'downloader.py', + 'egs_api.py', + 'game_card.py', + 'game_library_manager.py', + 'get_wine_module.py', + 'howlongtobeat_api.py', + 'image_utils.py', + 'input_manager.py', + 'keyboard_layouts.py', + 'localization.py', + 'logger.py', + 'main_window.py', + 'portproton_api.py', + 'preloader.py', + 'search_utils.py', + 'settings_manager.py', + 'steam_api.py', + 'system_overlay.py', + 'theme_manager.py', + 'theme_security.py', + 'time_utils.py', + 'tray_manager.py', + 'version_utils.py', + 'virtual_keyboard.py', + install_dir: pkgdatadir, +) + +# Install themes +install_subdir( + 'themes', + install_dir: pkgdatadir, + exclude_directories: ['__pycache__'], + exclude_files: ['*.pyc'], +) + +# Install locales - only .mo files (compiled translations) +# exclude_files doesn't work recursively, so we install each language manually +foreach lang : ['de', 'es', 'pt', 'ru'] + install_data( + 'locales' / lang / 'LC_MESSAGES' / 'portprotonqt.mo', + install_dir: get_option('localedir') / lang / 'LC_MESSAGES', + ) +endforeach