pax_global_header00006660000000000000000000000064145525241740014523gustar00rootroot0000000000000052 comment=759382b29b63f026c25ce28350f251574cae1fe1 sharkwouter-minigalaxy-759382b/000077500000000000000000000000001455252417400165265ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/.flake8000066400000000000000000000002251455252417400177000ustar00rootroot00000000000000[flake8] max-complexity = 10 max-line-length = 127 exclude = .git, build, data, debian, dist, *.egg-info, site-packages sharkwouter-minigalaxy-759382b/.flake8.soft000066400000000000000000000002501455252417400206500ustar00rootroot00000000000000[flake8] per-file-ignores = # Because of their nature, tests have a lot of long strings # Perhaps the situation can be improved in the future tests/*: E501 sharkwouter-minigalaxy-759382b/.github/000077500000000000000000000000001455252417400200665ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/.github/pull_request_template.md000066400000000000000000000003751455252417400250340ustar00rootroot00000000000000 ## Description ## Checklist - [ ] _CHANGELOG.md_ was updated (**format**: - Change made (thanks to github_username)) sharkwouter-minigalaxy-759382b/.github/workflows/000077500000000000000000000000001455252417400221235ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/.github/workflows/changelogtest.yml000066400000000000000000000003261455252417400254760ustar00rootroot00000000000000name: Changelog test on: [pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Check changelog format run: | ./scripts/check-changelog.sh sharkwouter-minigalaxy-759382b/.github/workflows/pythontests.yml000066400000000000000000000014211455252417400252500ustar00rootroot00000000000000name: Python tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Set up Python 3.8 uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements-testing.txt - name: Lint with flake8 run: | flake8 . --append-config=.flake8.soft --count --show-source --statistics - name: Lint with flake8 (strict, but non-fatal) run: | flake8 . --count --show-source --statistics --exit-zero - name: Run unit tests run: | python3 -m coverage run --source minigalaxy -m unittest discover -v tests && python3 -m coverage report -m sharkwouter-minigalaxy-759382b/.github/workflows/release.yml000066400000000000000000000025301455252417400242660ustar00rootroot00000000000000name: Release on: workflow_dispatch jobs: release: runs-on: ubuntu-latest steps: - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y debhelper-compat dh-sequence-python3 python3-all python3-setuptools help2man devscripts gettext lsb-release xmlstarlet git - uses: actions/checkout@v3 with: ref: master - name: Prepare release files id: tag run: | ./scripts/create-release.sh env: DEBFULLNAME: ${{ secrets.DEBFULLNAME }} DEBEMAIL: ${{ secrets.DEBEMAIL }} - name: Build deb package run: | dpkg-buildpackage -us -uc - name: Commit changes run: | git config --global user.name 'Wouter Wijsman' git config --global user.email 'sharkwouter@users.noreply.github.com' git add pyproject.toml data/io.github.sharkwouter.Minigalaxy.metainfo.xml debian/changelog minigalaxy/version.py git commit -m "Add new release" git push - name: Release uses: softprops/action-gh-release@v1 with: tag_name: ${{ steps.tag.outputs.VERSION }} name: Minigalaxy version ${{ steps.tag.outputs.VERSION }} body_path: release.md prerelease: true files: | ../minigalaxy_*.deb env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} sharkwouter-minigalaxy-759382b/.gitignore000066400000000000000000000007041455252417400205170ustar00rootroot00000000000000venv/ __pycache__/ dist/ .idea/ *~ *.pyc *.egg-info/ *.swp .eggs/ data/mo/ data/po/minigalaxy.pot data/po/*.mo .project .pydevproject release.md .coverage # Files generated when building the deb package .pybuild/ build/ debian/minigalaxy.6 debian/files debian/.debhelper/ debian/debhelper-build-stamp debian/minigalaxy/ debian/minigalaxy.substvars debian/minigalaxy.debhelper.log debian/minigalaxy.postinst.debhelper debian/minigalaxy.prerm.debhelper sharkwouter-minigalaxy-759382b/CHANGELOG.md000066400000000000000000000301041455252417400203350ustar00rootroot00000000000000**1.2.6** - Fix changing the install path causing an exception - Fix error detection & reporting on wineprefix creation failure (thanks to LeXofLeviafan) **1.2.5** - Fix filtering for installed games **1.2.4** - Fix packages missing a script **1.2.3** - Fix short freeze on startup (thanks to LeXofLeviafan) - Fix game information not showing in list view (thanks to TotalCaesar659) - Hide A Plague Tale Digital Goodies Pack (thanks to TotalCaesar659) - Remove round corners from top of the "play" button (thanks to lmeunier) - Move the Gametile menu button alongside the Play button (thanks to lmeunier) - Update Spanish translation (thanks to manurtinez) - Capitalize first letter of the "play/download/..." button (thanks to lmeunier) - Update Traditional Chinese translation (thanks to s8321414) - Added additional tooltips to buttons, labels, menu items and radio buttons (thanks to orende) - Hide CDPR Goodie Pack Content - Add notifications on successful download and installation of games (thanks to orende) - Add category filtering dialog for game library (thanks to orende) - Parallelize api.can_connect function with threads, futures (thanks to orende) - Fix available disk space being checked in parent directory (thanks to Unrud) - Create new config if reading it fails **1.2.2** - Fix progress bar not showing up for downloads - Fix downloads not being cancellable - Fix incompatibility with python 3.6 - Fix connection error texts (thanks to TotalCaesar659) - Show DLC titles in English (thanks to TotalCaesar659) - Fix version not being updated during a release - Update Norwegian Bokmål translation (thanks to kimmalmo) - Update Czech translation (thanks to jakbuz23) **1.2.1** - Fix downloads failing when content length is not returned by the server - Allow different types of downloads to happen at the same time (thanks to jgerrish) - Fix metadata file having releases in wrong order **1.2.0** - Split game information and properties into different windows (thanks to TotalCaesar659) - Add list view (thanks to TotalCaesar659) - Allow DLC to be queued up for downloading (thanks to flagrama) - Fix changing library to a directory with special characters in the name (thanks to makson96) - Fix signing in with Facebook (thanks to phlash) - Always use cached DLC icons and thumbnails (thanks to TotalCaesar659) - Cache information covers (thanks to TotalCaesar659) - Fix installers not being cleaned up like expected (thanks to Kzimir) - Fix error when opening game properties window when wine is not installed (thanks to lmeunier) - Fix freeze for games generating a lot of output (thanks to lmeunier) - Fix extracting rar based games with innoextract (thanks to Kzimir) - Allow setting wine executable per game (thanks to Kzimir) - Add GameMode support (thanks to TotalCaesar659) - Add MangoHud support (thanks to TotalCaesar659) - Add option to use Winetricks (thanks to TotalCaesar659) - Fix updates not always being detected directly after opening Minigalaxy (thanks to TotalCaesar659) - Fix desktop files generated not always being able to launch (thanks to otaconix) - Show percentage when hovering over download progress bar (thanks to TotalCaesar659) - Add option to disable update check per game (thanks to TotalCaesar659) - Add forum, GOG Database and PCGamingWiki URLs to game information (thanks to TotalCaesar659) - List genre as unknown in game information when none is found (thanks to mareksapota) - Fix changing installation path causing crashes in rare cases (thanks to makson96) - Fall back to English when locale cannot be determined (thanks to flagrama) - Add gettext to build dependencies (thanks to larslindq) - Improve error handling upon API errors - Fix several issues with launching Windows games from Minigalaxy - Fix some games getting stuck on in queue - Fix Windows game installation not caring about preferred language (thanks to Kzimir) - Add Greek translation (thanks to Pyrofanis) - Add Spanish (Spain) translation (thanks to mbarrio) - Add Romanian (Romania) translation (thanks to xSlendiX) - Update Norwegian Bokmål translation (thanks to kimmalmo) - Update Czech translation (thanks to jakbuz23) **1.1.0** - Improve integrity check after downloading (thanks to makson96) - Show an error showing Windows games cannot be enabled - Add properties menu for games where game specific actions can be made like setting launch options and opening the store page (thanks to Odelpasso and makson96) - Add a disk space check before downloading (thanks to SvdB-nonp and makson96) - Use a different color for the play button for installed games - Put installed games at the top of the list - Store saved installers in ``~/GOG Games/installer`` by default again (thanks to makson96) - Remember if the user had the installed filter enabled (thanks to makson96) - Extract Windows games in the background if Innoextract is available (thanks to makson96) - Extract Windows games in the background (thanks to Odelpasso) - Fix installing DLC for Windows games (thanks to makson96) - Fix an error showing if the user has no games (thanks to makson96) - Add option to hide games (thanks to TotalCaesar659) - Ask user if they are sure when logging out (thanks to TotalCaesar659) - Add a dark theme (thanks to TotalCaesar659) - Run post install script after installation. This fixes Full Throttle Remastered (thanks to makson96) - Fix games being shown twice - Fix crash when GOG is down (thanks to lmeunier) - Make the language configurable (thanks to TotalCaesar659 and zweif) - Add the following translations: - Czech (thanks to jakbuz23) - Finnish (thanks to heidiwenger and jonnelafin) - Italian (thanks to koraynilay) - Swedish (thanks to Newbytee) - Ukrainian (thanks to karaushu) - Update the following translations: - Dutch - German (thanks to zweif) - Norwegian Nynorsk (thanks to LordPilum) - Polish (thanks to ArturWroblewski) - Russian (thanks to TotalCaesar659) - Simplified Chinese (thanks to dummyx) - Spanish (thanks to LocalPinkRobin and advy99) - Turkish (thanks to fuzunspm) **1.0.2** - Fix updates sometimes not working - Fix some games always showing an update is available - Fix DLC not downloading (thanks to stephanlachnit) - Fix DLC update option not showing up (thanks to makson96) - Fix show store page button not showing anymore (thanks to makson96) - Fix missing thumbnails not being downloaded for already installed games (thanks to makson96) - Fix the login screen crashing in some cases (thanks to makson96) - Use the system's icon theme for icons used (thanks to stephanlachnit and makson96) **1.0.1** - Open maximized if the window was maximized when last closed (thanks to TotalCaesar659) - Kept installers are now stored in ~/.cache/minigalaxy/download - Fix about window displaying wrong version number - Fix show store page button not showing anymore (thanks to makson96) - Fix the download manager crashing when an installer has been damaged during downloading (thanks to makson96) - Fix games showing an update is available while the latest version is installed (thanks to makson96) - Fix loading the library taking a long time when many games are installed (thanks to makson96) - Fix Gex not launching - Add the following translations: - Swedish (thanks to Newbytee) - Update the following translations: - Polish (thanks to ArturWroblewski) - Russian (thanks to TotalCaesar659) **1.0.0** - Games can now be updated (thanks to mdgomes and makson96) - DLC can now be installed and updated (thanks to makson96) - The installed filter now also shows games which are downloading (thanks to makson96) - Fix crash on some systems where /usr/bin is linked to /bin (thanks to sgn) - Create new config file if old one is unreadable (thanks to SvdB-nonp) - Fix some Windows games not installing because of the directory name used (thanks to SvdB-nonp) - Fix some Windows games like Witcher 3 not launching because of the working directory not being set (thanks for kibun1) - Clean up installation files for cancelled downloads (thanks to SvdB-nonp) - Fix crash on flaky internet connection (thanks to makson96) - Use 755 permissions for all directories created by Minigalaxy - Remove cached files when cancelling a download (thanks to svdB-nonp) - Installed games should no longer be shown twice (thanks to makson96) - Add the following translations: - Simplified Chinese (thanks to dummyx) - Spanish (thanks to juanborda) - Update the following translations: - Brazilian Portuguese (thanks to EsdrasTarsis) - Dutch - French (thanks to Thomasb22) - German (thanks to BlindJerobine) - Norwegian Bokmål (thanks to kimmalmo) - Russian (thanks to protheory8) - Taiwanese Mandarin (thanks to s8321414) - Turkish (thanks to fuzunspm) **0.9.4** - Added the following translations: - Norwegian Nynorsk (thanks to LordPilum) - Russian (thanks to protheory8) - Updated the following translations: - Brazilian Portuguese (thanks to EsdrasTarsis) - French (thanks to Thomasb22) - German (thanks to BlindJerobine) - Norwegian Bokmål (thanks to kimmalmo) - Polish (thanks to ArturWroblewski) - Taiwanese Mandarin (thanks to s8321414) - Turkish (thanks to fuzunspm) - Added support for installing Windows games (with help from Odelpasso). - Added store page link to game menus (thanks to larslindq). - Fixed game directories being created without any spaces in the name (thanks to larslindq). - Fixed thumbnails not being downloaded for already installed games. - Fixed symlinks to libraries not being created correctly upon installation. - Made preparations for a Flathub package. - Added all contributors and translators to the about window. **0.9.3** - Added the following translations: - German (thanks to BlindJerobine) - Turkish (thanks to fuzunspm) - Brazilian Portuguese (thanks to EsdrasTarsis) - Norwegian Bokmål (thanks to kimmalmo) - Polish (thanks to ArturWroblewski) - French (thanks to thomansb22) - Added option to cancel downloads. - Changed the way games are downloaded to a queue instead of trying to download everything at once. - Added support option to game specific menus which open the GOG support page (thanks to BlindJerobine). - Ask for confirmation before uninstalling (thanks to Odelpasso). - Added option to display FPS in games (thanks to Odelpasso). - Downloads can now be resumed after having been cancelled before. - Installers are now verified before installing. - The active download is now resumed when restarting Minigalaxy. - Fixed issue with games not downloading. **0.9.2** - Added a button to installed games which allow you to: - Uninstall a game. - Open the directory in which the game is installed. - Added translation support. The following additional languages are now supported: - Dutch - Taiwanese Mandarin (thanks to s8321414) - German (thanks to BlindJerobine) - Added offline mode. - The system's Dosbox and Scummvm installations are now preferred over the ones bundled with games. - Improved game detection to check in all directories in the installation path. - Added the option to keep game installers (thanks to Odelpasso). - Added the option to disable staying logged in (thanks to Odelpasso). - The preferences menu now uses a file picker for setting the installation path (thanks to Odelpasso). - Startup time has been reduced. - Games which aren't installed are now grayed out. - Fixed FTL not being able to start. - Fixed issue with thumbnails sometimes not fully loading. - Fixed potential crash after logging in the first time. - Fixed close button on about window not working. **0.9.1** - Fixed crashes and freezes sometimes happening while downloading and installing games. - Fixed installation failing when the installation directory is not on same filesystem as ``/home``. - Fixed downloads crashing when the installation directory is changed or the refresh button is pressed. - Fixed changing installation directory not loading which games are installed in the new directory. - Fixed copyright file in deb package not being machine readable. - Moved binary to ``/usr/games`` in the deb package. - Add command line options ``--help``, ``--version`` and ``--reset``. The reset option will reset the cache and configuration. **0.9.0** - Initial release. sharkwouter-minigalaxy-759382b/LICENSE000066400000000000000000001045151455252417400175410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . sharkwouter-minigalaxy-759382b/README.md000066400000000000000000000171231455252417400200110ustar00rootroot00000000000000# Minigalaxy A simple GOG client for Linux. ![screenshot](screenshot.jpg?raw=true) ## Features The most important features of Minigalaxy: - Log in with your GOG account - Download the Linux games you own on GOG - Launch them! In addition to that, Minigalaxy also allows you to: - Update your games - Install and update DLC - Select which language you'd prefer to download your games in - Change where games are installed - Search your GOG Linux library - Show all games or just the ones you've installed - View the error message if a game fails to launch - Enable displaying the FPS in games - Use the system's ScummVM or DOSBox installation - Install Windows games using Wine ## Supported languages Currently, Minigalaxy can be displayed in the following languages: - Brazilian Portuguese - Czech - English - Dutch - French - Finnish - German - Italian - Norwegian Bokmål - Norwegian Nynorsk - Polish - Russian - Simplified Chinese - Spanish - Swedish - Taiwanese Mandarin - Turkish - Ukranian ## System requirements Minigalaxy should work on the following distributions: - Debian 10 or newer - Ubuntu 18.10 or newer - Linux Mint 20 or newer - Arch Linux - Manjaro - Fedora Linux 31 or newer - openSUSE Tumbleweed and Leap 15.2 or newer - MX Linux 19 or newer - Solus - Void Linux Minigalaxy does **not** ship for the following distributions because they do not contain the required version of PyGObject: - Ubuntu 18.04 - Linux Mint 19.3 - openSUSE 15.1 Other Linux distributions may work as well. Minigalaxy requires the following dependencies: - GTK+ - Python 3 - PyGObject 3.29.1+ - Webkit2gtk with API version 4.0 support - Python Requests - gettext ## Installation Packaging status
Debian/Ubuntu Available in the official repositories since Debian 11 and Ubuntu 21.04. You can install it with:
sudo apt install minigalaxy
You can also download the latest .deb package from the releases page and install it that way.
Arch/Manjaro Available the AUR. You can use an AUR helper or use the following set of commands to install Minigalaxy on Arch:
git clone https://aur.archlinux.org/minigalaxy.git
cd minigalaxy
makepkg -si
Fedora Available in the official repositories since Fedora 31. You can install it with:
sudo dnf install minigalaxy
openSUSE Available in the official repositories for openSUSE Tumbleweed and also Leap since 15.2. You can install it with:
sudo zypper in minigalaxy
Alternatively, you can use the following set of commands to install Minigalaxy on openSUSE from the devel project on OBS:
sudo zypper ar -f obs://games:tools gamestools
sudo zypper ref
sudo zypper in minigalaxy
MX Linux Available in the official repository. Please use MX Package Installer or Synaptic instead of manually installing the .deb from the repo.
Solus Available in the official repositories. You can install it with:
sudo eopkg it minigalaxy
Void Linux Available in the official repositories. You can install it with:
sudo xbps-install -S minigalaxy
Other distributions On other distributions, Minigalaxy can be downloaded and started with the following commands:
git clone https://github.com/sharkwouter/minigalaxy.git
cd minigalaxy
scripts/compile-translations.sh
bin/minigalaxy
This will be the development version. Alternatively, a tarball of a specific release can be downloaded from the releases page.
## Support If you need any help using Minigalaxy, feel free to join the [Minigalaxy Discord server](https://discord.gg/RC4cXVD). Bugs reports and feature requests can also be made [here](https://github.com/sharkwouter/minigalaxy/issues). ## Contribute Currently, help is needed with the following: - Reporting bugs in the [issue tracker](https://github.com/sharkwouter/minigalaxy/issues). - Translating to different languages. Instructions [here](https://github.com/sharkwouter/minigalaxy/wiki/Translating-Minigalaxy). - Testing issues with the ["needs testing"](https://github.com/sharkwouter/minigalaxy/issues?q=is%3Aissue+is%3Aopen+label%3A%22needs+testing%22) tag. - Working on or giving input on issues with the ["help wanted"](https://github.com/sharkwouter/minigalaxy/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) or ["good first issue"](https://github.com/sharkwouter/minigalaxy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) tag. Also check out the [the wiki](https://github.com/sharkwouter/minigalaxy/wiki/Developer-information) for developer information. Feel free to join the [Minigalaxy Discord](https://discord.gg/RC4cXVD) if you would like to help out. ## Other GOG tools - [LGOGDownloader](https://sites.google.com/site/gogdownloader/), a GOG client for the command line ## Special thanks Special thanks goes out to all contributors: - makson96 for multiple code contributions - Odelpasso for multiple code contributions - TotalCaesar659 for multiple code contributions - SvdB-nonp for multiple code contributions - tim77 for packaging Minigalaxy for Fedora, Flathub and multiple code contributions - larslindq for multiple code contributions - graag for multiple code contributions - lmeunier for multiple code contributions - BlindJerobine for translating to German and adding the support option - zweif contributions to code and the German translation - JoshuaFern for packaging Minigalaxy for NixOS and for contributing code - stephanlachnit for upstreaming to Debian and multiple code contributions - sgn for fixing a bug - otaconix for fixing a bug - phlash for fixing a bug - mareksapota for fixing a bug - zocker-160 for code cleanup - waltercool for contributing code - jgerrish for improving the download code - LexofLeviafan for fixing a bug - orende for contributing code - Unrud for contributing code - s8321414 for translating to Taiwanese Mandarin - fuzunspm for translating to Turkish - thomansb22 for translating to French - ArturWroblewski for translating to Polish - kimmalmo for translating to Norwegian Bokmål - EsdrasTarsis for translating to Brazilian Portuguese - protheory8 for translating to Russian - LordPilum for translating to Norwegian Nynorsk - dummyx for translating to simplified Chinese - juanborda for translating to Spanish - advy99i for translating to Spanish - LocalPinkRobin for translating to Spanish - Newbytee for translating to Swedish - Pyrofanis for translating to Greek - mbarrio for translating to Spanish - manurtinez for translating to Spanish - jubalh for packaging Minigalaxy for openSUSE - gasinvein for packaging Minigalaxy for flathub - metafarion for packaging Minigalaxy for Gentoo early on - SwampRabbit and Steven Pusser for packaging Minigalaxy for MX Linux - karaushu for translating to Ukrainian - koraynilay for translating to Italian - heidiwenger and jonnelafin for translating to Finnish - jakbuz23 for translating to Czech sharkwouter-minigalaxy-759382b/THIRD-PARTY-LICENSES.md000066400000000000000000000012541455252417400220240ustar00rootroot00000000000000## Logo image (data/minigalaxy.png) **[Copyright 2014 Epic Runes](https://opengameart.org/users/epic-runes)** You are free to: Share :copy and redistribute the material in any medium or format Adapt :remix, transform, and build upon the material for any purpose, even commercially. The licensor cannot revoke these freedoms as long as you follow the license terms. Under the following terms: Attribution :You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. Full license text: https://creativecommons.org/licenses/by/3.0/ sharkwouter-minigalaxy-759382b/_config.yml000066400000000000000000000002151455252417400206530ustar00rootroot00000000000000theme: jekyll-theme-primer title: Minigalaxy description: A simple GOG client for Linux that lets you download and play your GOG Linux games sharkwouter-minigalaxy-759382b/bin/000077500000000000000000000000001455252417400172765ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/bin/minigalaxy000077500000000000000000000034231455252417400213700ustar00rootroot00000000000000#!/usr/bin/env python3 import platform import sys import os import argparse import shutil from os.path import realpath, dirname, normpath import requests APPLICATION_NAME = "Minigalaxy" LAUNCH_PATH = dirname(realpath(__file__)) if os.path.isdir(os.path.join(LAUNCH_PATH, "../minigalaxy")): SOURCE_PATH = normpath(os.path.join(LAUNCH_PATH, '..')) sys.path.insert(0, SOURCE_PATH) os.chdir(SOURCE_PATH) from minigalaxy.version import VERSION from minigalaxy.paths import CONFIG_DIR, CACHE_DIR def conf_reset(): shutil.rmtree(CONFIG_DIR, ignore_errors=True) shutil.rmtree(CACHE_DIR, ignore_errors=True) def cli_params(): parser = argparse.ArgumentParser(description="A simple GOG Linux client") parser.add_argument("--reset", dest="reset", action="store_true", help="reset the configuration of Minigalaxy") parser.add_argument("-v", "--version", action="version", version=VERSION) return parser.parse_args() def main(): cli_args = cli_params() if cli_args.reset: conf_reset() # Import the gi module after parsing arguments from minigalaxy.ui.gtk import Gtk from minigalaxy.ui import Window from minigalaxy.config import Config from minigalaxy.api import Api from minigalaxy.download_manager import DownloadManager from minigalaxy.css import load_css # Start the application load_css() config = Config() session = requests.Session() session.headers.update({'User-Agent': 'Minigalaxy/{} (Linux {})'.format(VERSION, platform.machine())}) api = Api(config, session) download_manager = DownloadManager(session) window = Window(config, api, download_manager, APPLICATION_NAME) window.connect("destroy", Gtk.main_quit) Gtk.main() if __name__ == "__main__": main() sharkwouter-minigalaxy-759382b/data/000077500000000000000000000000001455252417400174375ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/icons/000077500000000000000000000000001455252417400205525ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/icons/128x128/000077500000000000000000000000001455252417400215075ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/icons/128x128/io.github.sharkwouter.Minigalaxy.png000066400000000000000000000437001455252417400305670ustar00rootroot00000000000000PNG  IHDR>agAMA a cHRMz&u0`:pQ<bKGD pHYs+tIME GH+IDATx}yeY;oή,ɒw{.ZH<06 J)#a*v.Dؽ{l]!)0sEE-$2i~8}0Ғy^."+ CGJ\7 fl~̺ @ɖb?_B 8n/7(mHpojݻwctttCwMpijMH)O"1b"zQQq%ZVZ\\Nu-f6h)eWv?AJsZg|u\D"﯊ n{W(2!b:cvYE4=Rʟ&4oYև-:533Sj pR4\}(+kR~R9:kvm۞i_5V VM|)y*# !P(!>v8v!<\0ooZ]BӆaI>F.\^w:cCc~lRs3t6dco߮BDfZ~T%X{}8baM(3{;FX^@Ji2[D4`4?VAp#&"kP8jZX)66ؾ8RV?~3wn*hZصki%0cBax333Nvfm'XJ(ÝÉ 9.ĺcn͕ ˲Z|>@L)u]<>n8w<.fq|q V[k]/ryDeuTeDqF2aÛ m=_)04n&MZ ې q˲T*Fz~ 0GҾBq qApOmwϞ{p\s;d{g8p{L2wipj!H5w!hPx* ؠ TXhK 2Δbq:o3 ^0{N嶘3 ~\ѷκތִ nf~ ߢpҥf3)`ffHak S|β } u{BN>1kb,atIœ2\ZdC; v8FpGl>م\|L+["rA|:LNN֚ݮiv]7.˽WΓK lvN@1"$>z_[DjϮ_+gȁ{6wDHDʲg(z$ZcYq- mJsssHvOeYQ\J.]:Ml޽;w6MSfO';?|$u) E1) duX:y՛e֥XZl=Sϗw|{\)*Q}00`"@(m3saf{^055ٙߺcaK?TAB29R^uÔ# ZPG/Ρy-i|U Ժw9gK[G++n 4}0TJM8uHY^ږ*3 KD x:8ff3RUAGΕ`R~ވ{',~!4p(叡?zQ:_\9:ȩ q?|V`0 _`P/F=[y~a!̇ҷ/Qf~h?Ju֘|va軱w⽨aT@$%W^1j;c9nXg~Թ_{mWnM8M! HUGk۶V7CB/L/1$ACtt:̃\ Q^sp,c >Ob8vJ֘E$/Z"c,,,VKD<"";9<^{Y 0s>Ua@+dvh׺OCJR va"t:|ty(jR Ȅ!K "%5Ǔ].$%g@I'Dw5(FbVpYKDQS~*y+Iaz ay Bko@Bdk :kJ@R Ċ-9dO?RM>^#VкoJJ ۶vU3NvS{2PfH PF0^8ki`"JsXx*, [ @\J&OMZUߟd}F+D z.۾:4w066T*H9As(^O|F\%IlH@Vj(`6@λjݦQ͠N%1cbbr \W0 HF"fNM00nf H" (~ىA dQt0M;|>{###-8z( B*EKXo=**iT/sHlb zQG{9#ߎ/K9\8>9rw<;]ʤYi,9@j1"kM foo9(03J7;bÜ!wߍ7 1ScC&I!:J!c$/;HtA)),p%Qg gѣG7d03lƽދ^x g _nnaH[MDJ̜klF O V dxߍ7M}df(p$ b_1DDa0DPJA)DPa 3;mhN}1͈E4VIkLYM(A7 KP:JM?VI&2QVJH+(eYsHA&8 Y &@Z9"`z!*PeE`ht`2+YF'qN`fw@ZaY;W{dpZ%dhP.9KD`?3Oa0b;ECXy% f^IRR)5>lz1 q|i'-YuUJNs)0:>s 8Je}knkg9xb{} R 2ՙRSq51fƉ'0::t:W;S (3ls_^/G ŠA_I kZjrq;waspE_ZZZB׃icJ4"Jn8Q*^R CH))kY qMWHONN$}zISNR@q//=Qjm;*vuf+sssZB!D0 z~X6i!|߿!:\Wb0A`&"z˭Vs)aСCJ)`B q@D<\%Eh[0 v7͝hsq aYVF 0 xeq=W6YJ)G@EX?xv` 'I]ZXj&0MRJh6Ŋl6aJ2"vau/_8]p_fepԩLj~6J%+{Hq.. Q"B.ť%JukB5\(t1)đᦔT_.=R fJ)Tenl_hZ(6~UlT*0s8i6VFDJffUY>nhZjׄZ7Ba7vVcu]eY/aT*a8v!\>?mYֹ0 /m3mX̆DǁyʲJ)r>j7GGG8N1H+hRF̌|>Jl @рy( rtn(P(ZZmKiˬ"5L ;5[J)%ZZֲN#6Mܬ@J "l5FnBxe>5} IfVaqqIVxZVyhZ18`☙-D4$=fe^hZ˼taR #4 uzYf5fup-+nVq&b66%3RylBkJGHNFSFms]7|mBD(zh4}&ctt4{غ xXGWF~#0l>bɍfv^a&@yk`vvq[|z_6Yͥ+Az0SPTmKkDD f~IКXjd &:OLL|P(r*[YZw, @նZ;e "2ӲgWǁnnqy~wΝ3$5mLAԦtBlIѳ,*AEчM<t:R3u]yXCeYukRx00Mrr hZOk~o4D^" FXa_}SZg[VT,1>>(GVo0D.8F+$'rT.FfN9:D˲ (8ϽCB0{+QmH "dA&#&(}V\ #"ٳ>i =c6_oy| <lBRÞ=C:,|Z ezFfJ ȊR\BM,Pe!\`*J49r0MqhzD5wD[R^HNHH)0-8!WeYlFQLZ/2Y82ـPe":l6wru8_z$Qkl WuzLDe{(Bkqf~&c ###8q7993]geF@ݿ7fx¶ռ\Z J']R>-ܒM̌9J*v4?BgϞEeuJ5Am8Vaͦ>tЪM=h[qָٔ!"= '&ִׅ&jS~H6v"숓<*D$ZvGy>a}ض' *f[qW>pǕRNyczz:^DZ=EDL3H":lG.ݠ 7lO癹6hD)c'.7MY;Tȳ07!,So/XVzRJ(ZiyIDBf]k[2KV#1 &{~~q0  3y= \.VbE d1i¶d}qL75}<̟"[5n?d!_qNk[me8'h+d' Pna!j[4xxEo5M2?x\ Lf4նmZ_,`l0No5 ?NJ7f#u>L0::t3eF)TEݕ l4g;jaTB?~Ri6nE}3opE/زSBRV,nzE|>od(͟a.\X5Z<QZi^Df\.˲HJGQznTF;/X`JV= <"zXann̼hY.uqP23@@_\ܳ>' !~qhay$'p٩D#)Ɯ]s^ 0p `.!$Gkc^43&/2ٯ Lf0 a٢x/3=L92M3T*5;j5xb(R,H $cFR:m;;vl46f6mlc6mlc6mlcbd$)eXIfII*nt|(1 2&&GIMP 2.10.82019:12:29 00:29:49  JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?(((((((((((((((((((((+|[W6wcqxemr)9PWw|[˦ [̺}ھ|?Q'O)p)RA*_+Kyk5}C0\S2.3M_ObbkC%v?gOht)%8K$&{?J3p>_~xͮ䰻KG(L מkZճ䴸hī%I >{9 T[[VH%C]OU?~ i G im")6cs:w_c}As m6ꦀ+QEQEQEQEQEQEQEQEQEQE{v1T s>n3Ǧ9mLmGwsD`[n0y9c#!:tG-X8Eր'ՠb< ͓YzU (jΖGBIc55I4;,R>?]yy:JԤ3+̭gvxϥr2jAl`xqw+L)0ŸE}t?y_^{ͧ:. b" 09ST.|w_c}Au m6ꧭV/>V0i_1pBpǥ|@Q@Q@Q@Q@Q@Q@Q@Q@/?D4n M$ pd6Z‘C4UQW| /u991q+pLSȶmBZF'kMJIKV6An0R Bu5IL&oFCRI|Lzp**GG?ªTk<)+ƯboI$%kpGr3[qx[#E8k+   YSi[xl #t%@"ڬ_Oe&_KM;VWDHH= fizZ$öz+RUaVUE_1}.(*͝mA:JZy5kKHDh>?Yboj3szR\j6G1B}ut XztMrY\o'55ō}kUvCbuʱ{nǧҪpդLgz|Vmv6ł\@W9Y}7"k2S"RV(4MT_|q\UOir 2u08a_m\6]T;X.嵺&U)$n2#&YIW t1;{ئ3>4*k [lU=jz}$im.ٷWoSץy0 ( ( ( ( ( ( (>>kQ% V^Z≊ZEckWP!Lēޒ+ AI%=ikVQ[̱wqjl@#l~b-6g-_2"Ǽ 5Ko\Cch%g}kAEP.R}5c=5m~xOĶ:/6RgҰN nqTQE|9xbi$'YGףW;ᩄzStWҾ,a~sP |?A`7.@AEPEPEPEPEPEPEPݖֲ"KSJ+Wúbǂd-n\N"C%p5 +&(444CZvhW%^]]A_M^Ks/P |חW|{o`9+k$(((((((#E#SyUO()l^vkeoqh@=3Uk3YdTaխ吡;N9{J}z(k{HdC/7o8X;r=hG]!ΩfO)M ci=]=xǠ`tQ{$&sE/q6Õ0ՁT(-G--.~a\Zw*lRkbQ\/:\Z4/GƷ副U7D>N{sy'UKAl!GXu֎\\$})Up >^!EPEPEPEPEPEPEP. }&PQF6 SVZkRgmݴ'i +cz!IR9 +m&ٿJHFa_3,4FPҠ;EuGC^ҟʓ _Ң9n._iqٷd~Tyڪu]a?zxSO&ˣ8?Un sIJ~uڍƆ?5t_pYug#m!VP`W@4H;Sl=M)ex+}'Q]:鶫,mOV*?I1C;RzݤRU`[@Wr\B4f(yTwF}2+T)*i=]ϙ(((((((4}Rҭ!sc;A~?4g,rZ̭|rQ8燴daAGu`w'jNРNI"(*z Җ1,eOzIcY*{`y2GB{k/X-Z* 7 : FJaր'(2NiUHȣi!?ݥ} O@ iJQ8J-!( o@aj <؃R4Q)-F Z(Heq *0'=:4"ƀ#˓MSrTG5eUa?ŏ“Cn4_)RWiKq#5֟awg|֗1`ھ8tkkwZMFD'HEPEPEPEPϾ:Xw' ?ð{쑉Pt5|:y))\(etN9Zټ~XRucOtYk M/h= bz.bnfv޸k^:~lG-W#I G;r3z>)_E54{y&w Tǎ~1Z( <ՊfR!܀uQ@Q@\@#QQ]_SV( z\e# ;#8)=>Zz w5̐^i . H ^/ vf0E&JuP$bŠK^>;ӖќAҭ 0 *^!¦h-wrAGn9==k/ CGREPFO9Ҁ<㿉VlT!LZZ2Cy0*_Z=eۈnbWh_3b9B x\oÍ"ֈ[%cjˈxu=EZe OCU20;BxlD7Gϐuu/N09)hvUj g@ v,I c!QC,}gxS:/u-Ua󍝳ۻj㧥MO/ kZL=E,|jWƣ6Ns%le1ڻ-O Gu*G/;I(##rH0z 1\Ha=vVoZn*NF#JܟDžRXEķ1a1p>\>\(ԫ>;tAssq)is֦2L{/*. & l8؏u5QEQEQEQEQEQEQEËhL`Kw a]Hx2=xgޛ I[o p9ݼ獾~|q,Y9#[fa{>ɓ 6Zg)U&8dU01eC mc*[}k q?[X-2>|N!=CĂ88ֽh>Q_Wۋsmu w6x捷+A(ay _uFGN/*(Cv[POM~RHg{t _ֺ; _5b/#xtѤy~\c#g9x]Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@O'!tEXtexif:DateTime2019:12:29 00:29:49PP>tEXtexif:ImageLength192BU`tEXtexif:ImageWidth192)tEXtexif:SoftwareGIMP 2.10.8IDMIENDB`sharkwouter-minigalaxy-759382b/data/icons/192x192/000077500000000000000000000000001455252417400215115ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/icons/192x192/io.github.sharkwouter.Minigalaxy.png000066400000000000000000000547201455252417400305750ustar00rootroot00000000000000PNG  IHDRRlzTXtRaw profile type exifxڭgEcx {v0˟Ն-"9d<]?ռ˥jzet|~߿uKg=_^~󷯻uؿNƷ&]9u?}7O_Ϗ_΍`Rt<߻di==&Cz='ع;?^O }E; ?7 Sy+롾=NBa2:&傅Hoan8 [WLﵞZ^R4l:Xd-r~/]V\y>'SFދUXq_Q5m(sΧHH_1-/T7&2X^;8bssg?:!څ R 5c 8v3rd wpܤTIN6Ǵ>K jjF!Y9N JvZZʨZj FK-jk7k{鵷޻aVZsl .:8'Ƙqgu٧ͱ(WYu՗Nu7=N8ɧzθM7rm_{־۬_2Y _YS\5^n)(gd,@ƛ2@AGsT3o(젌|B,7|ݏi\+o2町FRdm(%H]t6>p}O=_?ܩ-×Y uM"C^k0G{633';7r&C{ ~珶ݘ< \z'j=x۱O4 -a yv܇F2753G/5wcUaRnݜvdhPe\r#aes2zY Pҧ&=CqR[!g}s+?@ ph%CׯYiZk$][DM"o{c_p߼f;5/Y taWuTChsܚheR)ALxqZP `s\xgXR !m#|@2϶)EpvKbcg$+%s'xB5#'% `4>7rPtKD&!Aj=]ZPR8+Z~VO_/ٓZQUp;)U.::x$4d<~2CeG(̂{`DO%.uHޥ\hz'sntL ꪴz}v=Lh&( X8'M;$w̃0 DsUH7xrׯG hC4}BD zE)6 8 Tu=\hld./=>k}G #M`s dxQ%Y3|)ι;z,ߨmX^Cr͕EQB<E '5:!¼ zB H8_r.@00(OsQ7XE}PW}cjN.n7DG&?iمʞ٥ F֢݀Z;\$eZhD.j!<"Od*o~О< xJGIF&PC7mAڍ# a"2P5'r@UY^BH>$$I]@P*݇ڢSBux1&Tc,8.2L%$ʧ=#hԸCК `h(K`&H^ +N>ډAZ"@j[Gsha/ %@ I}8NkAwV-QմʅN5N(y@#xFRs$0A,CMworn+?"ZP^2@}]1w`H1Q-t+ܚO)*d5ŋDM"$+k$Ⳋy" DtUp]B7pzTwpy+M٤B%5° 'a)-40QN5MCD RF)hrVKBvÈDYPHv+YDE5c 9;N#? D,[Yji#bDP-01>` b~KM)Şwp8Am6Ϊ"i FJ6v}[8d]OUćS߫TabE'c'Քj(Tx偲z>tuTy`'SCS >2ƈ>*੠SU934Q29 DAȎ@*~8rgK:;NAQj \ԧؑaq.lt|6 v],6C/BC`qPF9{ZѠwC*srYv CGgiltjbD }Gh~GJ E`v<]n6.YiL,„Qh火6FMb╻b-bq\+s|m^P\t|~R>'pfғ+~0mM%#?ڒԲ)Syhk8CYSxdB>oÙe\ 4ϛh٠DE(p!ݚnbD2By׸a<4u0q=u'v(K)N7s]ؗZR0 @@hBkk)R}r yu5Pe ڨxNԁXRF! jOP .-N^~yls(Cpp(q4qxVehl SiۯO@Qk=&juJfL;,N:oە57Of*2y:eE& 7xm$fl9sY5+I>rl?nIK KiYkpp U=,P4dbYnLn͈wEމ`%ՂT*Mn[eMTR;36D<>RVp0*L^!C.V]L5\t [p#G+(0,IZl.M@"'VafBq[ܶ4K Qz4PAErGxGCjUK|Uh1~ZoÇh 5tȑ}X*V^^)huh«6m A/1e ᢳx- q,~IJHy [f:5X4`9hH5ĒO$')3X}* PT Ċ ->t5DV]n4{Qk`llA @39H;Ц>3b/lq(7X(*t_D]{2=T uQc;r|hؾݶ0{4褫"uAlhye?'-OQ @h ὴ!JkuU(9WuHCiӉYz\ >gT҈>+ᤧV_N 9&‘qDZMu_p1tQt&±m/rvj ?ͤ6MZO|Ii" z 22T}$1/zZAI|_[tAE)T<)#P<֠?`n@w{h#ϰĜ[ 蘙.EYUr:x"Q14'OP:o˄C q]>B WL/k~ni$ZH2jzhJFߏE<y,cMI:T"dZOedO ZI]UswNVI@"'\ iqQDNj#a_|"2*Y [id/EGi3\ @D"DkˀV9 "!z j"35 XGpAһvhdJ Ĭ䥦E{" @ @<c^GAvAyAd^P6UI>|JJwdҟV_cRbZzEO@VL\;9aF "ݬ +-QBViJ䁨m҆1 ؛AQ>5+>qo$k}4)0YblYY_mw&0P 6#kWN7YZ$oo.!>Ѳ-ό-kz /W{hջh1H]I{0!pQX( y@ym(921EB❴VIWODP޻>xNTbKGD pHYs+tIME 4D IDATxytdu'kCa}U$d4%'e򦱎{x/9v&9v29IrfqfflǶlcɖ%R,Rk$n"&htTW (~sP˫w] D!B"D!B"D!B"D!B"D!B"D!m `k°{ 8JDDXDefIDoJ)/xs޽G{dZ턪8 `aqfhD2sW ̒fv5f'׉EQ^&"7{333a 43ĈHۂu9"/!266vAl6Uޮ7SD]9DtNUuݻ E nܸW~"a)Q r hnXPPB \DD3_qC<`܊`5טkBOk,--ٶ^?ࡆ]o">9" Մ.UU}f4M <#A "tuM!~=|NH)2s?Q,_fOٳB׈`]6??30pdQB'Bt]/+Rcf0$\5 Hj C3OhKD:O={d`-CA u駄Ty!ĴeYyEQJ;5 pHh$\5]`qSRR0Ze槙o?|$vDv;PfYPxUQ/~VQy0򺮯KxgQRlg^dbQQTLg'62S jucOJ.Z[}O۷o)"@SU'=aS~^3_be\X,D$LtDw3xvǥ3}`@u1Jl_4ܐ õZTOJ)٦<DGM133W}Dn"}UӴ/Zu>Hql6;y^ZJRRJMJIu]_^>b}f#+\fVs睾an,d?5 ? ݸ[z7 3gԙEuf~"J]#EeY/麞urBu)[*D$ڀ=m-WG(Ixb~J"63_bB >(|/T<'yƮDpE#Jp ",[˲a ]kl6yڵ{j>AX[J9YcfM'0aT(t o=| p0 _JT%u*|?IDÍR<kff8.l\J ~N>3K!KeYb8tSMH)-w6`gūtNQU-[ a^2׻6m>VՂ iW uffk~~?~/"W}(O !i'˺Ws5==}8ϿMJ9]ax}Yz: `1Q{d&Kw'L.7JyGh7@D}_qƿ|9r@Ą?3!0t]D"x]ugjjjpvvqJ)nkb\ ?F1s_녁2VCZMjzx ǚۑٻA&",I)@SBD_i``϶RToI0Qj(Gmflˇ'~V ?beY_RUlߒ&'^jF~WUo-d2ydsssm>=" BrBHRÃ-ŊeuDF9Rip>gD.pՄOb?r9czzHV;kᯛ8L&3Y+"6[o׸r2('l NG͑fS~ O =DiN$Q_K=">⠪K)mCv s`I8Mӊe} QO'7oG}~uMD-} 41KKKrWJzm-:+eY g<( lŘg7 Ð,};:ǂIPD✪GDL|>p{#fvZc荗BSbRrU* [p*R룣 N:@Ijlxnbo ! T=ݮvܱ0K)? Bgu]˲Z?瓶m~<$ h 㼖\||ҰZ*1谘9{?#jΫjrF׆nč#3#㧶t{,+VR񹹹x>"m1# B|%_F[ up>RhqT0L|kWT[f%jQ,IM΋ ' [r֨vk3ϔEK]K{]MӼ8ηo2{{ճ#nǗB-/Z+2E  =s9!T@vԌ.ԷkPf!]e k^]UZnvnZվ=DӋuu g6^_LDR0a.jd2T*Uu=8:ܘ});,q0<@`BT4͑P2 ӂWǀ &9tdA]sǭCOȋC}7Ce<۞=`oW|;+/;E^;IYvFQUD"9MUnxTqUVh@o/y^Pi& RAbc 2,垽o~)1k!UE[RI)O6"B _H~0_JٚdvNUկbfF>pMu_/ 3sCFHʌHJ / :CH[18^PkSo,GʳĉLt:=dR %wJ;dG m\Bxm`*".Bci'FTU}Azobb1[Vj5ϼAIvL|՛bXQ ,~dgկ%n(u >"R^Sp, !^-L2\HRڭl6tӎ, zW ;lOC9J(s . R Q*@H8B0APyTO_/?-<ݷAx]hT  GVv|O#Rި4!J_vm{H#"wqqґ#Gh-a2١Aۙ)foq"Rk#HX'ai H! ?( pYTPs"!=R|ҕKn~ϯ{a={w4"d1":Iߍ;̟D .j8E_zulnn>۶0Ţ2==]>y05=3sIYq`ti }HOBSPD ti1Q3$@%8K(V^Bž)^R'jOUsqo\^cXZ'CIf~0".A|-+.)qFl>ؚBDARp}p~驟q`¯k#O'*Ia`tt("0I$OkX*|y0oALZS~ (|?̓q'fffbՈ;/<"UU=S.كjp.f|/M||rdWIqB_a =y[ێ1TJT%0NP. _E͹1 ]|"O[}'O{IJFjNDQT."ŋF{Q45M0k^f^i fR٥?๋Uo{0~ aGL9 ]e@&(_w!^g_jX(Ad6^>"0(aFa9EQn:#G~G.|? θu#D:T)! X کJ 4T5\ƓD FgCoNa<3%bEQn!2s8"`?B-O^Qx<^rI"[@H:{ Ӎat'[W?Or9\|SSSr(Jm 8z(0V!y?UI"a[sf>xڿ{;8W<ڸ4333Cو;HU D4/mփ 0WHsRpQnu&aG=prWmxWOcii }A"PUe!H`ttOߏaL`G lK#EJdչԍ?x`gV`2ض}}٨/:z7ۖB4HbAQFJfS1k'SP}H%7h8p###(JpA4yjXZZ4^y|'> |[B\ mCO"|4w-%gvqQoZqDv۞RŁ HE) CDojj}a PDl牽{-?yqY|3,,,tČ05{?DD9"@ɶPдS& "[3W|);TB"vn,`iߏ;a~~_}SSSH!i݃仡]#AP}X:C3ɵЦ[QE#"~jX ?11x =N _ӬgI7كt0&~ m(xSOaffpH@*~_SIe>{r -DDj((H&;6U܉'5 .:ă Lݢ` (K]("S߷n޼k׮mRJ\|gϞ8+4SixAlf&b$".UEQʝ>8jvxۺCe_aZ}aa/2_鋮VtRcXFV ~P9L8$@KzDDt)ڛ6Q2 y={zy pW`fܼy+ 4h,cߺ(V[mN頻nfܶNHV/N>mC׆;xg>wG0TUm/^ķ-;wBgqMض 4[&:M@n<]_r0yr^UBw4. ZG&2*%1aii 7n܀rxNN|\۪#bii {,_J NG4z+b"l*V._0ePetWYT%4gQ.8BB@4H)yަ">: )dum);&\o3 BdlA_e%ds^$f |tx jY l[ShK) V@A`߰;1M Xz˄!X@nT}oB\?2v "ɬNktkl=+òu:t"6&jIq&53/"hWYDCB~u` \]HY[QN;nY.R&gi~9+rӈEحNpսiI r+^.$I!X\oiCkyXNv 0jl{blvW݌kV`dd+L0?(qgfMى2ka oD͙Zz"C068tЊ\w56>EÉ9:k@Wrl:`y5!?SNm3<99ӧO#Zn7ӑWlkifF4,F]+&x`PsuߏӧOc߾}riɓ8qĊ4 (f_ezRU(Pd,ZCF{B.wںFyKm8qw"No N».+]}K]U"^Q zk"E9ucQTQRq${򛚦?a>}cܚ37a7l]5 ioޮs'(th%zqg4%S###x'066o~pBW ###xG#`Ϟ=д}\/WP(?fo:$t(9Yp]zD]`I)M{GžL*}[Wn:C=ܹsx055]aB >}!Lv AKo`1Kwc?K㵈j,\׵t]"&[X~ cLLp=`aaW\kאP.HRÇKR\\_܊ѕi9jQض``lj }JI +ȗF>uX X 8|0j?rY4aƚ[==?W}w[Ӭ\he̼t; ϝDw J5[~PB,/TUE*z /bxZ{[AGixj3 ̼`g C.1fnFt%YĖ d ʫ"l:~ )k(U_C6%+t ؼZE3u]hDB,[ SHlIHvQu]ZD B:}ԁRWQ-li lQu5fNy^D(9BIDAT)e$gS)Tm8栱[B͹|9$c!B8gQG2*7xsʶ-983[sE7fD ?i%SkLv3gz * CE<8M퇪AU ?( pYTjQs BcO[YkɶSN5G9&[&Ojz%{"<;r ("A:i v%${`ߑ6@zɀa"964@3h"-&P@k6b d`^#UL7 ,BH%$* >|?:@ dr N{GGM "Ѱ-XN02Dt31lA0RVӱX,Y%"Q` pmȕMR0Nv^L@ P zGj);o벎,Jvs|KÎ>_(I 'QboM=1XJ7uՉPs XF֏ Rօ]ʺ`^[?gPyd[ƭIENnp"j9|cǎ9vǙWUF-̃'?\'lTu_="Py`4fbIͩ&yBTJ#J#53unnf& @q f͟O* RD)Ewݑ-uHxHġ6/NU޲v K@ .TLה MT_Z]ЖiBchI$Rʈ{-56hY@@$@ [rĔ C~0i\0iZ!"^?N̨ &]Z_n!D>y!čD>kShBy.Xn߇6߰2mj@o=.EIAoCTKv lf+BDC"ajL4@ z*2x\z7㥃hݖ@DFI`!Ga+9{YN×%@YEW@CY_E$[!d4>Ho<i4D4{C7\@Y4z㰌`ןk/5Qj>. eF5\i`f]0[dk@ϩѲ09E @ ^Dn?6+eR vDtymDZ{47낋 >|_kMruxԟ5Ӭ!kSg_Bp6".þ} ]_W] DX1{D{rw51щH2s n Rۺ KzEэHTl|ћw5ah/FeUUUK V Л  -4;".^h AD'׊E?z}D"a !.?1MTjvn(Й3g'Bsk~!7hfaYֲ*.{MzgWCXʱ-;|ObÇ# }MMM=44={v_pɓfZ~D"X, Zbnc;}T*|Jj À H$:.3\. 'm-˲ _i)"j8}R @ض ˲Va4MT*TU3aQqx|Ei'( ?>~<by/tȓR m7')=Y`?;efeqqa=<#Ht$|JDjuQ(8E8|ޚ ECbkEQ3)[g~q:8XZZHHR>U*kDbSNw|?RUM&V''L"ϣT*5;w8|M 30 +Iukfhvi9ZEQzneh8rMuo%TTJ]3N>+ iOJ_`f+̲,8tt5Mx PSd2fSlVKnoZ s' g/a6udrյ04z+u]TUA]y;!;ʘ?bsݕPUU öm,..qU54M03 VMHE4}ٻ|i56{̈́D"U P.QV*Ds .4MC,KxүUaЅE ,aV)*ʪh1<< 48NGbucѺ>Ԗh( FGG߿yr[g Sh6 F#6Z!RJ妣#d2 ?rGN1`w]J)bubj\#Juʆ}=۸֑: n"@rsf~[]$X,d2jd}EJe!މ50Q3880P*fWuvCG+5"fffV23g݂p !i"c"z@od2d2UD"LG_9(-l%@6-3sjތ 8f}3444+ X\\ pT*'W7kka3O1 P*ϒ$߿>P( P[:39~j1]IDM{>\<TjE-4zt H"vl*-phtMUՋl6;}Dt0LjjyOAmT~p_"j6}yhh;ll}H&Rq 4Wl gf4+juPU%(ʕ5ګ4^5ߨXf___avoR_B|h-I)NuaYĪ&*H06hiX CCCH$}\bqٷv ]בJ_Z3_c\'K}h%UbʡfĚ{y>??C'hC$IrfUy k #-a(tkD4ED^ Rfc<ü"4Üd醘D6MӾs`ttyǻkFbGZm:D:}8CCCe" `a`8­k 3D4500puEA;}^JT,qƍfj(Hسg6u>̜%CwpcP. +&T*uɲO|eYs . Bgo@u]?rrSl6\Ψj aH$ŭؖ6Lbfw:?'''9*̌Zwk} Df!vZ ,822fxA)EQ*w E)sK8˄ UᅵJ>>::glhvZ#A;mrp5f>ww=ϻ900Nz*7Ct:]p[q|4P(rG?x 64 MG*3<44Xnѥ]U#jyuN8B]۩vTU8Ԁpy[ۭX,bqqL9Uy]56\9]_ɼk`bm~h(wGQ1ɼJDWqʅH$PUAZ6Ei4mݛ\ZL?<σyM7k,Y0sΖu/@S~rFWTj:Qn\V"z^i3.lf_c旈Xpn꿴>ZpJ͕Ř\b6R$Je^`4SۙmhA[ZZ:̿`}t:/ ^f/3z 뺾|ILZN"ZkhMκnWEZWӗډMo'X}Jk/..^mWoo;ih6LLe"zsppp]]FGGߜJ~"z[c``m%@z=snr^Wccc]%N[j uF{]UaUvmAn uaYw]}f7LwE!ī/8HYt:&_d2Yؠ)R{RʮÙRʔZYT*튢[,m<ƙd2 L&-_J)hy"?44266Xc?-<3)`963d2#`&IXlxe:vw80j8j5ItMF\樂יY\~H$@aIDijqUU ]h%6ql,ǁmpY;V;i26 [CAu13?+_]!R{H,k\UU*u ^B1W=cؚbw]wQkqժF+0\+Y;$ӉMUq]uwO>]Ф;t/hz=?9fN=4hbl{oZ=8ΚGk&hFjRY,k\.A\ժ.TUeY0im0{_&(gBuGmafV-ҪU5ҭa6(H$R afj` ZP֚WA}d:^y6f0MvnZ{4MR^p`ll*H${ l~ÅДX+cccuMf~r=_oD(pߌY4"% "}]Ӵa0N!ÞaƵrrZ{y5|(]ow|_}&ϭB:si=$b^۳gO-"݅7JD4Mџ03kVxkn[4&4-{رj_r%nuݾV!_kR]ﶆ@~k#t}ޭBp/sNEQev9Qֆw0$͍17} rG-B_Ӿ,0󳃃#}f\XX=-<@mNN}Z}u] Z&Pbll -1޽{0"(9"z4;0 ]`0>%R~m_2MOI' zU`7"@_A?==m9(f>GyA3`DTB}kf˨*pQU A\#p1(Bย(BDɆ=oP8nQQJy_pAQ)f>44T"D!B"D!B"D!B"D!B"D!B"D!B"D!B"Da{<IENDB`sharkwouter-minigalaxy-759382b/data/images/000077500000000000000000000000001455252417400207045ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/images/winehq_logo_glass.png000066400000000000000000000021231455252417400251140ustar00rootroot00000000000000PNG  IHDR pHYs  tIME  /!5iTXtCommentCreated with GIMPd.eIDAT8_lSeƟ;vj׵nutcu9Bt hbLH2+ ܈b)8tP 9Fڎn======>͛7u sMf2/Ȱ{ܵ]_t<ǃ1С:|~o_4&u/ >\i1(%`P%45—nܜ\xO=sgFxҮXJqw2b'NA+vͫ1dɲĮ:7}{fS ^bRX_pg$\:A8$,P #gU9z %,OL{gI"moV'$)Zҷ'lt_!$wO} C B2 yh9^ဨY!dұLw,7K(P7,A& ˽s`M_ϥ2 )-h @@J(Г8>}O}@i(rz^tӥa=3c}q+WڝNVРPNI.ja,n l~Sy%H7XW#Sц$ᭁ=-m65.2z_Hr&n,wvԌ'6T{##'< ^U𾡃kV~T:`q69:_ io.github.sharkwouter.Minigalaxy CC0-1.0 GPL-3.0-or-later Minigalaxy A simple GOG client for Linux

Minigalaxy lets you download, install and play your GOG Linux games without getting in your way.

Besides all that, it offers the following additional features:

  • Select in which language you'd prefer to download your games
  • Change where games are installed
  • Search your GOG Linux library
  • Show all games or just the ones you've installed
  • View the error message if a game fails to launch
  • Enable displaying the FPS in games

An account on gog.com is required to use this application.

io.github.sharkwouter.Minigalaxy.desktop Minigalaxy https://raw.githubusercontent.com/sharkwouter/minigalaxy/master/screenshot.jpg https://github.com/sharkwouter/minigalaxy Wouter "sharkwouter" Wijsman minigalaxy

Implements the following changes:

  • Fix changing the install path causing an exception

Implements the following changes:

  • Fix filtering for installed games

Implements the following changes:

  • Fix packages missing a script

Implements the following changes:

  • Fix short freeze on startup (thanks to LeXofLeviafan)
  • Fix game information not showing in list view (thanks to TotalCaesar659)
  • Hide A Plague Tale Digital Goodies Pack (thanks to TotalCaesar659)
  • Remove round corners from top of the "play" button (thanks to lmeunier)
  • Move the Gametile menu button alongside the Play button (thanks to lmeunier)
  • Update Spanish translation (thanks to manurtinez)
  • Capitalize first letter of the "play/download/..." button (thanks to lmeunier)
  • Update Traditional Chinese translation (thanks to s8321414)
  • Added additional tooltips to buttons, labels, menu items and radio buttons (thanks to orende)
  • Hide CDPR Goodie Pack Content
  • Add notifications on successful download and installation of games (thanks to orende)
  • Add category filtering dialog for game library (thanks to orende)
  • Parallelize api.can_connect function with threads, futures (thanks to orende)
  • Fix available disk space being checked in parent directory (thanks to Unrud)
  • Create new config if reading it fails

Implements the following changes:

  • Fix progress bar not showing up for downloads
  • Fix downloads not being cancellable
  • Fix incompatibility with python 3.6
  • Fix connection error texts (thanks to TotalCaesar659)
  • Show DLC titles in English (thanks to TotalCaesar659)
  • Fix version not being updated during a release
  • Update Norwegian Bokmål translation (thanks to kimmalmo)
  • Update Czech translation (thanks to jakbuz23)

Implements the following changes:

  • Fix downloads failing when content length is not returned by the server
  • Allow different types of downloads to happen at the same time (thanks to jgerrish)
  • Fix metadata file having releases in wrong order

Implements the following changes:

  • Split game information and properties into different windows (thanks to TotalCaesar659)
  • Add list view (thanks to TotalCaesar659)
  • Allow DLC to be queued up for downloading (thanks to flagrama)
  • Fix changing library to a directory with special characters in the name (thanks to makson96)
  • Fix signing in with Facebook (thanks to phlash)
  • Always use cached DLC icons and thumbnails (thanks to TotalCaesar659)
  • Cache information covers (thanks to TotalCaesar659)
  • Fix installers not being cleaned up like expected (thanks to Kzimir)
  • Fix error when opening game properties window when wine is not installed (thanks to lmeunier)
  • Fix freeze for games generating a lot of output (thanks to lmeunier)
  • Fix extracting rar based games with innoextract (thanks to Kzimir)
  • Allow setting wine executable per game (thanks to Kzimir)
  • Add GameMode support (thanks to TotalCaesar659)
  • Add MangoHud support (thanks to TotalCaesar659)
  • Add option to use Winetricks (thanks to TotalCaesar659)
  • Fix updates not always being detected directly after opening Minigalaxy (thanks to TotalCaesar659)
  • Fix desktop files generated not always being able to launch (thanks to otaconix)
  • Show percentage when hovering over download progress bar (thanks to TotalCaesar659)
  • Add option to disable update check per game (thanks to TotalCaesar659)
  • Add forum, GOG Database and PCGamingWiki URLs to game information (thanks to TotalCaesar659)
  • List genre as unknown in game information when none is found (thanks to mareksapota)
  • Fix changing installation path causing crashes in rare cases (thanks to makson96)
  • Fall back to English when locale cannot be determined (thanks to flagrama)
  • Add gettext to build dependencies (thanks to larslindq)
  • Improve error handling upon API errors
  • Fix several issues with launching Windows games from Minigalaxy
  • Fix some games getting stuck on in queue
  • Fix Windows game installation not caring about preferred language (thanks to Kzimir)
  • Add Greek translation (thanks to Pyrofanis)
  • Add Spanish (Spain) translation (thanks to mbarrio)
  • Add Romanian (Romania) translation (thanks to xSlendiX)
  • Update Norwegian Bokmål translation (thanks to kimmalmo)
  • Update Czech translation (thanks to jakbuz23)

Implements the following changes:

  • Improve integrity check after downloading (thanks to makson96)
  • Show an error showing Windows games cannot be enabled
  • Add properties menu for games where game specific actions can be made like setting launch options and opening the store page (thanks to Odelpasso and makson96)
  • Add a disk space check before downloading (thanks to SvdB-nonp and makson96)
  • Use a different color for the play button for installed games
  • Put installed games at the top of the list
  • Store saved installers in ``~/GOG Games/installer`` by default again (thanks to makson96)
  • Remember if the user had the installed filter enabled (thanks to makson96)
  • Extract Windows games in the background if Innoextract is available (thanks to makson96)
  • Extract Windows games in the background (thanks to Odelpasso)
  • Fix installing DLC for Windows games (thanks to makson96)
  • Fix an error showing if the user has no games (thanks to makson96)
  • Add option to hide games (thanks to TotalCaesar659)
  • Ask user if they are sure when logging out (thanks to TotalCaesar659)
  • Add a dark theme (thanks to TotalCaesar659)
  • Run post install script after installation. This fixes Full Throttle Remastered (thanks to makson96)
  • Fix games being shown twice
  • Fix crash when GOG is down (thanks to lmeunier)
  • Make the language configurable (thanks to TotalCaesar659 and zweif)

Implements the following changes:

  • Fix updates sometimes not working
  • Fix some games always showing an update is available
  • Fix DLC not downloading (thanks to stephanlachnit)
  • Fix DLC update option not showing up (thanks to makson96)
  • Fix show store page button not showing anymore (thanks to makson96)
  • Fix missing thumbnails not being downloaded for already installed games (thanks to makson96)
  • Fix the login screen crashing in some cases (thanks to makson96)
  • Use the system's icon theme for icons used (thanks to stephanlachnit and makson96)

Implements the following changes:

  • Open maximized if the window was maximized when last closed (thanks to TotalCaesar659)
  • Kept installers are now stored in ~/.cache/minigalaxy/download
  • Fix about window displaying wrong version number
  • Fix show store page button not showing anymore (thanks to makson96)
  • Fix the download manager crashing when an installer has been damaged during downloading (thanks to makson96)
  • Fix games showing an update is available while the latest version is installed (thanks to makson96)
  • Fix loading the library taking a long time when many games are installed (thanks to makson96)
  • Fix Gex not launching

Added the following translations:

  • Swedish (thanks to Newbytee)

Updated the following translations:

  • Polish (thanks to ArturWroblewski)
  • Russian (thanks to TotalCaesar659)

Implements the following changes:

  • Games can now be updated (thanks to mdgomes and makson96)
  • DLC can now be installed and updated (thanks to makson96)
  • The installed filter now also shows games which are downloading (thanks to makson96)
  • Fix crash on some systems where /usr/bin is linked to /bin (thanks to sgn)
  • Create new config file if old one is unreadable (thanks to SvdB-nonp)
  • Fix some Windows games not installing because of the directory name used (thanks to SvdB-nonp)
  • Fix some Windows games like Witcher 3 not launching because of the working directory not being set (thanks for kibun1)
  • Clean up installation files for cancelled downloads (thanks to SvdB-nonp)
  • Fix crash on flaky internet connection (thanks to makson96)
  • Use 755 permissions for all directories created by Minigalaxy
  • Remove cached files when cancelling a download (thanks to svdB-nonp)
  • Installed games should no longer be shown twice (thanks to makson96)

Added the following translations:

  • Simplified Chinese (thanks to dummyx)
  • Spanish (thanks to juanborda)

Updated the following translations:

  • Brazilian Portuguese (thanks to EsdrasTarsis)
  • Dutch
  • French (thanks to Thomasb22)
  • German (thanks to BlindJerobine)
  • Norwegian Bokmål (thanks to kimmalmo)
  • Russian (thanks to protheory8)
  • Taiwanese Mandarin (thanks to s8321414)
  • Turkish (thanks to fuzunspm
  • Added Norwegian Nynorsk translation (thanks to LordPilum).
  • Added Russian translation (thanks to protheory8).
  • Updated Brazilian Portuguese translation (thanks to EsdrasTarsis).
  • Updated French translation (thanks to thomasb22).
  • Updated German translation (thanks to BlindJerobine).
  • Updated Norwegian Bokmål translation (thanks to kimmalmo).
  • Updated Polish translation (thanks to ArturWroblewski).
  • Updated Taiwanese Mandarin translation (thanks to s8321414).
  • Updated Turkish translation (thanks to fuzunspm).
  • Added support for installing Windows games (with help from Odelpasso).
  • Added store page link to game menus (thanks to larslindq).
  • Fixed game directories being created without any spaces in the name (thanks to larslindq).
  • Fixed thumbnails not being downloaded for already installed games.
  • Fixed symlinks to libraries not being created correctly upon installation.
  • Made preparations for a Flathub package.
  • Added all contributors and translators to the about window.
  • Added German translation (thanks to BlindJerobine).
  • Added Turkish translation (thanks to fuzunspm).
  • Added Brazilian Portuguese translation (thanks to EsdrasTarsis).
  • Added Norwegian Bokmål translation (thanks to kimmalmo).
  • Added Polish translation (thanks to ArturWroblewski).
  • Added French translation (thanks to thomasb22).
  • Added option to cancel downloads.
  • Changed the way games are downloaded to a queue instead of trying to download everything at once.
  • Added support option to game specific menus which open the GOG support page (thanks to BlindJerobine).
  • Ask for confirmation before uninstalling (thanks to Odelpasso).
  • Added option to display FPS in games (thanks to Odelpasso).
  • Downloads can now be resumed after having been cancelled before.
  • Installers are now verified before installing.
  • The active download is now resumed when restarting Minigalaxy.
  • Fixed issue with games not downloading.
  • Added a button which allows you to uninstall game.
  • Added a button which allows you to open directory in which game is installed.
  • Added Dutch translation.
  • Added Taiwanese Mandarin translation (thanks to s8321414).
  • Added offline mode.
  • The system's Dosbox and Scummvm installations are now preferred over the ones bundled with games.
  • Improved game detection to check in all directories in the installation path.
  • Added the option to keep game installers (thanks to Odelpasso).
  • Added the option to disable staying logged in (thanks to Odelpasso).
  • The preferences menu now uses a file picker for setting the installation path (thanks to Odelpasso).
  • Startup time has been reduced.
  • Games which aren't installed are now grayed out.
  • Fixed crashes and freezes sometimes happening while downloading and installing games.
  • Fixed installation failing when the installation directory is not on same filesystem as /home.
  • Fixed downloads crashing when the installation directory is changed or the refresh button is pressed.
  • Fixed changing installation directory not loading which games are installed in the new directory.
  • Fixed copyright file in deb package not being machine readable.
  • Moved binary to /usr/games in the deb package.
  • Add command line options --help, --version and --reset. The reset option will reset the cache and configuration.

Initial release.

moderate moderate moderate intense intense intense none moderate moderate moderate moderate intense moderate moderate moderate moderate none none none intense intense intense intense intense moderate intense intense
sharkwouter-minigalaxy-759382b/data/po/000077500000000000000000000000001455252417400200555ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/po/cs_CZ.po000066400000000000000000000402761455252417400214270ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-09-24 10:09+0200\n" "PO-Revision-Date: 2022-09-24 10:17+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: cs_CZ\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>=2 && n<=4 ? 1 : 2);\n" "X-Generator: Poedit 3.1.1\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Jednoduchý klient GOG pro Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "O aplikaci" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "O aplikaci" #: data/ui/properties.ui:265 msgid "Added at the end of the command used to launch the game" msgstr "Přidáno na konec příkazu pro spuštění hry" #: data/ui/properties.ui:240 msgid "Added in front of the command used to launch the game" msgstr "Přidáno před příkaz pro spuštění hry" #: minigalaxy/ui/gametilelist.py:150 minigalaxy/ui/gametile.py:147 msgid "Are you sure you want to cancel downloading {}?" msgstr "Opravdu chcete zrušit stahování {}?" #: minigalaxy/ui/window.py:98 msgid "Are you sure you want to log out of GOG?" msgstr "Opravdu se chcete odhlásit z GOG?" #: minigalaxy/ui/gametilelist.py:163 minigalaxy/ui/gametile.py:160 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Opravdu chcete odinstalovat %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:31 msgid "Brazilian Portuguese" msgstr "Brazilská portugalština" #: data/ui/properties.ui:336 msgid "Cancel" msgstr "Zrušit" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Zrušit" #: data/ui/properties.ui:103 msgid "Check for updates:" msgstr "Kontrolovat aktualizace:" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Čínština" #: data/ui/properties.ui:253 msgid "Command flags:" msgstr "Příznaky příkazu:" #: minigalaxy/ui/information.py:72 minigalaxy/ui/information.py:82 #: minigalaxy/ui/information.py:92 msgid "Couldn't open forum page" msgstr "Stránku fóra se nepodařilo otevřít" #: minigalaxy/ui/information.py:62 msgid "Couldn't open store page" msgstr "Stránku obchodu se nepodařilo otevřít" #: minigalaxy/ui/information.py:52 msgid "Couldn't open support page" msgstr "Stránku podpory se nepodařilo otevřít" #. Has to end with ": " #: data/ui/preferences.ui:313 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Vytvořit zástupce: " #: minigalaxy/constants.py:32 msgid "Czech" msgstr "Čeština" #: data/ui/gametilelist.ui:194 data/ui/gametile.ui:193 msgid "DLC" msgstr "Stažitelný obsah (DLC)" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Dánština" #: minigalaxy/ui/gametilelist.py:234 minigalaxy/ui/gametilelist.py:273 #: minigalaxy/ui/gametile.py:231 minigalaxy/ui/gametile.py:270 msgid "Download error" msgstr "Chyba stahování" #: minigalaxy/constants.py:10 minigalaxy/constants.py:33 msgid "Dutch" msgstr "Nizozemština" #: minigalaxy/constants.py:11 minigalaxy/constants.py:34 msgid "English" msgstr "Angličtina" #: minigalaxy/ui/login.py:45 msgid "Facebook Login" msgstr "Přihlásit pomocí Facebooku" #: minigalaxy/ui/preferences.py:122 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" "Nepodařilo se změnit jazyk aplikace. Ujistěte se, že je na Vašem systému " "vygenerováno příslušné locale." #: minigalaxy/ui/gametilelist.py:340 minigalaxy/ui/gametile.py:337 msgid "Failed to install {}" msgstr "Instalace {} se nezdařila" #: minigalaxy/ui/library.py:146 msgid "Failed to retrieve library" msgstr "Knihovnu se nepodařilo získat" #: minigalaxy/launcher.py:52 minigalaxy/ui/gametilelist.py:134 #: minigalaxy/ui/gametile.py:131 msgid "Failed to start {}:" msgstr "{} se nezdařilo spustit:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:35 msgid "Finnish" msgstr "Finština" #: data/ui/information.ui:89 msgid "Forum" msgstr "Fórum" #: minigalaxy/constants.py:13 minigalaxy/constants.py:36 msgid "French" msgstr "Francouzština" #: minigalaxy/ui/properties.py:82 msgid "GameMode wasn't found. Using GameMode cannot be enabled." msgstr "GameMode nebyl nalezen. GameMode nelze zapnout." #: minigalaxy/ui/information.py:138 msgid "Genre" msgstr "Žánr" #: minigalaxy/constants.py:14 minigalaxy/constants.py:37 msgid "German" msgstr "Němčina" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Stránka na GitHubu" #: minigalaxy/constants.py:50 msgid "Greek" msgstr "Řečtina" #: minigalaxy/constants.py:55 msgid "Grid" msgstr "Mřížka" #: data/ui/properties.ui:153 msgid "Hide game:" msgstr "Skrýt hru:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Maďarština" #: data/ui/gametilelist.ui:239 data/ui/gametile.ui:223 msgid "Information" msgstr "Informace" #: minigalaxy/ui/information.py:28 msgid "Information about {}" msgstr "Informace o {}" #: minigalaxy/installer.py:149 msgid "Innoextract extraction failed." msgstr "Rozbalení pomocí Innoextract se nezdařilo." #: minigalaxy/installer.py:166 msgid "Innoextract not installed." msgstr "Balík Innoextract není nainstalován." #. The place directory in which games are installed. Has to end with ": " #: data/ui/preferences.ui:143 msgctxt "install_path" msgid "Installation path: " msgstr "Cesta pro instalaci: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Nainstalované" #: minigalaxy/constants.py:16 minigalaxy/constants.py:38 msgid "Italian" msgstr "Italština" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japonština" #: minigalaxy/ui/preferences.py:48 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Ponechat instalátory po stažení hry.\n" "Instalátory jsou uloženy v: {}" #. Whether installer files are kept after download. Has to end with ": " #: data/ui/preferences.ui:170 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Ponechat instalátory: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Korejština" #. The preferred language on Minigalaxy start. Has to end with ": " #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Jazyk: " #: minigalaxy/constants.py:56 msgid "List" msgstr "Seznam" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Přihlášení" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Odhlásit" #: minigalaxy/ui/properties.py:87 msgid "MangoHud wasn't found. Using MangoHud cannot be enabled." msgstr "Balík MangoHud nebyl nalezen. MangoHud nelze zapnout." #: minigalaxy/launcher.py:212 msgid "No executable was found in {}" msgstr "{} neobsahuje žádný spustitelný soubor" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norština" #: minigalaxy/constants.py:39 msgid "Norwegian Bokmål" msgstr "Norština Bokmål" #: minigalaxy/constants.py:40 msgid "Norwegian Nynorsk" msgstr "Norština Nynorsk" #: minigalaxy/installer.py:98 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Nedostatek místa pro rozbalení hry. Požadované: {} Dostupné: {}" #: data/ui/information.ui:165 data/ui/properties.ui:350 msgid "OK" msgstr "Budiž" #: data/ui/properties.ui:74 msgid "Open files" msgstr "Otevřít soubory" #: minigalaxy/ui/information.py:53 minigalaxy/ui/information.py:63 #: minigalaxy/ui/information.py:73 minigalaxy/ui/information.py:83 #: minigalaxy/ui/information.py:93 msgid "Please check your internet connection" msgstr "Zkontrolujte připojení k internetu" #: minigalaxy/constants.py:20 minigalaxy/constants.py:41 msgid "Polish" msgstr "Polština" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugalština" #: minigalaxy/ui/preferences.py:31 msgid "Preferences" msgstr "Možnosti" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Možnosti" #. Preferred language for downloading games in. Has to end with ": " #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Preferovaný jazyk hry: " #: data/ui/gametilelist.ui:254 data/ui/gametile.ui:238 msgid "Properties" msgstr "Možnosti" #: minigalaxy/ui/properties.py:34 msgid "Properties of {}" msgstr "Možnosti {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Obnovit seznam her" #: data/ui/properties.ui:48 msgid "Regedit" msgstr "Regedit" #: data/ui/properties.ui:275 msgid "Reset wine executable" msgstr "Obnovit cestu k wine" #: minigalaxy/constants.py:26 minigalaxy/constants.py:51 msgid "Romanian" msgstr "Rumunština" #: minigalaxy/constants.py:22 minigalaxy/constants.py:42 msgid "Russian" msgstr "Ruština" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Uložit" #: data/ui/properties.ui:128 msgid "Show FPS in game:" msgstr "Zobrazit FPS ve hře:" #. Has to end with ": " #: data/ui/preferences.ui:275 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Zobrazit hry pro Windows: " #. Has to end with ": " #: data/ui/preferences.ui:249 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Zobrazit skryté hry: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Zobrazit pouze nainstalované hry" #: minigalaxy/constants.py:43 msgid "Simplified Chinese" msgstr "Zjednodušená čínština" #: minigalaxy/constants.py:23 minigalaxy/constants.py:44 msgid "Spanish" msgstr "Španělština" #: minigalaxy/constants.py:45 msgid "Spanish (Spain)" msgstr "Španělština (Španělsko)" #. Whether the user will stay logged in after closing Minigalaxy. Has to end with ": " #: data/ui/preferences.ui:196 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Zapamatovat přihlášení:" #: data/ui/information.ui:76 msgid "Store" msgstr "Obchod" #: data/ui/information.ui:63 msgid "Support" msgstr "Podpora" #: minigalaxy/constants.py:24 minigalaxy/constants.py:46 msgid "Swedish" msgstr "Švédština" #: minigalaxy/constants.py:30 msgid "System default" msgstr "Výchozí hodnota systému" #: minigalaxy/installer.py:129 msgid "The installation of {} failed. Please try again." msgstr "Instalace {} se nezdařila. Zkuste to znovu." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Preferovaný jazyk pro stahované hry" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Preferovaný jazyk pro Minigalaxy" #: data/ui/preferences.ui:115 msgctxt "view_tooltip" msgid "The preferred view of game library" msgstr "Preferované zobrazení knihovny" #: minigalaxy/ui/gametilelist.py:235 minigalaxy/ui/gametile.py:232 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Došlo k chybě při získávání odkazu pro stažení!\n" "{}" #: data/ui/preferences.ui:140 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Sem se budou instalovat hry" #: minigalaxy/constants.py:47 msgid "Traditional Chinese" msgstr "Tradiční čínština" #: minigalaxy/constants.py:25 minigalaxy/constants.py:48 msgid "Turkish" msgstr "Turečtina" #: minigalaxy/constants.py:49 msgid "Ukrainian" msgstr "Ukrajinština" #: data/ui/gametilelist.ui:224 data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Odinstalovat" #: data/ui/gametilelist.ui:209 data/ui/gametile.ui:172 msgid "Update" msgstr "Aktualizovat" #: data/ui/properties.ui:178 msgid "Use GameMode:" msgstr "Použít GameMode:" #: data/ui/properties.ui:203 msgid "Use MangoHud:" msgstr "Použít MangoHud:" #. Has to end with ": " #: data/ui/preferences.ui:223 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Použít tmavé téma: " #: data/ui/properties.ui:228 msgid "Variable flags:" msgstr "Příznaky proměnných:" #: minigalaxy/ui/information.py:140 msgid "Version" msgstr "Verze" #. The preferred view of game library. Has to end with ": " #: data/ui/preferences.ui:118 msgctxt "view" msgid "View: " msgstr "Zobrazení: " #: data/ui/preferences.ui:193 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Pokud je tato možnost vypnuta, budete se muset přihlásit při každém startu " "programu Minigalaxy" #: data/ui/preferences.ui:220 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Určuje zdali se použije tmavý vzhled" #: data/ui/preferences.ui:272 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Určuje zdali se mají zobrazovat hry pro Windows" #: data/ui/preferences.ui:246 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Určuje zdali se mají zobrazovat skryté hry" #: data/ui/preferences.ui:310 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Určuje zdali se mají vytvářet zástupci pro nově nainstalované hry" #: data/ui/properties.ui:291 msgid "Wine executable:" msgstr "Cesta k wine:" #: minigalaxy/installer.py:180 msgid "Wine extraction failed." msgstr "Rozbalení Wine se nezdařilo." #: minigalaxy/ui/preferences.py:192 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine nebyl nalezen. Zobrazení her pro Windows nemůže být zapnuto." #: data/ui/properties.ui:61 msgid "Winecfg" msgstr "Winecfg" #: data/ui/properties.ui:87 msgid "Winetricks" msgstr "Winetricks" #: minigalaxy/ui/properties.py:113 msgid "Winetricks wasn't found and cannot be used." msgstr "Balík Winetricks nebyl nalezen a nemůže být použit." #: minigalaxy/ui/gametilelist.py:507 minigalaxy/ui/gametile.py:504 msgid "download" msgstr "stáhnout" #: minigalaxy/ui/gametilelist.py:547 minigalaxy/ui/gametile.py:544 msgid "downloading…" msgstr "stahování…" #: minigalaxy/ui/gametilelist.py:538 minigalaxy/ui/gametile.py:535 msgid "in queue…" msgstr "ve frontě…" #: minigalaxy/ui/gametilelist.py:526 minigalaxy/ui/gametile.py:523 msgid "install" msgstr "nainstalovat" #: minigalaxy/ui/gametilelist.py:558 minigalaxy/ui/gametile.py:555 msgid "installing…" msgstr "instalace…" #: minigalaxy/ui/gametilelist.py:573 minigalaxy/ui/gametilelist.py:603 #: minigalaxy/ui/gametile.py:570 minigalaxy/ui/gametile.py:600 msgid "play" msgstr "hrát" #: minigalaxy/ui/gametilelist.py:589 minigalaxy/ui/gametile.py:586 msgid "uninstalling…" msgstr "odinstalace…" #: minigalaxy/ui/information.py:134 msgid "unknown" msgstr "neznámé" #: minigalaxy/ui/gametilelist.py:612 minigalaxy/ui/gametile.py:609 msgid "updating…" msgstr "aktualizace…" #: minigalaxy/installer.py:131 msgid "{} could not be unzipped." msgstr "{} nemohl být rozbalen." #: minigalaxy/installer.py:74 msgid "{} failed to download." msgstr "{} se nezdařilo stáhnout." #: minigalaxy/ui/preferences.py:204 msgid "{} isn't a usable path" msgstr "{} není použitelná cesta" #: minigalaxy/installer.py:86 msgid "{} was corrupted. Please download it again." msgstr "{} byl poškozen. Stáhněte ho znovu." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Stáhnout" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Stahování…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "Ve frontě…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Nainstalovat" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Instalace…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Hrát" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Odinstalace…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Aktualizace…" sharkwouter-minigalaxy-759382b/data/po/de.po000066400000000000000000000371601455252417400210140ustar00rootroot00000000000000# 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: minigalaxy 0.9.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-10-10 21:36+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.2\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Ein einfacher GOG-Client für Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Info" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Info" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Wird am Ende des Befehls zum Starten des Spiels eingefügt" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Wird vor dem Befehl zum Starten des Spiels eingefügt" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Bist du sicher, dass du den Download von {} abbrechen möchtest?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Bist du sicher, dass du dich von GOG abmelden möchtest?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Bist du sicher, dass du %s deinstallieren möchtest?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Brasilianisches Portugiesisch" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Abbrechen" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Abbrechen" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chinesisch" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Kommandozeilenparameter:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Konnte die Store-Seite nicht öffnen" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Konnte die Support-Seite nicht öffnen" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Menü-Verknüpfungen erstellen: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Dänisch" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Download-Fehler" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Niederländisch" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Englisch" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Fehler beim Installieren von {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Fehler beim Abrufen der Bibliothek" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Fehler beim Starten von {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finnisch" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Französisch" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Genre" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Deutsch" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub-Seite" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Spiel verstecken:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Ungarisch" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Entpacken mit Innoextract fehlgeschlagen." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract ist nicht installiert." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Installationspfad: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Installiert" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italienisch" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japanisch" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Installationsdateien behalten, nachdem ein Spiel heruntergeladen wurde.\n" "Installationsdateien werden gespeichert in: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Installationsdateien behalten: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreanisch" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Sprache: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Anmelden" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Abmelden" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Es wurden keine Ausführbaren Dateien in {} gefunden" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norwegisch" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norwegisch Bokmål" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norwegisch Nynorsk" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" "Nicht genug Speicher, um das Spiel zu entpacken. Benötigt: {} Verfügbar: {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Dateien öffnen" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Bitte überprüfe deine Internetverbindung" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polnisch" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugiesisch" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Einstellungen" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Einstellungen" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Bevorzugte Sprache für Spiele: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Eigenschaften" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Eigenschaften von {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Spieleliste aktualisieren" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Russisch" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Speichern" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "FPS im Spiel anzeigen:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Windows-Spiele anzeigen: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Versteckte Spiele anzeigen: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Nur installierte Spiele anzeigen" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Vereinfachtes Chinesisch" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Spanisch" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Angemeldet bleiben:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Store" #: data/ui/properties.ui:63 msgid "Support" msgstr "Support" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Schwedisch" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Systemstandard" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Die Installation von {} ist fehlgeschlagen. Bitte versuche es erneut." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Die bevorzugte Sprache, in der Spiele heruntergeladen werden" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Die bevorzugte Sprache beim Start von Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Fehler beim Abrufen des Download-Links!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Ort, an dem Spiele installiert werden" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Traditionelles Chinesisch" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Türkisch" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ukrainisch" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Deinstallieren" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Aktualisieren" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Dunkles Farbschema verwenden: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Umgebungsvariablen:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Version" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Wenn deaktiviert, wirst du bei jedem Start von Minigalaxy aufgefordert, dich " "anzumelden" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Ob Minigalaxy ein dunkles Farbschema verwenden soll oder nicht" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Ob Windows-Spiele in der Bibliothek angezeigt werden oder nicht" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Ob versteckte Spiele in der Bibliothek angezeigt werden oder nicht" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" "Ob Verknüpfungen für neu installierte Spiele erstellt werden oder nicht" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Entpacken mit Wine fehlgeschlagen." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Wine wurde nicht gefunden. Das Anzeigen von Windows-Spielen kann nicht " "aktiviert werden." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "Herunterladen" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "Wird heruntergeladen…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "In der Warteschlange…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "Installieren" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "Wird installiert…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "Spielen" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "Wird deinstalliert…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "Wird aktualisiert…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} konnte nicht entpackt werden." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "Der Download von {} schlug fehl." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} ist kein gültiger Pfad" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} wurde beschädigt. Bitte starte den Download erneut." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Keine Verbindung zu den GOG Servern möglich" #~ msgid "Install Wine to enable this feature" #~ msgstr "Installiere Wine, um diese Funktion zu aktivieren" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Einstellungen" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Zeigt die Framerate während du spielst. Funktioniert nur mit AMD und " #~ "Nvidia Grafikkarten." #~ msgid "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgstr "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgid "Couldn't start subprocess" #~ msgstr "Konnte Unterprozess nicht starten" #~ msgid "No error message was returned" #~ msgstr "Keine Fehlermeldungen vorhanden" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy läuft nun im Offline-Modus" #~ msgid "Try again with an active internet connection" #~ msgstr "Versuche es mit einer aktiven Internetverbindung erneut" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Herunterladen" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Wird heruntergeladen…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "In der warteschlange…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installieren" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Wird installiert…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Spielen" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Wird deinstalliert…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Wird aktualisiert…" sharkwouter-minigalaxy-759382b/data/po/el.po000066400000000000000000000466271455252417400210340ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-04-03 10:04+0300\n" "PO-Revision-Date: 2023-04-03 10:07+0300\n" "Last-Translator: \n" "Language-Team: \n" "Language: el\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-Generator: Poedit 3.1.1\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Ένας απλός εξυπηρετητής για το GOG" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Περί" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Περί" #: data/ui/properties.ui:265 msgid "Added at the end of the command used to launch the game" msgstr "" "Χρησιμοποιείται για την εκκίνηση του παιχνιδιού,Προστίθεται στο τέλος της " "εντολής" #: data/ui/properties.ui:240 msgid "Added in front of the command used to launch the game" msgstr "" "Χρησιμοποιείται για την εκκίνηση του παιχνιδιού,Προστίθεται στην αρχή της " "εντολής" #: minigalaxy/ui/gametile.py:148 minigalaxy/ui/gametilelist.py:154 msgid "Are you sure you want to cancel downloading {}?" msgstr "Είστε σίγουρος/η ότι θέλετε να ακυρώσετε την εγκατάσταση του {};" #: minigalaxy/ui/window.py:101 msgid "Are you sure you want to log out of GOG?" msgstr "Είστε σίγουρος/η ότι θέλετε να αποσυνδεθείτε από το GOG;" #: minigalaxy/ui/gametile.py:161 minigalaxy/ui/gametilelist.py:167 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Είστε σίγουρος/η ότι θέλετε να απεγκαταστήσετε το %s ;" #: minigalaxy/constants.py:4 minigalaxy/constants.py:28 msgid "Brazilian Portuguese" msgstr "Πορτογαλικά (Βραζιλίας)" #: data/ui/properties.ui:336 msgid "Cancel" msgstr "Ακύρωση" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Ακύρωση" #: data/ui/properties.ui:103 msgid "Check for updates:" msgstr "Έλεγχος για ενημερώσεις:" #: minigalaxy/constants.py:5 msgid "Chinese" msgstr "Κινεζικά" #: data/ui/properties.ui:253 msgid "Command flags:" msgstr "Παράμετροι εντολών:" #: minigalaxy/ui/information.py:85 msgid "Couldn't open GOG Database page" msgstr "Δεν μπορούσαμε να ανοίξουμε την σελίδα της βάσης δεδομένων του GOG" #: minigalaxy/ui/information.py:95 msgid "Couldn't open PCGamingWiki page" msgstr "Δεν μπορούσαμε να ανοίξουμε την σελίδα PCGamingWiki" #: minigalaxy/ui/information.py:75 msgid "Couldn't open forum page" msgstr "Δεν μπορούσαμε να ανοίξουμε την σελίδα φόρουμ" #: minigalaxy/ui/information.py:65 msgid "Couldn't open store page" msgstr "Δεν μπορούσαμε να ανοίξουμε την σελίδα του καταστήματος" #: minigalaxy/ui/information.py:55 msgid "Couldn't open support page" msgstr "Δεν μπορούσαμε να ανοίξουμε την σελίδα υποστήριξης" #. Has to end with ": " #: data/ui/preferences.ui:313 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Δημιουργία καταχωρίσεων μενού: " #: minigalaxy/constants.py:29 msgid "Czech" msgstr "Τσέχικα" #: data/ui/gametile.ui:203 data/ui/gametilelist.ui:194 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:6 msgid "Danish" msgstr "Δανέζικα" #: minigalaxy/ui/gametile.py:504 msgid "Download" msgstr "Λήψη" #: minigalaxy/ui/gametile.py:232 minigalaxy/ui/gametile.py:271 #: minigalaxy/ui/gametilelist.py:238 minigalaxy/ui/gametilelist.py:277 msgid "Download error" msgstr "Σφάλμα κατα την διαρκεια κατεβασματος" #: minigalaxy/ui/gametile.py:539 msgid "Downloading…" msgstr "Ενυμέρωση…" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Dutch" msgstr "Ολλανδικά" #: minigalaxy/constants.py:8 minigalaxy/constants.py:31 msgid "English" msgstr "Αγγλικά" #: minigalaxy/ui/login.py:45 msgid "Facebook Login" msgstr "Σύνδεση μέσο Facebook" #: minigalaxy/ui/preferences.py:123 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" "Αδυναμία αλλαγής γλωσσάς προγράμματος.Βεβαιωθείτε ότι έχει δημιουργηθεί η " "καταχώρηση τοπικότητας στο σύστημα σας." #: minigalaxy/ui/gametile.py:347 minigalaxy/ui/gametilelist.py:353 msgid "Failed to install {}" msgstr "Αδυναμία εγκατάστασης {}" #: minigalaxy/ui/library.py:161 msgid "Failed to retrieve library" msgstr "Αδυναμία εύρεσης βιβλιοθήκης" #: minigalaxy/launcher.py:52 minigalaxy/ui/gametile.py:132 #: minigalaxy/ui/gametilelist.py:138 msgid "Failed to start {}:" msgstr "Αδυναμία έναρξης {}:" #: minigalaxy/constants.py:9 minigalaxy/constants.py:32 msgid "Finnish" msgstr "Φινλανδικά" #: data/ui/information.ui:89 msgid "Forum" msgstr "Φόρουμ" #: minigalaxy/constants.py:10 minigalaxy/constants.py:33 msgid "French" msgstr "Γαλλικά" #: minigalaxy/ui/properties.py:82 msgid "GameMode wasn't found. Using GameMode cannot be enabled." msgstr "" "Αποτυχία εύρεσης του GameMode. Δεν είναι δυνατή η ενεργοποιήσει χρίσης " "GameMode." #: minigalaxy/ui/information.py:141 msgid "Genre" msgstr "Είδος" #: minigalaxy/constants.py:11 minigalaxy/constants.py:34 msgid "German" msgstr "Γερμανικά" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Σελίδα GitHub" #: minigalaxy/constants.py:47 msgid "Greek" msgstr "Ελληνικά" #: minigalaxy/constants.py:52 msgid "Grid" msgstr "Πλέγμα" #: data/ui/properties.ui:153 msgid "Hide game:" msgstr "Απόκρυψη παιχνιδιού:" #: minigalaxy/constants.py:12 msgid "Hungarian" msgstr "Ουγγαρέζικα" #: minigalaxy/ui/gametile.py:530 msgid "In queue…" msgstr "Σε αναμονή…" #: data/ui/gametile.ui:233 data/ui/gametilelist.ui:239 msgid "Information" msgstr "Πληροφορίες" #: minigalaxy/ui/information.py:29 msgid "Information about {}" msgstr "Πληροφορίες περί {}" #: minigalaxy/installer.py:160 msgid "Innoextract extraction failed." msgstr "Αποτυχία αποσυμπίεσης Innoextract." #: minigalaxy/installer.py:177 msgid "Innoextract not installed." msgstr "Δεν εγκαταστάθηκε το Innoextract." #: minigalaxy/ui/gametile.py:520 msgid "Install" msgstr "Εγκατάστασή" #. The place directory in which games are installed. Has to end with ": " #: data/ui/preferences.ui:143 msgctxt "install_path" msgid "Installation path: " msgstr "Προορισμός εγκατάστασης: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Εγκατεστημένα" #: minigalaxy/ui/gametile.py:548 msgid "Installing…" msgstr "Εγκαθίσταται…" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "Italian" msgstr "Ιταλικά" #: minigalaxy/constants.py:14 msgid "Japanese" msgstr "Ιαπωνικά" #: minigalaxy/ui/preferences.py:49 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Διατήρηση αρχείων εγκατάστασης μετά από την λήψη \n" "Τα αρχεία αποθηκεύονται στον: {}" #. Whether installer files are kept after download. Has to end with ": " #: data/ui/preferences.ui:170 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Διατήρηση αρχείων εγκατάστασης: " #: minigalaxy/constants.py:15 msgid "Korean" msgstr "Κορεάτικα" #. The preferred language on Minigalaxy start. Has to end with ": " #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Γλώσσα: " #: minigalaxy/constants.py:53 msgid "List" msgstr "Λίστα" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Σύνδεση" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Αποσύνδεση" #: minigalaxy/ui/properties.py:87 msgid "MangoHud wasn't found. Using MangoHud cannot be enabled." msgstr "" "Αποτυχία εύρεσης του MangoHud. Δεν είναι δυνατή η ενεργοποιήσει χρίσης " "MangoHud." #: minigalaxy/launcher.py:212 msgid "No executable was found in {}" msgstr "Αποτυχία εύρεσης εκτελέσιμου {}" #: minigalaxy/constants.py:16 msgid "Norwegian" msgstr "Νορβηγικά" #: minigalaxy/constants.py:36 msgid "Norwegian Bokmål" msgstr "Νορβηγικά Bokmål" #: minigalaxy/constants.py:37 msgid "Norwegian Nynorsk" msgstr "Νορβηγικά Nynorsk" #: minigalaxy/installer.py:109 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" "Δεν επαρκεί χώρος για την αποσυμπίεση του παιχνιδιού.Χρειάζονται:{}Διαθέσιμα:" "{}" #: data/ui/information.ui:165 data/ui/properties.ui:350 msgid "OK" msgstr "Εντάξει" #: data/ui/properties.ui:74 msgid "Open files" msgstr "Άνοιγμα αρχείων" #: minigalaxy/ui/gametile.py:560 minigalaxy/ui/gametile.py:589 msgid "Play" msgstr "Παίξε" #: minigalaxy/ui/information.py:56 minigalaxy/ui/information.py:66 #: minigalaxy/ui/information.py:76 minigalaxy/ui/information.py:86 #: minigalaxy/ui/information.py:96 msgid "Please check your internet connection" msgstr "Παρακαλώ ελέγξτε την σύνδεση του δικτύου σας" #: minigalaxy/constants.py:17 minigalaxy/constants.py:38 msgid "Polish" msgstr "Πολωνικά" #: minigalaxy/constants.py:18 msgid "Portuguese" msgstr "Πορτογάλικα" #: minigalaxy/ui/preferences.py:31 msgid "Preferences" msgstr "Προτιμήσεις" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Προτιμήσεις" #. Preferred language for downloading games in. Has to end with ": " #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Προεπιλεγμένη γλωσσά παιχνιδιών: " #: data/ui/gametile.ui:248 data/ui/gametilelist.ui:254 msgid "Properties" msgstr "Ιδιότητες" #: minigalaxy/ui/properties.py:34 msgid "Properties of {}" msgstr "Ιδιότητες του {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Ανανέωση λίστας παιχνιδιών" #: data/ui/properties.ui:48 msgid "Regedit" msgstr "Επεξεργασία μητρώου wine" #: data/ui/properties.ui:275 msgid "Reset wine executable" msgstr "Επαναφορά εκτελέσιμου wine" #: minigalaxy/constants.py:23 minigalaxy/constants.py:48 msgid "Romanian" msgstr "Ρουμανικά" #: minigalaxy/constants.py:19 minigalaxy/constants.py:39 msgid "Russian" msgstr "Ρώσικα" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Αποθήκευση" #: data/ui/properties.ui:128 msgid "Show FPS in game:" msgstr "Προβολή FPS στο παιχνίδι:" #. Has to end with ": " #: data/ui/preferences.ui:275 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Προβολή παιχνιδιών Windows: " #. Has to end with ": " #: data/ui/preferences.ui:249 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Προβολή κρυφών παιχνιδιών: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Προβολή μόνο εγκατεστημένων παιχνιδιών" #: minigalaxy/constants.py:40 msgid "Simplified Chinese" msgstr "Κινεζικά Απλοποιημένα" #: minigalaxy/constants.py:20 minigalaxy/constants.py:41 msgid "Spanish" msgstr "Ισπανικά" #: minigalaxy/constants.py:42 msgid "Spanish (Spain)" msgstr "Ισπανικά (Ισπανίας)" #. Whether the user will stay logged in after closing Minigalaxy. Has to end with ": " #: data/ui/preferences.ui:196 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Διατήρηση Σύνδεσης:" #: data/ui/information.ui:76 msgid "Store" msgstr "Κατάστημα" #: data/ui/information.ui:63 msgid "Support" msgstr "Υποστήριξη" #: minigalaxy/constants.py:21 minigalaxy/constants.py:43 msgid "Swedish" msgstr "Σουηδικά" #: minigalaxy/constants.py:27 msgid "System default" msgstr "Προκαθορισμένη Συστήματος" #: minigalaxy/installer.py:140 msgid "The installation of {} failed. Please try again." msgstr "Η εγκατάσταση του {} απέτυχε.Παρακαλώ προσπαθήστε ξανά." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Επιθυμητή γλωσσά για την λήψη παιχνιδιών" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Επιθυμητή γλωσσά κατά την εκκίνηση του Minigalaxy" #: data/ui/preferences.ui:115 msgctxt "view_tooltip" msgid "The preferred view of game library" msgstr "Επιθυμητή απεικόνιση βιβλιοθήκης παιχνιδιών" #: minigalaxy/ui/gametile.py:233 minigalaxy/ui/gametilelist.py:239 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "Σφάλμα κατά την προσπάθεια απόκτησης συνδέσμου λήψης!" #: data/ui/preferences.ui:140 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Εδώ θα εγκαθίστανται τα παιχνίδια" #: minigalaxy/constants.py:44 msgid "Traditional Chinese" msgstr "Κινεζικά Παραδοσιακά" #: minigalaxy/constants.py:22 minigalaxy/constants.py:45 msgid "Turkish" msgstr "Τουρκικά" #: minigalaxy/constants.py:46 msgid "Ukrainian" msgstr "Ουκρανικά" #: minigalaxy/installer.py:74 msgid "Unhandled error." msgstr "Μη διαχειρίσιμο σφάλμα." #: data/ui/gametile.ui:218 data/ui/gametilelist.ui:224 msgid "Uninstall" msgstr "Απεγκατάσταση" #: minigalaxy/ui/gametile.py:575 msgid "Uninstalling…" msgstr "Απ εγκαθίσταται…" #: data/ui/gametile.ui:182 data/ui/gametilelist.ui:209 msgid "Update" msgstr "Ενημέρωση" #: minigalaxy/ui/gametile.py:598 msgid "Updating…" msgstr "Ενημερώνεται…" #: data/ui/properties.ui:178 msgid "Use GameMode:" msgstr "Χρήση GameMode:" #: data/ui/properties.ui:203 msgid "Use MangoHud:" msgstr "Χρήση MangoHud:" #. Has to end with ": " #: data/ui/preferences.ui:223 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Χρύση σκοτεινού θέματος: " #: data/ui/properties.ui:228 msgid "Variable flags:" msgstr "Μεταβλητές παραμέτρων:" #: minigalaxy/ui/information.py:143 msgid "Version" msgstr "Εκδοσή" #. The preferred view of game library. Has to end with ": " #: data/ui/preferences.ui:118 msgctxt "view" msgid "View: " msgstr "Απεικόνιση: " #: data/ui/preferences.ui:193 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Όταν απενεργοποιηθεί θα σας ζητείται σύνδεση κάθε φόρα που εκκινείται το " "Minigalaxy" #: data/ui/preferences.ui:220 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "" "Άμα θα επιτρέπεται στο Minigalaxy να χρησιμοποιήσει το σκοτεινό θέμα η όχι" #: data/ui/preferences.ui:272 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "" "Άμα θα επιτρέπεται στα παιχνίδια windows η προβολή στην βιβλιοθήκη η όχι" #: data/ui/preferences.ui:246 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "" "Άμα θα επιτρέπεται στα κριμένα παιχνίδια η προβολή στην βιβλιοθήκη η όχι" #: data/ui/preferences.ui:310 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" "Άμα θα επιτρέπεται η δημιουργία συντομεύσεων για τα νέα εγκατεστημένα " "παιχνίδια η όχι" #: data/ui/properties.ui:291 msgid "Wine executable:" msgstr "Εκτελέσιμο wine:" #: minigalaxy/installer.py:198 msgid "Wine extraction failed." msgstr "Αποτυχία αποσυμπίεσης wine." #: minigalaxy/ui/preferences.py:193 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Αποτυχία εύρεσης wine.Δεν είναι δυνατή η δυνατότητα προβολής παιχνιδιών " "Windows." #: data/ui/properties.ui:61 msgid "Winecfg" msgstr "Winecfg" #: data/ui/properties.ui:87 msgid "Winetricks" msgstr "Winetricks" #: minigalaxy/ui/properties.py:113 msgid "Winetricks wasn't found and cannot be used." msgstr "Αποτυχία εύρεσης winetricks.Δεν είναι δυνατή η χρήση του." #: minigalaxy/ui/gametilelist.py:519 msgid "download" msgstr "λήψη" #: minigalaxy/ui/gametilelist.py:559 msgid "downloading…" msgstr "ενυμέρωση…" #: minigalaxy/ui/gametilelist.py:550 msgid "in queue…" msgstr "σε αναμονή…" #: minigalaxy/ui/gametilelist.py:538 msgid "install" msgstr "εγκατάστασή" #: minigalaxy/ui/gametilelist.py:570 msgid "installing…" msgstr "εγκαθίσταται…" #: minigalaxy/ui/gametilelist.py:585 minigalaxy/ui/gametilelist.py:615 msgid "play" msgstr "παίξε" #: minigalaxy/ui/gametilelist.py:601 msgid "uninstalling…" msgstr "απ εγκαθίσταται…" #: minigalaxy/ui/information.py:137 msgid "unknown" msgstr "άγνωστο" #: minigalaxy/ui/gametilelist.py:624 msgid "updating…" msgstr "ενημερώνεται…" #: minigalaxy/installer.py:142 msgid "{} could not be unzipped." msgstr "{} αδυναμία αποσυμπίεσης." #: minigalaxy/installer.py:85 msgid "{} failed to download." msgstr "() αποτυχία λήψης." #: minigalaxy/ui/preferences.py:205 msgid "{} isn't a usable path" msgstr "()μη εύχρηστη διαδρομη" #: minigalaxy/installer.py:97 msgid "{} was corrupted. Please download it again." msgstr "()διαφθείρει.Παρακαλώ κατεβαστε το ξανά." sharkwouter-minigalaxy-759382b/data/po/es.po000066400000000000000000000341701455252417400210310ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-10-07 18:22+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.3.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Un cliente sencillo de GOG para Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Acerca de" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Acerca de" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Añadido al final del comando utilizado para lanzar el juego" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Añadido al comienzo del comando utilizado para lanzar el juego" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "¿Estás seguro que deseas cancelar la descarga de {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "¿Estás seguro que quieres cerrar la sesión de GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "¿Estás seguro que deseas desinstalar %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Portugués de Brasil" #: data/ui/properties.ui:266 #, fuzzy msgid "Cancel" msgstr "Cancelar" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Cancelar" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chino" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Argumentos del comando:" #: minigalaxy/ui/properties.py:103 #, fuzzy msgid "Couldn't open store page" msgstr "No se pudo abrir la página de soporte" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "No se pudo abrir la página de soporte" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Crear accesos directos en el menú: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Danés" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 #, fuzzy msgid "Download error" msgstr "Error en la descarga" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Holandés" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Inglés" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" "Fallo al cambiar el idioma del programa. Asegúrate de que el local se " "hagenerado en tu sistema" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "No se pudo instalar {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "No se pudo recuperar la biblioteca" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "No se pudo iniciar {}" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finlandés" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Francés" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Género" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Alemán" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Página de GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Ocultar juego:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Húngaro" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "La extracción de Innoextract falló." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract no instalado." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Ruta de instalación: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Instalados" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italiano" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japones" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Guardar los instaladores al descargar un juego.\n" "Los instaladores se guardan en: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Guardar los instaladores: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Coreano" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Idioma: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Iniciar sesión" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Cerrar sesión" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "No se encontró ningún ejecutable en {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Noruego" #: minigalaxy/constants.py:38 #, fuzzy msgid "Norwegian Bokmål" msgstr "Noruego" #: minigalaxy/constants.py:39 #, fuzzy msgid "Norwegian Nynorsk" msgstr "Noruego" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" "No hay suficiente espacio para extraer el juego. Se necesita: {} Disponible: " "{}" #: data/ui/properties.ui:280 msgid "OK" msgstr "Aceptar" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Abrir Archivos" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Por favor, verifique su conexión a internet" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polaco" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugués" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Preferencias" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Preferencias" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Idioma preferido: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Propiedades" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Propiedades de {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Refrescar lista de juegos" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Ruso" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Guardar" #: data/ui/properties.ui:154 #, fuzzy msgid "Show FPS in game:" msgstr "Mostrar el indicador de FPS en los juegos:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Mostrar los juegos para Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 #, fuzzy msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Mostrar los juegos ocultos: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Mostrar solo los juegos instalados" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Chino simplificado" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Español" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Permanecer conectado:" #: data/ui/properties.ui:77 #, fuzzy msgid "Store" msgstr "Pagina de la tienda" #: data/ui/properties.ui:63 #, fuzzy msgid "Support" msgstr "Soporte" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Sueco" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Por defecto del sistema" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "La instalación de {} ha fallado. Por favor, intentelo de nuevo." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "El lenguage preferido en el que descargar los juegos" #: data/ui/preferences.ui:65 #, fuzzy msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "El lenguage preferido en el que lanzar Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "¡Ocurrio un error al intentar obtener el enlace de descarga!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "La ruta donde se instalaran los juegos" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Chino tradicional" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turco" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ucraniano" #: data/ui/gametile.ui:208 #, fuzzy msgid "Uninstall" msgstr "Desinstalar" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Actualizar" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Usar tema oscuro: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Parámetros" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Versión" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Si se desactiva se te requerira que inicies sesion cada vez que abras " "Minigalaxy" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Si Minigalaxy ha de utilizar el tema oscuro o no" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Si mostrar los juegos para Windows en la biblioteca o no" #: data/ui/preferences.ui:221 #, fuzzy msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Si mostrar los juegos para Windows en la biblioteca o no" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Si los atajos son creados al instalar nuevos juegos o no" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "La extracción de Wine fallo." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "No se encontro Wine. Mostrar los juegos para Windows no se puede activar" #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "descarga" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "descargando…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "en cola…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "instalar" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "instalando…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "jugar" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "desinstalando…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 #, fuzzy msgid "updating…" msgstr "descargando…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} no se pudo descomprimir." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} no se pudo descargar." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} no es una ruta valida" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} se ha corrompido. Por favor descargalo nuevamente." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "No se pudo conectar a los servidores de GOG" #~ msgid "Install Wine to enable this feature" #~ msgstr "Instala Wine para habilitar esta función" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Configuración" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Muestra el indicador de los cuadros por segundo mientras juegas. Solo " #~ "funciona con placas graficas de AMD y nVidia." #~ msgid "Couldn't start subprocess" #~ msgstr "No se pudo iniciar el sub-proceso" #~ msgid "No error message was returned" #~ msgstr "Ningún mensaje de error fue devuelto" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Descarga" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Descargando…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "En cola…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Instalar" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Instalando…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Jugar" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Desinstalando…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Descargando…" sharkwouter-minigalaxy-759382b/data/po/es_ES.po000066400000000000000000000410771455252417400214240ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-02-21 13:23-0300\n" "PO-Revision-Date: 2023-02-21 13:26-0300\n" "Last-Translator: \n" "Language-Team: \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-Generator: Poedit 3.2.2\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Un cliente sencillo de GOG para Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Acerca de" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Acerca de" #: data/ui/properties.ui:265 msgid "Added at the end of the command used to launch the game" msgstr "Añadido al final del comando utilizado para lanzar el juego" #: data/ui/properties.ui:240 msgid "Added in front of the command used to launch the game" msgstr "Añadido al comienzo del comando utilizado para lanzar el juego" #: minigalaxy/ui/gametile.py:151 minigalaxy/ui/gametilelist.py:154 msgid "Are you sure you want to cancel downloading {}?" msgstr "¿Estás seguro de que quieres cancelar la descarga de {}?" #: minigalaxy/ui/window.py:101 msgid "Are you sure you want to log out of GOG?" msgstr "¿Estás seguro de que quieres cerrar la sesión de GOG?" #: minigalaxy/ui/gametile.py:164 minigalaxy/ui/gametilelist.py:167 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "¿Estás seguro de que quieres desinstalar %s?" #: minigalaxy/constants.py:4 minigalaxy/constants.py:28 msgid "Brazilian Portuguese" msgstr "Portugués de Brasil" #: data/ui/properties.ui:336 msgid "Cancel" msgstr "Cancelar" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Cancelar" #: data/ui/properties.ui:103 msgid "Check for updates:" msgstr "Comprobar actualizaciones:" #: minigalaxy/constants.py:5 msgid "Chinese" msgstr "Chino" #: data/ui/properties.ui:253 msgid "Command flags:" msgstr "Argumentos del comando:" #: minigalaxy/ui/information.py:85 msgid "Couldn't open GOG Database page" msgstr "No se ha podido abrir la página de la base de datos de GOG" #: minigalaxy/ui/information.py:95 msgid "Couldn't open PCGamingWiki page" msgstr "No se ha podido abrir la página de PCGamingWiki" #: minigalaxy/ui/information.py:75 msgid "Couldn't open forum page" msgstr "No se ha podido abrir la página del foro" #: minigalaxy/ui/information.py:65 msgid "Couldn't open store page" msgstr "No se ha podido abrir la página de la tienda" #: minigalaxy/ui/information.py:55 msgid "Couldn't open support page" msgstr "No se ha podido abrir la página de soporte" #. Has to end with ": " #: data/ui/preferences.ui:313 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Crear accesos directos en el menú: " #: minigalaxy/constants.py:29 msgid "Czech" msgstr "Checo" #: data/ui/gametile.ui:193 data/ui/gametilelist.ui:194 msgid "DLC" msgstr "Contenido descargable" #: minigalaxy/constants.py:6 msgid "Danish" msgstr "Danés" #: minigalaxy/ui/gametile.py:235 minigalaxy/ui/gametile.py:274 #: minigalaxy/ui/gametilelist.py:238 minigalaxy/ui/gametilelist.py:277 msgid "Download error" msgstr "Error en la descarga" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Dutch" msgstr "Holandés" #: minigalaxy/constants.py:8 minigalaxy/constants.py:31 msgid "English" msgstr "Inglés" #: minigalaxy/ui/login.py:45 msgid "Facebook Login" msgstr "Login de Facebook" #: minigalaxy/ui/preferences.py:123 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" "Fallo al cambiar el idioma del programa. Asegúrate de que el local se ha " "generado en tu sistema." #: minigalaxy/ui/gametile.py:350 minigalaxy/ui/gametilelist.py:353 msgid "Failed to install {}" msgstr "No se ha podido instalar {}" #: minigalaxy/ui/library.py:161 msgid "Failed to retrieve library" msgstr "No se ha podido recuperar la biblioteca" #: minigalaxy/launcher.py:52 minigalaxy/ui/gametile.py:135 #: minigalaxy/ui/gametilelist.py:138 msgid "Failed to start {}:" msgstr "No se ha podido iniciar {}:" #: minigalaxy/constants.py:9 minigalaxy/constants.py:32 msgid "Finnish" msgstr "Finlandés" #: data/ui/information.ui:89 msgid "Forum" msgstr "Foro" #: minigalaxy/constants.py:10 minigalaxy/constants.py:33 msgid "French" msgstr "Francés" #: minigalaxy/ui/properties.py:82 msgid "GameMode wasn't found. Using GameMode cannot be enabled." msgstr "" "No se ha encontrado GameMode. No es posible activar el uso de GameMode." #: minigalaxy/ui/information.py:141 msgid "Genre" msgstr "Género" #: minigalaxy/constants.py:11 minigalaxy/constants.py:34 msgid "German" msgstr "Alemán" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Página de GitHub" #: minigalaxy/constants.py:47 msgid "Greek" msgstr "Griego" #: minigalaxy/constants.py:52 msgid "Grid" msgstr "Rejilla" #: data/ui/properties.ui:153 msgid "Hide game:" msgstr "Ocultar juego:" #: minigalaxy/constants.py:12 msgid "Hungarian" msgstr "Húngaro" #: data/ui/gametile.ui:223 data/ui/gametilelist.ui:239 msgid "Information" msgstr "Información" #: minigalaxy/ui/information.py:29 msgid "Information about {}" msgstr "Información sobre {}" #: minigalaxy/installer.py:157 msgid "Innoextract extraction failed." msgstr "Ha fallado la extracción de Innoextract." #: minigalaxy/installer.py:174 msgid "Innoextract not installed." msgstr "Innoextract no está instalado." #. The place directory in which games are installed. Has to end with ": " #: data/ui/preferences.ui:143 msgctxt "install_path" msgid "Installation path: " msgstr "Ruta de instalación: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Instalados" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "Italian" msgstr "Italiano" #: minigalaxy/constants.py:14 msgid "Japanese" msgstr "Japonés" #: minigalaxy/ui/preferences.py:49 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Guardar los instaladores tras descargar un juego.\n" "Los instaladores se guardan en: {}" #. Whether installer files are kept after download. Has to end with ": " #: data/ui/preferences.ui:170 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Guardar los instaladores: " #: minigalaxy/constants.py:15 msgid "Korean" msgstr "Coreano" #. The preferred language on Minigalaxy start. Has to end with ": " #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Idioma: " #: minigalaxy/constants.py:53 msgid "List" msgstr "Lista" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Iniciar sesión" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Cerrar sesión" #: minigalaxy/ui/properties.py:87 msgid "MangoHud wasn't found. Using MangoHud cannot be enabled." msgstr "" "No se ha encontrado MangoHud. No es posible activar el uso de MangoHud." #: minigalaxy/launcher.py:212 msgid "No executable was found in {}" msgstr "No se ha encontrado ningún ejecutable en {}" #: minigalaxy/constants.py:16 msgid "Norwegian" msgstr "Noruego" #: minigalaxy/constants.py:36 msgid "Norwegian Bokmål" msgstr "Noruego Bokmål" #: minigalaxy/constants.py:37 msgid "Norwegian Nynorsk" msgstr "Noruego Nynorsk" #: minigalaxy/installer.py:106 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" "No hay suficiente espacio para extraer el juego. Se necesita: {} Disponible: " "{}" #: data/ui/information.ui:165 data/ui/properties.ui:350 msgid "OK" msgstr "Aceptar" #: data/ui/properties.ui:74 msgid "Open files" msgstr "Abrir archivos" #: minigalaxy/ui/information.py:56 minigalaxy/ui/information.py:66 #: minigalaxy/ui/information.py:76 minigalaxy/ui/information.py:86 #: minigalaxy/ui/information.py:96 msgid "Please check your internet connection" msgstr "Por favor, compruebe su conexión a Internet" #: minigalaxy/constants.py:17 minigalaxy/constants.py:38 msgid "Polish" msgstr "Polaco" #: minigalaxy/constants.py:18 msgid "Portuguese" msgstr "Portugués" #: minigalaxy/ui/preferences.py:31 msgid "Preferences" msgstr "Preferencias" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Preferencias" #. Preferred language for downloading games in. Has to end with ": " #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Idioma preferido para el juego: " #: data/ui/gametile.ui:238 data/ui/gametilelist.ui:254 msgid "Properties" msgstr "Propiedades" #: minigalaxy/ui/properties.py:34 msgid "Properties of {}" msgstr "Propiedades de {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Refrescar lista de juegos" #: data/ui/properties.ui:48 msgid "Regedit" msgstr "Regedit" #: data/ui/properties.ui:275 msgid "Reset wine executable" msgstr "Restablecer ejecutable de wine" #: minigalaxy/constants.py:23 minigalaxy/constants.py:48 msgid "Romanian" msgstr "Rumano" #: minigalaxy/constants.py:19 minigalaxy/constants.py:39 msgid "Russian" msgstr "Ruso" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Guardar" #: data/ui/properties.ui:128 msgid "Show FPS in game:" msgstr "Mostrar indicador de FPS en el juego:" #. Has to end with ": " #: data/ui/preferences.ui:275 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Mostrar los juegos para Windows: " #. Has to end with ": " #: data/ui/preferences.ui:249 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Mostrar los juegos ocultos: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Mostrar solo los juegos instalados" #: minigalaxy/constants.py:40 msgid "Simplified Chinese" msgstr "Chino simplificado" #: minigalaxy/constants.py:20 minigalaxy/constants.py:41 msgid "Spanish" msgstr "Español" #: minigalaxy/constants.py:42 msgid "Spanish (Spain)" msgstr "Español (España)" #. Whether the user will stay logged in after closing Minigalaxy. Has to end with ": " #: data/ui/preferences.ui:196 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Permanecer conectado:" #: data/ui/information.ui:76 msgid "Store" msgstr "Tienda" #: data/ui/information.ui:63 msgid "Support" msgstr "Soporte" #: minigalaxy/constants.py:21 minigalaxy/constants.py:43 msgid "Swedish" msgstr "Sueco" #: minigalaxy/constants.py:27 msgid "System default" msgstr "Por defecto del sistema" #: minigalaxy/installer.py:137 msgid "The installation of {} failed. Please try again." msgstr "Ha fallado la instalación de {}. Por favor, inténtalo de nuevo." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "El idioma preferido al descargar los juegos" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "El idioma preferido al iniciar Minigalaxy" #: data/ui/preferences.ui:115 msgctxt "view_tooltip" msgid "The preferred view of game library" msgstr "La vista preferida de la biblioteca de juegos" #: minigalaxy/ui/gametile.py:236 minigalaxy/ui/gametilelist.py:239 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Ocurrió un error al intentar obtener el enlace de descarga\n" "{}" #: data/ui/preferences.ui:140 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "La ruta donde se instalarán los juegos" #: minigalaxy/constants.py:44 msgid "Traditional Chinese" msgstr "Chino tradicional" #: minigalaxy/constants.py:22 minigalaxy/constants.py:45 msgid "Turkish" msgstr "Turco" #: minigalaxy/constants.py:46 msgid "Ukrainian" msgstr "Ucraniano" #: data/ui/gametile.ui:208 data/ui/gametilelist.ui:224 msgid "Uninstall" msgstr "Desinstalar" #: data/ui/gametile.ui:172 data/ui/gametilelist.ui:209 msgid "Update" msgstr "Actualizar" #: data/ui/properties.ui:178 msgid "Use GameMode:" msgstr "Usar GameMode:" #: data/ui/properties.ui:203 msgid "Use MangoHud:" msgstr "Usar MangoHud:" #. Has to end with ": " #: data/ui/preferences.ui:223 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Usar tema oscuro: " #: data/ui/properties.ui:228 msgid "Variable flags:" msgstr "Parámetros:" #: minigalaxy/ui/information.py:143 msgid "Version" msgstr "Versión" #. The preferred view of game library. Has to end with ": " #: data/ui/preferences.ui:118 msgctxt "view" msgid "View: " msgstr "Vista: " #: data/ui/preferences.ui:193 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Si se desactiva se te requerirá que inicies sesión cada vez que inicies " "Minigalaxy" #: data/ui/preferences.ui:220 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Si Minigalaxy ha de utilizar el tema oscuro o no" #: data/ui/preferences.ui:272 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Si mostrar los juegos para Windows en la biblioteca o no" #: data/ui/preferences.ui:246 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Si mostrar los juegos ocultos en la biblioteca o no" #: data/ui/preferences.ui:310 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Si los atajos son creados al instalar nuevos juegos o no" #: data/ui/properties.ui:291 msgid "Wine executable:" msgstr "Ejecutable de Wine:" #: minigalaxy/installer.py:188 msgid "Wine extraction failed." msgstr "Ha fallado la extracción de Wine." #: minigalaxy/ui/preferences.py:193 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "No se ha encontrado Wine. No es posible activar mostrar los juegos para " "Windows." #: data/ui/properties.ui:61 msgid "Winecfg" msgstr "Winecfg" #: data/ui/properties.ui:87 msgid "Winetricks" msgstr "Winetricks" #: minigalaxy/ui/properties.py:113 msgid "Winetricks wasn't found and cannot be used." msgstr "No se ha encontrado Winetricks y no se puede utilizar." #: minigalaxy/ui/gametile.py:516 minigalaxy/ui/gametilelist.py:519 msgid "download" msgstr "descargar" #: minigalaxy/ui/gametile.py:556 minigalaxy/ui/gametilelist.py:559 msgid "downloading…" msgstr "descargando…" #: minigalaxy/ui/gametile.py:547 minigalaxy/ui/gametilelist.py:550 msgid "in queue…" msgstr "en cola…" #: minigalaxy/ui/gametile.py:535 minigalaxy/ui/gametilelist.py:538 msgid "install" msgstr "instalar" #: minigalaxy/ui/gametile.py:567 minigalaxy/ui/gametilelist.py:570 msgid "installing…" msgstr "instalando…" #: minigalaxy/ui/gametile.py:582 minigalaxy/ui/gametile.py:612 #: minigalaxy/ui/gametilelist.py:585 minigalaxy/ui/gametilelist.py:615 msgid "play" msgstr "jugar" #: minigalaxy/ui/gametile.py:598 minigalaxy/ui/gametilelist.py:601 msgid "uninstalling…" msgstr "desinstalando…" #: minigalaxy/ui/information.py:137 msgid "unknown" msgstr "desconocido" #: minigalaxy/ui/gametile.py:621 minigalaxy/ui/gametilelist.py:624 msgid "updating…" msgstr "actualizando…" #: minigalaxy/installer.py:139 msgid "{} could not be unzipped." msgstr "{} no se ha podido descomprimir." #: minigalaxy/installer.py:82 msgid "{} failed to download." msgstr "{} no se ha podido descargar." #: minigalaxy/ui/preferences.py:205 msgid "{} isn't a usable path" msgstr "{} no es una ruta válida" #: minigalaxy/installer.py:94 msgid "{} was corrupted. Please download it again." msgstr "{} se ha corrompido. Por favor, descárgalo nuevamente." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Descargar" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Descargando…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "En cola…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Instalar" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Instalando…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Jugar" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Desinstalando…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Actualizando…" sharkwouter-minigalaxy-759382b/data/po/fi.po000066400000000000000000000323521455252417400210200ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-09-30 12:59+0300\n" "Last-Translator: \n" "Language-Team: \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.2\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-Bookmarks: 3,-1,-1,-1,-1,-1,-1,-1,-1,-1\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Yksinkertainen GOG-asiakasohjelma Linuxille" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Tietoja" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Tietoja" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Lisätään pelin käynnistämiseen käytettävän komennon loppuun" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Lisätään pelin käynnistämiseen käytettävän komennon alkuun" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Oletko varma että haluat keskeyttää latauksen {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Oletko varma että halua kirjautua ulos palvelusta GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Oletko varma että haluat poistaa asennuksen %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Brasilian portugali" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Peruuta" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Peruuta" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Kiina" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Komentoparametrit:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Kauppasivua ei voitu avata" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Tukisivua ei voitu avata" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Luo valikon pikakuvakkeet: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC-lisäosa" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Tanska" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Latausvirhe" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Hollanti" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Englanti" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "{} asentaminen epäonnistui" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Kirjaston noutaminen epäonnistui" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Kohteen {} käynnistäminen epäonnistui:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Suomi" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Ranska" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Genre" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Saksa" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub-sivu" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Piilota peli:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Unkari" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Innoextract-purku epäonnistui." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract ei asennettu." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Asennuspolku: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Asennetut" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italia" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japani" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Säilytä asennusohjelmat pelin lataamisen jälkeen.\n" "Asennusohjelmat löytyvät täältä: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Säilytä asennusohjelmat: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Korea" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Kieli: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Kirjaudu sisään" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Kirjaudu ulos" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Ajettavaa käynnistystiedostoa ei löytynyt kohteelle {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norja" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norja (kirjakieli)" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norja (Nynorsk)" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" "Levytilaa ei ole tarpeeksi pelin purkamiseen. Tarvitaan {} Saatavilla {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Avaa tiedostot" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Ole hyvä ja tarkista, että internet-yhteytesi on toiminnassa" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Puola" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugali" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Asetukset" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Asetukset" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Ensisijainen kieli peleille: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Ominaisuudet" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Ominaisuudet kohteelle {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Virkistä peliluettelo" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit-muokkaus" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Venäjä" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Tallenna" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Näytä ruudunpäivitysnopeus pelissä:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Näytä Windows-pelit: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Näytä piilotetut pelit: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Näytä ainoastaan asennetut pelit" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Yksinkertaistettu kiina" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Espanja" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Pysy kirjautuneena:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Kauppa" #: data/ui/properties.ui:63 msgid "Support" msgstr "Tuki" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Ruotsi" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Järjestelmän oletus" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Asennus {} epäonnistui. Yritä uudelleen." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Ensisijainen kieli jolle pelit ladataan" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Ensisijainen kieli jota minigalaxy itsessään käyttää" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Ilmeni virhe noudettaessa latauslinkkiä!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Tämä on sijainti johon pelit asennetaan" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Perinteinen kiina" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turkki" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ukraina" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Poista asennus" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Päivitä" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Käytä tummaa teemaa: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Muuttujat:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Versio" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Mikäli pois päältä, sinua pyydetään kirjautumaan uudelleen aina Minigalaxyn " "käynnistyksessä" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Tulisiko minigalaxyn käyttää tummaa teemaa vai ei" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Näytetäänkö Windows-pelit kirjastossa vaiko ei" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Näytetäänkö piilotetut pelit kirjastossa vaiko ei" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Luodaanko uusille peleille pikakuvakkeet vaiko ei" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Wine-purku epäonnistui." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Wine-ohjelmaa ei löytynyt. Windows-pelien näyttämistä ei voida kytkeä päälle." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg-asetukset" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "lataa" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "ladataan…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "jonossa…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "asenna" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "asennetaan…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "pelaa" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "poistetaan asennusta…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "päivitetään…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} ei voitu purkaa." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} lataus epäonnistui." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} ei ole käytettävä polku" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} korruptoitui. Suorita lataus uudelleen." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Lataa" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Ladataan…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "Jonossa…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Asenna" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Asennetaan…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Pelaa" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Poistetaan asennusta…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Päivitetään…" sharkwouter-minigalaxy-759382b/data/po/fr.po000066400000000000000000000372151455252417400210340ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-10-23 16:33+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.2\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Un client GOG simple pour GNU/Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "À propos" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "À propos" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Ajouté à la fin de la commande utilisée pour lancer le jeu" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Ajouté devant la commande utilisée pour lancer le jeu" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Êtes-vous sûr de vouloir annuler le téléchargement {} ?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Êtes-vous sûr de vouloir vous déconnecter de GOG ?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Êtes-vous sûr de vouloir désinstaller %s ?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Portugais Brésilien" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Annuler" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Annuler" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chinois" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Paramètres de la commande :" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Impossible d'ouvrir la page de la boutique" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Impossible d'ouvrir la page de support" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Créez des raccourcis dans le menu : " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Danois" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Erreur de téléchargement" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Néerlandais" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Anglais" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Impossible de démarrer {} :" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Impossible de récupérer la bibliothèque" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Impossible de démarrer {} :" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finlandais" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Français" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Genre" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Allemand" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Page GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Cacher le jeu :" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Hongrois" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "L'extraction de l'Innoextract a échoué." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract n'est pas installé." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Chemin d'installation : " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Installé" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italien" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japonais" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Conservez les installateurs après avoir téléchargé un jeu.\n" "Les installateurs sont stockés dans : {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Conserver les installateurs : " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Coréen" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Langue : " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Connexion" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Déconnexion" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Aucun exécutable n'a été trouvé dans {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norvégien" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norvégien Bokmål" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norvégien Nynorsk" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Pas assez d'espace pour extraire le jeu. Requis : {} Disponible : {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Ouvrir le dossier" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Veuillez vérifier votre connexion Internet" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polonais" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugais" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Préférences" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Préférences" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Langue préférée : " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Propriétés" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Propriétés de {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Rafraîchir la liste de jeux" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Russe" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Sauvegarder" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Montrer les IPS en jeu :" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Montrer les jeux Windows : " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Montrer les jeux cachés : " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Afficher uniquement les jeux installés" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Chinois simplifié" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Espagnol" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Rester connecté :" #: data/ui/properties.ui:77 msgid "Store" msgstr "Boutique" #: data/ui/properties.ui:63 msgid "Support" msgstr "Support" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Suédois" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Système par défaut" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "L'installation de {} a échoué. Veuillez réessayer." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "La langue préférée pour le téléchargement des jeux" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "La langue préférée au démarrage de Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Il y a eu une erreur en essayant de récupérer le lien de téléchargement !\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "C'est ici que les jeux seront installés" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Chinois traditionnel" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turc" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ukrainien" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Désinstaller" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Mise à jour" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Utilisez le thème sombre : " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Paramètres variables :" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Version" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Lorsque cette option est désactivée, il vous sera demandé de vous connecter " "à chaque démarrage de Minigalaxy" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Minigalaxy doit-il utiliser un thème sombre ou non" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Si les jeux Windows sont affichés dans la bibliothèque ou non" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Si les jeux cachés sont montrés dans la bibliothèque ou non" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" "Si les raccourcis sont créés pour les jeux nouvellement installés ou non" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "L'extraction de Wine a échoué." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Wine n'a pas été trouvé. L'affichage des jeux Windows ne peut pas être " "activé." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "télécharger" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "téléchargement…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "dans la file d'attente…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "installer" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "installation…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "jouer" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "désinstallation…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "mise à jour…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} n'a pas pu être décompressé." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} n'a pas pu être téléchargé." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} n'est pas un chemin utilisable" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} a été corrompu. Veuillez le télécharger à nouveau." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Impossible de se connecter aux serveurs de GOG" #~ msgid "Install Wine to enable this feature" #~ msgstr "Installez Wine pour activer cette fonction" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Paramètres" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Montrez le framerate en jouant. Ne fonctionne qu'avec les cartes " #~ "graphiques AMD et Nvidia." #~ msgid "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgstr "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgid "Couldn't start subprocess" #~ msgstr "Impossible de démarrer le sous-processus" #~ msgid "No error message was returned" #~ msgstr "Aucun message d'erreur n'a été renvoyé" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy fonctionne maintenant en mode hors ligne" #~ msgid "Try again with an active internet connection" #~ msgstr "Essayez à nouveau avec une connexion Internet active" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Télécharger" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Téléchargement…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "Dans la file d'attente…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installer" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Installation…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Jouer" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Désinstallation…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Mise à jour…" sharkwouter-minigalaxy-759382b/data/po/it_IT.po000066400000000000000000000316701455252417400214340ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-05-25 19:07+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.3\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Un semplice client Linux per GOG" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Informazioni" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Informazioni" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Sei sicuro di voler annullare lo scaricamento di {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Sei sicuro di voler disinstallare %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Portoghese Brasiliano" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Annulla" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Annulla" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Cinese" # I don't know where it's used, so this translation might be wrong. #: data/ui/properties.ui:180 #, fuzzy msgid "Command flags:" msgstr "Opzione dei comandi" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Apertura della pagina del negozio non riuscita" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Apertura della pagina di supporto non riuscita" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "" #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Danese" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Errore di download" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Olandese" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Inglese" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Installazione di {} fallita" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Recupero della libreria fallito" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Apertura di {} fallita:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finlandese" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Francese" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Tedesco" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Pagina di GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Ungherese" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "" #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "" #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Percorso di installazione: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Installati" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italiano" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Giapponese" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Mantieni i file di installazione dopo aver scaricato un gioco.\n" "I file di installazione sono salvati in: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Mantieni file di installazione: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Coreano" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Accedi" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Disconnettiti" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Nessun eseguibile trovato in {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norvegese" #: minigalaxy/constants.py:38 #, fuzzy msgid "Norwegian Bokmål" msgstr "Norvegese" #: minigalaxy/constants.py:39 #, fuzzy msgid "Norwegian Nynorsk" msgstr "Norvegese" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Apri cartella" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Controlla la tua connessione a internet" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polacco" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portoghese" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Impostazioni" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Impostazioni" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Lingua preferita: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Proprietà" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Proprietà di {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Aggiorna lista dei giochi" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Russo" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Salva" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Visualizza FPS in gioco:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Visualizza giochi di Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 #, fuzzy msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Visualizza giochi di Windows: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Visualizza solo i giochi installati" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Spagnolo" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Mantieni l'accesso:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Negozio" #: data/ui/properties.ui:63 msgid "Support" msgstr "Supporto" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Svedese" #: minigalaxy/constants.py:29 msgid "System default" msgstr "" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "L'installazione di {} è fallita. Riprova." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "La lingua in cui si preferisce avere i giochi scaricati" #: data/ui/preferences.ui:65 #, fuzzy msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "La lingua in cui si preferisce avere i giochi scaricati" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "C'è stato un errore cercando di recuperare il link per lo scaricamento!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Qui è dove i giochi verranno installati" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turco" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Disinstalla" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Aggiorna" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "" # I don't know where it's used, so this translation might be wrong. #: data/ui/properties.ui:167 #, fuzzy msgid "Variable flags:" msgstr "Opzione delle variabili" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Se disabilitato ti verrà richiesto di fare l'accesso ogni volta che avvierai " "Minigalaxy" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Se abilitato visualizza anche i giochi di Windows nella libreria" #: data/ui/preferences.ui:221 #, fuzzy msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Se abilitato visualizza anche i giochi di Windows nella libreria" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "" #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Wine non è stato trovato. Non potrai abilitare la Visualizzazione dei giochi " "di Windows." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "scarica" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "scaricando…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "in coda…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "installa" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "installando…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "gioca" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "disinstallando…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "aggiornando…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "Estrazione di {} non riuscita." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "Scaricamento di {} fallito." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} non è un percorso utilizzabile" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} era corrotto. Riscaricalo." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Connessione ai server di GOG non riuscita" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Scarica" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Scaricando…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "In coda…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installa" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Installando…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Gioca" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Disinstallando…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Aggiornando…" sharkwouter-minigalaxy-759382b/data/po/nb_NO.po000066400000000000000000000413641455252417400214200ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-09-22 12:15+0200\n" "PO-Revision-Date: 2021-11-10 14:20+0100\n" "Last-Translator: Kim Malmo \n" "Language-Team: \n" "Language: nb_NO\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.0\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Enkel GOG klient for Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Om" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Om" #: data/ui/properties.ui:265 msgid "Added at the end of the command used to launch the game" msgstr "Lagt til etter kommandoen brukt til å starte spillet" #: data/ui/properties.ui:240 msgid "Added in front of the command used to launch the game" msgstr "Lagt til foran kommandoen brukt til å starte spillet" #: minigalaxy/ui/gametilelist.py:150 minigalaxy/ui/gametile.py:147 msgid "Are you sure you want to cancel downloading {}?" msgstr "Er du sikker på at du vil avbryte nedlastingen av {}?" #: minigalaxy/ui/window.py:98 msgid "Are you sure you want to log out of GOG?" msgstr "Er du sikker på at du vil logge ut av GOG?" #: minigalaxy/ui/gametilelist.py:163 minigalaxy/ui/gametile.py:160 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Er du sikker på at du vil avinstallere %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:31 msgid "Brazilian Portuguese" msgstr "Brasiliansk Portugisisk" #: data/ui/properties.ui:336 msgid "Cancel" msgstr "Avbryt" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Avbryt" #: data/ui/properties.ui:103 msgid "Check for updates:" msgstr "Se etter oppdateringer:" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Kinesisk" #: data/ui/properties.ui:253 msgid "Command flags:" msgstr "Kommando flagg:" #: minigalaxy/ui/information.py:72 minigalaxy/ui/information.py:82 #: minigalaxy/ui/information.py:92 msgid "Couldn't open forum page" msgstr "Kunne ikke åpne forumsiden" #: minigalaxy/ui/information.py:62 msgid "Couldn't open store page" msgstr "Kunne ikke åpne butikksiden" #: minigalaxy/ui/information.py:52 msgid "Couldn't open support page" msgstr "Kunne ikke åpne siden for støtte" #. Has to end with ": " #: data/ui/preferences.ui:313 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Lag menysnarvei: " #: minigalaxy/constants.py:32 msgid "Czech" msgstr "Tsjekkisk" #: data/ui/gametilelist.ui:194 data/ui/gametile.ui:193 msgid "DLC" msgstr "Tillegg" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Dansk" #: minigalaxy/ui/gametilelist.py:234 minigalaxy/ui/gametilelist.py:273 #: minigalaxy/ui/gametile.py:231 minigalaxy/ui/gametile.py:270 msgid "Download error" msgstr "Feil ved nedlasting" #: minigalaxy/constants.py:10 minigalaxy/constants.py:33 msgid "Dutch" msgstr "Nederlandsk" #: minigalaxy/constants.py:11 minigalaxy/constants.py:34 msgid "English" msgstr "Engelsk" #: minigalaxy/ui/login.py:45 msgid "Facebook Login" msgstr "Facebook innlogging" #: minigalaxy/ui/preferences.py:122 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" "Kunne ikke forandre språk for programmet. Sørg for at lokasjonsdata er " "generert på systemet ditt." #: minigalaxy/ui/gametilelist.py:340 minigalaxy/ui/gametile.py:337 msgid "Failed to install {}" msgstr "Kunne ikke installere {}:" #: minigalaxy/ui/library.py:146 msgid "Failed to retrieve library" msgstr "Kunne ikke hente bibliotek" #: minigalaxy/launcher.py:52 minigalaxy/ui/gametilelist.py:134 #: minigalaxy/ui/gametile.py:131 msgid "Failed to start {}:" msgstr "Kunne ikke starte {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:35 msgid "Finnish" msgstr "Finsk" #: data/ui/information.ui:89 msgid "Forum" msgstr "" #: minigalaxy/constants.py:13 minigalaxy/constants.py:36 msgid "French" msgstr "Fransk" #: minigalaxy/ui/properties.py:82 msgid "GameMode wasn't found. Using GameMode cannot be enabled." msgstr "GameMode ble ikke funnet. GameMode kan ikke aktiveres." #: minigalaxy/ui/information.py:138 msgid "Genre" msgstr "Sjanger" #: minigalaxy/constants.py:14 minigalaxy/constants.py:37 msgid "German" msgstr "Tysk" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub side" #: minigalaxy/constants.py:50 msgid "Greek" msgstr "Gresk" #: minigalaxy/constants.py:55 msgid "Grid" msgstr "Rutenett" #: data/ui/properties.ui:153 msgid "Hide game:" msgstr "Skjul spill:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Ungarsk" #: data/ui/gametilelist.ui:239 data/ui/gametile.ui:223 msgid "Information" msgstr "Informasjon" #: minigalaxy/ui/information.py:28 msgid "Information about {}" msgstr "Informasjon om {}" #: minigalaxy/installer.py:149 msgid "Innoextract extraction failed." msgstr "Utpakking av Innoextract feilet." #: minigalaxy/installer.py:166 msgid "Innoextract not installed." msgstr "Innoextract er ikke innstallert." #. The place directory in which games are installed. Has to end with ": " #: data/ui/preferences.ui:143 msgctxt "install_path" msgid "Installation path: " msgstr "Installasjonssti: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Installert" #: minigalaxy/constants.py:16 minigalaxy/constants.py:38 msgid "Italian" msgstr "Italiensk" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japansk" #: minigalaxy/ui/preferences.py:48 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Behold installere etter nedlasting av spill.\n" "Installere er plassert i: {}" #. Whether installer files are kept after download. Has to end with ": " #: data/ui/preferences.ui:170 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Behold installere: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreansk" #. The preferred language on Minigalaxy start. Has to end with ": " #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Språk: " #: minigalaxy/constants.py:56 msgid "List" msgstr "Liste" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Innlogging" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Logg ut" #: minigalaxy/ui/properties.py:87 msgid "MangoHud wasn't found. Using MangoHud cannot be enabled." msgstr "MangoHud ble ikke funnet. MangoHud kan ikke aktiveres." #: minigalaxy/launcher.py:212 msgid "No executable was found in {}" msgstr "Ingen kjørbar fil funnet i {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norsk" #: minigalaxy/constants.py:39 msgid "Norwegian Bokmål" msgstr "Norsk Bokmål" #: minigalaxy/constants.py:40 msgid "Norwegian Nynorsk" msgstr "Nynorsk" #: minigalaxy/installer.py:98 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Ikke nok plass til å pakke ut spillet. Trenger: {} Tilgjengelig: {}" #: data/ui/information.ui:165 data/ui/properties.ui:350 msgid "OK" msgstr "Ok" #: data/ui/properties.ui:74 msgid "Open files" msgstr "Åpne filer" #: minigalaxy/ui/information.py:53 minigalaxy/ui/information.py:63 #: minigalaxy/ui/information.py:73 minigalaxy/ui/information.py:83 #: minigalaxy/ui/information.py:93 msgid "Please check your internet connection" msgstr "Vennligst sjekk internettforbindelsen din" #: minigalaxy/constants.py:20 minigalaxy/constants.py:41 msgid "Polish" msgstr "Polsk" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugisisk" #: minigalaxy/ui/preferences.py:31 msgid "Preferences" msgstr "Brukervalg" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Brukervalg" #. Preferred language for downloading games in. Has to end with ": " #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Foretrukket språk: " #: data/ui/gametilelist.ui:254 data/ui/gametile.ui:238 msgid "Properties" msgstr "Egenskaper" #: minigalaxy/ui/properties.py:34 msgid "Properties of {}" msgstr "Egenskaper for {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Oppdater spillisten" #: data/ui/properties.ui:48 msgid "Regedit" msgstr "" #: data/ui/properties.ui:275 msgid "Reset wine executable" msgstr "" #: minigalaxy/constants.py:26 minigalaxy/constants.py:51 msgid "Romanian" msgstr "Rumensk" #: minigalaxy/constants.py:22 minigalaxy/constants.py:42 msgid "Russian" msgstr "Russisk" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Lagre" #: data/ui/properties.ui:128 msgid "Show FPS in game:" msgstr "Vis FPS i spill:" #. Has to end with ": " #: data/ui/preferences.ui:275 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Vis Windows spill: " #. Has to end with ": " #: data/ui/preferences.ui:249 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Vis skjulte spill: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Vis kun installerte spill" #: minigalaxy/constants.py:43 msgid "Simplified Chinese" msgstr "Simplifisert Kinesisk" #: minigalaxy/constants.py:23 minigalaxy/constants.py:44 msgid "Spanish" msgstr "Spansk" #: minigalaxy/constants.py:45 msgid "Spanish (Spain)" msgstr "Spansk (Spania)" #. Whether the user will stay logged in after closing Minigalaxy. Has to end with ": " #: data/ui/preferences.ui:196 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Forbli pålogget:" #: data/ui/information.ui:76 msgid "Store" msgstr "Butikkside" #: data/ui/information.ui:63 msgid "Support" msgstr "Støtte" #: minigalaxy/constants.py:24 minigalaxy/constants.py:46 msgid "Swedish" msgstr "Svensk" #: minigalaxy/constants.py:30 msgid "System default" msgstr "Systemstandard" #: minigalaxy/installer.py:129 msgid "The installation of {} failed. Please try again." msgstr "Feil ved installasjon av {}. Vennligst prøv igjen." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Foretrukket språk for nedlasting av spill" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Foretrukket språk når Minigalaxy startes" #: data/ui/preferences.ui:115 msgctxt "view_tooltip" msgid "The preferred view of game library" msgstr "Foretrukket visning av spillbibliotek" #: minigalaxy/ui/gametilelist.py:235 minigalaxy/ui/gametile.py:232 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Det oppstod en feil ved henting av nedlastingslenken!\n" "{}" #: data/ui/preferences.ui:140 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Dette er hvor spill vil bli installert" #: minigalaxy/constants.py:47 msgid "Traditional Chinese" msgstr "Tradisjonell Kinesisk" #: minigalaxy/constants.py:25 minigalaxy/constants.py:48 msgid "Turkish" msgstr "Tyrkisk" #: minigalaxy/constants.py:49 msgid "Ukrainian" msgstr "Ukrainsk" #: data/ui/gametilelist.ui:224 data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Avinstaller" #: data/ui/gametilelist.ui:209 data/ui/gametile.ui:172 msgid "Update" msgstr "Oppdater" #: data/ui/properties.ui:178 msgid "Use GameMode:" msgstr "Bruk GameMode:" #: data/ui/properties.ui:203 msgid "Use MangoHud:" msgstr "Bruk MangoHud:" #. Has to end with ": " #: data/ui/preferences.ui:223 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Bruk mørkt tema: " #: data/ui/properties.ui:228 msgid "Variable flags:" msgstr "Variable flagg:" #: minigalaxy/ui/information.py:140 msgid "Version" msgstr "Versjon" #. The preferred view of game library. Has to end with ": " #: data/ui/preferences.ui:118 msgctxt "view" msgid "View: " msgstr "Visning: " #: data/ui/preferences.ui:193 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Om deaktivert vil du bli spurt om å logge inn hver gang Minigalaxy starter" #: data/ui/preferences.ui:220 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Om Minigalaxy skal bruke mørkt tema eller ikke" #: data/ui/preferences.ui:272 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Om Windows spill skal vises i bibloteket eller ikke" #: data/ui/preferences.ui:246 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Om skjulte spill skal vises i bibloteket eller ikke" #: data/ui/preferences.ui:310 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Om snarveier blir opprettet for nylig innstallerte spill eller ikke" #: data/ui/properties.ui:291 msgid "Wine executable:" msgstr "Kjørbar fil for Wine:" #: minigalaxy/installer.py:180 msgid "Wine extraction failed." msgstr "Utpakking av Wine feilet." #: minigalaxy/ui/preferences.py:192 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine ble ikke funnet. Visning av Windows spill kan ikke aktiveres." #: data/ui/properties.ui:61 msgid "Winecfg" msgstr "" #: data/ui/properties.ui:87 msgid "Winetricks" msgstr "" #: minigalaxy/ui/properties.py:113 msgid "Winetricks wasn't found and cannot be used." msgstr "Winetricks ble ikke funnet. Winetricks kan ikke aktiveres." #: minigalaxy/ui/gametilelist.py:507 minigalaxy/ui/gametile.py:504 msgid "download" msgstr "last ned" #: minigalaxy/ui/gametilelist.py:547 minigalaxy/ui/gametile.py:544 msgid "downloading…" msgstr "laster ned…" #: minigalaxy/ui/gametilelist.py:538 minigalaxy/ui/gametile.py:535 msgid "in queue…" msgstr "i kø…" #: minigalaxy/ui/gametilelist.py:526 minigalaxy/ui/gametile.py:523 msgid "install" msgstr "installer" #: minigalaxy/ui/gametilelist.py:558 minigalaxy/ui/gametile.py:555 msgid "installing…" msgstr "installerer…" #: minigalaxy/ui/gametilelist.py:573 minigalaxy/ui/gametilelist.py:603 #: minigalaxy/ui/gametile.py:570 minigalaxy/ui/gametile.py:600 msgid "play" msgstr "spill" #: minigalaxy/ui/gametilelist.py:589 minigalaxy/ui/gametile.py:586 msgid "uninstalling…" msgstr "avinstallerer…" #: minigalaxy/ui/information.py:134 msgid "unknown" msgstr "ukjent" #: minigalaxy/ui/gametilelist.py:612 minigalaxy/ui/gametile.py:609 msgid "updating…" msgstr "oppdaterer…" #: minigalaxy/installer.py:131 msgid "{} could not be unzipped." msgstr "{} kunne ikke pakkes ut." #: minigalaxy/installer.py:74 msgid "{} failed to download." msgstr "{} kunne ikke lastes ned." #: minigalaxy/ui/preferences.py:204 msgid "{} isn't a usable path" msgstr "{} er ikke en brukbar sti" #: minigalaxy/installer.py:86 msgid "{} was corrupted. Please download it again." msgstr "{} ble ødelagt. Vennligst last ned på nytt." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Kunne ikke koble til GOG tjenerne" #~ msgid "Install Wine to enable this feature" #~ msgstr "Installér Wine for å aktivere denne egenskapen" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Innstillinger" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Vis bildeoppdateringen mens du spiller. Kun mulig med AMD og Nvidia " #~ "grafikk-kort." #~ msgid "Couldn't start subprocess" #~ msgstr "Kunne ikke starte underprosess" #~ msgid "No error message was returned" #~ msgstr "Ingen feilmelding ble returnert" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy kjører i frakoblet modus" #~ msgid "Try again with an active internet connection" #~ msgstr "Prøv igjen med en aktiv internettforbindelse" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Last ned" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Laster ned…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "I kø…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installer" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Installerer…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Spill" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Avinstallerer…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Oppdaterer…" sharkwouter-minigalaxy-759382b/data/po/nl.po000066400000000000000000000344101455252417400210300ustar00rootroot00000000000000# Dutch translations for minigalaxy package. # Copyright (C) 2019 THE minigalaxy'S COPYRIGHT HOLDER # This file is distributed under the same license as the minigalaxy package. # Wouter Wijsman , 2019. # msgid "" msgstr "" "Project-Id-Version: minigalaxy 0.9.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-09-30 11:00+0200\n" "Last-Translator: Wouter Wijsman \n" "Language-Team: Dutch\n" "Language: 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-Generator: Poedit 2.3\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Een simpele GOG client voor Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Over Minigalaxy" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Over Minigalaxy" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "" "Wordt aan het commando dat gebruikt wordt om de game te starten toegevoegd" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "" "Wordt voor het commando dat gebruikt wordt om de game te starten toegevoegd" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Weet je zeker dat je de {} download wilt stoppen?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Weet je zeker dat je uit wilt loggen?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Weet je zeker dat je %s wilt verwijderen?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Braziliaans-Portugees" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Annuleren" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Annuleren" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chinees" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Commando flaggetjes:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "De pagina kon niet worden geopend" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "De ondersteuningspagina kon niet worden geopend" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Maak menusnelkoppelingen: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Deens" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Downloadfout" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Nederlands" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Engels" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Kon {} niet geïnstalleerd worden:" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "De lijst me games kon niet opgehaald worden" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Kon {} niet starten:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Fins" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Frans" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Genre" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Duits" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub pagina" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Verberg game:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Hongaars" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Innoextract kon de game niet uitpakken." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract is niet geïnstalleerd." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Installatiepad: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Geïnstalleerd" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italiaans" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japans" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Behoud installatiebestanden nadat een game is gedownload.\n" "Installatie bestanden worden opgeslagen in: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Behoud installatiebestanden: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreaans" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Taal: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Log in" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Uitloggen" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Geen programma gevonden in {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Noors" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Noors (Bokmål)" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Noors (Nynorsk)" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Niet genoeg ruimt om" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Bestanden openen" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Controleer je internetverbinding" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Pools" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugees" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Voorkeuren" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Voorkeuren" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Voorkeurstaal: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Instellingen" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Instellingen van {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Haal gamelijst opnieuw op" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Russisch" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Opslaan" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Laat FPS zijn tijdens het spelen:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Toon games voor Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Toon verborgen games: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Laat alleen geïnstalleerde games zien" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Vereenvoudigd Chinees" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Spaans" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Blijf ingelogd:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Winkelpagina" #: data/ui/properties.ui:63 msgid "Support" msgstr "Ondersteuning" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Zweeds" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Systeemsstandard" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "De installatie van {} is mislukt. Probeer het opnieuw." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "De voorkeurstaal voor gedownloadde games" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "De voorkeurstaal voorMinigalaxy bij het starten" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Er is een fout opgetreden bij het ophalen van de downloadlink!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Dit is waar games geinstalleerd worden" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Traditioneel Chinees" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turks" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Oekraïens" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Verwijderen" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Update" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Gebruik donker thema: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Variabelen:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Versie" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Als deze optie uitgeschakeld is, zal Minigalaxy altijd het inlogscherm tonen " "bij het opstarten" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Of Minigalaxy een donkerder thema gebruikt of niet" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Of Windows games zichtbaar zijn in de bibliotheek" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Of verborgen games zichtbaar zijn in de bibliotheek" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" "Of snelkoppelingen in het menu van het besturingssysteem aangemaakt worden " "voor nieuw geïnstalleerde games" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Het uitpakken met wine is mislukt." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Wine kon niet gevonden worden. Het tonen van Windows games kan niet aangezet " "worden." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "download" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "downloaden…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "in de wachtrij…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "installeren" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "installeren…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "spelen" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "verwijderen…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "updaten…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} kan niet uitgepakt worden." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} kon niet gedownload worden." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} is geen bruikbaar pad" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} is beschadigd. Download het opnieuw." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Er kon geen verbinding gemaakt worden met de GOG servers" #~ msgid "Install Wine to enable this feature" #~ msgstr "Installeer Wine om deze feature te te kunnen gebruiken" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Instellingen" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Laat de hoeveelheid beelden per seconde on het scherm zien tijdens het " #~ "spelen van games. Dit werkt alleen met AMD en Nvidia grafische kaarten." #~ msgid "Couldn't start subprocess" #~ msgstr "Het subprocess kon niet gestart worden" #~ msgid "No error message was returned" #~ msgstr "Er is geen foutmelding afgegeven" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy is nu in offline modus" #~ msgid "Try again with an active internet connection" #~ msgstr "Probeer het opnieuw als u weer met het internet vebonden bent" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Download" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Downloaden…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "In de wachtrij…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installeren" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Installeren…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Spelen" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Verwijderen…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Updaten…" sharkwouter-minigalaxy-759382b/data/po/nn_NO.po000066400000000000000000000360631455252417400214340ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-10-04 11:33+0200\n" "Last-Translator: Jan Kjetil Myklebust\n" "Language-Team: \n" "Language: nb_NO\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.4\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Ein enkel GOG klient for Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Om" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Om" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Lagt til bakom kommandoen som er brukt for å starte spelet" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Lagt til framføre kommandoen som er brukt for å starte spelet" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Er du sikker på at du vil avbryte nedlastinga av {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Er du sikker på at du vil logge ut av GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Er du sikker på at du vil avinstallere %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Brasiliansk Portugisisk" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Avbryt" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Avbryt" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Kinesisk" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Kommandolinjeflagg:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Kunne ikkje opne produktsida" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Kunne ikkje opne hjelpesida" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Opprett menysnarvegar: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Dansk" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Nedlastingsfeil" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Nederlandsk" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Engelsk" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Klarte ikkje å installere {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Klarte ikkje å hente bibliotek" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Klarte ikkje å starte {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finsk" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Fransk" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Sjanger" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Tysk" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub-side" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Skjul spel:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Ungarsk" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Innoextract utpakking feila." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract er ikkje installert." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Installasjonssti: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Installert" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italiensk" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japansk" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Behalde installeringsfiler etter nedlasting av spel.\n" "Installerarar ligg under: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Behalde installerarar: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreansk" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Språk: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Inlogging" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Logg ut" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Inga køyrbar fil funnen i {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norsk" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norsk Bokmål" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norsk Nynorsk" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Ikkje nok plass til å pakke ut spelet. Trengs: {} Tilgjengeleg: {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Opne filer" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Ver venleg og sjekk internettilkoplinga di" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polsk" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugisisk" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Brukarval" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Brukarval" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Foretrukke språk: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Eigenskapar" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Eigenskapar for {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Oppdater spelelista" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Russisk" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Lagre" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Vis FPS i spelet:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Vis spel for Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Vis spel for Windows: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Vis berre installerte spel" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Foreinkla Kinesisk" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Spansk" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Bli verande pålogga:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Produktside" #: data/ui/properties.ui:63 msgid "Support" msgstr "Støtte" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Svensk" #: minigalaxy/constants.py:29 msgid "System default" msgstr "System standardverdi" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Installasjonen av {} feila. Ver venleg og prøv på nytt." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Foretrukke språk for nedlasting av spel" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Foretrukke språk ved oppstart av Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Ein feil oppstod ved henting av nedlastingslink!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Spel vil verte installerte her" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Tradisjonell kinesisk" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Tyrkisk" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ukrainsk" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Avinstaller" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Oppdater" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Bruk mørkt tema: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Variabelflagg:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Versjon" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Viss deaktivert vil du bli spurd om å logge på kvar gong Minigalaxy starter" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Om Minigalaxy skal nytte mørtk tema eller ikkje" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Om spel for Windows vert vist i biblioteket eller ikkje" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Om skjulte spel vert vist i biblioteket eller ikkje" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Om snarvegar blir oppretta for nyinstallerte spel eller ikkje" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Wine utpakking feila." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Fann ikkje Wine. Kan ikkje skru på vising av Windows-spel." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "last ned" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "lastar ned…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "i kø…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "installer" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "installerar…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "spel" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "avinstallerar…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "oppdaterar…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} kunne ikkje pakkast ut." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} kunne ikkje lastast ned." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} er ikkje ein brukbar sti" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} var korrumpert. Ver venleg å last den ned på ny." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Kunne ikkje kople til GOG sine tenarar" #~ msgid "Install Wine to enable this feature" #~ msgstr "Installer Wine for å aktivere denne funksjonen" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Innstillingar" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Vis framerate medan du speler spel. Fungerar berre med AMD og Nvidia " #~ "grafikkort." #~ msgid "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgstr "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgid "Couldn't start subprocess" #~ msgstr "Kunne ikkje starte underprosess" #~ msgid "No error message was returned" #~ msgstr "Inga feilmelding vart returnert" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy køyrer i fråkopla modus" #~ msgid "Try again with an active internet connection" #~ msgstr "Prøv igjen med ein aktiv internettilkopling" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Last ned" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Lastar ned…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "I kø…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installer" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Installerar…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Spel" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Avinstallerar…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Oppdaterar…" sharkwouter-minigalaxy-759382b/data/po/pl.po000066400000000000000000000343441455252417400210400ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Artur Krzysztof Wróblewski , 2020-2021. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-09-30 10:17+0200\n" "Last-Translator: \n" "Language-Team: Artur Wróblewski\n" "Language: pl_PL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.2.3\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 " "|| n%100>14) ? 1 : 2);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Prosty klient GOG dla Linuksa" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "O programie" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "O programie" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Dodano na końcu polecenie służące do uruchomienia gry" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Dodano przed komendą służącą do uruchomienia gry" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Czy na pewno chcesz anulować pobieranie {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Czy na pewno chcesz wylogować się z GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Czy na pewno chcesz odinstalować %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Portugalski brazylijski" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Anuluj" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Anuluj" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chiński" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Flagi poleceń:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Nie można otworzyć strony w sklepie" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Nie można otworzyć strony pomocy technicznej" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Utwórz skróty w menu: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Duński" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Błąd pobierania" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Holenderski" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Angielski" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Instalacja {} nie powiodła się" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Nie można pobrać biblioteki" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Nie można uruchomić {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Fiński" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Francuski" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Gatunek" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Niemiecki" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Strona GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Ukryj grę:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Węgierski" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Ekstrakcja Innoextract nie powiodła się." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract nie jest zainstalowany." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Ścieżka instalacji: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Zainstalowane" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Włoski" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japoński" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Zachowaj pliki instalacyjne po pobraniu gry.\n" "Pliki instalacyjne są przechowywane w: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Zachowaj pliki instalacyjne: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreański" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Język: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Zaloguj się" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Wyloguj" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Nie znaleziono pliku wykonywalnego w {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norweski" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norweski Bokmål" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norweski Nynorsk" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" #: data/ui/properties.ui:280 msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Otwórz pliki" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Sprawdź swoje połączenie internetowe" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polski" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugalski" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Ustawienia" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Ustawienia" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Preferowany język: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Właściwości" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Właściwości {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Odśwież listę gier" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Rosyjski" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Zapis" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Pokaż FPS w grze:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Wyświetl gry dla Systemu Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Pokaż ukryte gry: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Pokaż tylko zainstalowane gry" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Chiński uproszczony" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Hiszpański" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Pozostań zalogowany:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Sklep" #: data/ui/properties.ui:63 msgid "Support" msgstr "Wsparcie" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Szwedzki" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Domyślny systemu" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Instalacja {} zakończona niepowodzeniem. Proszę spróbować ponownie." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Preferowany język pobierania gier w" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Preferowany język podczas uruchamiania Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Wystąpił błąd podczas próby pozyskania linku do pobrania!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "W tym miejscu zostaną zainstalowane gry" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Chinski tradycyjny" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turecki" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Odinstaluj" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Aktualizuj" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Użyj ciemnego motywu: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Flagi zmienne:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Wersja" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Po wyłączeniu zostanie wyświetlony monit o zalogowanie się przy każdym " "uruchomieniu Minigalaxy" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Czy Minigalaxy ma używać ciemnego motywu, czy nie" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "" "Czy gry natywnie przeznaczone dla systemu Windows mają być wyświetlane w " "bibliotece, czy nie" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Czy ukryte gry są wyświetlane w bibliotece, czy nie" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Czy skróty mają być tworzone dla nowo zainstalowanych gier, czy nie" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Ekstrakcja Wine nie powiodła się." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" "Wine nie został znaleziony. Nie można włączyć wyświetlania gier systemu " "Windows." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "pobierz" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "pobieranie…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "w kolejce…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "zainstaluj" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "instalowanie…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "graj" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "odinstalowywanie…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "aktualizowanie..." #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} nie można rozpakować." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} nie udało się pobrać." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} nie jest poprawną ścieżką" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} jest uszkodzony. Pobierz go ponownie." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Nie można połączyć się z serwerami GOG" #~ msgid "Install Wine to enable this feature" #~ msgstr "Zainstaluj Wine, aby włączyć tę funkcję" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Ustawienia" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Pokaż ilość klatek na sekundę FPS w trakcie gry. Działa tylko z kartami " #~ "graficznymi AMD i Nvidia." #~ msgid "Couldn't start subprocess" #~ msgstr "Nie można uruchomić podprocesu" #~ msgid "No error message was returned" #~ msgstr "Nie został zwrócony żaden komunikat o błędzie" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy jest teraz uruchomiony w trybie offline" #~ msgid "Try again with an active internet connection" #~ msgstr "Spróbuj ponownie z aktywnym połączeniem internetowym" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Pobierz" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Pobieranie…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "W kolejce…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Zainstaluj" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Instalowanie…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Graj" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Odinstalowywanie…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Aktualizowanie..." sharkwouter-minigalaxy-759382b/data/po/pt_BR.po000066400000000000000000000327471455252417400214400ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Esdras Tarsis , 2020. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2020-11-17 03:06-0300\n" "Last-Translator: Esdras Tarsis \n" "Language-Team: \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.1\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Um cliente simples da GOG para o Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Sobre" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Sobre" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Tem certeza de que deseja cancelar o download do game {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Você tem certeza de que deseja desinstalar %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Português Brasileiro" #: data/ui/properties.ui:266 #, fuzzy msgid "Cancel" msgstr "Cancelar" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Cancelar" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chinês" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "" #: minigalaxy/ui/properties.py:103 #, fuzzy msgid "Couldn't open store page" msgstr "Não foi possível abrir a página de suporte" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Não foi possível abrir a página de suporte" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "" #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Dinamarquês" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Erro ao baixar" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Holandês" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Inglês" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Falha ao instalar {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Falha ao recuperar a biblioteca" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Falha ao iniciar {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finlandês" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Francês" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Alemão" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Página no GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Húngaro" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "" #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "" #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Caminho de instalação: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Instalados" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italiano" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japonês" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Manter instaladores após baixar um jogo.\n" "Instaladores são armazenados em: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Manter instaladores: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Coreano" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Login" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Logout" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Nenhum executável foi encontrado em {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norueguês" #: minigalaxy/constants.py:38 #, fuzzy msgid "Norwegian Bokmål" msgstr "Norueguês" #: minigalaxy/constants.py:39 #, fuzzy msgid "Norwegian Nynorsk" msgstr "Norueguês" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" #: data/ui/properties.ui:280 msgid "OK" msgstr "" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Abrir arquivos" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Por favor, verifique sua conexão à internet" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polonês" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Português" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Preferências" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Preferências" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Idioma preferido: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Recarregar lista de jogos" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Russo" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Salvar" #: data/ui/properties.ui:154 #, fuzzy msgid "Show FPS in game:" msgstr "Mostrar FPS nos jogos:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Mostrar jogos para Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 #, fuzzy msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Mostrar jogos para Windows: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Mostrar apenas jogos instalados" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Espanhol" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Permanecer conectado:" #: data/ui/properties.ui:77 #, fuzzy msgid "Store" msgstr "Página da Loja" #: data/ui/properties.ui:63 #, fuzzy msgid "Support" msgstr "Suporte" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Sueco" #: minigalaxy/constants.py:29 msgid "System default" msgstr "" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "A instalação de {} falhou. Por favor tente novamente." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "O idioma preferido para baixar os jogos" #: data/ui/preferences.ui:65 #, fuzzy msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "O idioma preferido para baixar os jogos" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Ocorreu um erro ao tentar obter o link de download!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "É onde os jogos serão instalados" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turco" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "" #: data/ui/gametile.ui:208 #, fuzzy msgid "Uninstall" msgstr "Desinstalar" #: data/ui/gametile.ui:193 #, fuzzy msgid "Update" msgstr "Atualizar" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "" #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Quando desativado, você será solicitado a fazer login sempre que o " "Minigalaxy for iniciado" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Mostrar jogos para Windows na biblioteca ou não" #: data/ui/preferences.ui:221 #, fuzzy msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Mostrar jogos para Windows na biblioteca ou não" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "" #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "" #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "Baixar" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "Baixando…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "na fila…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "Instalar" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "Instalando…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "Jogar" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "Desinstalando…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "Atualizando…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} não pode ser descompactado." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} falhou ao realizar o download." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} não é um caminho utilizável" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} está corrompido. Por favor faça o download novamente." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Não foi possível conectar aos servidores da GOG" #~ msgid "Install Wine to enable this feature" #~ msgstr "Instale o Wine para habilitar esse recurso" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Configurações" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Mostrar a taxa de quadros enquanto joga. Funciona apenas com placas " #~ "gráficas AMD e Nvidia." #~ msgid "Couldn't start subprocess" #~ msgstr "Não foi possível iniciar o subprocesso" #~ msgid "No error message was returned" #~ msgstr "Nenhuma mensagem de erro foi retornada" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy está rodando no modo offline" #~ msgid "Try again with an active internet connection" #~ msgstr "Tente novamente com uma conexão ativa com a internet" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Baixar" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Baixando…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "Na fila…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Instalar" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Instalando…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Jogar" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Desinstalando…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Atualizando…" sharkwouter-minigalaxy-759382b/data/po/ro.po000066400000000000000000000330051455252417400210360ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-09-29 19:02+0300\n" "Last-Translator: \n" "Language-Team: \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.0\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Un client simplu GOG pentru Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Despre" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Despre" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Adăugat la sfârșitul comenzii folosită pentru pornirea jocului" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Adăugat în fața comenzii folosită pentru pornirea jocului" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Ești sigur că vrei să oprești descărcarea jocului {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Ești sigur că vrei să te deconectezi din GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Ești sigur că vrei să dezinstalezi %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Portugheză Braziliană" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Anulează" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Anulează" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Chineză" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Flag-uri comandă:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Nu s-a putut deschide pagina magazinului" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Nu s-a putut deschide pagina de suport" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Creează scurtături meniu: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "Ceh" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Danez" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Eroare de descărcare" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Olandeză" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Engleză" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "Nu s-a putut schimba limba programului. Asigură-te că locale-ul este generat" "în sistemul tău" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Nu s-a putut instala {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Nu s-a putut descărca libraria" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Nu s-a putut porni {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finlandeză" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Franceză" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Gen" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Germană" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Pagină GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Ascunde joc:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Maghiară" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Nu s-a putut extrage innoextract." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract nu s-a instalat." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Locație instalare: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Instalat" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italiană" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japoneză" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Ține installer-urile dupa descărcarea unui joc\n" "Installerele sunt salvate în: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Ține installerele: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreană" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Limbă: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Autentificare" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Deconectare" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Nici-un executabil nu a fost găsit în {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norvegiană" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norvegiană Bokmål" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norvegiană Nynorsk" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Nu-i destul de mult spațiu pentru extragerea jocului. Necesar: {} Valabil: {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "ОК" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Deschide fișiere" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Te rog verifică conexiunea la internet" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Poloneză" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugheză" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Preferințe" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Preferințe" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Limbaj preferat pentru joc: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Proprietăți" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Proprietățile {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Reîmprospătează listă jocuri" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Rusă" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Salvează" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Arată FPS în joc:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Arată jocuri Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Arată jocuri ascunse: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Arată doar jocurile instalate" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Chineză Simplificată" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Spaniolă" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Rămâi conectat:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Magazin" #: data/ui/properties.ui:63 msgid "Support" msgstr "Suport" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Suedeză" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Implicit sistemului" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Instalarea a eșuat. Te rog incearcă din nou." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Limbajul preferat pentru descărcarea jocurilor" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Limbajul preferat la deschiderea Minigalaxy-ului" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "A fost o eroare la luat-ul linkului de descărcare!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Aici vor fi jocurile descărcate" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Chineză Tradițională" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turcă" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ukraineană" #: minigalaxy/constants.py:26 minigalaxy/constants.py:51 msgid "Romanian" msgstr "Română" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Dezinstalează" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Actualizează" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Folosește mod întunecat" #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Flag-uri variabile:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Versiune" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Cât timp este dezactivat, vei fi întrebat de fiecare dată când Minigalaxy se deschide " "să te autentifici" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Dacă Minigalaxy să folosească modul întunecat sau nu" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Dacă jocurile Windows sunt afișate în librărie sau nu" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Dacă jocurile ascunse sunt afișate în librărie sau nu" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Dacă scurtăturile sunt create pentru jocuri nou-instalate sau nu" "" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Extragerea Wine a eșuat." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine nu a fost găsit. Arătarea jocurilor Windows nu poate fi pornită." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "descarcă" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "se descarcă…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "în coadă…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "instalează" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "se instalează…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "joacă" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "se dezinstalează…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "necunoscut" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "se actualizează…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} nu poate fi unzipped." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "Nu s-a putut descărca {}." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} nu este o locație validă" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} este corupt. Te rog descarcă din nou." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Descarcă" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Se descarcă…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "În coadă…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Instalează" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Se instalează…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Joacă" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Se dezinstalează…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Se actualizează…" sharkwouter-minigalaxy-759382b/data/po/ru_RU.po000066400000000000000000000363631455252417400214640ustar00rootroot00000000000000# 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. # Artem Polishchuk , 2020. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-11-05 18:00+0300\n" "Last-Translator: Artem Polishchuk \n" "Language-Team: \n" "Language: ru_RU\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.4.1\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Простой клиент GOG для Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "О Minigalaxy" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "О Minigalaxy" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Добавляется в конце команды запуска игры" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Добавляется в начале команды запуска игры" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Вы уверены, что хотите отменить загрузку {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Вы уверены, что хотите выйти из GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Вы уверены, что хотите удалить %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Бразильский португальский" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Отменить" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Отменить" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Китайский" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Флаги команд:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Не удалось открыть страницу магазина" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Не удалось открыть страницу поддержки" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Создавать ярлыки в меню: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "Чешский" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "Дополнения" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Датский" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Ошибка загрузки" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Нидерландский" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Английский" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" "Не удалось изменить язык программы. Убедитесь, что на вашей системе " "сгенерирована локаль." #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Не удалось установить {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Не удалось получить библиотеку" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Не удалось запустить {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Финский" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Французский" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Жанр" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Немецкий" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Страница на GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Скрывать игру:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Венгерский" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Не удалось извлечь игру с помощью Innoextract." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Не установлен Innoextract." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Путь установки: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Установленные" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Итальянский" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Японский" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Сохранять файлы установки после загрузки игры.\n" "Файлы установки хранятся в: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Сохранять файлы установки: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Корейский" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Язык: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Войти" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Выйти" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Не найден запускаемый файл в {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Норвежский" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Норвежский букмол" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Норвежский нюнорск" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Недостаточно места для извлечения игры. Требуется: {} Доступно: {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Открыть файлы" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Проверьте ваше подключение к Интернету" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Польский" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Португальский" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Настройки" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Настройки" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Предпочтительный язык игр: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Свойства" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Свойства {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Обновить список игр" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Русский" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Сохранить" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Показывать FPS в игре:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Показывать игры для Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Показывать скрытые игры: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Показывать только установленные игры" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Китайский упрощенный" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Испанский" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Не выходить из системы:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Магазин" #: data/ui/properties.ui:63 msgid "Support" msgstr "Поддержка" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Шведский" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Системный по умолчанию" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Не удалось установить {}. Попробуйте ещё раз." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Язык, на котором вы хотите скачивать игры" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Язык, на котором вы хотите запускать Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Произошла ошибка при получении ссылки для скачивания!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Место, куда будут установлены игры" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Китайский традиционный" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Турецкий" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Украинский" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Удалить" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Обновить" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Использовать темную тему: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Переменные-флаги:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Версия" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Если эта опция отключена, вам придется всегда входить в GOG при запуске " "Minigalaxy" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Использовать темную тему для Minigalaxy или нет" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Показывать игры для Windows в библиотеке или нет" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Показывать скрытые игры в библиотеке или нет" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Создавать ярлыки для недавно установленных игр или нет" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Не удалось извлечь игру с помощью Wine." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine не найден. Отображение игр для Windows не может быть включено." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "загрузить" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "загрузка…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "в очереди…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "установить" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "установка…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "играть" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "удаление…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "обновление…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "Не удалось распаковать {}." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "Не удалось загрузить {}." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "Невозможно использовать {} для установки" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} поврежден. Пожалуйста, загрузите заново." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Загрузить" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Загрузка…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "В очереди…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Установить" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Установка…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Играть" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Удаление…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Обновление…" sharkwouter-minigalaxy-759382b/data/po/sv_SE.po000066400000000000000000000332761455252417400214470ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-10-01 12:46+0200\n" "Last-Translator: \n" "Language-Team: \n" "Language: sv_SE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.0\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "En simpel GOG-klient för Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Om" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Om" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Läggs till vid slutet av kommandot som används för att starta spelet" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Läggs till vid början av kommandot som används för att starta spelet" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Är du säker på att du vill avbryta nedladdningen av {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Är du säker på att du vill logga ut från GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Är du säker på att du vill avinstallera %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Brasiliansk portugisiska" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Avbryt" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Avbryt" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Kinesiska" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Kommandoflaggor:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Kunde inte öppna butikssidan" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Kunde inte öppna supportsidan" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Skapa menygenvägar: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "Nedladdningsbart innehåll" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Danska" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Nedladdningsfel" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Nederländska" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Engelska" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Misslyckades att installera {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Misslyckades att hämta bibliotek" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Misslyckades att starta {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Finska" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Franska" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Genre" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Tyska" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub sida" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Göm spel:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Ungerska" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Innoextract-extraheringen misslyckades." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract är inte installerat." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Installationssökväg: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Installerade" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Italienska" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japanska" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Behåll installationsprogram efter spel är nedladdade.\n" "Installationsprogram sparas i: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Behåll installationsprogram: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Koreanska" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Språk: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Inloggning" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Logga ut" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "Ingen körbar fil hittades i {}" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norska" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Bokmål" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Nynorska" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "" "Det finns inte tillräckligt mycket utrymme för att packa upp spelet. Behövs: " "{} Tillgängligt: {}" #: data/ui/properties.ui:280 #, fuzzy msgid "OK" msgstr "OK" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Öppna filer" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Var god kontrollera din internetanslutning" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Polska" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portugisiska" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Inställningar" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Inställningar" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Föredraget språk: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Egenskaper" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Egenskaper för {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Uppdatera spellistan" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Ryska" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Spara" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Visa bilder per sekund (FPS) i spel:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Visa Windowsspel: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Visa gömda spel: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Visa endast installerade spel" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Förenklad kinesiska" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Spanska" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Håll mig inloggad:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Butikssida" #: data/ui/properties.ui:63 msgid "Support" msgstr "Support" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Svenska" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Standardvärde för systemet" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Installationen av {} misslyckades. Var god försök igen." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Det föredragna språket att ladda ner spel i" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Det föredragna språket vid uppstart av Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Ett fel uppstod när ledladdningslänken hämtades\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Detta är var spel kommer installeras" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Traditionell kinesiska" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Turkiska" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "Ukrainska" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Avinstallera" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Uppdatera" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Använd mörkt tema: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Variabelflaggor:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Version" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "När detta är avstängt kommer du bli frågad att logga in varje gång " "Minigalaxy startas" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Om Minigalaxy ska använda mörkt tema eller inte" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Om Windowsspel ska visas i biblioteket eller inte" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Om gömda spel ska visas i biblioteket eller inte" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Om genvägar ska skapas för nyinstallerade spel eller inte" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Extrahering av Wine misslyckades." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine kunde inte hittas. Visning av Windowsspel kan inte slås på." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "ladda ner" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "laddar ner…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "i kö…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "installera" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "installerar…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "spela" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "avinstallerar…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "uppdaterar…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} kunde inte packas upp." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} kunde inte laddas ner." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} är inte en korrekt filsökväg" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} var korrupt. Försök ladda ner det igen." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "Kunde inte ansluta till GOG-serverarn" #~ msgid "Install Wine to enable this feature" #~ msgstr "Installera Wine för att möjliggöra denna funktion" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Inställningar" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Visa bildhastighet (FPS) i spel. Fungerar endast med AMD och NVIDIA " #~ "grafikkort." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Ladda ner" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Laddar ner…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "I kö…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Installera" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Installerar…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Spela" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Avinstallerar…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Uppdaterar…" sharkwouter-minigalaxy-759382b/data/po/tr.po000066400000000000000000000363331455252417400210520ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-09-30 11:22+0300\n" "Last-Translator: Hüseyin Fahri Uzun \n" "Language-Team: \n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 2.3\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Linux için basit bir GOG uygulaması" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Hakkında" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Hakkında" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Oyun çalıştırılırken kullanılacak olan komutun sonuna eklendi" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Oyun çalıştırılırken kullanılacak olan komutun başına eklendi" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "{} indirmesini iptal etmek istediğine emin misin?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "GOG üzerinden çıkış yapmak istediğinize emin misiniz?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "%s silmek istediğine emin misin?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Brezilya Portekizçesi" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "İptal" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "İptal" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Çince" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Komut bayrakları:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Mağaza sayfası açılamadı" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Destek sayfası açılamadı" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Menü kısayolları oluştur: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Danimarka Dili" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "İndirme hatası" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Flemenkçe" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "İngilzce" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "{} yüklenemedi:" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Kütüphane alınamadı" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "{} başlatılamadı:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Fince" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Fransızca" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Genre" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Almanca" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub sayfası" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Oyunu gizle:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Macarca" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Innoextract dışa aktarım başarısız." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract yüklü değil." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Yükleme klasör yolu: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Yüklendi" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "İtalyanca" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Japonca" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Oyun indirmesi sonrasında yükleyiciyi saklı tut.\n" "Yükleyiciler şurada tutuluyor: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Yükleyicileri saklı tut: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Korece" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Dil: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Giriş Yap" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Çıkış Yap" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "{} burada başlatma dosyası bulunamadı" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Norveçce" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Norveçce" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Norveçce" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Oyunu dışarı çıkartmak için yeterli alan yok. Gerekli: {} Mevcut {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "Tamam" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Dosyaları Aç" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Lütfen internet bağlantınızı kontrol ediniz" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Lehçe" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Portekizce" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Ayarlar" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Ayarlar" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Tercih edilen dil: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Seçenekler" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "{} Seçenekleri" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Oyun listesini güncelle" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Rusça" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Kaydet" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Oyunlarda FPS göster:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Windows oyunlarını göster: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Gizli oyunları göster: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Sadece yüklü oyunları göster" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Basitleştirilmiş Çince" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "İspanyolca" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Çevrimci tut:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Market Sayfası" #: data/ui/properties.ui:63 msgid "Support" msgstr "Destek" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "İsveçce" #: minigalaxy/constants.py:29 msgid "System default" msgstr "Sistem varsayılanları" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "{} yüklemesi başarısız oldu. Lütfen bir daha deneyiniz." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "İndirilen oyunlar için tercih edilen dil" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Minigalaxy çalıştırıldığında tercih edilecek dil" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "İndirme linki alınırken bir hata oluştu! {}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Burası oyunların indireleceği yerdir" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Geleneksel Çince" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Türkçe" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Kaldır" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Güncelle" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Koyu tema kullan: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Değişken bayrakları:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Versiyon" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "Etkin değilken her Minigalaxy açıldığında giriş yapılması istenir" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Minigalaxy'nin koyu tema kullanıp kullanmayacağı" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Windows oyunları kütüphanede gözükmeli mi" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Gizlenmiş oyunların kütüphanede gözükmeli mi" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "Yeni yüklenen oyunların kısa yol oluşturup oluşturmayacağı" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Wine dışarı aktarım başarısız." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine bulunamadı. Windows oyunlarının gösterimi etkinleştirilemez." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "indir" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "indirliyor…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "sırada…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "yükle" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "yükleniyor…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "oyna" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "siliniyor…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "güncelleniyor…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} arşivden çıkartılamadı." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} indirirken hata." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} kullanılabilinir bir yol değil" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} bozuk. Lütfen yeniden indirin." #~ msgid "Couldn't connect to GOG servers" #~ msgstr "GOG sunucularına bağlanılanamıyor" #~ msgid "Install Wine to enable this feature" #~ msgstr "Bu özelliği etkinleştirmek için Wine yükleyin" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "Ayarlar" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "" #~ "Oyun oynarken kare hızını göster. Sadece AMD ve Nvidia ekran kartlarında " #~ "çalışır." #~ msgid "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgstr "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgid "Couldn't start subprocess" #~ msgstr "Alt işlem başlatılamadı" #~ msgid "No error message was returned" #~ msgstr "Hata mesajı alınmadı" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy şuan çevrimdışı modunda çalışıyor" #~ msgid "Try again with an active internet connection" #~ msgstr "Aktif bir internet bağlantısı ile yeniden deneyiniz" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Indir" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Indirliyor…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "Sırada…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Yükle" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Yükleniyor…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Oyna" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Siliniyor…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Güncelleniyor…" sharkwouter-minigalaxy-759382b/data/po/uk.po000066400000000000000000000357031455252417400210440ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-09-29 19:02+0300\n" "Last-Translator: \n" "Language-Team: \n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.0\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Простий клієнт GOG для Linux" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "Інформація" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "Інформація" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "Додано в кінці команди, яка використовується для запуску гри" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "Додано перед командою, яка використовується для запуску гри" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "Ви впевнені, що хочете скасувати завантаження {}?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "Ви дійсно хочете вийти з GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "Ви впевнені, що хочете видалити %s?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "Бразильський Португальська" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "Скасувати" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "Скасувати" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "Китайська" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "Флажки для команди:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "Не вдалося відкрити сторінку магазину" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "Не вдалося відкрити сторінку підтримки" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "Створити ярлики меню: " #: minigalaxy/constants.py:31 msgid "Czech" msgstr "" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "Датська" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "Помилка при завантаженні" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "Голландська" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "Англійська" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "Не вдалося встановити {}" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "Не вдалося отримати бібліотеку" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "Не вдалося запустити {}:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "Фінська" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "Французька" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "Жанр" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "Німецька" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "Сторінка GitHub" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "Сховати гру:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "Угорська" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Помилка вилучення Innoextract." #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract не встановлено." #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "Шлях установки: " #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "Встановлено" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "Італійська" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "Японська" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "Зберегти інсталятори після завантаження гри.\n" "Інсталятори зберігаються в: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "Зберігати інсталятори: " #: minigalaxy/constants.py:18 msgid "Korean" msgstr "Корейська" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "Мова: " #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "Увійти" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "Вийти" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "У {} не знайдено жодного виконуваного файлу" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "Норвезька" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "Норвезька Букмол" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "Норвезька Ньорск" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "Не вистачає місця для розпакування гри. Потрібно: {} Доступно: {}" #: data/ui/properties.ui:280 msgid "OK" msgstr "ОК" #: data/ui/properties.ui:216 msgid "Open files" msgstr "Відкрийте файли" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "Перевірте підключення до Інтернету" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "Польська" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "Португальська" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "Налаштування" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "Налаштування" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "Бажана мова: " #: data/ui/gametile.ui:223 msgid "Properties" msgstr "Властивості" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "Властивості {}" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "Оновити список ігор" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "Regedit (регіт)" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "Російська" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "Зберегти" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "Показати FPS у грі:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "Показати ігри для Windows: " #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "Показати приховані ігри: " #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "Показати лише встановлені ігри" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "Спрощена Китайська" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "Іспанська" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "Залишатися в системі:" #: data/ui/properties.ui:77 msgid "Store" msgstr "Магазин" #: data/ui/properties.ui:63 msgid "Support" msgstr "Підтримка" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "Шведська" #: minigalaxy/constants.py:29 msgid "System default" msgstr "За замовчуванням" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "Не вдалося встановити {}. Спробуйте ще раз." #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "Бажана мова для завантаження ігор в" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Бажана мова для запуску Minigalaxy" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "Під час спроби отримати посилання для завантаження сталася помилка!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "Тут будуть встановлені ігри" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "Традиційна Китайська" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "Турецька" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "Видалити" #: data/ui/gametile.ui:193 msgid "Update" msgstr "Оновити" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "Використовувати темну тему: " #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "Флажки для змінної:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "Версія" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "" "Коли вимкнено, вам буде запропоновано входити в систему кожного разу при " "запуску Minigalaxy" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Використовувати темну тему для Minigalaxy чи ні" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Відображати ігри Windows у бібліотеці чи ні" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "Показувати приховані ігри в бібліотеці чи ні" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "" "Незалежно від того, створені ярлики для нещодавно встановлених ігор чи ні" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Помилка вилучення Wine." #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "Wine не знайдено. Показ ігор Windows не може бути ввімкнено." #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "завантажити" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "завантаження…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "в черзі…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "встановити" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "встановлення…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "грати" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "видалення…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "оновлення…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} не вдалося розпакувати." #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "Не вдалося завантажити {}." #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} некоректний шлях" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} пошкоджено. Завантажте ще раз." #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "Завантажити" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "Завантаження…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "В черзі…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "Встановити" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "Встановлення…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "Грати" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "Видалення…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "Оновлення…" sharkwouter-minigalaxy-759382b/data/po/zh_CN.po000066400000000000000000000324321455252417400214220ustar00rootroot00000000000000# 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: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2021-11-06 23:37+0900\n" "Last-Translator: \n" "Language-Team: \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.0\n" "Plural-Forms: nplurals=1; plural=0;\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "用于 Linux 的简易 GOG 客户端" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "关于" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "关于" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "已添加至游戏启动命令末尾处" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "已添加至游戏启动命令开头处" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "确定要取消下载 {} 吗?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "确定要登出GOG吗?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "确定要卸载 %s 吗?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "巴西葡萄牙语" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "取消" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "取消" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "汉语" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "命令标记:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "无法打开商店页面" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "无法打开支持页面" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "创建菜单快捷方式" #: minigalaxy/constants.py:31 msgid "Czech" msgstr "捷克语" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "丹麦语" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "下载错误" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "荷兰语" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "英语" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "更改软件语言失败。请确保已生成locale。" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "安装 {} 失败" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "获取游戏库失败" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "启动 {} 失败:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "芬兰语" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "法语" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "类型" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "德语" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub 页面" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "隐藏游戏:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "匈牙利语" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "使用Innoextract提取失败。" #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "未安装Innoextract" #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "安装路径:" #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "仅显示已安装的游戏" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "意大利语" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "日语" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "下载后保留安装包。安装包会保存在: {}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "保留安装包:" #: minigalaxy/constants.py:18 msgid "Korean" msgstr "韩语" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "语言" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "登录" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "退出登录" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "在 {} 内未找到可执行文件" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "挪威语" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "书面挪威语" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "新挪威语" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "可用空间不足,无法解压游戏。需要:{},可用:{}" #: data/ui/properties.ui:280 msgid "OK" msgstr "好" #: data/ui/properties.ui:216 msgid "Open files" msgstr "打开文件" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "请检查网络连接" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "波兰语" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "葡萄牙语" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "偏好设置" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "偏好设置" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "首选语言:" #: data/ui/gametile.ui:223 msgid "Properties" msgstr "属性" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "{} 的属性" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "刷新游戏列表" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "注册表" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "俄语" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "保存" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "在游戏内显示 FPS:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "显示 Windows 游戏:" #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "显示隐藏的游戏:" #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "只显示已安装的游戏" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "简体中文" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "西班牙语" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "保持登录:" #: data/ui/properties.ui:77 msgid "Store" msgstr "商店" #: data/ui/properties.ui:63 msgid "Support" msgstr "支持" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "瑞典语" #: minigalaxy/constants.py:29 msgid "System default" msgstr "系统默认" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "{} 安装错误,请重试。" #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "下载游戏时优先选择的语言" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "启动 Minigalaxy 时有限选择的语言" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "尝试获取下载链接时发生错误!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "游戏会被安装到这里" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "繁体中文" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "土耳其语" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "乌克兰语" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "卸载" #: data/ui/gametile.ui:193 msgid "Update" msgstr "更新" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "使用暗色主题:" #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "变量标记:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "版本" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "如果禁用,Minigalaxy 每次启动时将会请求您登录" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "是否让 Minigalaxy 使用暗色主题" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "是否在游戏库内显示 Windows 游戏" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "是否在游戏库内显示隐藏的游戏" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "游戏安装完成后是否创建快捷方式" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Wine 提取失败" #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "未找到 Wine。无法启用显示 Windows 游戏" #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "下载" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "正在下载…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "队列中…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "安装" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "正在安装…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "开始游戏" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "正在卸载…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "正在更新…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "{} 无法解压。" #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} 下载失败。" #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} 路径不可用" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} 已损坏,请重新下载。" #~ msgid "Couldn't connect to GOG servers" #~ msgstr "无法连接至 GOG 服务器" #~ msgid "Install Wine to enable this feature" #~ msgstr "安装 Wine 以启用这项功能" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "设置" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "在游戏内显示帧率。仅支持 AMD 和英伟达显卡。" #~ msgid "Couldn't start subprocess" #~ msgstr "无法启动子进程" #~ msgid "No error message was returned" #~ msgstr "未返回错误信息" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "下载" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "正在下载…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "队列中…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "安装" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "正在安装…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "开始游戏" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "正在卸载…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "正在更新…" sharkwouter-minigalaxy-759382b/data/po/zh_TW.po000066400000000000000000000356121455252417400214570ustar00rootroot00000000000000# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # # Jeff Huang , 2019, 2020, 2023. msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-11-26 14:30-0800\n" "PO-Revision-Date: 2023-03-19 20:48+0800\n" "Last-Translator: Jeff Huang \n" "Language-Team: Chinese \n" "Language: zh_TW\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-Generator: Lokalize 22.12.3\n" #. A short description of what Minigalaxy is #: data/ui/about.ui:10 msgctxt "short_description" msgid "A simple GOG client for Linux" msgstr "Linux 上簡單的 GOG 客戶端" #: minigalaxy/ui/about.py:13 msgid "About" msgstr "關於" #. Opens about dialog #: data/ui/application.ui:58 msgctxt "about" msgid "About" msgstr "關於" #: data/ui/properties.ui:129 msgid "Added at the end of the command used to launch the game" msgstr "新增於用來啟動遊戲的指令結尾" #: data/ui/properties.ui:141 msgid "Added in front of the command used to launch the game" msgstr "新增於用來啟動遊戲的指令之前" #: minigalaxy/ui/gametile.py:132 msgid "Are you sure you want to cancel downloading {}?" msgstr "您確定您想要取消下載 {} 嗎?" #: minigalaxy/ui/window.py:100 msgid "Are you sure you want to log out of GOG?" msgstr "您確定要登出 GOG?" #: minigalaxy/ui/gametile.py:145 #, python-format msgid "Are you sure you want to uninstall %s?" msgstr "您確定要解除安裝 %s 嗎?" #: minigalaxy/constants.py:7 minigalaxy/constants.py:30 msgid "Brazilian Portuguese" msgstr "葡萄牙文(巴西)" #: data/ui/properties.ui:266 msgid "Cancel" msgstr "取消" #: data/ui/preferences.ui:20 msgctxt "cancel" msgid "Cancel" msgstr "取消" #: minigalaxy/constants.py:8 msgid "Chinese" msgstr "中文" #: data/ui/properties.ui:180 msgid "Command flags:" msgstr "命令列旗標:" #: minigalaxy/ui/properties.py:103 msgid "Couldn't open store page" msgstr "無法開啟商店頁面" #: minigalaxy/ui/properties.py:93 msgid "Couldn't open support page" msgstr "無法開啟支援頁面" #. Has to end with ": " #: data/ui/preferences.ui:288 msgctxt "create_menu_shortcuts" msgid "Create menu shortcuts: " msgstr "建立選單捷徑:" #: minigalaxy/constants.py:31 msgid "Czech" msgstr "捷克文" #: data/ui/gametile.ui:178 msgid "DLC" msgstr "DLC" #: minigalaxy/constants.py:9 msgid "Danish" msgstr "丹麥文" #: minigalaxy/ui/gametile.py:207 minigalaxy/ui/gametile.py:237 msgid "Download error" msgstr "下載錯誤" #: minigalaxy/constants.py:10 minigalaxy/constants.py:32 msgid "Dutch" msgstr "荷蘭文" #: minigalaxy/constants.py:11 minigalaxy/constants.py:33 msgid "English" msgstr "英文" #: minigalaxy/ui/preferences.py:102 msgid "" "Failed to change program language. Make sure locale is generated on your " "system." msgstr "變更程式語言失敗。請確定您的系統上以產生對應的語系檔。" #: minigalaxy/ui/gametile.py:301 msgid "Failed to install {}" msgstr "安裝 {} 失敗" #: minigalaxy/ui/library.py:141 msgid "Failed to retrieve library" msgstr "擷取收藏庫失敗" #: minigalaxy/launcher.py:34 minigalaxy/ui/gametile.py:122 msgid "Failed to start {}:" msgstr "啟動 {} 失敗:" #: minigalaxy/constants.py:12 minigalaxy/constants.py:34 msgid "Finnish" msgstr "芬蘭文" #: minigalaxy/constants.py:13 minigalaxy/constants.py:35 msgid "French" msgstr "法文" #: minigalaxy/ui/properties.py:140 msgid "Genre" msgstr "類型" #: minigalaxy/constants.py:14 minigalaxy/constants.py:36 msgid "German" msgstr "德文" #. A link to the GitHub page #: data/ui/about.ui:12 msgctxt "github_page_link" msgid "GitHub page" msgstr "GitHub 頁面" #: data/ui/properties.ui:105 msgid "Hide game:" msgstr "隱藏遊戲:" #: minigalaxy/constants.py:15 msgid "Hungarian" msgstr "匈牙利文" #: minigalaxy/installer.py:147 msgid "Innoextract extraction failed." msgstr "Innoextract 擷取失敗。" #: minigalaxy/installer.py:158 msgid "Innoextract not installed." msgstr "Innoextract 未安裝。" #. The place directory in which games are installed. Ends with ": ". #: data/ui/preferences.ui:118 msgctxt "install_path" msgid "Installation path: " msgstr "安裝路徑:" #. Used next to a checkbox which switches between showing all games and only installed ones #: data/ui/application.ui:116 msgctxt "installed" msgid "Installed" msgstr "已安裝" #: minigalaxy/constants.py:16 minigalaxy/constants.py:37 msgid "Italian" msgstr "義大利文" #: minigalaxy/constants.py:17 msgid "Japanese" msgstr "日文" #: minigalaxy/ui/preferences.py:46 msgid "" "Keep installers after downloading a game.\n" "Installers are stored in: {}" msgstr "" "在下載遊戲後保留安裝程式。\n" "安裝程式儲存於:{}" #. Whether installer files are kept after download. Ends with ": ". #: data/ui/preferences.ui:145 msgctxt "keep_installer" msgid "Keep installers: " msgstr "保留安裝程式:" #: minigalaxy/constants.py:18 msgid "Korean" msgstr "韓文" #. The preferred language on Minigalaxy start. Ends with ": ". #: data/ui/preferences.ui:68 msgctxt "language" msgid "Language: " msgstr "語言:" #: minigalaxy/ui/login.py:20 msgid "Login" msgstr "登入" #. Logs the users gog account out and returns to login page #: data/ui/application.ui:17 msgctxt "logout" msgid "Logout" msgstr "登出" #: minigalaxy/launcher.py:179 msgid "No executable was found in {}" msgstr "在 {} 找不到可執行檔" #: minigalaxy/constants.py:19 msgid "Norwegian" msgstr "挪威文" #: minigalaxy/constants.py:38 msgid "Norwegian Bokmål" msgstr "書面挪威文" #: minigalaxy/constants.py:39 msgid "Norwegian Nynorsk" msgstr "新挪威文" #: minigalaxy/installer.py:97 msgid "Not enough space to extract game. Required: {} Available: {}" msgstr "沒有足夠的空間可以用來解壓縮遊戲。需要:{} 可用:{}" #: data/ui/properties.ui:280 msgid "OK" msgstr "確定" #: data/ui/properties.ui:216 msgid "Open files" msgstr "開啟檔案" #: minigalaxy/ui/properties.py:94 minigalaxy/ui/properties.py:104 msgid "Please check your internet connection" msgstr "請檢查您的網際網路連線" #: minigalaxy/constants.py:20 minigalaxy/constants.py:40 msgid "Polish" msgstr "波蘭文" #: minigalaxy/constants.py:21 msgid "Portuguese" msgstr "葡萄牙文" #: minigalaxy/ui/preferences.py:30 msgid "Preferences" msgstr "偏好設定" #. Opens preferences dialog #: data/ui/application.ui:32 msgctxt "preferences" msgid "Preferences" msgstr "偏好設定" #. Preferred language for downloading games in. Ends with ": ". #: data/ui/preferences.ui:93 msgctxt "preferred_game_language" msgid "Preferred game language: " msgstr "偏好的語言:" #: data/ui/gametile.ui:223 msgid "Properties" msgstr "屬性" #: minigalaxy/ui/properties.py:33 msgid "Properties of {}" msgstr "{} 的屬性" #. Tooltip for refresh button #: data/ui/application.ui:89 msgctxt "refresh" msgid "Refresh game list" msgstr "重新整理遊戲清單" #: data/ui/properties.ui:203 msgid "Regedit" msgstr "登錄檔編輯" #: minigalaxy/constants.py:22 minigalaxy/constants.py:41 msgid "Russian" msgstr "俄文" #. Saves the preferences #: data/ui/preferences.ui:34 msgctxt "save" msgid "Save" msgstr "儲存" #: data/ui/properties.ui:154 msgid "Show FPS in game:" msgstr "在遊戲中顯示 FPS:" #. Has to end with ": " #: data/ui/preferences.ui:250 msgctxt "show_windows_games" msgid "Show Windows games: " msgstr "顯示 Windows 遊戲:" #. Has to end with ": " #: data/ui/preferences.ui:224 msgctxt "show_hidden_games" msgid "Show hidden games: " msgstr "顯示隱藏的遊戲:" #. Tooltip for the switch which allows only showing installed games or all games #: data/ui/application.ui:105 msgctxt "tooltip_installed" msgid "Show only installed games" msgstr "僅顯示已安裝的遊戲" #: minigalaxy/constants.py:42 msgid "Simplified Chinese" msgstr "簡體中文" #: minigalaxy/constants.py:23 minigalaxy/constants.py:43 msgid "Spanish" msgstr "西班牙文" #. Whether the user will stay logged in after closing Minigalaxy. Ends with ": ". #: data/ui/preferences.ui:171 msgctxt "stay_logged_in" msgid "Stay logged in:" msgstr "保持登入:" #: data/ui/properties.ui:77 msgid "Store" msgstr "商店" #: data/ui/properties.ui:63 msgid "Support" msgstr "支援" #: minigalaxy/constants.py:24 minigalaxy/constants.py:44 msgid "Swedish" msgstr "瑞典文" #: minigalaxy/constants.py:29 msgid "System default" msgstr "系統預設" #: minigalaxy/installer.py:128 msgid "The installation of {} failed. Please try again." msgstr "{} 安裝失敗。請再試一次。" #: data/ui/preferences.ui:90 msgctxt "preferred_game_language_tooltip" msgid "The preferred language for downloading games in" msgstr "下載遊戲的偏好語言" #: data/ui/preferences.ui:65 msgctxt "language_tooltip" msgid "The preferred language on Minigalaxy start" msgstr "Minigalaxy 啟動時的偏好語言" #: minigalaxy/ui/gametile.py:208 msgid "" "There was an error when trying to fetch the download link!\n" "{}" msgstr "" "嘗試擷取下載連結時發生錯誤!\n" "{}" #: data/ui/preferences.ui:115 msgctxt "install_path_tooltip" msgid "This is where games will be installed" msgstr "遊戲會安裝在" #: minigalaxy/constants.py:45 msgid "Traditional Chinese" msgstr "正體中文" #: minigalaxy/constants.py:25 minigalaxy/constants.py:46 msgid "Turkish" msgstr "土耳其文" #: minigalaxy/constants.py:47 msgid "Ukrainian" msgstr "烏克蘭文" #: data/ui/gametile.ui:208 msgid "Uninstall" msgstr "解除安裝" #: data/ui/gametile.ui:193 msgid "Update" msgstr "更新" #. Has to end with ": " #: data/ui/preferences.ui:198 msgctxt "use_dark_theme" msgid "Use dark theme: " msgstr "使用深色佈景主題:" #: data/ui/properties.ui:167 msgid "Variable flags:" msgstr "變數旗標:" #: minigalaxy/ui/properties.py:142 msgid "Version" msgstr "版本" #: data/ui/preferences.ui:168 msgctxt "stay_logged_in_tooltip" msgid "When disabled you'll be asked to log in each time Minigalaxy is started" msgstr "若停用,您每次打開 Minigalaxy 時都會被要求登入" #: data/ui/preferences.ui:195 msgctxt "use_dark_theme_tooltip" msgid "Whether Minigalaxy should use dark theme or not" msgstr "Minigalaxy 是否應該使用深色佈景主題" #: data/ui/preferences.ui:247 msgctxt "show_windows_games_tooltip" msgid "Whether Windows games are shown in the library or not" msgstr "Windows 遊戲是否要顯示在收藏庫中" #: data/ui/preferences.ui:221 msgctxt "show_hidden_games_tooltip" msgid "Whether hidden games are shown in the library or not" msgstr "隱藏的遊戲是否要顯示在收藏庫中" #: data/ui/preferences.ui:285 msgctxt "create_menu_shortcuts_tooltip" msgid "Whether shortcuts are created for newly installed games or not" msgstr "新安裝的遊戲是否應建立捷徑" #: minigalaxy/installer.py:172 msgid "Wine extraction failed." msgstr "Wine 解壓縮失敗。" #: minigalaxy/ui/preferences.py:162 msgid "Wine wasn't found. Showing Windows games cannot be enabled." msgstr "找不到 Wine。無法啟用顯示 Windows 遊戲。" #: data/ui/properties.ui:190 msgid "Winecfg" msgstr "Winecfg" #: minigalaxy/ui/gametile.py:460 msgid "download" msgstr "下載" #: minigalaxy/ui/gametile.py:500 msgid "downloading…" msgstr "正在下載…" #: minigalaxy/ui/gametile.py:491 msgid "in queue…" msgstr "在佇列中…" #: minigalaxy/ui/gametile.py:479 msgid "install" msgstr "安裝" #: minigalaxy/ui/gametile.py:511 msgid "installing…" msgstr "正在安裝…" #: minigalaxy/ui/gametile.py:526 minigalaxy/ui/gametile.py:556 msgid "play" msgstr "遊玩" #: minigalaxy/ui/gametile.py:542 msgid "uninstalling…" msgstr "正在解除安裝…" #: minigalaxy/ui/properties.py:136 msgid "unknown" msgstr "未知" #: minigalaxy/ui/gametile.py:565 msgid "updating…" msgstr "正在更新…" #: minigalaxy/installer.py:130 msgid "{} could not be unzipped." msgstr "無法解壓縮 {}。" #: minigalaxy/installer.py:73 msgid "{} failed to download." msgstr "{} 下載失敗。" #: minigalaxy/ui/preferences.py:174 msgid "{} isn't a usable path" msgstr "{} 不是可用的路徑" #: minigalaxy/installer.py:85 msgid "{} was corrupted. Please download it again." msgstr "{} 已損毀。請再下載一次。" #: minigalaxy/ui/gametile.py:519 msgid "Download" msgstr "下載" #: minigalaxy/ui/gametile.py:559 msgid "Downloading…" msgstr "正在下載…" #: minigalaxy/ui/gametile.py:550 msgid "In queue…" msgstr "在佇列中…" #: minigalaxy/ui/gametile.py:538 msgid "Install" msgstr "安裝" #: minigalaxy/ui/gametile.py:570 msgid "Installing…" msgstr "正在安裝…" #: minigalaxy/ui/gametile.py:585 minigalaxy/ui/gametile.py:617 msgid "Play" msgstr "遊玩" #: minigalaxy/ui/gametile.py:602 msgid "Uninstalling…" msgstr "正在解除安裝…" #: minigalaxy/ui/gametile.py:626 msgid "Updating…" msgstr "正在更新…" #~ msgid "Couldn't connect to GOG servers" #~ msgstr "無法連線至 GOG 伺服器" #~ msgid "Install Wine to enable this feature" #~ msgstr "安裝 Wine 以啟用此功能" #~ msgctxt "Wine Settings" #~ msgid "Settings" #~ msgstr "設定" #~ msgctxt "show_fps_tooltip" #~ msgid "" #~ "Show the framerate while playing games. Only works with AMD and Nvidia " #~ "graphics cards." #~ msgstr "在遊玩遊戲時顯示畫面率。僅可與 AMD 及 Nvidia 的顯示卡一同運作。" #~ msgid "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgstr "" #~ "Artur Wróblewski\n" #~ "BlindJerobine\n" #~ "Esdras Tarsis\n" #~ "Hüseyin Fahri Uzun\n" #~ "Jan Kjetil Myklebust\n" #~ "Jeff Huang\n" #~ "kimmalmo\n" #~ "ProTheory8\n" #~ "thomasb22" #~ msgid "Couldn't start subprocess" #~ msgstr "無法啟動子行程" #~ msgid "No error message was returned" #~ msgstr "未傳回錯誤訊息" #~ msgid "Minigalaxy is now running in offline mode" #~ msgstr "Minigalaxy 目前以離線模式執行" #~ msgid "Try again with an active internet connection" #~ msgstr "使用有效的網際網路連線重試" sharkwouter-minigalaxy-759382b/data/style.css000066400000000000000000000007011455252417400213070ustar00rootroot00000000000000.test { border: 1px solid green; color: red; background-color: purple; } #gametile .button-left { border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-right-width: 0px; margin-right: 0px; } #gametile .button-right { margin-left: 0px; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-left-radius: 0px; } #gametile .progress-bar * { border-radius: 0px; }sharkwouter-minigalaxy-759382b/data/ui/000077500000000000000000000000001455252417400200545ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/data/ui/about.ui000066400000000000000000000120561455252417400215310ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/application.ui000066400000000000000000000215251455252417400227230ustar00rootroot00000000000000 False True False vertical True True True Logout True False True 0 True True True Preferences True False True 1 True False False True 2 True True True About True False True 3 sharkwouter-minigalaxy-759382b/data/ui/categoryfilters.ui000066400000000000000000000073571455252417400236350ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/filterswitch.ui000066400000000000000000000023651455252417400231300ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/gametile.ui000066400000000000000000000250031455252417400222020ustar00rootroot00000000000000 False True False vertical False True False vertical True True True Update True False True 0 True True none left dlc_popover True False DLC False True 1 True True True Uninstall True False True 2 True True True Information True False True 3 True True True Properties True False True 4 sharkwouter-minigalaxy-759382b/data/ui/gametilelist.ui000066400000000000000000000272001455252417400230770ustar00rootroot00000000000000 False True False vertical False True False vertical True True none left dlc_popover True False DLC False True 0 True True True Update True False True 1 True True True Uninstall True False True 2 True True True Information True False True 3 True True True Properties True False True 4 sharkwouter-minigalaxy-759382b/data/ui/information.ui000066400000000000000000000221041455252417400227370ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/library.ui000066400000000000000000000016431455252417400220630ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/login.ui000066400000000000000000000025561455252417400215330ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/preferences.ui000066400000000000000000000416051455252417400227220ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/data/ui/properties.ui000066400000000000000000000533371455252417400226220ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/debian/000077500000000000000000000000001455252417400177505ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/debian/changelog000066400000000000000000000340761455252417400216340ustar00rootroot00000000000000minigalaxy (1.2.6) jammy; urgency=medium * Fix changing the install path causing an exception * Fix error detection & reporting on wineprefix creation failure (thanks to LeXofLeviafan) -- Wouter Wijsman Fri, 19 Jan 2024 16:50:38 +0000 minigalaxy (1.2.5) jammy; urgency=medium * Fix filtering for installed games -- Wouter Wijsman Fri, 11 Aug 2023 14:06:56 +0000 minigalaxy (1.2.4) jammy; urgency=medium * Fix packages missing a script -- Wouter Wijsman Fri, 11 Aug 2023 11:42:09 +0000 minigalaxy (1.2.3) jammy; urgency=medium * Fix short freeze on startup (thanks to LeXofLeviafan) * Fix game information not showing in list view (thanks to TotalCaesar659) * Hide A Plague Tale Digital Goodies Pack (thanks to TotalCaesar659) * Remove round corners from top of the "play" button (thanks to lmeunier) * Move the Gametile menu button alongside the Play button (thanks to lmeunier) * Update Spanish translation (thanks to manurtinez) * Capitalize first letter of the "play/download/..." button (thanks to lmeunier) * Update Traditional Chinese translation (thanks to s8321414) * Added additional tooltips to buttons, labels, menu items and radio buttons (thanks to orende) * Hide CDPR Goodie Pack Content * Add notifications on successful download and installation of games (thanks to orende) * Add category filtering dialog for game library (thanks to orende) * Parallelize api.can_connect function with threads, futures (thanks to orende) * Fix available disk space being checked in parent directory (thanks to Unrud) * Create new config if reading it fails -- Wouter Wijsman Fri, 11 Aug 2023 09:24:02 +0000 minigalaxy (1.2.2) focal; urgency=medium * Fix progress bar not showing up for downloads * Fix downloads not being cancellable * Fix incompatibility with python 3.6 * Fix connection error texts (thanks to TotalCaesar659) * Show DLC titles in English (thanks to TotalCaesar659) * Fix version not being updated during a release * Update Norwegian Bokmål translation (thanks to kimmalmo) * Update Czech translation (thanks to jakbuz23) -- Wouter Wijsman Tue, 27 Sep 2022 09:26:25 +0000 minigalaxy (1.2.1) focal; urgency=medium * Fix downloads failing when content length is not returned by the server * Allow different types of downloads to happen at the same time (thanks to jgerrish) * Fix metadata file having releases in wrong order -- Wouter Wijsman Mon, 19 Sep 2022 23:47:07 +0000 minigalaxy (1.2.0) focal; urgency=medium * Split game information and properties into different windows (thanks to TotalCaesar659) * Add list view (thanks to TotalCaesar659) * Allow DLC to be queued up for downloading (thanks to flagrama) * Fix changing library to a directory with special characters in the name (thanks to makson96) * Fix signing in with Facebook (thanks to phlash) * Always use cached DLC icons and thumbnails (thanks to TotalCaesar659) * Cache information covers (thanks to TotalCaesar659) * Fix installers not being cleaned up like expected (thanks to Kzimir) * Fix error when opening game properties window when wine is not installed (thanks to lmeunier) * Fix freeze for games generating a lot of output (thanks to lmeunier) * Fix extracting rar based games with innoextract (thanks to Kzimir) * Allow setting wine executable per game (thanks to Kzimir) * Add GameMode support (thanks to TotalCaesar659) * Add MangoHud support (thanks to TotalCaesar659) * Add option to use Winetricks (thanks to TotalCaesar659) * Fix updates not always being detected directly after opening Minigalaxy (thanks to TotalCaesar659) * Fix desktop files generated not always being able to launch (thanks to otaconix) * Show percentage when hovering over download progress bar (thanks to TotalCaesar659) * Add option to disable update check per game (thanks to TotalCaesar659) * Add forum, GOG Database and PCGamingWiki URLs to game information (thanks to TotalCaesar659) * List genre as unknown in game information when none is found (thanks to mareksapota) * Fix changing installation path causing crashes in rare cases (thanks to makson96) * Fall back to English when locale cannot be determined (thanks to flagrama) * Add gettext to build dependencies (thanks to larslindq) * Improve error handling upon API errors * Fix several issues with launching Windows games from Minigalaxy * Fix some games getting stuck on in queue * Fix Windows game installation not caring about preferred language (thanks to Kzimir) * Add Greek translation (thanks to Pyrofanis) * Add Spanish (Spain) translation (thanks to mbarrio) * Add Romanian (Romania) translation (thanks to xSlendiX) * Update Norwegian Bokmål translation (thanks to kimmalmo) * Update Czech translation (thanks to jakbuz23) -- Wouter Wijsman Mon, 19 Sep 2022 18:59:03 +0000 minigalaxy (1.1.0) focal; urgency=medium * Improve integrity check after downloading (thanks to makson96) * Show an error showing Windows games cannot be enabled * Add properties menu for games where game specific actions can be made like setting launch options and opening the store page (thanks to Odelpasso and makson96) * Add a disk space check before downloading (thanks to SvdB-nonp and makson96) * Use a different color for the play button for installed games * Put installed games at the top of the list * Store saved installers in ``~/GOG Games/installer`` by default again (thanks to makson96) * Remember if the user had the installed filter enabled (thanks to makson96) * Extract Windows games in the background if Innoextract is available (thanks to makson96) * Extract Windows games in the background (thanks to Odelpasso) * Fix installing DLC for Windows games (thanks to makson96) * Fix an error showing if the user has no games (thanks to makson96) * Add option to hide games (thanks to TotalCaesar659) * Ask user if they are sure when logging out (thanks to TotalCaesar659) * Add a dark theme (thanks to TotalCaesar659) * Run post install script after installation. This fixes Full Throttle Remastered (thanks to makson96) * Fix games being shown twice * Fix crash when GOG is down (thanks to lmeunier) * Make the language configurable (thanks to TotalCaesar659 and zweif) * Add the following translations: * Czech (thanks to jakbuz23) * Finnish (thanks to heidiwenger and jonnelafin) * Italian (thanks to koraynilay) * Swedish (thanks to Newbytee) * Ukrainian (thanks to karaushu) * Update the following translations: * Dutch * German (thanks to zweif) * Norwegian Nynorsk (thanks to LordPilum) * Polish (thanks to ArturWroblewski) * Russian (thanks to TotalCaesar659) * Simplified Chinese (thanks to dummyx) * Spanish (thanks to LocalPinkRobin and advy99) * Turkish (thanks to fuzunspm) -- Wouter Wijsman Mon, 08 Nov 2021 10:40:34 +0100 minigalaxy (1.0.2) buster; urgency=medium * Fix updates sometimes not working * Fix some games always showing an update is available * Fix DLC not downloading (thanks to stephanlachnit) * Fix DLC update option not showing up (thanks to makson96) * Fix show store page button not showing anymore (thanks to makson96) * Fix missing thumbnails not being downloaded for already installed games (thanks to makson96) * Fix the login screen crashing in some cases (thanks to makson96) * Use the system's icon theme for icons used (thanks to stephanlachnit and makson96) -- Wouter Wijsman Thu, 14 Jan 2021 09:20:17 +0100 minigalaxy (1.0.1) buster; urgency=medium * Open maximized if the window was maximized when last closed (thanks to TotalCaesar659) * Kept installers are now stored in ~/.cache/minigalaxy/download * Fix about window displaying wrong version number * Fix show store page button not showing anymore (thanks to makson96) * Fix the download manager crashing when an installer has been damaged during downloading (thanks to makson96) * Fix games showing an update is available while the latest version is installed (thanks to makson96) * Fix loading the library taking a long time when many games are installed (thanks to makson96) * Fix Gex not launching * Add the following translations: * Swedish (thanks to Newbytee) * Update the following translations: * Polish (thanks to ArturWroblewski) * Russian (thanks to TotalCaesar659) -- Wouter Wijsman Thu, 07 Jan 2021 12:51:33 +0100 minigalaxy (1.0.0) buster; urgency=medium * Games can now be updated (thanks to mdgomes and makson96) * DLC can now be installed and updated (thanks to makson96) * The installed filter now also shows games which are downloading (thanks to makson96) * Fix crash on some systems where /usr/bin is linked to /bin (thanks to sgn) * Create new config file if old one is unreadable (thanks to SvdB-nonp) * Fix some Windows games not installing because of the directory name used (thanks to SvdB-nonp) * Fix some Windows games like Witcher 3 not launching because of the working directory not being set (thanks for kibun1) * Clean up installation files for cancelled downloads (thanks to SvdB-nonp) * Fix crash on flaky internet connection (thanks to makson96) * Use 755 permissions for all directories created by Minigalaxy * Remove cached files when cancelling a download (thanks to svdB-nonp) * Installed games should no longer be shown twice (thanks to makson96) * Add the following translations: * Simplified Chinese (thanks to dummyx) * Spanish (thanks to juanborda) * Update the following translations: * Brazilian Portuguese (thanks to EsdrasTarsis) * Dutch * French (thanks to Thomasb22) * German (thanks to BlindJerobine) * Norwegian Bokmål (thanks to kimmalmo) * Russian (thanks to protheory8) * Taiwanese Mandarin (thanks to s8321414) * Turkish (thanks to fuzunspm) -- Wouter Wijsman Sun, 29 Nov 2020 13:24:10 +0100 minigalaxy (0.9.4) buster; urgency=medium * Added the following translations: * Norwegian Nynorsk (thanks to LordPilum) * Russian (thanks to protheory8) * Updated the following translations: * Brazilian Portuguese (thanks to EsdrasTarsis) * French (thanks to Thomasb22) * German (thanks to BlindJerobine) * Norwegian Bokmål (thanks to kimmalmo) * Polish (thanks to ArturWroblewski) * Taiwanese Mandarin (thanks to s8321414) * Turkish (thanks to fuzunspm) * Added support for installing Windows games (with help from Odelpasso). * Added store page link to game menus (thanks to larslindq). * Fixed game directories being created without any spaces in the name (thanks to larslindq). * Fixed thumbnails not being downloaded for already installed games. * Fixed symlinks to libraries not being created correctly upon installation. * Made preparations for a Flathub package. * Added all contributors and translators to the about window. -- Wouter Wijsman Mon, 20 Apr 2020 20:51:08 +0200 minigalaxy (0.9.3) buster; urgency=medium * Added the following translations: * German (thanks to BlindJerobine) * Turkish (thanks to fuzunspm) * Brazilian Portuguese (thanks to EsdrasTarsis) * Norwegian Bokmål (thanks to kimmalmo) * Polish (thanks to ArturWroblewski) * French (thanks to thomansb22) * Added option to cancel downloads. * Changed the way games are downloaded to a queue instead of trying to download everything at once. * Added support option to game specific menus which open the GOG support page (thanks to BlindJerobine). * Ask for confirmation before uninstalling (thanks to Odelpasso). * Downloads can now be resumed after having been cancelled before. * Installers are now verified before installing. * The active download is now resumed when restarting Minigalaxy. * Fixed issue with games not downloading. -- Wouter Wijsman Wed, 04 Mar 2020 21:15:37 +0100 minigalaxy (0.9.2) buster; urgency=medium * Added a button to installed games which allow you to: * Uninstall a game. * Open the directory in which the game is installed. * Added translations for the following languages: * Dutch * Taiwanese Mandarin (thanks to s8321414) * German (thanks to BlindJerobine) * Added offline mode. * The system's Dosbox and Scummvm installations are now preferred over the ones bundled with games. * Improved game detection to check in all directories in the installation path. * Added the option to keep game installers (thanks to Odelpasso). * Added the option to disable staying logged in (thanks to Odelpasso). * The preferences menu now uses a file picker for setting the installation path (thanks to Odelpasso). * Startup time has been reduced. * Games which aren't installed are now grayed out. * Fixed FTL not being able to start. * Fixed issue with thumbnails sometimes not fully loading. * Fixed potential crash after logging in the first time. * Fixed close button on about window not working. -- Wouter Wijsman Tue, 07 Jan 2020 21:22:38 +0100 minigalaxy (0.9.1) buster; urgency=medium * Fixed crashes and freezes sometimes happening while downloading and installing games. * Fixed installation failing when the installation directory is not on same filesystem as /home. * Fixed downloads crashing when the installation directory is changed or the refresh button is pressed. * Fixed changing installation directory not loading which games are installed in the new directory. * Fixed copyright file in deb package not being machine readable. * Moved binary to /usr/games in the deb package. * Add command line options --help, --version and --reset. The reset option will reset the cache and configuration. -- Wouter Wijsman Fri, 27 Dec 2019 22:31:31 +0100 minigalaxy (0.9.0) buster; urgency=medium * Initial Release. -- Wouter Wijsman Tue, 24 Dec 2019 12:09:27 +0100 sharkwouter-minigalaxy-759382b/debian/clean000066400000000000000000000001121455252417400207470ustar00rootroot00000000000000minigalaxy.egg-info/ minigalaxy/__pycache__/ data/mo/ debian/minigalaxy.6 sharkwouter-minigalaxy-759382b/debian/control000066400000000000000000000030461455252417400213560ustar00rootroot00000000000000Source: minigalaxy Section: games Priority: optional Maintainer: Wouter Wijsman Build-Depends: debhelper-compat (= 12), dh-sequence-python3, python3-all, python3-setuptools, python3-requests, help2man, gettext, Standards-Version: 4.5.0 Rules-Requires-Root: no Vcs-Git: https://github.com/sharkwouter/minigalaxy.git Vcs-Browser: https://github.com/sharkwouter/minigalaxy Homepage: https://sharkwouter.github.io/minigalaxy/ Package: minigalaxy Architecture: all Depends: ${misc:Depends}, ${python3:Depends}, python3-requests, python3-gi (>= 3.29.1), python3-gi-cairo, gir1.2-gtk-3.0, gir1.2-glib-2.0, gir1.2-gdkpixbuf-2.0, gir1.2-webkit2-4.0, unzip, xdg-utils, unrar-free (>= 1:0.1.0) | unrar, Suggests: dosbox, scummvm, wine32 | wine32-development | wine-stable-i386 | wine-devel-i386 | wine-staging-i386, innoextract, Description: Simple GOG Linux client Allows you to download and play Linux games from the gog.com game store. . Besides installing games, it offers the following feature: - Update your games - Install and update DLC - Select in which language you'd prefer to download your games - Change where games are installed - Search your GOG Linux library - Show all games or just the ones you've installed - View the error message if a game fails to launch . A GOG account is required to use this software. sharkwouter-minigalaxy-759382b/debian/copyright000066400000000000000000000457111455252417400217130ustar00rootroot00000000000000Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: minigalaxy Upstream-Contact: Wouter Wijsman Source: https://github.com/sharkwouter/minigalaxy Files: * Copyright: 2019-2020 Wouter Wijsman License: GPL-3+ Files: data/icons/* Copyright: 2014 Epic Runes License: CC-BY-3.0 Files: data/io.github.sharkwouter.Minigalaxy.metainfo.xml data/io.github.sharkwouter.Minigalaxy.desktop Copyright: 2019-2020 Wouter Wijsman . . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". License: CC-BY-3.0 http://creativecommons.org/licenses/by/3.0/ . THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. . BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. . 1. Definitions . a. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. b. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. c. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. d. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. e. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. f. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. g. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. h. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. i. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. . 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. . 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: . a. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; b. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; c. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, d. to Distribute and Publicly Perform Adaptations. e. For the avoidance of doubt: . i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, iii. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. . The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. . 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: . a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. c. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. . 5. Representations, Warranties and Disclaimer . UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. . 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. . 7. Termination . a. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. b. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. . 8. Miscellaneous . a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. b. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. c. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. e. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. License: CC0-1.0 On Debian systems, the complete text of the Create Commons Universal license version 1.0 can be found in "/usr/share/common-licenses/CC0-1.0". sharkwouter-minigalaxy-759382b/debian/minigalaxy.docs000066400000000000000000000000121455252417400227550ustar00rootroot00000000000000README.md sharkwouter-minigalaxy-759382b/debian/minigalaxy.manpages000066400000000000000000000000231455252417400236220ustar00rootroot00000000000000debian/minigalaxy.6sharkwouter-minigalaxy-759382b/debian/rules000077500000000000000000000005201455252417400210250ustar00rootroot00000000000000#!/usr/bin/make -f #export DH_VERBOSE = 1 export PYBUILD_DISABLE=test export PYBUILD_NAME=minigalaxy export PYBUILD_INSTALL_ARGS_python3=--install-scripts=/usr/games/ %: dh $@ --buildsystem=pybuild override_dh_auto_build: help2man -N -s 6 -n "a simple GTK based GOG Linux client" bin/minigalaxy > debian/minigalaxy.6 dh_auto_build sharkwouter-minigalaxy-759382b/debian/source/000077500000000000000000000000001455252417400212505ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/debian/source/format000066400000000000000000000000151455252417400224570ustar00rootroot000000000000003.0 (native) sharkwouter-minigalaxy-759382b/minigalaxy/000077500000000000000000000000001455252417400206705ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/minigalaxy/__init__.py000066400000000000000000000000341455252417400227760ustar00rootroot00000000000000""" MiniGalaxy packages """ sharkwouter-minigalaxy-759382b/minigalaxy/api.py000066400000000000000000000332011455252417400220120ustar00rootroot00000000000000import http import os import time from urllib.parse import urlencode import requests import xml.etree.ElementTree as ET from concurrent.futures import ThreadPoolExecutor, as_completed from requests import Session from minigalaxy.file_info import FileInfo from minigalaxy.game import Game from minigalaxy.constants import IGNORE_GAME_IDS from minigalaxy.config import Config from minigalaxy.logger import logger class NoDownloadLinkFound(BaseException): pass class Api: def __init__(self, config: Config, session: Session): self.config = config self.session = session self.login_success_url = "https://embed.gog.com/on_login_success" self.redirect_uri = "https://embed.gog.com/on_login_success?origin=client" self.client_id = "46899977096215655" self.client_secret = "9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9" self.debug = os.environ.get("MG_DEBUG") self.active_token = False self.active_token_expiration_time = time.time() self.conn_check_thpool = ThreadPoolExecutor(max_workers=2) # use a method to authenticate, based on the information we have # Returns an empty string if no information was entered def authenticate(self, login_code: str = None, refresh_token: str = None) -> str: if refresh_token: return self.__refresh_token(refresh_token) elif login_code: return self.__get_token(login_code) else: return '' # Get a new token with the refresh token received when authenticating the last time def __refresh_token(self, refresh_token: str) -> str: params = { 'client_id': self.client_id, 'client_secret': self.client_secret, 'grant_type': 'refresh_token', 'refresh_token': refresh_token, } response_token = self.__get_refresh_token(params) return response_token # Get a token based on the code returned by the login screen def __get_token(self, login_code: str) -> str: params = { 'client_id': self.client_id, 'client_secret': self.client_secret, 'grant_type': 'authorization_code', 'code': login_code, 'redirect_uri': self.redirect_uri, } response_token = self.__get_refresh_token(params) return response_token def __get_refresh_token(self, params: dict) -> str: request_url = "https://auth.gog.com/token" response = self.session.get(request_url, params=params) response_params = response.json() if "access_token" in response_params and "expires_in" in response_params and "refresh_token" in response_params: self.active_token = response_params["access_token"] expires_in = response_params["expires_in"] self.active_token_expiration_time = time.time() + int(expires_in) refresh_token = response_params["refresh_token"] else: refresh_token = "" return refresh_token # Get all Linux games in the library of the user. Ignore other platforms and movies def get_library(self): err_msg = "" games = [] if self.active_token: current_page = 1 all_pages_processed = False url = "https://embed.gog.com/account/getFilteredProducts" while not all_pages_processed: params = { 'mediaType': 1, # 1 means game 'page': current_page, } response = self.__request(url, params=params) total_pages = response["totalPages"] for product in response["products"]: if product["id"] not in IGNORE_GAME_IDS: # Only support Linux unless the show_windows_games setting is enabled if product["worksOn"]["Linux"]: platform = "linux" elif self.config.show_windows_games: platform = "windows" else: continue if not product["url"]: logger.warning("{} ({}) has no store page url".format(product["title"], product['id'])) game = Game(name=product["title"], url=product["url"], game_id=product["id"], image_url=product["image"], platform=platform, category=product["category"]) games.append(game) if current_page == total_pages: all_pages_processed = True current_page += 1 else: err_msg = "Couldn't connect to GOG servers" return games, err_msg def get_owned_products_ids(self): if not self.active_token: return url2 = "https://embed.gog.com/user/data/games" response2 = self.__request(url2) return response2["owned"] # Generate the URL for the login page for GOG def get_login_url(self) -> str: params = { 'client_id': self.client_id, 'redirect_uri': self.redirect_uri, 'response_type': 'code', 'layout': 'client2', } return "https://auth.gog.com/auth?" + urlencode(params) def get_redirect_url(self) -> str: return self.redirect_uri # Get Extrainfo about a game def get_info(self, game: Game) -> dict: request_url = "https://api.gog.com/products/{}?locale=en-US&expand=downloads,expanded_dlcs,description,screenshots," \ "videos,related_products,changelog".format(str(game.id)) response = self.__request(request_url) return response # This returns a unique download url and a link to the checksum of the download def get_download_info(self, game: Game, operating_system="linux", dlc_installers="") -> dict: if dlc_installers: installers = dlc_installers else: response = self.get_info(game) installers = response["downloads"]["installers"] possible_downloads = [] for installer in installers: if installer["os"] == operating_system: possible_downloads.append(installer) if not possible_downloads: if operating_system == "linux": return self.get_download_info(game, "windows") else: raise NoDownloadLinkFound("Error: {} with id {} couldn't be installed".format(game.name, game.id)) download_info = possible_downloads[0] for installer in possible_downloads: if installer['language'] == self.config.lang: download_info = installer break if installer['language'] == "en": download_info = installer # Return last entry in possible_downloads. This will either be English or the first langauge in the list # This is just a backup, if the preferred language has been found, this part won't execute return download_info def get_real_download_link(self, url): return self.__request(url)['downlink'] def get_download_file_info(self, url): """ Returns some information about a downloadable file based on an XML file offered by GOG :param url: Url to get download and checksum links from the API :return: a FileInfo object with md5 set to the md5 or and empty string and size set to the file size or 0 """ file_info = FileInfo(md5="", size=0) try: checksum_data = self.__request(url) if 'checksum' in checksum_data.keys() and len(checksum_data['checksum']) > 0: xml_data = self.__get_xml_checksum(checksum_data['checksum']) if "md5" in xml_data.keys() and len(xml_data["md5"]) > 0: file_info.md5 = xml_data["md5"] if "total_size" in xml_data.keys() and len(xml_data["total_size"]) > 0: file_info.size = int(xml_data["total_size"]) except requests.exceptions.RequestException: logger.error("Couldn't retrieve file info. Encountered HTTP exception: {}", exc_info=1) if not file_info.md5: logger.warning("Couldn't find md5 in xml checksum data") if not file_info.size: logger.warning("Couldn't find file size in xml checksum data") return file_info def __get_xml_checksum(self, url): result = {} try: response = self.session.get(url) if response.status_code == http.HTTPStatus.OK and len(response.text) > 0: response_object = ET.fromstring(response.text) if response_object and response_object.attrib: result = response_object.attrib else: logger.error("Couldn't read xml data. Response with code %s received with the following content: %s", response.status_code, response.text, exc_info=1) except requests.exceptions.RequestException: logger.error("Couldn't read xml data. Received RequestException", exc_info=1) finally: return result def get_user_info(self) -> str: username = self.config.username if not username: url = "https://embed.gog.com/userData.json" response = self.__request(url) if "username" in response.keys(): username = response["username"] self.config.username = username return username def get_version(self, game: Game, gameinfo=None, dlc_name="") -> str: if gameinfo is None: gameinfo = self.get_info(game) version = "0" if dlc_name: installers = {} for dlc in gameinfo["expanded_dlcs"]: if dlc["title"] == dlc_name: installers = dlc["downloads"]["installers"] break else: installers = gameinfo["downloads"]["installers"] for installer in installers: if installer["os"] == game.platform: version = installer["version"] break return version def can_connect(self) -> bool: urls = [ "https://embed.gog.com", "https://auth.gog.com", ] threads = [] def make_request(url_to_check: str): try: self.session.get(url_to_check, timeout=5) return True except requests.exceptions.ConnectionError: return False for url in urls: threads.append(self.conn_check_thpool.submit(make_request, url)) results = [] for thread in as_completed(threads): if thread.cancelled(): return False results += [thread.result()] return all(results) # Make a request with the active token def __request(self, url: str = None, params: dict = None) -> dict: # Refresh the token if needed if self.active_token_expiration_time < time.time(): logger.debug("Refreshing token") refresh_token = self.config.refresh_token self.config.refresh_token = self.__refresh_token(refresh_token) # Make the request headers = { 'Authorization': "Bearer {}".format(str(self.active_token)), } result = {} try: response = self.session.get(url, headers=headers, params=params) if self.debug: logger.debug("Request %s, return code %s, response body %s", url, response.status_code, response.text) if response.status_code < 300: result = response.json() except requests.exceptions.RequestException: logger.error("Encountered exception while making HTTP request. Request: %s", url, exc_info=1) return result def __request_gamesdb(self, game: Game): request_url = "https://gamesdb.gog.com/platforms/gog/external_releases/{}".format(game.id) try: response = self.session.get(request_url) response_dict = response.json() except (requests.exceptions.ConnectionError, ValueError): logger.error("Error retrieving game info for gamesdb", exc_info=1) response_dict = {} return response_dict def get_gamesdb_info(self, game: Game) -> dict: gamesdb_dict = {"cover": "", "vertical_cover": "", "background": ""} response_json = self.__request_gamesdb(game) if "game" in response_json: for gamesdb_key in gamesdb_dict: if gamesdb_key in response_json['game']: gamesdb_dict[gamesdb_key] = response_json["game"][gamesdb_key]["url_format"].replace( '{formatter}.{ext}', '.png') gamesdb_dict["summary"] = {} for summary_key in response_json["game"]["summary"]: gamesdb_dict["summary"][summary_key] = response_json["game"]["summary"][summary_key] gamesdb_dict["genre"] = {} if len(response_json["game"]["genres"]) > 0: for genre in response_json["game"]["genres"]: for genre_key, genre_value in genre["name"].items(): if genre_key in gamesdb_dict["genre"] and len(gamesdb_dict["genre"][genre_key]) > 0: gamesdb_dict["genre"][genre_key] += ', ' else: gamesdb_dict["genre"][genre_key] = '' gamesdb_dict["genre"][genre_key] += genre["name"][genre_key] else: gamesdb_dict["summary"] = {} gamesdb_dict["genre"] = {} return gamesdb_dict sharkwouter-minigalaxy-759382b/minigalaxy/config.py000066400000000000000000000121251455252417400225100ustar00rootroot00000000000000import os import json from typing import List from minigalaxy.logger import logger from minigalaxy.paths import CONFIG_FILE_PATH, DEFAULT_INSTALL_DIR class Config: __config_file: str __config: dict def __init__(self, config_file: str = CONFIG_FILE_PATH): self.__config_file = config_file self.__config = {} self.__load() def __load(self) -> None: if os.path.isfile(self.__config_file): with open(self.__config_file, "r") as file: try: self.__config = json.loads(file.read()) except (json.decoder.JSONDecodeError, UnicodeDecodeError): logger.warning("Reading config.json failed, creating new config file.") os.remove(self.__config_file) def __write(self) -> None: if not os.path.isfile(self.__config_file): config_dir = os.path.dirname(self.__config_file) os.makedirs(config_dir, mode=0o700, exist_ok=True) temp_file = f"{self.__config_file}.tmp" with open(temp_file, "w") as file: file.write(json.dumps(self.__config, ensure_ascii=False)) os.rename(temp_file, self.__config_file) @property def locale(self) -> str: return self.__config.get("locale", "") @locale.setter def locale(self, new_value: str) -> None: self.__config["locale"] = new_value self.__write() @property def lang(self) -> str: return self.__config.get("lang", "en") @lang.setter def lang(self, new_value: str) -> None: self.__config["lang"] = new_value self.__write() @property def view(self) -> str: return self.__config.get("view", "grid") @view.setter def view(self, new_value: str) -> None: self.__config["view"] = new_value self.__write() @property def install_dir(self) -> str: return self.__config.get("install_dir", DEFAULT_INSTALL_DIR) @install_dir.setter def install_dir(self, new_value: str) -> None: self.__config["install_dir"] = new_value self.__write() @property def username(self) -> str: return self.__config.get("username", "") @username.setter def username(self, new_value: str) -> None: self.__config["username"] = new_value self.__write() @property def refresh_token(self) -> str: return self.__config.get("refresh_token", "") @refresh_token.setter def refresh_token(self, new_value: str) -> None: self.__config["refresh_token"] = new_value self.__write() @property def keep_installers(self) -> bool: return self.__config.get("keep_installers", False) @keep_installers.setter def keep_installers(self, new_value: bool) -> None: self.__config["keep_installers"] = new_value self.__write() @property def stay_logged_in(self) -> bool: return self.__config.get("stay_logged_in", True) @stay_logged_in.setter def stay_logged_in(self, new_value: bool) -> None: self.__config["stay_logged_in"] = new_value self.__write() @property def use_dark_theme(self) -> bool: return self.__config.get("use_dark_theme", False) @use_dark_theme.setter def use_dark_theme(self, new_value: bool) -> None: self.__config["use_dark_theme"] = new_value self.__write() @property def show_hidden_games(self) -> bool: return self.__config.get("show_hidden_games", False) @show_hidden_games.setter def show_hidden_games(self, new_value: bool) -> None: self.__config["show_hidden_games"] = new_value self.__write() @property def show_windows_games(self) -> bool: return self.__config.get("show_windows_games", False) @show_windows_games.setter def show_windows_games(self, new_value: bool) -> None: self.__config["show_windows_games"] = new_value self.__write() @property def keep_window_maximized(self) -> bool: return self.__config.get("keep_window_maximized", False) @keep_window_maximized.setter def keep_window_maximized(self, new_value: bool) -> None: self.__config["keep_window_maximized"] = new_value self.__write() @property def installed_filter(self) -> bool: return self.__config.get("installed_filter", False) @installed_filter.setter def installed_filter(self, new_value: bool) -> None: self.__config["installed_filter"] = new_value self.__write() @property def create_applications_file(self) -> bool: return self.__config.get("create_applications_file", False) @create_applications_file.setter def create_applications_file(self, new_value: bool) -> None: self.__config["create_applications_file"] = new_value self.__write() @property def current_downloads(self) -> List[int]: return self.__config.get("current_downloads", []) @current_downloads.setter def current_downloads(self, new_value: List[int]) -> None: self.__config["current_downloads"] = new_value self.__write() sharkwouter-minigalaxy-759382b/minigalaxy/constants.py000066400000000000000000000041171455252417400232610ustar00rootroot00000000000000from minigalaxy.translation import _ SUPPORTED_DOWNLOAD_LANGUAGES = [ ["br", _("Brazilian Portuguese")], ["cn", _("Chinese")], ["da", _("Danish")], ["nl", _("Dutch")], ["en", _("English")], ["fi", _("Finnish")], ["fr", _("French")], ["de", _("German")], ["hu", _("Hungarian")], ["it", _("Italian")], ["jp", _("Japanese")], ["ko", _("Korean")], ["no", _("Norwegian")], ["pl", _("Polish")], ["pt", _("Portuguese")], ["ru", _("Russian")], ["es", _("Spanish")], ["sv", _("Swedish")], ["tr", _("Turkish")], ["ro", _("Romanian")], ] SUPPORTED_LOCALES = [ ["", _("System default")], ["pt_BR", _("Brazilian Portuguese")], ["cs_CZ", _("Czech")], ["nl", _("Dutch")], ["en_US", _("English")], ["fi", _("Finnish")], ["fr", _("French")], ["de", _("German")], ["it_IT", _("Italian")], ["nb_NO", _("Norwegian Bokmål")], ["nn_NO", _("Norwegian Nynorsk")], ["pl", _("Polish")], ["ru_RU", _("Russian")], ["zh_CN", _("Simplified Chinese")], ["es", _("Spanish")], ["es_ES", _("Spanish (Spain)")], ["sv_SE", _("Swedish")], ["zh_TW", _("Traditional Chinese")], ["tr", _("Turkish")], ["uk", _("Ukrainian")], ["el", _("Greek")], ["ro", _("Romanian")], ] VIEWS = [ ["grid", _("Grid")], ["list", _("List")], ] # Game IDs to ignore when received by the API IGNORE_GAME_IDS = [ 1424856371, # Hotline Miami 2: Wrong Number - Digital Comics 1980301910, # The Witcher Goodies Collection 2005648906, # Spring Sale Goodies Collection #1 1486144755, # Cyberpunk 2077 Goodies Collection 1581684020, # A Plague Tale Digital Goodies Pack 1185685769, # CDPR Goodie Pack Content ] DOWNLOAD_CHUNK_SIZE = 1024 * 1024 # 1 MB # This is the file size needed for the download manager to consider resuming worthwhile MINIMUM_RESUME_SIZE = 50 * 1024**2 # 50 MB # UI download threads are for UI assets like thumbnails or icons UI_DOWNLOAD_THREADS = 4 # Game download threads are for long-running downloads like games, DLC or updates GAME_DOWNLOAD_THREADS = 4 sharkwouter-minigalaxy-759382b/minigalaxy/css.py000066400000000000000000000010011455252417400220220ustar00rootroot00000000000000from minigalaxy.logger import logger from minigalaxy.ui.gtk import Gtk, Gdk from minigalaxy.paths import CSS_PATH CSS_PROVIDER = Gtk.CssProvider() def load_css(): try: with open(CSS_PATH) as style: CSS_PROVIDER.load_from_data(style.read().encode('utf-8')) except Exception: logger.error("The CSS in %s could not be loaded", CSS_PATH, exc_info=1) Gtk.StyleContext().add_provider_for_screen(Gdk.Screen.get_default(), CSS_PROVIDER, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) sharkwouter-minigalaxy-759382b/minigalaxy/download.py000066400000000000000000000046331455252417400230570ustar00rootroot00000000000000from enum import Enum from zipfile import BadZipFile # Enums were added in Python 3.4 class DownloadType(Enum): ICON = 1 THUMBNAIL = 2 GAME = 3 GAME_UPDATE = 4 GAME_DLC = 5 class Download: """ A class to easily download from URLs and save the file. Usage: >>> import os >>> from minigalaxy.download import Download, DownloadType >>> from minigalaxy.download_manager import DownloadManager >>> def your_function(): >>> image_url = "https://www.gog.com/bundles/gogwebsitestaticpages/images/icon_section1-header.png" >>> thumbnail = os.path.join(".", "{}.jpg".format("test-icon")) >>> download = Download(image_url, thumbnail, DownloadType.THUMBNAIL, finish_func=lambda x: print("Done downloading {}!".format(x))) # noqa: E501 >>> your_function() # doctest: +SKIP """ def __init__(self, url, save_location, download_type=None, finish_func=None, progress_func=None, cancel_func=None, number=1, out_of_amount=1, game=None): self.url = url self.save_location = save_location self.__finish_func = finish_func self.__progress_func = progress_func self.__cancel_func = cancel_func self.number = number self.out_of_amount = out_of_amount self.game = game # Type of object, e.g. icon, thumbnail, game, dlc, self.download_type = download_type def set_progress(self, percentage: int) -> None: "Set the download progress of the Download" if self.__progress_func: if self.out_of_amount > 1: # Change the percentage based on which number we are progress_start = 100 / self.out_of_amount * (self.number - 1) percentage = progress_start + percentage / self.out_of_amount percentage = int(percentage) self.__progress_func(percentage) def finish(self): """ finish is called when the download has completed If a finish_func was specified when the Download was created, call the function """ if self.__finish_func: try: self.__finish_func(self.save_location) except (FileNotFoundError, BadZipFile): self.cancel() def cancel(self): "Cancel the download, calling a cancel_func if one was specified" if self.__cancel_func: self.__cancel_func() sharkwouter-minigalaxy-759382b/minigalaxy/download_manager.py000066400000000000000000000403611455252417400245470ustar00rootroot00000000000000""" A DownloadManager that provides an interface to manage and retry downloads. First, you need to create a Download object, then pass the Download object to the DownloadManager to download it. Example: >>> import os >>> from minigalaxy.download import Download, DownloadType >>> from minigalaxy.download_manager import DownloadManager >>> def your_function(): >>> image_url = "https://www.gog.com/bundles/gogwebsitestaticpages/images/icon_section1-header.png" >>> thumbnail = os.path.join(".", "{}.jpg".format("test-icon")) >>> download = Download(image_url, thumbnail, DownloadType.THUMBNAIL, finish_func=lambda x: print("Done downloading {}!".format(x))) # noqa: E501 >>> DownloadManager.download(download) >>> your_function() # doctest: +SKIP """ import logging import os import shutil import time import threading import queue from requests import Session from requests.exceptions import RequestException from minigalaxy.constants import DOWNLOAD_CHUNK_SIZE, MINIMUM_RESUME_SIZE, GAME_DOWNLOAD_THREADS, UI_DOWNLOAD_THREADS from minigalaxy.download import Download, DownloadType import minigalaxy.logger # noqa: F401 module_logger = logging.getLogger("minigalaxy.download_manager") class QueuedDownloadItem: """ Wrap downloads in a simple class so we can manage when to download them """ def __init__(self, download, priority=1): """ Create a QueuedDownloadItem with a given download and priority level """ self.priority = priority self.item = download self.queue_time = time.time() def __lt__(self, other): """ Only compare QueuedDownloadItems on their priority level and time added to the queue. Later versions might use other factors (size, download type, etc.) For example, UX needs might want to prioritize items and other UI assets. """ if self.priority != other.priority: return self.priority < other.priority else: return self.queue_time < other.queue_time class DownloadManager: """ A DownloadManager that provides an interface to manage and retry downloads. First, you need to create a Download object, then pass the Download object to the DownloadManager download or download_now method to download it. """ def __init__(self, session: Session): """ Create a new DownloadManager Object Args: This initializer takes no arguments """ self.session = session # A queue for UI elements self.__ui_queue = queue.PriorityQueue() # A queue for games and other long-running downloads self.__game_queue = queue.PriorityQueue() # The queues and associated limits self.queues = [(self.__ui_queue, UI_DOWNLOAD_THREADS), (self.__game_queue, GAME_DOWNLOAD_THREADS)] self.__cancel = {} self.workers = [] # The list of currently active downloads # These are items not in the queue, but currently being downloaded self.active_downloads = {} self.active_downloads_lock = threading.Lock() self.logger = logging.getLogger("minigalaxy.download_manager.DownloadManager") for q, number_threads in self.queues: for i in range(number_threads): download_thread = threading.Thread(target=lambda: self.__download_thread(q)) download_thread.daemon = True download_thread.start() self.workers.append(download_thread) def download(self, download): """ Add a download or list of downloads to the queue for downloading You can download a single Download or a list of Downloads Args: A single Download or a list of Download objects """ if isinstance(download, Download): self.put_in_proper_queue(download) else: # Assume we've received a list of downloads for d in download: self.put_in_proper_queue(d) def put_in_proper_queue(self, download): "Put the download in the proper queue" # Add game type downloads to the game queue if download.download_type == DownloadType.GAME: self.__game_queue.put(QueuedDownloadItem(download, 1)) elif download.download_type == DownloadType.GAME_UPDATE: self.__game_queue.put(QueuedDownloadItem(download, 1)) elif download.download_type == DownloadType.GAME_DLC: self.__game_queue.put(QueuedDownloadItem(download, 1)) elif download.download_type == DownloadType.ICON: self.__ui_queue.put(QueuedDownloadItem(download, 0)) elif download.download_type == DownloadType.THUMBNAIL: self.__ui_queue.put(QueuedDownloadItem(download, 0)) else: # Add other items to the UI queue self.__ui_queue.put(QueuedDownloadItem(download, 0)) def download_now(self, download): """ Download an item with a higher priority Any item with the download_now priority set will get downloaded before other items. This is useful for things like thumbnails and icons that are important for UX responsiveness. Args: The Download to download """ self.__ui_queue.put(QueuedDownloadItem(download, 0)) def cancel_download(self, downloads): """ Cancel a download or a list of downloads Args: A single Download or a list of Download objects """ # Make sure we're always dealing with a list if isinstance(downloads, Download): downloads = [downloads] # Create a dictionary with the keys that are the downloads and the value is True # We use this to test for downloads more efficiently download_dict = dict(zip(downloads, [True] * len(downloads))) # This follows the previous logic # First cancel all the active downloads self.cancel_active_downloads(download_dict) # Next, loop through the downloads queued for download, comparing them to the # cancel list self.cancel_queued_downloads(download_dict) def cancel_active_downloads(self, download_dict): """ Cancel active downloads This is called by cancel_download Args: download_dict is a dictionary of downloads to cancel with values True """ with self.active_downloads_lock: for download in self.active_downloads: if download in download_dict: self.logger.debug("Found download") # mark it for canceling self.__cancel[download] = True # Remove it from the downloads to cancel del download_dict[download] def cancel_queued_downloads(self, download_dict): """ Cancel selected downloads in the queue This is called by cancel_download Args: download_dict is a dictionary of downloads to cancel with values True """ for download in download_dict.keys(): self.logger.debug("download: {}".format(download)) for download_queue, limit in self.queues: new_queue = queue.PriorityQueue() while not download_queue.empty(): queued_download = download_queue.get() # Test a Download against a QueuedDownloadItem if download == queued_download.item: download.cancel() # test for games elif (download.game is not None) and \ (download.game == queued_download.item.game): download.cancel() # test for other assets elif download.save_location == queued_download.item.save_location: download.cancel() else: new_queue.put(queued_download) # Mark the task as "done" to keep counts correct so # we can use join() or other functions later download_queue.task_done() # Copy items back over to the queue while not new_queue.empty(): item = new_queue.get() download_queue.put(item) def cancel_current_downloads(self): """ Cancel the currentl downloads """ self.logger.debug("Canceling current download") with self.active_downloads_lock: for download in self.active_downloads: self.__cancel[download] = True def cancel_all_downloads(self): """ Cancel all current downloads queued """ self.logger.debug("Canceling all downloads") for download_queue, limit in self.queues: self.logger.debug("queue length: {}".format(download_queue.qsize())) while not download_queue.empty(): download = download_queue.get().item self.logger.debug("canceling another download: {}".format(download.save_location)) # Mark the task as "done" to keep counts correct so # we can use join() or other functions later download_queue.task_done() self.cancel_current_downloads() def __remove_download_from_active_downloads(self, download): "Remove a download from the list of active downloads" with self.active_downloads_lock: if download in self.active_downloads: self.logger.debug("Removing download from active downloads list") del self.active_downloads[download] else: self.logger.debug("Didn't find download in active downloads list") def __download_thread(self, download_queue): """ The main DownloadManager thread calls this when it is created It checks the queue, starting new downloads when they are available Users of this library should not need to call this. """ while True: if not download_queue.empty(): # Update the active downloads with self.active_downloads_lock: download = download_queue.get().item self.active_downloads[download] = download self.__download_file(download, download_queue) # Mark the task as done to keep counts correct so # we can use join() or other functions later download_queue.task_done() time.sleep(0.01) def __download_file(self, download, download_queue): """ This is called by __download_thread to download a file It is also called directly by the thread created in download_now Users of this library should not need to call this. Args: The Download to download """ self.__prepare_location(download.save_location) download_max_attempts = 5 download_attempt = 0 result = False while download_attempt < download_max_attempts: try: start_point, download_mode = self.__get_start_point_and_download_mode(download) result = self.__download_operation(download, start_point, download_mode) break except RequestException as e: self.logger.error("Error downloading file {}, received error {}".format(download.url, e)) download_attempt += 1 # Successful downloads if result: if download.number == download.out_of_amount: self.logger.debug("Download finished, thread {}".format(threading.get_ident())) finish_thread = threading.Thread(target=download.finish) finish_thread.start() self.__remove_download_from_active_downloads(download) # Unsuccessful downloads and cancels else: if download in self.__cancel: del self.__cancel[download] download.cancel() self.__remove_download_from_active_downloads(download) os.remove(download.save_location) # We may want to unset current_downloads here # For example, if a download was added that is impossible to complete def __prepare_location(self, save_location): """ Make sure the download directory exists and the file doesn't already exist Args: A filename string, where the download should be saved """ # Make sure the directory exists save_directory = os.path.dirname(save_location) if not os.path.isdir(save_directory): os.makedirs(save_directory, mode=0o755) # Fail if the file already exists if os.path.isdir(save_location): shutil.rmtree(save_location) self.logger.debug("{} is a directory. Will remove it, to make place for installer.".format(save_location)) def __get_start_point_and_download_mode(self, download): """ Resume the previous download if possible Args: The Download to download """ start_point = 0 download_mode = 'wb' if os.path.isfile(download.save_location): if self.__is_same_download_as_before(download): self.logger.debug("Resuming download {}".format(download.save_location)) download_mode = 'ab' start_point = os.stat(download.save_location).st_size else: os.remove(download.save_location) return start_point, download_mode def __download_operation(self, download, start_point, download_mode): """ Download the file This is called by __download_file to actually perform the download Args: The Download to download The start point in the download, specified in bytes The file mode to save the file as. For example, "wb" to create a new file or "ab" to append to a file for download resumes """ resume_header = {'Range': 'bytes={}-'.format(start_point)} download_request = self.session.get(download.url, headers=resume_header, stream=True, timeout=30) downloaded_size = start_point download.set_progress(0) file_size = None try: file_size = int(download_request.headers.get('content-length')) except (ValueError, TypeError): self.logger.error(f"Couldn't get file size for {download.save_location}. No progress will be shown.") result = True if file_size is None or downloaded_size < file_size: with open(download.save_location, download_mode) as save_file: for chunk in download_request.iter_content(chunk_size=DOWNLOAD_CHUNK_SIZE): save_file.write(chunk) downloaded_size += len(chunk) if download in self.__cancel: self.logger.debug("Canceling download: {}".format(download.save_location)) result = False del self.__cancel[download] break if file_size is not None: progress = int(downloaded_size / file_size * 100) download.set_progress(progress) if result: download.set_progress(100) self.logger.debug("Returning result from _download_operation: {}".format(result)) return result def __is_same_download_as_before(self, download): """ Return true if the download is the same as an item with the same save_location already downloaded. Args: The Download to check """ file_stats = os.stat(download.save_location) # Don't resume for very small files if file_stats.st_size < MINIMUM_RESUME_SIZE: return False # Check if the first part of the file download_request = self.session.get(download.url, stream=True) size_to_check = DOWNLOAD_CHUNK_SIZE * 5 for chunk in download_request.iter_content(chunk_size=size_to_check): with open(download.save_location, "rb") as file: file_content = file.read(size_to_check) return file_content == chunk sharkwouter-minigalaxy-759382b/minigalaxy/entity/000077500000000000000000000000001455252417400222045ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/minigalaxy/entity/__init__.py000066400000000000000000000000001455252417400243030ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/minigalaxy/entity/state.py000066400000000000000000000005021455252417400236730ustar00rootroot00000000000000from enum import Enum, auto class State(Enum): DOWNLOADABLE = auto() INSTALLABLE = auto() UPDATABLE = auto() QUEUED = auto() DOWNLOADING = auto() INSTALLING = auto() INSTALLED = auto() NOTLAUNCHABLE = auto() UNINSTALLING = auto() UPDATING = auto() UPDATE_INSTALLABLE = auto() sharkwouter-minigalaxy-759382b/minigalaxy/file_info.py000066400000000000000000000003021455252417400231670ustar00rootroot00000000000000class FileInfo: """ Just a container for the md5 checksum and file size of a downloadable file """ def __init__(self, md5, size): self.md5 = md5 self.size = size sharkwouter-minigalaxy-759382b/minigalaxy/game.py000066400000000000000000000152731455252417400221630ustar00rootroot00000000000000import os import re import json from minigalaxy.paths import CONFIG_GAMES_DIR class Game: def __init__(self, name: str, url: str = "", md5sum=None, game_id: int = 0, install_dir: str = "", image_url="", platform="linux", dlcs=None, category=""): self.name = name self.url = url self.md5sum = {} if md5sum is None else md5sum self.id = game_id self.install_dir = install_dir self.image_url = image_url self.platform = platform self.dlcs = [] if dlcs is None else dlcs self.category = category self.status_file_path = self.get_status_file_path() def get_stripped_name(self): return self.__strip_string(self.name) def get_install_directory_name(self): return re.sub('[^A-Za-z0-9 ]+', '', self.name) def get_status_file_path(self): if self.install_dir: last_install_dir = os.path.basename(os.path.normpath(self.install_dir)) else: last_install_dir = self.get_install_directory_name() status_file_path = os.path.join(CONFIG_GAMES_DIR, "{}.json".format(last_install_dir)) return status_file_path def load_minigalaxy_info_json(self): json_dict = {} if os.path.isfile(self.status_file_path): with open(self.status_file_path, 'r') as status_file: json_dict = json.load(status_file) return json_dict def save_minigalaxy_info_json(self, json_dict): if not os.path.exists(CONFIG_GAMES_DIR): os.makedirs(CONFIG_GAMES_DIR, mode=0o755) with open(self.status_file_path, 'w') as status_file: json.dump(json_dict, status_file) @staticmethod def __strip_string(string): return re.sub('[^A-Za-z0-9]+', '', string) def is_installed(self, dlc_title="") -> bool: installed = False if dlc_title: dlc_version = self.get_dlc_info("version", dlc_title) installed = bool(dlc_version) else: if self.install_dir and os.path.exists(self.install_dir): installed = True return installed def is_update_available(self, version_from_api, dlc_title="") -> bool: update_available = False if dlc_title: installed_version = self.get_dlc_info("version", dlc_title) else: installed_version = self.get_info("version") if not installed_version: installed_version = self.fallback_read_installed_version() self.set_info("version", installed_version) if installed_version and version_from_api and version_from_api != installed_version: update_available = True return update_available def fallback_read_installed_version(self): gameinfo = os.path.join(self.install_dir, "gameinfo") gameinfo_list = [] if os.path.isfile(gameinfo): with open(gameinfo, 'r') as file: gameinfo_list = file.readlines() if len(gameinfo_list) > 1: version = gameinfo_list[1].strip() else: version = "0" return version def set_info(self, key, value): json_dict = self.load_minigalaxy_info_json() json_dict[key] = value self.save_minigalaxy_info_json(json_dict) def set_dlc_info(self, key, value, dlc_title): json_dict = self.load_minigalaxy_info_json() if "dlcs" not in json_dict: json_dict["dlcs"] = {} if dlc_title not in json_dict["dlcs"]: json_dict["dlcs"][dlc_title] = {} json_dict["dlcs"][dlc_title][key] = value self.save_minigalaxy_info_json(json_dict) def get_info(self, key): value = "" json_dict = self.load_minigalaxy_info_json() if key in json_dict: value = json_dict[key] # Start: Code for compatibility with minigalaxy 1.0.1 and 1.0.2 elif os.path.isfile(os.path.join(self.install_dir, "minigalaxy-info.json")): with open(os.path.join(self.install_dir, "minigalaxy-info.json"), 'r') as status_file: json_dict = json.load(status_file) if key in json_dict: value = json_dict[key] # Lets move this value to new config self.set_info(key, value) # End: Code for compatibility with minigalaxy 1.0.1 and 1.0.2 return value def get_dlc_info(self, key, dlc_title): value = "" json_dict = self.load_minigalaxy_info_json() if "dlcs" in json_dict: if dlc_title in json_dict["dlcs"]: if key in json_dict["dlcs"][dlc_title]: value = json_dict["dlcs"][dlc_title][key] # Start: Code for compatibility with minigalaxy 1.0.1 and 1.0.2 if os.path.isfile(os.path.join(self.install_dir, "minigalaxy-info.json")) and not value: with open(os.path.join(self.install_dir, "minigalaxy-info.json"), 'r') as status_file: json_dict = json.load(status_file) if "dlcs" in json_dict: if dlc_title in json_dict["dlcs"]: if key in json_dict["dlcs"][dlc_title]: value = json_dict["dlcs"][dlc_title][key] # Lets move this value to new config self.set_dlc_info(key, value, dlc_title) # End: Code for compatibility with minigalaxy 1.0.1 and 1.0.2 return value def set_install_dir(self, install_dir) -> None: """ Set the install directory based on the given install dir and the game name :param install_dir: the global install directory from the config """ if not self.install_dir: self.install_dir = os.path.join(install_dir, self.get_install_directory_name()) def __str__(self): return self.name def __eq__(self, other): if self.id > 0 and other.id > 0: return self.id == other.id if self.name == other.name: return True # Compare names with special characters and capital letters removed if self.get_stripped_name().lower() == other.get_stripped_name().lower(): return True if self.install_dir and \ other.get_install_directory_name() == os.path.basename(os.path.normpath(self.install_dir)): return True if other.install_dir and \ self.get_install_directory_name() == os.path.basename(os.path.normpath(other.install_dir)): return True return False def __lt__(self, other): # Sort installed games before not installed ones if self.is_installed() != other.is_installed(): return self.is_installed() return str(self) < str(other) sharkwouter-minigalaxy-759382b/minigalaxy/installer.py000066400000000000000000000361111455252417400232410ustar00rootroot00000000000000import sys import os import shutil import subprocess import hashlib import textwrap from minigalaxy.game import Game from minigalaxy.logger import logger from minigalaxy.translation import _ from minigalaxy.launcher import get_execute_command from minigalaxy.paths import CACHE_DIR, THUMBNAIL_DIR, APPLICATIONS_DIR def get_available_disk_space(location): """Check disk space available to the user. This method uses the absolute path so symlinks to disks with sufficient space are correctly measured. Note this is a linux-specific command.""" absolute_location = os.path.realpath(location) disk_status = os.statvfs(os.path.dirname(absolute_location)) available_diskspace = disk_status.f_frsize * disk_status.f_bavail return available_diskspace def get_game_size_from_unzip(installer): var = subprocess.Popen(['unzip', '-v', installer], stdout=subprocess.PIPE) output = var.communicate()[0].decode("utf-8") lines_list = output.split("\n") if len(lines_list) > 2 and not lines_list[-1].strip(): last_line = lines_list[-2].strip() else: last_line = lines_list[-1].strip() size_value = int(last_line.split()[0]) return size_value def check_diskspace(required_size, location): """This method will return True when the disk space available is sufficient for the Download and Install. If not sufficient, it returns False.""" installed_game_size = int(required_size) diskspace_available = get_available_disk_space(location) return diskspace_available >= installed_game_size def install_game( # noqa: C901 game: Game, installer: str, language: str, install_dir: str, keep_installers: bool, create_desktop_file: bool, use_innoextract: bool = True, # not set externally as of yet ): error_message = "" tmp_dir = "" logger.info("Installing {}".format(game.name)) try: _use_innoextract = use_innoextract and bool(shutil.which('innoextract')) # single decision point if not error_message: error_message = verify_installer_integrity(game, installer) if not error_message: error_message = verify_disk_space(game, installer) if not error_message: error_message, tmp_dir = make_tmp_dir(game) if not error_message: error_message = extract_installer(game, installer, tmp_dir, language, _use_innoextract) if not error_message: error_message = move_and_overwrite(game, tmp_dir, _use_innoextract) if not error_message: error_message = copy_thumbnail(game) if not error_message and create_desktop_file: error_message = create_applications_file(game) except Exception: logger.error("Error installing game %s", game.name, exc_info=1) error_message = _("Unhandled error.") _removal_error = remove_installer(game, installer, install_dir, keep_installers) error_message = error_message or _removal_error or postinstaller(game) if error_message: logger.error(error_message) return error_message def verify_installer_integrity(game, installer): error_message = "" if not os.path.exists(installer): error_message = _("{} failed to download.").format(installer) if not error_message: for installer_file_name in os.listdir(os.path.dirname(installer)): hash_md5 = hashlib.md5() with open(os.path.join(os.path.dirname(installer), installer_file_name), "rb") as installer_file: for chunk in iter(lambda: installer_file.read(4096), b""): hash_md5.update(chunk) calculated_checksum = hash_md5.hexdigest() if installer_file_name in game.md5sum: if game.md5sum[installer_file_name] == calculated_checksum: logger.info("%s integrity is preserved. MD5 is: %s", installer_file_name, calculated_checksum) else: error_message = _("{} was corrupted. Please download it again.").format(installer_file_name) break else: logger.warning("Warning. No info about correct %s MD5 checksum", installer_file_name) return error_message def verify_disk_space(game, installer): err_msg = "" if game.platform == "linux": required_space = get_game_size_from_unzip(installer) if not check_diskspace(required_space, game.install_dir): err_msg = _("Not enough space to extract game. Required: {} Available: {}" ).format(required_space, get_available_disk_space(game.install_dir)) return err_msg def make_tmp_dir(game): # Make a temporary empty directory for extracting the installer error_message = "" extract_dir = os.path.join(CACHE_DIR, "extract") temp_dir = os.path.join(extract_dir, str(game.id)) if os.path.exists(temp_dir): shutil.rmtree(temp_dir, ignore_errors=True) os.makedirs(temp_dir, mode=0o755) return error_message, temp_dir def extract_installer(game: Game, installer: str, temp_dir: str, language: str, use_innoextract: bool): # Extract the installer if game.platform in ["linux"]: err_msg = extract_linux(installer, temp_dir) else: err_msg = extract_windows(game, installer, temp_dir, language, use_innoextract) return err_msg def extract_linux(installer, temp_dir): err_msg = "" command = ["unzip", "-qq", installer, "-d", temp_dir] stdout, stderr, exitcode = _exe_cmd(command) if (exitcode not in [0]) and \ (exitcode not in [1] and "(attempting to process anyway)" not in stderr): err_msg = _("The installation of {} failed. Please try again.").format(installer) elif len(os.listdir(temp_dir)) == 0: err_msg = _("{} could not be unzipped.".format(installer)) return err_msg def extract_windows(game: Game, installer: str, temp_dir: str, language: str, use_innoextract: bool): err_msg = extract_by_innoextract(installer, temp_dir, language, use_innoextract) if err_msg: err_msg = extract_by_wine(game, installer, temp_dir) return err_msg def extract_by_innoextract(installer: str, temp_dir: str, language: str, use_innoextract: bool): err_msg = "" if use_innoextract: lang = lang_install(installer, language) cmd = ["innoextract", installer, "-d", temp_dir, "--gog", lang] stdout, stderr, exitcode = _exe_cmd(cmd) if exitcode not in [0]: err_msg = _("Innoextract extraction failed.") else: # In the case the game is installed in "temp_dir/app" like Zeus + Poseidon (Acropolis) inno_app_dir = os.path.join(temp_dir, "app") if os.path.isdir(inno_app_dir): _mv(inno_app_dir, temp_dir) # In the case the game is installed in "temp_dir/game" like Dragon Age™: Origins - Ultimate Edition inno_game_dir = os.path.join(temp_dir, "game") if os.path.isdir(inno_game_dir): _mv(inno_game_dir, temp_dir) innoextract_unneeded_dirs = ["__redist", "tmp", "commonappdata", "app", "DirectXpackage", "dotNet35"] innoextract_unneeded_dirs += ["MSVC2005", "MSVC2005_x64", "support", "__unpacker", "userdocs", "game"] for unneeded_dir in innoextract_unneeded_dirs: unneeded_dir_full_path = os.path.join(temp_dir, unneeded_dir) if os.path.isdir(unneeded_dir_full_path): shutil.rmtree(unneeded_dir_full_path) else: err_msg = _("Innoextract not installed.") return err_msg def extract_by_wine(game, installer, temp_dir): err_msg = "" # Set the prefix for Windows games prefix_dir = os.path.join(game.install_dir, "prefix") drive = os.path.join(prefix_dir, "dosdevices", "d:") if not os.path.exists(prefix_dir): os.makedirs(prefix_dir, mode=0o755) # Creating the prefix before modifying dosdevices command = ["env", "WINEPREFIX={}".format(prefix_dir), "wine", "start", "/B", "cmd", "/C", "exit"] stdout, stderr, exitcode = _exe_cmd(command) if exitcode not in [0]: print(stderr, file=sys.stderr) return _("Wineprefix creation failed.") if os.path.exists(drive): os.unlink(drive) os.symlink(temp_dir, drive) _dir = os.path.join(temp_dir, os.path.basename(game.install_dir)) # can't install to drive root # It's possible to set install dir as argument before installation command = ["env", "WINEPREFIX={}".format(prefix_dir), "wine", installer, "/dir={}".format(_dir), "/VERYSILENT"] stdout, stderr, exitcode = _exe_cmd(command) if exitcode not in [0]: err_msg = _("Wine extraction failed.") else: os.unlink(drive) os.symlink("../../..", drive) return err_msg def move_and_overwrite(game, temp_dir, use_innoextract): # Copy the game files into the correct directory error_message = "" source_dir = (os.path.join(temp_dir, "data", "noarch") if game.platform == 'linux' else temp_dir if use_innoextract else os.path.join(temp_dir, os.path.basename(game.install_dir))) target_dir = game.install_dir _mv(source_dir, target_dir) # Remove the temporary directory shutil.rmtree(temp_dir, ignore_errors=True) if game.platform in ["windows"] and "unins000.exe" not in os.listdir(game.install_dir): open(os.path.join(game.install_dir, "unins000.exe"), "w").close() return error_message def copy_thumbnail(game): error_message = "" new_thumbnail_path = os.path.join(game.install_dir, "thumbnail.jpg") # Copy thumbnail if not os.path.isfile(new_thumbnail_path): try: shutil.copyfile(os.path.join(THUMBNAIL_DIR, "{}.jpg".format(game.id)), new_thumbnail_path) except Exception as e: error_message = e return error_message def get_exec_line(game): exe_cmd_list = get_execute_command(game) for i in range(len(exe_cmd_list)): exe_cmd_list[i] = exe_cmd_list[i].replace(" ", "\\ ") return " ".join(exe_cmd_list) def create_applications_file(game): error_message = "" path_to_shortcut = os.path.join(APPLICATIONS_DIR, "{}.desktop".format(game.name)) exe_cmd = get_exec_line(game) # Create desktop file definition desktop_context = { "game_bin_path": os.path.join('"{}"'.format(game.install_dir.replace('"', '\\"')), exe_cmd), "game_name": game.name, "game_install_dir": game.install_dir, "game_icon_path": os.path.join(game.install_dir, 'support/icon.png') } desktop_definition = """\ [Desktop Entry] Type=Application Terminal=false StartupNotify=true Exec={game_bin_path} Path={game_install_dir} Name={game_name} Icon={game_icon_path}""".format(**desktop_context) if not os.path.isfile(path_to_shortcut): try: with open(path_to_shortcut, 'w+') as desktop_file: desktop_file.writelines(textwrap.dedent(desktop_definition)) except Exception as e: os.remove(path_to_shortcut) error_message = e return error_message def compare_directories(dir1, dir2): files_1 = [] files_2 = [] if os.path.isdir(dir1): files_1 = os.listdir(dir1) if os.path.isdir(dir2): files_2 = os.listdir(dir2) if not set(files_1).issubset(set(files_2)): return False result = True for f in files_1: if os.path.getsize(os.path.join(dir1, f)) != \ os.path.getsize(os.path.join(dir2, f)): result = False return result def remove_installer(game: Game, installer: str, install_dir: str, keep_installers: bool): error_message = "" installer_directory = os.path.dirname(installer) if not os.path.isdir(installer_directory): error_message = "No installer directory is present: {}".format(installer_directory) return error_message if keep_installers: keep_dir = os.path.join(install_dir, "installer") keep_dir2 = os.path.join(keep_dir, game.get_install_directory_name()) if keep_dir2 == installer_directory: # We are using the keep installer already return error_message if not compare_directories(installer_directory, keep_dir2): shutil.rmtree(keep_dir2, ignore_errors=True) try: shutil.move(installer_directory, keep_dir2) except Exception as e: error_message = str(e) else: for file in os.listdir(installer_directory): os.remove(os.path.join(installer_directory, file)) return error_message def postinstaller(game): err_msg = "" postinst_script = os.path.join(game.install_dir, "support", "postinst.sh") if os.path.isfile(postinst_script): os.chmod(postinst_script, 0o775) stdout, stderr, exitcode = _exe_cmd([postinst_script]) if exitcode not in [0]: err_msg = "Postinstallation script failed: {}".format(postinst_script) return err_msg def uninstall_game(game): shutil.rmtree(game.install_dir, ignore_errors=True) if os.path.isfile(game.status_file_path): os.remove(game.status_file_path) if os.path.isfile(os.path.join(APPLICATIONS_DIR, "{}.desktop".format(game.name))): os.remove(os.path.join(APPLICATIONS_DIR, "{}.desktop".format(game.name))) def _exe_cmd(cmd): process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() stdout = stdout.decode("utf-8") stderr = stderr.decode("utf-8") return stdout, stderr, process.returncode def _mv(source_dir, target_dir): for src_dir, dirs, files in os.walk(source_dir): destination_dir = src_dir.replace(source_dir, target_dir, 1) if not os.path.exists(destination_dir): os.makedirs(destination_dir) for src_file in files: file_to_copy = os.path.join(src_dir, src_file) dst_file = os.path.join(destination_dir, src_file) if os.path.exists(dst_file): os.remove(dst_file) shutil.move(file_to_copy, destination_dir) # Some installers allow to choose game's language before installation (Divinity Original Sin or XCom EE / XCom 2) # "--list-languages" option returns "en-US", "fr-FR" etc... for these games. # Others installers return "French : Français" but disallow to choose game's language before installation def lang_install(installer: str, language: str): languages = [] arg = "" process = subprocess.Popen(["innoextract", installer, "--list-languages"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = process.communicate() output = stdout.decode("utf-8") for line in output.split('\n'): if not line.startswith(' -'): continue languages.append(line[3:]) for lang in languages: if "-" in lang: # lang must be like "en-US" only. if language == lang[0:2]: arg = "--language={}".format(lang) break else: arg = "--language=en-US" break return arg sharkwouter-minigalaxy-759382b/minigalaxy/launcher.py000066400000000000000000000215461455252417400230530ustar00rootroot00000000000000import os import subprocess import shutil import re import json import glob import threading from minigalaxy.logger import logger from minigalaxy.translation import _ def get_wine_path(game): binary_name = "wine" custom_wine_path = game.get_info("custom_wine") if custom_wine_path and custom_wine_path != shutil.which(binary_name): binary_name = custom_wine_path return binary_name def config_game(game): prefix = os.path.join(game.install_dir, "prefix") os.environ["WINEPREFIX"] = prefix subprocess.Popen([get_wine_path(game), 'winecfg']) def regedit_game(game): prefix = os.path.join(game.install_dir, "prefix") os.environ["WINEPREFIX"] = prefix subprocess.Popen([get_wine_path(game), 'regedit']) def winetricks_game(game): prefix = os.path.join(game.install_dir, "prefix") os.environ["WINEPREFIX"] = prefix subprocess.Popen(['winetricks']) def start_game(game): error_message = "" process = None if not error_message: error_message = set_fps_display(game) if not error_message: error_message, process = run_game_subprocess(game) if not error_message: error_message = check_if_game_started_correctly(process, game) if not error_message: send_game_output_to_stdout(process) if error_message: logger.error(_("Failed to start {}:").format(game.name), exc_info=1) logger.error("Cause of error: %s", error_message) return error_message def get_execute_command(game) -> list: files = os.listdir(game.install_dir) launcher_type = determine_launcher_type(files) if launcher_type in ["start_script", "wine"]: exe_cmd = get_start_script_exe_cmd() elif launcher_type == "windows": exe_cmd = get_windows_exe_cmd(game, files) elif launcher_type == "dosbox": exe_cmd = get_dosbox_exe_cmd(game, files) elif launcher_type == "scummvm": exe_cmd = get_scummvm_exe_cmd(game, files) elif launcher_type == "final_resort": exe_cmd = get_final_resort_exe_cmd(game, files) else: # If no executable was found at all, raise an error raise FileNotFoundError() if game.get_info("use_gamemode") is True: exe_cmd.insert(0, "gamemoderun") if game.get_info("use_mangohud") is True: exe_cmd.insert(0, "mangohud") exe_cmd.insert(1, "--dlsym") exe_cmd = get_exe_cmd_with_var_command(game, exe_cmd) logger.info("Launch command for %s: %s", game.name, " ".join(exe_cmd)) return exe_cmd def determine_launcher_type(files): launcher_type = "unknown" if "unins000.exe" in files: launcher_type = "windows" elif "dosbox" in files and shutil.which("dosbox"): launcher_type = "dosbox" elif "scummvm" in files and shutil.which("scummvm"): launcher_type = "scummvm" elif "start.sh" in files: launcher_type = "start_script" elif "prefix" in files and shutil.which("wine"): launcher_type = "wine" elif "game" in files: launcher_type = "final_resort" return launcher_type def get_exe_cmd_with_var_command(game, exe_cmd): var_list = game.get_info("variable").split() command_list = game.get_info("command").split() if var_list: if var_list[0] not in ["env"]: var_list.insert(0, "env") exe_cmd = var_list + exe_cmd + command_list return exe_cmd def get_windows_exe_cmd(game, files): exe_cmd = [""] prefix = os.path.join(game.install_dir, "prefix") os.environ["WINEPREFIX"] = prefix # Find game executable file for file in files: if re.match(r'^goggame-\d*\.info$', file): os.chdir(game.install_dir) with open(file, 'r') as info_file: info = json.loads(info_file.read()) # if we have the workingDir property, start the executable at that directory if "playTasks" in info: for task in info["playTasks"]: if "isPrimary" not in task or not task["isPrimary"]: continue if "category" in task and task["category"] == "game" and "path" in task: working_dir = task["workingDir"] if "workingDir" in task else "." path = task["path"] exe_cmd = [get_wine_path(game), "start", "/b", "/wait", "/d", working_dir, path] if "arguments" in task: exe_cmd += task["arguments"].split(" ") break if exe_cmd == [""]: # in case no goggame info file was found executables = glob.glob(game.install_dir + '/*.exe') executables.remove(os.path.join(game.install_dir, "unins000.exe")) filename = os.path.splitext(os.path.basename(executables[0]))[0] + '.exe' exe_cmd = [get_wine_path(game), filename] return exe_cmd def get_dosbox_exe_cmd(game, files): dosbox_config = "" dosbox_config_single = "" for file in files: if re.match(r'^dosbox_?([a-z]|[A-Z]|\d)+\.conf$', file): dosbox_config = file if re.match(r'^dosbox_?([a-z]|[A-Z]|\d)+_single\.conf$', file): dosbox_config_single = file logger.info("Using system's dosbox to launch %s", game.name) return ["dosbox", "-conf", dosbox_config, "-conf", dosbox_config_single, "-no-console", "-c", "exit"] def get_scummvm_exe_cmd(game, files): scummvm_config = "" for file in files: if re.match(r'^.*\.ini$', file): scummvm_config = file break logger.info("Using system's scrummvm to launch %s", game.name) return ["scummvm", "-c", scummvm_config] def get_start_script_exe_cmd(): return ["./start.sh"] def get_final_resort_exe_cmd(game, files): # This is the final resort, applies to FTL exe_cmd = [""] game_dir = "game" game_files = os.listdir(os.path.join(game.install_dir, game_dir)) if game_dir in files else [] for file in game_files: if re.match(r'^goggame-[0-9]*\.info$', file): os.chdir(os.path.join(game.install_dir, game_dir)) with open(file, 'r') as info_file: info = json.loads(info_file.read()) exe_cmd = ["./{}".format(info["playTasks"][0]["path"])] return exe_cmd def set_fps_display(game): error_message = "" # Enable FPS Counter for Nvidia or AMD (Mesa) users if game.get_info("show_fps"): os.environ["__GL_SHOW_GRAPHICS_OSD"] = "1" # For Nvidia users + OpenGL/Vulkan games os.environ["GALLIUM_HUD"] = "simple,fps" # For AMDGPU users + OpenGL games os.environ["VK_INSTANCE_LAYERS"] = "VK_LAYER_MESA_overlay" # For AMDGPU users + Vulkan games else: os.environ["__GL_SHOW_GRAPHICS_OSD"] = "0" os.environ["GALLIUM_HUD"] = "" os.environ["VK_INSTANCE_LAYERS"] = "" return error_message def run_game_subprocess(game): try: process = subprocess.Popen( get_execute_command(game), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=0, cwd=game.install_dir ) error_message = "" except FileNotFoundError: process = None error_message = _("No executable was found in {}").format(game.install_dir) return error_message, process def check_if_game_started_correctly(process, game): error_message = "" # Check if the application has started and see if it is still runnning after a short timeout try: process.wait(timeout=float(3)) error_message = "Game start process has finished prematurely" except subprocess.TimeoutExpired: pass if error_message in ["Game start process has finished prematurely"]: error_message = check_if_game_start_process_spawned_final_process(error_message, game) # Set the error message to what's been received in stdout if not yet set if error_message: stdout, _ = process.communicate() error_message = stdout.decode("utf-8") return error_message def check_if_game_start_process_spawned_final_process(error_message, game): ps_ef = subprocess.check_output(["ps", "-ef"]).decode("utf-8") ps_list = ps_ef.split("\n") for ps in ps_list: ps_split = ps.split() if len(ps_split) < 2: continue if not ps_split[1].isdigit(): continue if int(ps_split[1]) > os.getpid() and game.get_install_directory_name() in ps: error_message = "" break return error_message def send_game_output_to_stdout(process): def _internal_call(process): for line in iter(process.stdout.readline, b''): print(line.decode('utf-8'), end='') # TODO Is this intentionally a print statement? process.stdout.close() process.wait() t = threading.Thread(target=_internal_call, args=(process,)) t.start() sharkwouter-minigalaxy-759382b/minigalaxy/logger.py000066400000000000000000000022201455252417400225150ustar00rootroot00000000000000import logging import datetime import os import sys class MinigalaxyLogFormatter(logging.Formatter): converter = datetime.date.fromtimestamp def formatTime(self, record, datefmt=None): ct = self.converter(record.created) if datefmt: logger.debug("Have date format") return ct.strftime(datefmt) else: time_string = ct.strftime("%Y-%m-%d %H:%M:%S") time_string_with_ms = "%s,%03d" % (time_string, record.msecs) return time_string_with_ms # create logger for the minigalaxy application logger = logging.getLogger('minigalaxy') logger.setLevel(logging.DEBUG) # The console should log DEBUG messages and up ch = logging.StreamHandler(stream=sys.stdout) debug = os.environ.get("MG_DEBUG") if debug: ch.setLevel(logging.DEBUG) else: ch.setLevel(logging.ERROR) # create formatter and add it to the handlers # This doesn't use the MinigalaxyLogFormatter yet, it uses the default logging Formatter formatter = logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s") ch.setFormatter(formatter) # add the handlers to the logger logger.addHandler(ch) sharkwouter-minigalaxy-759382b/minigalaxy/paths.py000066400000000000000000000036571455252417400223740ustar00rootroot00000000000000import os import sys LAUNCH_DIR = os.path.abspath(os.path.dirname(sys.argv[0])) if LAUNCH_DIR == "/bin" or LAUNCH_DIR == "/sbin": LAUNCH_DIR = "/usr" + LAUNCH_DIR CONFIG_DIR = os.path.join(os.getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config')), "minigalaxy") CONFIG_GAMES_DIR = os.path.join(CONFIG_DIR, "games") CONFIG_FILE_PATH = os.path.join(CONFIG_DIR, "config.json") CACHE_DIR = os.path.join(os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')), "minigalaxy") THUMBNAIL_DIR = os.path.join(CACHE_DIR, "thumbnails") COVER_DIR = os.path.join(CACHE_DIR, "covers") ICON_DIR = os.path.join(CACHE_DIR, "icons") CATEGORIES_FILE_PATH = os.path.join(CACHE_DIR, "categories.json") APPLICATIONS_DIR = os.path.expanduser("~/.local/share/applications") DEFAULT_INSTALL_DIR = os.path.expanduser("~/GOG Games") UI_DIR = os.path.abspath(os.path.join(LAUNCH_DIR, "../data/ui")) if not os.path.exists(UI_DIR): UI_DIR = os.path.abspath(os.path.join(LAUNCH_DIR, "../share/minigalaxy/ui")) LOGO_IMAGE_PATH = os.path.abspath(os.path.join(LAUNCH_DIR, "../data/icons/192x192/io.github.sharkwouter.Minigalaxy.png")) if not os.path.exists(LOGO_IMAGE_PATH): LOGO_IMAGE_PATH = os.path.abspath( os.path.join(LAUNCH_DIR, "../share/icons/hicolor/192x192/apps/io.github.sharkwouter.Minigalaxy.png") ) ICON_WINE_PATH = os.path.abspath(os.path.join(LAUNCH_DIR, "../data/images/winehq_logo_glass.png")) if not os.path.exists(ICON_WINE_PATH): ICON_WINE_PATH = os.path.abspath(os.path.join(LAUNCH_DIR, "../share/minigalaxy/images/winehq_logo_glass.png")) LOCALE_DIR = os.path.abspath(os.path.join(LAUNCH_DIR, "../data/mo")) if not os.path.exists(LOCALE_DIR): LOCALE_DIR = os.path.abspath(os.path.join(LAUNCH_DIR, "../share/minigalaxy/translations")) CSS_PATH = os.path.abspath(os.path.join(LAUNCH_DIR, "../data/style.css")) if not os.path.exists(CSS_PATH): CSS_PATH = os.path.abspath(os.path.join(LAUNCH_DIR, "../share/minigalaxy/style.css")) sharkwouter-minigalaxy-759382b/minigalaxy/translation.py000066400000000000000000000026211455252417400236010ustar00rootroot00000000000000import os import gettext import locale from minigalaxy.config import Config from minigalaxy.logger import logger from minigalaxy.paths import LOCALE_DIR TRANSLATION_DOMAIN = "minigalaxy" try: locale.setlocale(locale.LC_ALL, '') except locale.Error: logger.error("Unsupported locale detected, continuing without translation support", exc_info=1) try: locale.bindtextdomain(TRANSLATION_DOMAIN, LOCALE_DIR) except AttributeError: logger.error("Couldn't run locale.bindtextdomain. Translations might not work correctly.", exc_info=1) try: locale.textdomain(TRANSLATION_DOMAIN) except AttributeError: logger.error("Couldn't run locale.textdomain. Translations might not work correctly.", exc_info=1) gettext.bindtextdomain(TRANSLATION_DOMAIN, LOCALE_DIR) gettext.textdomain(TRANSLATION_DOMAIN) # Make sure LANGUAGE and LANG are not set, or translations will not be loaded os.unsetenv("LANGUAGE") os.unsetenv("LANG") current_locale = Config().locale default_locale = locale.getdefaultlocale()[0] if current_locale == '': if default_locale is None: lang = gettext.translation(TRANSLATION_DOMAIN, LOCALE_DIR, languages=['en'], fallback=True) else: lang = gettext.translation(TRANSLATION_DOMAIN, LOCALE_DIR, languages=[default_locale], fallback=True) else: lang = gettext.translation(TRANSLATION_DOMAIN, LOCALE_DIR, languages=[current_locale], fallback=True) _ = lang.gettext sharkwouter-minigalaxy-759382b/minigalaxy/ui/000077500000000000000000000000001455252417400213055ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/minigalaxy/ui/__init__.py000066400000000000000000000004131455252417400234140ustar00rootroot00000000000000""" MiniGalaxy gui windows """ # Flake8 thinks these imports are unused from minigalaxy.ui.window import Window # noqa: F401 from minigalaxy.ui.preferences import Preferences # noqa: F401 from minigalaxy.ui.gametile import GameTile # noqa: F401 sharkwouter-minigalaxy-759382b/minigalaxy/ui/about.py000066400000000000000000000010551455252417400227720ustar00rootroot00000000000000import os from minigalaxy.version import VERSION from minigalaxy.translation import _ from minigalaxy.paths import LOGO_IMAGE_PATH, UI_DIR from minigalaxy.ui.gtk import Gtk, GdkPixbuf @Gtk.Template.from_file(os.path.join(UI_DIR, "about.ui")) class About(Gtk.AboutDialog): __gtype_name__ = "About" def __init__(self, parent): Gtk.AboutDialog.__init__(self, title=_("About"), parent=parent, modal=True) self.set_version(VERSION) new_image = GdkPixbuf.Pixbuf().new_from_file(LOGO_IMAGE_PATH) self.set_logo(new_image) sharkwouter-minigalaxy-759382b/minigalaxy/ui/categoryfilters.py000066400000000000000000000042251455252417400250700ustar00rootroot00000000000000import os from minigalaxy.logger import logger from minigalaxy.translation import _ from minigalaxy.paths import UI_DIR from minigalaxy.ui.filterswitch import FilterSwitch from minigalaxy.ui.gtk import Gtk @Gtk.Template.from_file(os.path.join(UI_DIR, "categoryfilters.ui")) class CategoryFilters(Gtk.Dialog): __gtype_name__ = "CategoryFilters" genre_filtering_grid = Gtk.Template.Child() filter_dict = {} categories = [ 'Action', 'Adventure', 'Racing', 'Role-playing', 'Shooter', 'Simulation', 'Sports', 'Strategy', ] def __init__(self, parent, library): Gtk.Dialog.__init__(self, title=_("Category Filters"), parent=parent, modal=True) self.parent = parent self.library = library for idx, category in enumerate(self.categories): initial_state = category in self.library.category_filters self.filter_dict[category] = initial_state filter_switch = FilterSwitch(self, category, lambda key, value: self.filter_dict.__setitem__(key, value), initial_state) self.genre_filtering_grid.attach( filter_switch, left=idx % 3, top=int(idx / 3), width=1, height=1) # Center filters window self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) @Gtk.Template.Callback("on_button_category_filters_apply_clicked") def on_apply_clicked(self, button): logger.debug("Filtering library according to category filter dict: %s", self.filter_dict) self.library.filter_library(self) self.destroy() @Gtk.Template.Callback("on_button_category_filters_cancel_clicked") def on_cancel_clicked(self, button): self.destroy() @Gtk.Template.Callback("on_button_category_filters_reset_clicked") def on_reset_clicked(self, button): logger.debug("Resetting category filters") for child in self.genre_filtering_grid.get_children(): child.switch_category_filter.set_active(False) self.library.filter_library(self) sharkwouter-minigalaxy-759382b/minigalaxy/ui/filterswitch.py000066400000000000000000000014051455252417400243660ustar00rootroot00000000000000import os from minigalaxy.paths import UI_DIR from minigalaxy.ui.gtk import Gtk from minigalaxy.translation import _ @Gtk.Template.from_file(os.path.join(UI_DIR, "filterswitch.ui")) class FilterSwitch(Gtk.Box): __gtype_name__ = "FilterSwitch" label_category_filter = Gtk.Template.Child() switch_category_filter = Gtk.Template.Child() def __init__(self, parent, category_name, on_toggle_fn, initial_state=False): Gtk.Frame.__init__(self) self.parent = parent self.label_category_filter.set_label(_(category_name)) self.switch_category_filter.set_active(initial_state) def on_click(self): on_toggle_fn(category_name, self.get_active()) self.switch_category_filter.connect('toggled', on_click) sharkwouter-minigalaxy-759382b/minigalaxy/ui/gametile.py000066400000000000000000000657651455252417400234710ustar00rootroot00000000000000import shutil import locale import os import threading import re import time import urllib.parse from minigalaxy.config import Config from minigalaxy.entity.state import State from minigalaxy.game import Game from minigalaxy.logger import logger from minigalaxy.translation import _ from minigalaxy.paths import CACHE_DIR, THUMBNAIL_DIR, ICON_DIR, UI_DIR from minigalaxy.download import Download, DownloadType from minigalaxy.download_manager import DownloadManager from minigalaxy.launcher import start_game from minigalaxy.installer import uninstall_game, install_game, check_diskspace from minigalaxy.paths import ICON_WINE_PATH from minigalaxy.api import NoDownloadLinkFound, Api from minigalaxy.ui.gtk import Gtk, GLib, Notify from minigalaxy.ui.information import Information from minigalaxy.ui.properties import Properties @Gtk.Template.from_file(os.path.join(UI_DIR, "gametile.ui")) class GameTile(Gtk.Box): __gtype_name__ = "GameTile" image = Gtk.Template.Child() button = Gtk.Template.Child() button_cancel = Gtk.Template.Child() menu_button = Gtk.Template.Child() wine_icon = Gtk.Template.Child() update_icon = Gtk.Template.Child() menu_button_update = Gtk.Template.Child() menu_button_dlc = Gtk.Template.Child() menu_button_uninstall = Gtk.Template.Child() dlc_horizontal_box = Gtk.Template.Child() menu_button_information = Gtk.Template.Child() menu_button_properties = Gtk.Template.Child() progress_bar = Gtk.Template.Child() def __init__(self, parent, game: Game, config: Config, api: Api, download_manager: DownloadManager): self.config = config current_locale = self.config.locale default_locale = locale.getdefaultlocale()[0] if current_locale == '': locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) else: try: locale.setlocale(locale.LC_ALL, (current_locale, 'UTF-8')) except NameError: locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) Gtk.Frame.__init__(self) self.parent = parent self.game = game self.api = api self.download_manager = download_manager self.offline = parent.offline self.thumbnail_set = False self.download_list = [] self.dlc_dict = {} self.current_state = State.DOWNLOADABLE self.image.set_tooltip_text(self.game.name) # Set folder for download installer self.download_dir = os.path.join(CACHE_DIR, "download", self.game.get_install_directory_name()) # Set folder if user wants to keep installer (disabled by default) self.keep_dir = os.path.join(self.config.install_dir, "installer") self.keep_path = os.path.join(self.keep_dir, self.game.get_install_directory_name()) if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR, mode=0o755) self.reload_state() load_thumbnail_thread = threading.Thread(target=self.load_thumbnail) load_thumbnail_thread.start() # Start download if Minigalaxy was closed while downloading this game self.resume_download_if_expected() # Icon for Windows games if self.game.platform == "windows": self.image.set_tooltip_text("{} (Wine)".format(self.game.name)) self.wine_icon.set_from_file(ICON_WINE_PATH) self.wine_icon.show() # Downloads if Minigalaxy was closed with this game downloading def resume_download_if_expected(self): download_ids = self.config.current_downloads if download_ids: for download_id in download_ids: if download_id and download_id == self.game.id and self.current_state == State.DOWNLOADABLE: download_thread = threading.Thread(target=self.__download_game) download_thread.start() # Do not restart the download if Minigalaxy is restarted def prevent_resume_on_startup(self): download_ids = self.config.current_downloads if download_ids: new_download_ids = set() for download_id in download_ids: if not (download_id and download_id == self.game.id): new_download_ids.add(download_id) self.config.current_downloads = list(new_download_ids) def __str__(self): return self.game.name @Gtk.Template.Callback("on_button_clicked") def on_button_click(self, widget) -> None: dont_act_in_states = [State.QUEUED, State.DOWNLOADING, State.INSTALLING, State.UNINSTALLING] err_msg = "" if self.current_state in dont_act_in_states: pass elif self.current_state in [State.INSTALLED, State.UPDATABLE]: err_msg = start_game(self.game) elif self.current_state == State.INSTALLABLE: install_thread = threading.Thread(target=self.__install_game, args=(self.get_keep_executable_path(),)) install_thread.start() elif self.current_state == State.DOWNLOADABLE: download_thread = threading.Thread(target=self.__download_game) download_thread.start() if err_msg: self.parent.parent.show_error(_("Failed to start {}:").format(self.game.name), err_msg) @Gtk.Template.Callback("on_menu_button_information_clicked") def show_information(self, button): information_window = Information(self, self.game, self.config, self.api, self.download_manager) information_window.run() information_window.destroy() @Gtk.Template.Callback("on_menu_button_properties_clicked") def show_properties(self, button): properties_window = Properties(self, self.game, self.api) properties_window.run() properties_window.destroy() @Gtk.Template.Callback("on_button_cancel_clicked") def on_button_cancel(self, widget): question = _("Are you sure you want to cancel downloading {}?").format(self.game.name) if self.parent.parent.show_question(question): self.prevent_resume_on_startup() self.download_manager.cancel_download(self.download_list) try: for filename in os.listdir(self.download_dir): if self.game.get_install_directory_name() in filename: os.remove(os.path.join(self.download_dir, filename)) except FileNotFoundError: pass @Gtk.Template.Callback("on_menu_button_uninstall_clicked") def on_menu_button_uninstall(self, widget): question = _("Are you sure you want to uninstall %s?" % self.game.name) if self.parent.parent.show_question(question): uninstall_thread = threading.Thread(target=self.__uninstall_game) uninstall_thread.start() @Gtk.Template.Callback("on_menu_button_update_clicked") def on_menu_button_update(self, widget): download_thread = threading.Thread(target=self.__download_update) download_thread.start() def load_thumbnail(self): set_result = self.__set_image("") if not set_result: tries = 10 performed_try = 0 while performed_try < tries: if self.game.image_url and self.game.id: # Download the thumbnail image_url = "https:{}_196.jpg".format(self.game.image_url) thumbnail = os.path.join(THUMBNAIL_DIR, "{}.jpg".format(self.game.id)) download = Download(image_url, thumbnail, DownloadType.THUMBNAIL, finish_func=self.__set_image) self.download_manager.download_now(download) set_result = True break performed_try += 1 time.sleep(1) return set_result def __set_image(self, save_location): set_result = False self.game.set_install_dir(self.config.install_dir) thumbnail_install_dir = os.path.join(self.game.install_dir, "thumbnail.jpg") if os.path.isfile(thumbnail_install_dir): GLib.idle_add(self.image.set_from_file, thumbnail_install_dir) set_result = True elif save_location and os.path.isfile(save_location): GLib.idle_add(self.image.set_from_file, save_location) # Copy image to if os.path.isdir(os.path.dirname(thumbnail_install_dir)): shutil.copy2(save_location, thumbnail_install_dir) set_result = True thumbnail_path = os.path.join(THUMBNAIL_DIR, "{}.jpg".format(self.game.id)) if os.path.isfile(thumbnail_path): GLib.idle_add(self.image.set_from_file, thumbnail_path) set_result = True return set_result def get_keep_executable_path(self): keep_path = "" if os.path.isdir(self.keep_path): for dir_content in os.listdir(self.keep_path): kept_file = os.path.join(self.keep_path, dir_content) if os.access(kept_file, os.X_OK) or os.path.splitext(kept_file)[-1] in [".exe", ".sh"]: keep_path = kept_file break return keep_path def get_download_info(self, platform="linux"): try: download_info = self.api.get_download_info(self.game, platform) result = True except NoDownloadLinkFound as e: logger.error("No download link found", exc_info=1) current_download_ids = self.config.current_downloads if current_download_ids: new_current_download_ids = set() for current_download_id in current_download_ids: if current_download_id != self.game.id: new_current_download_ids.add(current_download_id) self.config.current_downloads = list(new_current_download_ids) GLib.idle_add(self.parent.parent.show_error, _("Download error"), _("There was an error when trying to fetch the download link!\n{}".format(e))) download_info = False result = False return result, download_info def __download_game(self) -> None: finish_func = self.__install_game cancel_to_state = State.DOWNLOADABLE result, download_info = self.get_download_info() if result: result = self.__download(download_info, DownloadType.GAME, finish_func, cancel_to_state) if not result: GLib.idle_add(self.update_to_state, cancel_to_state) def __download(self, download_info, download_type, finish_func, cancel_to_state): # noqa: C901 download_success = True self.game.set_install_dir(self.config.install_dir) GLib.idle_add(self.update_to_state, State.QUEUED) # Need to update the config with DownloadType metadata current_download_ids = self.config.current_downloads if current_download_ids is None: current_download_ids = set() else: current_download_ids = set(current_download_ids) current_download_ids.add(self.game.id) self.config.current_downloads = list(current_download_ids) # Start the download for all files self.download_list = [] number_of_files = len(download_info['files']) total_file_size = 0 executable_path = None download_files = [] for key, file_info in enumerate(download_info['files']): try: download_url = self.api.get_real_download_link(file_info["downlink"]) except ValueError as e: logger.error("Error getting download URL from file_info downlink: %s", file_info["downlink"], exc_info=1) GLib.idle_add(self.parent.parent.show_error, _("Download error"), _(str(e))) download_success = False break info = self.api.get_download_file_info(file_info["downlink"]) total_file_size += info.size try: # Extract the filename from the download url (filename is between %2F and &token) filename = urllib.parse.unquote(re.search('%2F(((?!%2F).)*)&t', download_url).group(1)) except AttributeError: filename = "{}-{}.bin".format(self.game.get_stripped_name(), key) download_path = os.path.join(self.download_dir, filename) if key == 0: # If key = 0, denote the file as the executable's path executable_path = download_path if info.md5: self.game.md5sum[os.path.basename(download_path)] = info.md5 download = Download( url=download_url, save_location=download_path, download_type=DownloadType.GAME, finish_func=finish_func if download_path == executable_path else None, progress_func=self.set_progress, cancel_func=lambda: self.__cancel(to_state=cancel_to_state), number=number_of_files - key, out_of_amount=number_of_files, game=self.game ) download_files.insert(0, download) self.download_list.extend(download_files) if check_diskspace(total_file_size, self.game.install_dir): self.download_manager.download(download_files) ds_msg_title = "" ds_msg_text = "" else: ds_msg_title = "Download error" ds_msg_text = "Not enough disk space to install game." download_success = False if ds_msg_title: GLib.idle_add(self.parent.parent.show_error, _(ds_msg_title), _(ds_msg_text)) return download_success def __install_game(self, save_location): if self.game.id in self.config.current_downloads: self.config.current_downloads.remove(self.game.id) self.download_list = [] self.game.set_install_dir(self.config.install_dir) install_success = self.__install(save_location) if install_success: self.__check_for_dlc(self.api.get_info(self.game)) def __install(self, save_location, update=False, dlc_title=""): if update: processing_state = State.UPDATING failed_state = State.INSTALLED else: processing_state = State.INSTALLING failed_state = State.DOWNLOADABLE success_state = State.INSTALLED GLib.idle_add(self.update_to_state, processing_state) err_msg = install_game( self.game, save_location, self.config.lang, self.config.install_dir, self.config.keep_installers, self.config.create_applications_file ) if not err_msg: GLib.idle_add(self.update_to_state, success_state) install_success = True if dlc_title: self.game.set_dlc_info("version", self.api.get_version(self.game, dlc_name=dlc_title), dlc_title) else: self.game.set_info("version", self.api.get_version(self.game)) popup = Notify.Notification.new("Minigalaxy", _("Finished downloading and installing {}") .format(self.game.name), "dialog-information") popup.show() else: GLib.idle_add(self.parent.parent.show_error, _("Failed to install {}").format(self.game.name), err_msg) GLib.idle_add(self.update_to_state, failed_state) install_success = False return install_success def __cancel(self, to_state): self.download_list = [] GLib.idle_add(self.update_to_state, to_state) GLib.idle_add(self.reload_state) def __download_update(self) -> None: finish_func = self.__update cancel_to_state = State.UPDATABLE result, download_info = self.get_download_info(self.game.platform) if result: result = self.__download(download_info, DownloadType.GAME_UPDATE, finish_func, cancel_to_state) if not result: GLib.idle_add(self.update_to_state, cancel_to_state) def __check_for_update_dlc(self): if self.game.is_installed() and self.game.id and not self.offline: game_info = self.api.get_info(self.game) if self.game.get_info("check_for_updates") == "": self.game.set_info("check_for_updates", True) if self.game.get_info("check_for_updates"): game_version = self.api.get_version(self.game, gameinfo=game_info) update_available = self.game.is_update_available(game_version) if update_available: GLib.idle_add(self.update_to_state, State.UPDATABLE) self.__check_for_dlc(game_info) if self.offline: GLib.idle_add(self.menu_button_dlc.hide) def __update(self, save_location): install_success = self.__install(save_location, update=True) if install_success: if self.game.platform == "windows": self.image.set_tooltip_text("{} (Wine)".format(self.game.name)) else: self.image.set_tooltip_text(self.game.name) for dlc in self.game.dlcs: download_info = self.api.get_download_info(self.game, dlc_installers=dlc["downloads"]["installers"]) if self.game.is_update_available(version_from_api=download_info["version"], dlc_title=dlc["title"]): self.__download_dlc(dlc["downloads"]["installers"]) def __download_dlc(self, dlc_installers) -> None: def finish_func(save_location): self.__install_dlc(save_location, dlc_title=dlc_title) download_info = self.api.get_download_info(self.game, dlc_installers=dlc_installers) dlc_title = self.game.name for dlc in self.game.dlcs: if dlc["downloads"]["installers"] == dlc_installers: dlc_title = dlc["title"] cancel_to_state = State.INSTALLED result = self.__download(download_info, DownloadType.GAME_DLC, finish_func, cancel_to_state) if not result: GLib.idle_add(self.update_to_state, cancel_to_state) def __install_dlc(self, save_location, dlc_title): install_success = self.__install(save_location, dlc_title=dlc_title) if not install_success: GLib.idle_add(self.update_to_state, State.INSTALLED) self.__check_for_update_dlc() def __check_for_dlc(self, game_info): dlcs = game_info["expanded_dlcs"] for dlc in dlcs: if dlc["is_installable"] and dlc["id"] in self.parent.owned_products_ids: d_id = dlc["id"] d_installer = dlc["downloads"]["installers"] d_icon = dlc["images"]["sidebarIcon"] d_name = dlc["title"] GLib.idle_add(self.update_gtk_box_for_dlc, d_id, d_icon, d_name, d_installer) if dlc not in self.game.dlcs: self.game.dlcs.append(dlc) if self.game.dlcs: GLib.idle_add(self.menu_button_dlc.show) def update_gtk_box_for_dlc(self, dlc_id, icon, title, installer): if title not in self.dlc_dict: dlc_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) dlc_box.set_spacing(8) image = Gtk.Image() image.set_from_icon_name("media-optical", Gtk.IconSize.BUTTON) dlc_box.pack_start(image, False, True, 0) label = Gtk.Label(label=title, xalign=0) dlc_box.pack_start(label, True, True, 0) install_button = Gtk.Button() dlc_box.pack_start(install_button, False, True, 0) self.dlc_dict[title] = [install_button, image] self.dlc_dict[title][0].connect("clicked", self.__dlc_button_clicked, installer) self.dlc_horizontal_box.pack_start(dlc_box, False, True, 0) dlc_box.show_all() self.get_async_image_dlc_icon(dlc_id, image, icon, title) download_info = self.api.get_download_info(self.game, dlc_installers=installer) if self.game.is_update_available(version_from_api=download_info["version"], dlc_title=title): icon_name = "emblem-synchronizing" self.dlc_dict[title][0].set_sensitive(True) elif self.game.is_installed(dlc_title=title): icon_name = "object-select" self.dlc_dict[title][0].set_sensitive(False) else: icon_name = "document-save" if not self.download_list: self.dlc_dict[title][0].set_sensitive(True) install_button_image = Gtk.Image() install_button_image.set_from_icon_name(icon_name, Gtk.IconSize.BUTTON) self.dlc_dict[title][0].set_image(install_button_image) def __dlc_button_clicked(self, button, installer): button.set_sensitive(False) threading.Thread(target=self.__download_dlc, args=(installer,)).start() def get_async_image_dlc_icon(self, dlc_id, image, icon, title): dlc_icon_path = os.path.join(ICON_DIR, "{}.jpg".format(dlc_id)) if icon: if os.path.isfile(dlc_icon_path): GLib.idle_add(image.set_from_file, dlc_icon_path) else: url = "http:{}".format(icon) dlc_icon = os.path.join(ICON_DIR, "{}.jpg".format(dlc_id)) download = Download(url, dlc_icon) self.download_manager.download_now(download) GLib.idle_add(image.set_from_file, dlc_icon_path) def set_progress(self, percentage: int): if self.current_state in [State.QUEUED, State.INSTALLED]: GLib.idle_add(self.update_to_state, State.DOWNLOADING) if self.progress_bar: GLib.idle_add(self.progress_bar.set_fraction, percentage / 100) GLib.idle_add(self.progress_bar.set_tooltip_text, "{}%".format(percentage)) def __uninstall_game(self): GLib.idle_add(self.update_to_state, State.UNINSTALLING) uninstall_game(self.game) GLib.idle_add(self.update_to_state, State.DOWNLOADABLE) GLib.idle_add(self.reload_state) def reload_state(self): self.game.set_install_dir(self.config.install_dir) dont_act_in_states = [State.QUEUED, State.DOWNLOADING, State.INSTALLING, State.UNINSTALLING, State.UPDATING, State.DOWNLOADING] if self.current_state in dont_act_in_states: return if self.game.is_installed(): self.update_to_state(State.INSTALLED) check_update_thread = threading.Thread(target=self.__check_for_update_dlc) check_update_thread.start() elif self.get_keep_executable_path(): self.update_to_state(State.INSTALLABLE) else: self.update_to_state(State.DOWNLOADABLE) def __state_downloadable(self): self.button.set_label(_("Download")) self.button.set_tooltip_text(_("Download and install the game")) self.button.set_sensitive(True) self.image.set_sensitive(False) # The user must have the possibility to access # to the store button even if the game is not installed self.menu_button.show() self.menu_button.set_tooltip_text(_("Show game options menu")) self.menu_button_update.hide() self.menu_button_dlc.hide() self.menu_button_uninstall.hide() self.button_cancel.hide() self.progress_bar.hide() self.game.install_dir = "" def __state_installable(self): self.button.set_label(_("Install")) self.button.set_tooltip_text(_("Install the game")) self.button.set_sensitive(True) self.image.set_sensitive(False) self.menu_button.hide() self.button_cancel.hide() self.progress_bar.hide() self.game.install_dir = "" def __state_queued(self): self.button.set_label(_("In queue…")) self.button.set_sensitive(False) self.image.set_sensitive(False) self.menu_button_uninstall.hide() self.menu_button_update.hide() self.button_cancel.show() self.progress_bar.show() def __state_downloading(self): self.button.set_label(_("Downloading…")) self.button.set_sensitive(False) self.image.set_sensitive(False) self.menu_button_uninstall.hide() self.menu_button_update.hide() self.button_cancel.show() self.progress_bar.show() def __state_installing(self): self.button.set_label(_("Installing…")) self.button.set_sensitive(False) self.image.set_sensitive(True) self.menu_button_uninstall.hide() self.menu_button_update.hide() self.button_cancel.hide() self.progress_bar.hide() self.game.set_install_dir(self.config.install_dir) self.parent.filter_library() def __state_installed(self): self.button.set_label(_("Play")) self.button.set_tooltip_text(_("Launch the game")) self.button.get_style_context().add_class("suggested-action") self.menu_button.get_style_context().add_class("suggested-action") self.button.set_sensitive(True) self.image.set_sensitive(True) self.menu_button.set_tooltip_text(_("Show game options menu")) self.menu_button.show() self.menu_button_uninstall.show() self.button_cancel.hide() self.progress_bar.hide() self.menu_button_update.hide() self.update_icon.hide() self.game.set_install_dir(self.config.install_dir) def __state_uninstalling(self): self.button.set_label(_("Uninstalling…")) self.button.get_style_context().remove_class("suggested-action") self.menu_button.get_style_context().remove_class("suggested-action") self.button.set_sensitive(False) self.image.set_sensitive(False) self.menu_button.hide() self.button_cancel.hide() self.game.install_dir = "" self.parent.filter_library() def __state_updatable(self): self.update_icon.show() self.update_icon.set_from_icon_name("emblem-synchronizing", Gtk.IconSize.LARGE_TOOLBAR) self.button.set_label(_("Play")) self.menu_button.show() tooltip_text = "{} (update{})".format(self.game.name, ", Wine" if self.game.platform == "windows" else "") self.image.set_tooltip_text(tooltip_text) self.menu_button_update.show() if self.game.platform == "windows": self.wine_icon.set_margin_left(22) def __state_updating(self): self.button.set_label(_("Updating…")) STATE_UPDATE_HANDLERS = { State.DOWNLOADABLE: __state_downloadable, State.INSTALLABLE: __state_installable, State.QUEUED: __state_queued, State.DOWNLOADING: __state_downloading, State.INSTALLING: __state_installing, State.INSTALLED: __state_installed, State.UNINSTALLING: __state_uninstalling, State.UPDATABLE: __state_updatable, State.UPDATING: __state_updating, } def update_to_state(self, state): self.current_state = state if state in self.STATE_UPDATE_HANDLERS: self.STATE_UPDATE_HANDLERS[state](self) sharkwouter-minigalaxy-759382b/minigalaxy/ui/gametilelist.py000066400000000000000000000674271455252417400243620ustar00rootroot00000000000000import shutil import locale import os import threading import re import time import urllib.parse from minigalaxy.config import Config from minigalaxy.entity.state import State from minigalaxy.game import Game from minigalaxy.logger import logger from minigalaxy.translation import _ from minigalaxy.paths import CACHE_DIR, THUMBNAIL_DIR, ICON_DIR, UI_DIR from minigalaxy.download import Download, DownloadType from minigalaxy.download_manager import DownloadManager from minigalaxy.launcher import start_game from minigalaxy.installer import uninstall_game, install_game, check_diskspace from minigalaxy.css import CSS_PROVIDER from minigalaxy.paths import ICON_WINE_PATH from minigalaxy.api import NoDownloadLinkFound, Api from minigalaxy.ui.gtk import Gtk, GLib, Notify from minigalaxy.ui.information import Information from minigalaxy.ui.properties import Properties @Gtk.Template.from_file(os.path.join(UI_DIR, "gametilelist.ui")) class GameTileList(Gtk.Box): __gtype_name__ = "GameTileList" image = Gtk.Template.Child() button = Gtk.Template.Child() button_cancel = Gtk.Template.Child() menu_button = Gtk.Template.Child() wine_icon = Gtk.Template.Child() update_icon = Gtk.Template.Child() menu_button_update = Gtk.Template.Child() menu_button_dlc = Gtk.Template.Child() menu_button_uninstall = Gtk.Template.Child() dlc_horizontal_box = Gtk.Template.Child() menu_button_information = Gtk.Template.Child() menu_button_properties = Gtk.Template.Child() game_label = Gtk.Template.Child() def __init__(self, parent, game: Game, config: Config, api: Api, download_manager: DownloadManager): self.config = config current_locale = self.config.locale default_locale = locale.getdefaultlocale()[0] if current_locale == '': locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) else: try: locale.setlocale(locale.LC_ALL, (current_locale, 'UTF-8')) except NameError: locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) Gtk.Frame.__init__(self) Gtk.StyleContext.add_provider(self.button.get_style_context(), CSS_PROVIDER, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) self.parent = parent self.game = game self.api = api self.download_manager = download_manager self.offline = parent.offline self.progress_bar = None self.thumbnail_set = False self.download_list = [] self.dlc_dict = {} self.current_state = State.DOWNLOADABLE self.image.set_tooltip_text(self.game.name) self.game_label.set_label(self.game.name) # Set folder for download installer self.download_dir = os.path.join(CACHE_DIR, "download", self.game.get_install_directory_name()) # Set folder if user wants to keep installer (disabled by default) self.keep_dir = os.path.join(self.config.install_dir, "installer") self.keep_path = os.path.join(self.keep_dir, self.game.get_install_directory_name()) if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR, mode=0o755) self.reload_state() load_thumbnail_thread = threading.Thread(target=self.load_thumbnail) load_thumbnail_thread.start() # Start download if Minigalaxy was closed while downloading this game self.resume_download_if_expected() # Icon for Windows games if self.game.platform == "windows": self.image.set_tooltip_text("{} (Wine)".format(self.game.name)) self.wine_icon.set_from_file(ICON_WINE_PATH) self.wine_icon.show() # Downloads if Minigalaxy was closed with this game downloading def resume_download_if_expected(self): download_ids = self.config.current_downloads if download_ids: for download_id in download_ids: if download_id and download_id == self.game.id and self.current_state == State.DOWNLOADABLE: download_thread = threading.Thread(target=self.__download_game) download_thread.start() # Do not restart the download if Minigalaxy is restarted def prevent_resume_on_startup(self): download_ids = self.config.current_downloads if download_ids: new_download_ids = set() for download_id in download_ids: if not (download_id and download_id == self.game.id): new_download_ids.add(download_id) self.config.current_downloads = list(new_download_ids) def __str__(self): return self.game.name @Gtk.Template.Callback("on_button_clicked") def on_button_click(self, widget) -> None: dont_act_in_states = [State.QUEUED, State.DOWNLOADING, State.INSTALLING, State.UNINSTALLING] err_msg = "" if self.current_state in dont_act_in_states: pass elif self.current_state in [State.INSTALLED, State.UPDATABLE]: err_msg = start_game(self.game) elif self.current_state == State.INSTALLABLE: install_thread = threading.Thread(target=self.__install_game, args=(self.get_keep_executable_path(),)) install_thread.start() elif self.current_state == State.DOWNLOADABLE: download_thread = threading.Thread(target=self.__download_game) download_thread.start() if err_msg: self.parent.parent.show_error(_("Failed to start {}:").format(self.game.name), err_msg) @Gtk.Template.Callback("on_menu_button_information_clicked") def show_information(self, button): information_window = Information(self, self.game, self.config, self.api, self.download_manager) information_window.run() information_window.destroy() @Gtk.Template.Callback("on_menu_button_properties_clicked") def show_properties(self, button): properties_window = Properties(self, self.game, self.api) properties_window.run() properties_window.destroy() @Gtk.Template.Callback("on_button_cancel_clicked") def on_button_cancel(self, widget): question = _("Are you sure you want to cancel downloading {}?").format(self.game.name) if self.parent.parent.show_question(question): self.prevent_resume_on_startup() self.download_manager.cancel_download(self.download_list) try: for filename in os.listdir(self.download_dir): if self.game.get_install_directory_name() in filename: os.remove(os.path.join(self.download_dir, filename)) except FileNotFoundError: pass @Gtk.Template.Callback("on_menu_button_uninstall_clicked") def on_menu_button_uninstall(self, widget): question = _("Are you sure you want to uninstall %s?" % self.game.name) if self.parent.parent.show_question(question): uninstall_thread = threading.Thread(target=self.__uninstall_game) uninstall_thread.start() @Gtk.Template.Callback("on_menu_button_update_clicked") def on_menu_button_update(self, widget): download_thread = threading.Thread(target=self.__download_update) download_thread.start() def load_thumbnail(self): set_result = self.__set_image("") if not set_result: tries = 10 performed_try = 0 while performed_try < tries: if self.game.image_url and self.game.id: # Download the thumbnail image_url = "https:{}_196.jpg".format(self.game.image_url) thumbnail = os.path.join(THUMBNAIL_DIR, "{}.jpg".format(self.game.id)) download = Download(image_url, thumbnail, DownloadType.THUMBNAIL, finish_func=self.__set_image) self.download_manager.download_now(download) set_result = True break performed_try += 1 time.sleep(1) return set_result def __set_image(self, save_location): set_result = False self.game.set_install_dir(self.config.install_dir) thumbnail_install_dir = os.path.join(self.game.install_dir, "thumbnail.jpg") if os.path.isfile(thumbnail_install_dir): GLib.idle_add(self.image.set_from_file, thumbnail_install_dir) set_result = True elif save_location and os.path.isfile(save_location): GLib.idle_add(self.image.set_from_file, save_location) # Copy image to if os.path.isdir(os.path.dirname(thumbnail_install_dir)): shutil.copy2(save_location, thumbnail_install_dir) set_result = True thumbnail_path = os.path.join(THUMBNAIL_DIR, "{}.jpg".format(self.game.id)) if os.path.isfile(thumbnail_path): GLib.idle_add(self.image.set_from_file, thumbnail_path) set_result = True return set_result def get_keep_executable_path(self): keep_path = "" if os.path.isdir(self.keep_path): for dir_content in os.listdir(self.keep_path): kept_file = os.path.join(self.keep_path, dir_content) if os.access(kept_file, os.X_OK) or os.path.splitext(kept_file)[-1] in [".exe", ".sh"]: keep_path = kept_file break return keep_path def get_download_info(self, platform="linux"): try: download_info = self.api.get_download_info(self.game, platform) result = True except NoDownloadLinkFound as e: logger.error("No download link found", exc_info=1) current_download_ids = self.config.current_downloads if current_download_ids: new_current_download_ids = set() for current_download_id in current_download_ids: if current_download_id != self.game.id: new_current_download_ids.add(current_download_id) self.config.current_downloads = list(new_current_download_ids) GLib.idle_add(self.parent.parent.show_error, _("Download error"), _("There was an error when trying to fetch the download link!\n{}".format(e))) download_info = False result = False return result, download_info def __download_game(self) -> None: finish_func = self.__install_game cancel_to_state = State.DOWNLOADABLE result, download_info = self.get_download_info() if result: result = self.__download(download_info, DownloadType.GAME, finish_func, cancel_to_state) if not result: GLib.idle_add(self.update_to_state, cancel_to_state) def __download(self, download_info, download_type, finish_func, cancel_to_state): # noqa: C901 download_success = True self.game.set_install_dir(self.config.install_dir) GLib.idle_add(self.update_to_state, State.QUEUED) # Need to update the config with DownloadType metadata current_download_ids = self.config.current_downloads if current_download_ids is None: current_download_ids = set() else: current_download_ids = set(current_download_ids) current_download_ids.add(self.game.id) self.config.current_downloads = list(current_download_ids) # Start the download for all files self.download_list = [] number_of_files = len(download_info['files']) total_file_size = 0 executable_path = None download_files = [] for key, file_info in enumerate(download_info['files']): try: download_url = self.api.get_real_download_link(file_info["downlink"]) except ValueError as e: logger.error("Error getting download URL from file_info downlink: %s", file_info["downlink"], exc_info=1) GLib.idle_add(self.parent.parent.show_error, _("Download error"), _(str(e))) download_success = False break info = self.api.get_download_file_info(file_info["downlink"]) total_file_size += info.size try: # Extract the filename from the download url (filename is between %2F and &token) filename = urllib.parse.unquote(re.search('%2F(((?!%2F).)*)&t', download_url).group(1)) except AttributeError: filename = "{}-{}.bin".format(self.game.get_stripped_name(), key) download_path = os.path.join(self.download_dir, filename) if key == 0: # If key = 0, denote the file as the executable's path executable_path = download_path if info.md5: self.game.md5sum[os.path.basename(download_path)] = info.md5 download = Download( url=download_url, save_location=download_path, download_type=DownloadType.GAME, finish_func=finish_func if download_path == executable_path else None, progress_func=self.set_progress, cancel_func=lambda: self.__cancel(to_state=cancel_to_state), number=number_of_files - key, out_of_amount=number_of_files, game=self.game ) download_files.insert(0, download) self.download_list.extend(download_files) if check_diskspace(total_file_size, self.game.install_dir): self.download_manager.download(download_files) ds_msg_title = "" ds_msg_text = "" else: ds_msg_title = "Download error" ds_msg_text = "Not enough disk space to install game." download_success = False if ds_msg_title: GLib.idle_add(self.parent.parent.show_error, _(ds_msg_title), _(ds_msg_text)) return download_success def __install_game(self, save_location): if self.game.id in self.config.current_downloads: self.config.current_downloads.remove(self.game.id) self.download_list = [] self.game.set_install_dir(self.config.install_dir) install_success = self.__install(save_location) if install_success: self.__check_for_dlc(self.api.get_info(self.game)) popup = Notify.Notification.new("Minigalaxy", _("Finished downloading and installing {}") .format(self.game.name), "dialog-information") popup.show() def __install(self, save_location, update=False, dlc_title=""): if update: processing_state = State.UPDATING failed_state = State.INSTALLED else: processing_state = State.INSTALLING failed_state = State.DOWNLOADABLE success_state = State.INSTALLED GLib.idle_add(self.update_to_state, processing_state) err_msg = install_game( self.game, save_location, self.config.lang, self.config.install_dir, self.config.keep_installers, self.config.create_applications_file ) if not err_msg: GLib.idle_add(self.update_to_state, success_state) install_success = True if dlc_title: self.game.set_dlc_info("version", self.api.get_version(self.game, dlc_name=dlc_title), dlc_title) else: self.game.set_info("version", self.api.get_version(self.game)) else: GLib.idle_add(self.parent.parent.show_error, _("Failed to install {}").format(self.game.name), err_msg) GLib.idle_add(self.update_to_state, failed_state) install_success = False return install_success def __cancel(self, to_state): self.download_list = [] GLib.idle_add(self.update_to_state, to_state) GLib.idle_add(self.reload_state) def __download_update(self) -> None: finish_func = self.__update cancel_to_state = State.UPDATABLE result, download_info = self.get_download_info(self.game.platform) if result: result = self.__download(download_info, DownloadType.GAME_UPDATE, finish_func, cancel_to_state) if not result: GLib.idle_add(self.update_to_state, cancel_to_state) def __check_for_update_dlc(self): if self.game.is_installed() and self.game.id and not self.offline: game_info = self.api.get_info(self.game) if self.game.get_info("check_for_updates") == "": self.game.set_info("check_for_updates", True) if self.game.get_info("check_for_updates"): game_version = self.api.get_version(self.game, gameinfo=game_info) update_available = self.game.is_update_available(game_version) if update_available: GLib.idle_add(self.update_to_state, State.UPDATABLE) self.__check_for_dlc(game_info) if self.offline: GLib.idle_add(self.menu_button_dlc.hide) def __update(self, save_location): install_success = self.__install(save_location, update=True) if install_success: if self.game.platform == "windows": self.image.set_tooltip_text("{} (Wine)".format(self.game.name)) else: self.image.set_tooltip_text(self.game.name) for dlc in self.game.dlcs: download_info = self.api.get_download_info(self.game, dlc_installers=dlc["downloads"]["installers"]) if self.game.is_update_available(version_from_api=download_info["version"], dlc_title=dlc["title"]): self.__download_dlc(dlc["downloads"]["installers"]) def __download_dlc(self, dlc_installers) -> None: def finish_func(save_location): self.__install_dlc(save_location, dlc_title=dlc_title) download_info = self.api.get_download_info(self.game, dlc_installers=dlc_installers) dlc_title = self.game.name for dlc in self.game.dlcs: if dlc["downloads"]["installers"] == dlc_installers: dlc_title = dlc["title"] cancel_to_state = State.INSTALLED result = self.__download(download_info, DownloadType.GAME_DLC, finish_func, cancel_to_state) if not result: GLib.idle_add(self.update_to_state, cancel_to_state) def __install_dlc(self, save_location, dlc_title): install_success = self.__install(save_location, dlc_title=dlc_title) if not install_success: GLib.idle_add(self.update_to_state, State.INSTALLED) self.__check_for_update_dlc() def __check_for_dlc(self, game_info): dlcs = game_info["expanded_dlcs"] for dlc in dlcs: if dlc["is_installable"] and dlc["id"] in self.parent.owned_products_ids: d_id = dlc["id"] d_installer = dlc["downloads"]["installers"] d_icon = dlc["images"]["sidebarIcon"] d_name = dlc["title"] GLib.idle_add(self.update_gtk_box_for_dlc, d_id, d_icon, d_name, d_installer) if dlc not in self.game.dlcs: self.game.dlcs.append(dlc) if self.game.dlcs: GLib.idle_add(self.menu_button_dlc.show) def update_gtk_box_for_dlc(self, dlc_id, icon, title, installer): if title not in self.dlc_dict: dlc_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) dlc_box.set_spacing(8) image = Gtk.Image() image.set_from_icon_name("media-optical", Gtk.IconSize.BUTTON) dlc_box.pack_start(image, False, True, 0) label = Gtk.Label(label=title, xalign=0) dlc_box.pack_start(label, True, True, 0) install_button = Gtk.Button() dlc_box.pack_start(install_button, False, True, 0) self.dlc_dict[title] = [install_button, image] self.dlc_dict[title][0].connect("clicked", self.__dlc_button_clicked, installer) self.dlc_horizontal_box.pack_start(dlc_box, False, True, 0) dlc_box.show_all() self.get_async_image_dlc_icon(dlc_id, image, icon, title) download_info = self.api.get_download_info(self.game, dlc_installers=installer) if self.game.is_update_available(version_from_api=download_info["version"], dlc_title=title): icon_name = "emblem-synchronizing" self.dlc_dict[title][0].set_sensitive(True) elif self.game.is_installed(dlc_title=title): icon_name = "object-select" self.dlc_dict[title][0].set_sensitive(False) else: icon_name = "document-save" if not self.download_list: self.dlc_dict[title][0].set_sensitive(True) install_button_image = Gtk.Image() install_button_image.set_from_icon_name(icon_name, Gtk.IconSize.BUTTON) self.dlc_dict[title][0].set_image(install_button_image) def __dlc_button_clicked(self, button, installer): button.set_sensitive(False) threading.Thread(target=self.__download_dlc, args=(installer,)).start() def get_async_image_dlc_icon(self, dlc_id, image, icon, title): dlc_icon_path = os.path.join(ICON_DIR, "{}.jpg".format(dlc_id)) if icon: if os.path.isfile(dlc_icon_path): GLib.idle_add(image.set_from_file, dlc_icon_path) else: url = "http:{}".format(icon) dlc_icon = os.path.join(ICON_DIR, "{}.jpg".format(dlc_id)) download = Download(url, dlc_icon) self.download_manager.download_now(download) GLib.idle_add(image.set_from_file, dlc_icon_path) def set_progress(self, percentage: int): if self.current_state in [State.QUEUED, State.INSTALLED]: GLib.idle_add(self.update_to_state, State.DOWNLOADING) if self.progress_bar: GLib.idle_add(self.progress_bar.set_fraction, percentage / 100) GLib.idle_add(self.progress_bar.set_tooltip_text, "{}%".format(percentage)) def __uninstall_game(self): GLib.idle_add(self.update_to_state, State.UNINSTALLING) uninstall_game(self.game) GLib.idle_add(self.update_to_state, State.DOWNLOADABLE) GLib.idle_add(self.reload_state) def __create_progress_bar(self) -> None: self.progress_bar = Gtk.ProgressBar() self.progress_bar.set_halign(Gtk.Align.START) self.progress_bar.set_size_request(196, -1) self.progress_bar.set_hexpand(False) self.progress_bar.set_vexpand(False) self.progress_bar.set_fraction(0.0) self.set_center_widget(self.progress_bar) def reload_state(self): self.game.set_install_dir(self.config.install_dir) dont_act_in_states = [State.QUEUED, State.DOWNLOADING, State.INSTALLING, State.UNINSTALLING, State.UPDATING, State.DOWNLOADING] if self.current_state in dont_act_in_states: return if self.game.is_installed(): self.update_to_state(State.INSTALLED) check_update_thread = threading.Thread(target=self.__check_for_update_dlc) check_update_thread.start() elif self.get_keep_executable_path(): self.update_to_state(State.INSTALLABLE) else: self.update_to_state(State.DOWNLOADABLE) def __state_downloadable(self): self.button.set_label(_("download")) self.button.set_tooltip_text(_("Download and install the game")) self.button.set_sensitive(True) self.image.set_sensitive(False) # The user must have the possibility to access # to the store button even if the game is not installed self.menu_button.show() self.menu_button.set_tooltip_text(_("Show game options menu")) self.menu_button_update.hide() self.menu_button_dlc.hide() self.menu_button_uninstall.hide() self.button_cancel.hide() self.game.install_dir = "" if self.progress_bar: self.progress_bar.destroy() def __state_installable(self): self.button.set_label(_("install")) self.button.set_tooltip_text(_("Install the game")) self.button.set_sensitive(True) self.image.set_sensitive(False) self.menu_button.hide() self.button_cancel.hide() self.game.install_dir = "" if self.progress_bar: self.progress_bar.destroy() def __state_queued(self): self.button.set_label(_("in queue…")) self.button.set_sensitive(False) self.image.set_sensitive(False) self.menu_button_uninstall.hide() self.menu_button_update.hide() self.button_cancel.show() self.__create_progress_bar() def __state_downloading(self): self.button.set_label(_("downloading…")) self.button.set_sensitive(False) self.image.set_sensitive(False) self.menu_button_uninstall.hide() self.menu_button_update.hide() self.button_cancel.show() if not self.progress_bar: self.__create_progress_bar() self.progress_bar.show_all() def __state_installing(self): self.button.set_label(_("installing…")) self.button.set_sensitive(False) self.image.set_sensitive(True) self.menu_button_uninstall.hide() self.menu_button_update.hide() self.button_cancel.hide() self.game.set_install_dir(self.config.install_dir) if self.progress_bar: self.progress_bar.destroy() self.parent.filter_library() def __state_installed(self): self.button.set_label(_("play")) self.button.set_tooltip_text(_("Launch the game")) self.button.get_style_context().add_class("suggested-action") self.button.set_sensitive(True) self.image.set_sensitive(True) self.menu_button.show() self.menu_button.set_tooltip_text(_("Show game options menu")) self.menu_button_uninstall.show() self.button_cancel.hide() self.game.set_install_dir(self.config.install_dir) if self.progress_bar: self.progress_bar.destroy() self.menu_button_update.hide() self.update_icon.hide() def __state_uninstalling(self): self.button.set_label(_("uninstalling…")) self.button.get_style_context().remove_class("suggested-action") self.button.set_sensitive(False) self.image.set_sensitive(False) self.menu_button.hide() self.button_cancel.hide() self.game.install_dir = "" self.parent.filter_library() def __state_updatable(self): self.update_icon.show() self.update_icon.set_from_icon_name("emblem-synchronizing", Gtk.IconSize.LARGE_TOOLBAR) self.button.set_label(_("play")) self.menu_button.show() tooltip_text = "{} (update{})".format(self.game.name, ", Wine" if self.game.platform == "windows" else "") self.image.set_tooltip_text(tooltip_text) self.menu_button_update.show() if self.game.platform == "windows": self.wine_icon.set_margin_left(22) def __state_updating(self): self.button.set_label(_("updating…")) STATE_UPDATE_HANDLERS = { State.DOWNLOADABLE: __state_downloadable, State.INSTALLABLE: __state_installable, State.QUEUED: __state_queued, State.DOWNLOADING: __state_downloading, State.INSTALLING: __state_installing, State.INSTALLED: __state_installed, State.UNINSTALLING: __state_uninstalling, State.UPDATABLE: __state_updatable, State.UPDATING: __state_updating, } def update_to_state(self, state): self.current_state = state if state in self.STATE_UPDATE_HANDLERS: self.STATE_UPDATE_HANDLERS[state](self) sharkwouter-minigalaxy-759382b/minigalaxy/ui/gtk.py000066400000000000000000000003201455252417400224370ustar00rootroot00000000000000import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Gdk, Gio, GLib, GdkPixbuf # noqa: E402,F401 gi.require_version('Notify', '0.7') from gi.repository import Notify # noqa: E402,F401 sharkwouter-minigalaxy-759382b/minigalaxy/ui/information.py000066400000000000000000000142631455252417400242120ustar00rootroot00000000000000import urllib import os import webbrowser from minigalaxy.api import Api from minigalaxy.paths import UI_DIR, THUMBNAIL_DIR, COVER_DIR from minigalaxy.translation import _ from minigalaxy.config import Config from minigalaxy.download import Download from minigalaxy.download_manager import DownloadManager from minigalaxy.ui.gtk import Gtk, GLib, Gio, GdkPixbuf @Gtk.Template.from_file(os.path.join(UI_DIR, "information.ui")) class Information(Gtk.Dialog): __gtype_name__ = "Information" gogBaseUrl = "https://www.gog.com" image = Gtk.Template.Child() button_information_ok = Gtk.Template.Child() button_information_support = Gtk.Template.Child() button_information_store = Gtk.Template.Child() button_information_forum = Gtk.Template.Child() button_information_gog_database = Gtk.Template.Child() button_information_pcgamingwiki = Gtk.Template.Child() label_game_description = Gtk.Template.Child() def __init__(self, parent, game, config: Config, api: Api, download_manager: DownloadManager): Gtk.Dialog.__init__(self, title=_("Information about {}").format(game.name), parent=parent.parent.parent, modal=True) self.parent = parent self.game = game self.config = config self.api = api self.download_manager = download_manager self.gamesdb_info = self.api.get_gamesdb_info(self.game) # Show the image self.load_thumbnail() self.load_description() # Center information window self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) @Gtk.Template.Callback("on_button_information_ok_clicked") def ok_pressed(self, button): self.destroy() @Gtk.Template.Callback("on_button_information_support_clicked") def on_menu_button_support(self, widget): try: webbrowser.open(self.api.get_info(self.game)['links']['support'], new=2) except webbrowser.Error: self.parent.parent.show_error( _("Couldn't open support page"), _("Please check your internet connection") ) @Gtk.Template.Callback("on_button_information_store_clicked") def on_menu_button_store(self, widget): try: webbrowser.open(self.gogBaseUrl + self.game.url) except webbrowser.Error: self.parent.parent.show_error( _("Couldn't open store page"), _("Please check your internet connection") ) @Gtk.Template.Callback("on_button_information_forum_clicked") def on_menu_button_forum(self, widget): try: webbrowser.open(self.api.get_info(self.game)['links']['forum'], new=2) except webbrowser.Error: self.parent.parent.show_error( _("Couldn't open forum page"), _("Please check your internet connection") ) @Gtk.Template.Callback("on_button_information_gog_database_clicked") def on_menu_button_gog_database(self, widget): try: webbrowser.open("https://www.gogdb.org/product/{}".format(self.game.id)) except webbrowser.Error: self.parent.parent.show_error( _("Couldn't open GOG Database page"), _("Please check your internet connection") ) @Gtk.Template.Callback("on_button_information_pcgamingwiki_clicked") def on_menu_button_pcgamingwiki(self, widget): try: webbrowser.open("https://pcgamingwiki.com/api/gog.php?page={}".format(self.game.id)) except webbrowser.Error: self.parent.parent.show_error( _("Couldn't open PCGamingWiki page"), _("Please check your internet connection") ) def load_thumbnail(self): if self.gamesdb_info["cover"]: cover_path = os.path.join(COVER_DIR, "{}.jpg".format(self.game.id)) if os.path.isfile(cover_path): pixbuf = GdkPixbuf.Pixbuf.new_from_file(cover_path) pixbuf = pixbuf.scale_simple(340, 480, GdkPixbuf.InterpType.BILINEAR) GLib.idle_add(self.image.set_from_pixbuf, pixbuf) else: url = "{}".format(self.gamesdb_info["cover"]) download = Download(url, cover_path) self.download_manager.download_now(download) response = urllib.request.urlopen(url) input_stream = Gio.MemoryInputStream.new_from_data(response.read(), None) pixbuf = GdkPixbuf.Pixbuf.new_from_stream(input_stream, None) pixbuf = pixbuf.scale_simple(340, 480, GdkPixbuf.InterpType.BILINEAR) GLib.idle_add(self.image.set_from_pixbuf, pixbuf) else: thumbnail_path = os.path.join(THUMBNAIL_DIR, "{}.jpg".format(self.game.id)) if not os.path.isfile(thumbnail_path) and self.game.is_installed: thumbnail_path = os.path.join(self.game.install_dir, "thumbnail.jpg") GLib.idle_add(self.image.set_from_file, thumbnail_path) def load_description(self): description = "" lang = self.config.lang if self.gamesdb_info["summary"]: desc_lang = "*" for summary_key in self.gamesdb_info["summary"].keys(): if lang in summary_key: desc_lang = summary_key description_len = 470 if len(self.gamesdb_info["summary"][desc_lang]) > description_len: description = "{}...".format(self.gamesdb_info["summary"][desc_lang][:description_len]) else: description = self.gamesdb_info["summary"][desc_lang] if "*" in self.gamesdb_info["genre"]: genre = self.gamesdb_info["genre"]["*"] else: genre = _("unknown") for genre_key, genre_value in self.gamesdb_info["genre"].items(): if lang in genre_key: genre = genre_value description = "{}: {}\n{}".format(_("Genre"), genre, description) if self.game.is_installed(): description = "{}: {}\n{}".format(_("Version"), self.game.get_info("version"), description) GLib.idle_add(self.label_game_description.set_text, description) sharkwouter-minigalaxy-759382b/minigalaxy/ui/library.py000066400000000000000000000217721455252417400233340ustar00rootroot00000000000000import os import re import json import threading from typing import List from minigalaxy.download_manager import DownloadManager from minigalaxy.entity.state import State from minigalaxy.logger import logger from minigalaxy.paths import UI_DIR, CATEGORIES_FILE_PATH from minigalaxy.api import Api from minigalaxy.game import Game from minigalaxy.ui.categoryfilters import CategoryFilters from minigalaxy.ui.gametile import GameTile from minigalaxy.ui.gametilelist import GameTileList from minigalaxy.ui.gtk import Gtk, GLib from minigalaxy.config import Config from minigalaxy.translation import _ @Gtk.Template.from_file(os.path.join(UI_DIR, "library.ui")) class Library(Gtk.Viewport): __gtype_name__ = "Library" flowbox = Gtk.Template.Child() def __init__(self, parent, config: Config, api: Api, download_manager: DownloadManager): Gtk.Viewport.__init__(self) self.parent = parent self.config = config self.api = api self.download_manager = download_manager self.show_installed_only = self.config.installed_filter self.search_string = "" self.offline = False self.games = [] self.owned_products_ids = [] self._queue = [] self.category_filters = [] def _debounce(self, thunk): if thunk not in self._queue: self._queue.append(thunk) GLib.idle_add(self._run_queue) def _run_queue(self): queue, self._queue = self._queue, [] for thunk in queue: GLib.idle_add(thunk) def reset(self): self.games = [] for child in self.flowbox.get_children(): self.flowbox.remove(child) self.flowbox.show_all() self.update_library() def update_library(self) -> None: library_update_thread = threading.Thread(target=self.__update_library) library_update_thread.daemon = True library_update_thread.start() def __update_library(self): GLib.idle_add(self.__load_tile_states) self.owned_products_ids = self.api.get_owned_products_ids() # Get already installed games first self.games = self.__get_installed_games() GLib.idle_add(self.__create_gametiles) # Get games from the API self.__add_games_from_api() GLib.idle_add(self.__create_gametiles) GLib.idle_add(self.filter_library) def __load_tile_states(self): for child in self.flowbox.get_children(): tile = child.get_children()[0] tile.reload_state() def filter_library(self, widget: Gtk.Widget = None): if isinstance(widget, Gtk.Switch): self.show_installed_only = widget.get_active() elif isinstance(widget, Gtk.SearchEntry): self.search_string = widget.get_text() elif isinstance(widget, Gtk.Dialog) and isinstance(widget, CategoryFilters): # filter all true category-bool pairs and then extract category names self.category_filters = [j[0] for j in filter(lambda i: i[1], widget.filter_dict.items())] self.flowbox.set_filter_func(self.__filter_library_func) def __filter_library_func(self, child): tile = child.get_children()[0] if self.search_string.lower() not in str(tile).lower(): return False if self.show_installed_only: if tile.current_state in [State.DOWNLOADABLE, State.INSTALLABLE]: return False if not self.config.show_hidden_games and tile.game.get_info("hide_game"): return False if len(self.category_filters) > 0: if tile.game.category not in self.category_filters: return False return True def sort_library(self): self.flowbox.set_sort_func(self.__sort_library_func) def __sort_library_func(self, child1, child2): tile1 = child1.get_children()[0].game tile2 = child2.get_children()[0].game return tile2 < tile1 def __create_gametiles(self) -> None: games_with_tiles = [] for child in self.flowbox.get_children(): tile = child.get_children()[0] if tile.game in self.games: games_with_tiles.append(tile.game) for game in self.games: if game not in games_with_tiles: self.__add_gametile(game) def __add_gametile(self, game): view = self.config.view if view == "grid": self.flowbox.add(GameTile(self, game, self.config, self.api, self.download_manager)) elif view == "list": self.flowbox.add(GameTileList(self, game, self.config, self.api, self.download_manager)) self._debounce(self.sort_library) self._debounce(self.flowbox.show_all) def __get_installed_games(self) -> List[Game]: # Make sure the install directory exists library_dir = self.config.install_dir if not os.path.exists(library_dir): os.makedirs(library_dir, mode=0o755) directories = os.listdir(library_dir) games = [] game_categories_dict = read_game_categories_file(CATEGORIES_FILE_PATH) for directory in directories: full_path = os.path.join(self.config.install_dir, directory) # Only scan directories if not os.path.isdir(full_path): continue # Make sure the gameinfo file exists gameinfo = os.path.join(full_path, "gameinfo") if os.path.isfile(gameinfo): with open(gameinfo, 'r') as file: name = file.readline().strip() version = file.readline().strip() # noqa: F841 version_dev = file.readline().strip() # noqa: F841 language = file.readline().strip() # noqa: F841 game_id = file.readline().strip() if not game_id: game_id = 0 else: game_id = int(game_id) category = game_categories_dict.get(name, "") games.append(Game(name=name, game_id=game_id, install_dir=full_path, category=category)) else: games.extend(get_installed_windows_games(full_path, game_categories_dict)) return games def __add_games_from_api(self): retrieved_games, err_msg = self.api.get_library() if not err_msg: self.offline = False else: self.offline = True logger.info("Client is offline, showing installed games only") GLib.idle_add(self.parent.show_error, _("Failed to retrieve library"), _(err_msg)) game_category_dict = {} for game in retrieved_games: if game not in self.games: self.games.append(game) elif self.games[self.games.index(game)].id == 0 or self.games[self.games.index(game)].name != game.name: self.games[self.games.index(game)].id = game.id self.games[self.games.index(game)].name = game.name self.games[self.games.index(game)].image_url = game.image_url self.games[self.games.index(game)].url = game.url self.games[self.games.index(game)].category = game.category if len(game.category) > 0: # exclude games without set category game_category_dict[game.name] = game.category update_game_categories_file(game_category_dict, CATEGORIES_FILE_PATH) def get_installed_windows_games(full_path, game_categories_dict=None): games = [] game_files = os.listdir(full_path) for file in game_files: if re.match(r'^goggame-[0-9]*\.info$', file): with open(os.path.join(full_path, file), 'rb') as info_file: info = json.loads(info_file.read().decode('utf-8-sig')) game = Game( name=info["name"], game_id=int(info["gameId"]), install_dir=full_path, platform="windows", category=(game_categories_dict or {}).get(info["name"], "") ) games.append(game) return games def update_game_categories_file(game_category_dict, categories_file_path): if len(game_category_dict) == 0: return if not os.path.exists(categories_file_path): # if file does not exist, create it and write dict with open(categories_file_path, 'wt') as fd: json.dump(game_category_dict, fd) else: with open(categories_file_path, 'r+t') as fd: # if file exists, write dict only if not equal to file data cached_game_category_dict = json.load(fd) if game_category_dict != cached_game_category_dict: fd.seek(os.SEEK_SET) fd.truncate(0) json.dump(game_category_dict, fd) def read_game_categories_file(categories_file_path): cached_game_category_dict = {} if os.path.exists(categories_file_path): with open(categories_file_path, 'rt') as fd: cached_game_category_dict = json.load(fd) return cached_game_category_dict sharkwouter-minigalaxy-759382b/minigalaxy/ui/login.py000066400000000000000000000051301455252417400227660ustar00rootroot00000000000000import os from urllib.parse import urlparse, parse_qsl from minigalaxy.translation import _ from minigalaxy.paths import UI_DIR from minigalaxy.ui.gtk import Gtk from minigalaxy.ui.webkit import WebKit2 @Gtk.Template.from_file(os.path.join(UI_DIR, "login.ui")) class Login(Gtk.Dialog): __gtype_name__ = "Login" box = Gtk.Template.Child() redirect_url = None result = None def __init__(self, login_url=None, redirect_url=None, parent=None): Gtk.Dialog.__init__(self, title=_("Login"), parent=parent, flags=0, buttons=()) self.redirect_url = redirect_url # https://stackoverflow.com/questions/9147875/webview-dont-display-javascript-windows-open settings = WebKit2.Settings.new() settings.props.javascript_can_open_windows_automatically = True webview = WebKit2.WebView.new_with_settings(settings) webview.load_uri(login_url) webview.connect('load-changed', self.on_navigation) webview.connect('create', self.on_create) self.box.pack_start(webview, True, True, 0) self.show_all() # Check if the login has completed when the page is changed. Set the result to the code value found within the url def on_navigation(self, widget, load_event): if load_event == WebKit2.LoadEvent.FINISHED: uri = widget.get_uri() if uri.startswith(self.redirect_url): self.result = self.__get_code_from_url(uri) self.hide() # Create any pop-up windows during authentication def on_create(self, widget, action): popup = Gtk.Dialog(title=_("Facebook Login"), parent=self, flags=0, buttons=()) webview = WebKit2.WebView.new_with_related_view(widget) webview.load_uri(action.get_request().get_uri()) webview.__dict__['popup'] = popup webview.connect('close', self.on_close_popup) popup.get_content_area().pack_start(webview, True, True, 0) popup.set_size_request(400, 600) popup.set_modal(True) popup.show_all() return webview # When a pop up is closed (by Javascript), close the Gtk window too def on_close_popup(self, widget): if 'popup' in widget.__dict__: widget.__dict__['popup'].hide() # Return the code when can be used by the API to authenticate def get_result(self): return self.result # Get the code from the url returned by GOG when logging in has succeeded def __get_code_from_url(self, url: str): parsed_url = urlparse(url) input_params = dict(parse_qsl(parsed_url.query)) return input_params.get('code') sharkwouter-minigalaxy-759382b/minigalaxy/ui/preferences.py000066400000000000000000000216431455252417400241660ustar00rootroot00000000000000import os import locale import shutil from minigalaxy.translation import _ from minigalaxy.paths import UI_DIR from minigalaxy.constants import SUPPORTED_DOWNLOAD_LANGUAGES, SUPPORTED_LOCALES, VIEWS from minigalaxy.download_manager import DownloadManager from minigalaxy.ui.gtk import Gtk from minigalaxy.config import Config @Gtk.Template.from_file(os.path.join(UI_DIR, "preferences.ui")) class Preferences(Gtk.Dialog): __gtype_name__ = "Preferences" combobox_program_language = Gtk.Template.Child() combobox_language = Gtk.Template.Child() combobox_view = Gtk.Template.Child() button_file_chooser = Gtk.Template.Child() label_keep_installers = Gtk.Template.Child() switch_keep_installers = Gtk.Template.Child() switch_stay_logged_in = Gtk.Template.Child() switch_show_hidden_games = Gtk.Template.Child() switch_show_windows_games = Gtk.Template.Child() switch_create_applications_file = Gtk.Template.Child() switch_use_dark_theme = Gtk.Template.Child() button_cancel = Gtk.Template.Child() button_save = Gtk.Template.Child() def __init__(self, parent, config: Config, download_manager: DownloadManager): Gtk.Dialog.__init__(self, title=_("Preferences"), parent=parent, modal=True) self.parent = parent self.config = config self.download_manager = download_manager self.__set_locale_list() self.__set_language_list() self.__set_view_list() self.button_file_chooser.set_filename(self.config.install_dir) self.switch_keep_installers.set_active(self.config.keep_installers) self.switch_stay_logged_in.set_active(self.config.stay_logged_in) self.switch_use_dark_theme.set_active(self.config.use_dark_theme) self.switch_show_hidden_games.set_active(self.config.show_hidden_games) self.switch_show_windows_games.set_active(self.config.show_windows_games) self.switch_create_applications_file.set_active(self.config.create_applications_file) # Set tooltip for keep installers label installer_dir = os.path.join(self.button_file_chooser.get_filename(), "installer") self.label_keep_installers.set_tooltip_text( _("Keep installers after downloading a game.\nInstallers are stored in: {}").format(installer_dir) ) def __set_locale_list(self) -> None: locales = Gtk.ListStore(str, str) for local in SUPPORTED_LOCALES: locales.append(local) self.combobox_program_language.set_model(locales) self.combobox_program_language.set_entry_text_column(1) self.renderer_text = Gtk.CellRendererText() self.combobox_program_language.pack_start(self.renderer_text, False) self.combobox_program_language.add_attribute(self.renderer_text, "text", 1) # Set the active option current_locale = self.config.locale default_locale = locale.getdefaultlocale() if current_locale is None: locale.setlocale(locale.LC_ALL, default_locale) for key in range(len(locales)): if locales[key][:1][0] == current_locale: self.combobox_program_language.set_active(key) break def __set_language_list(self) -> None: languages = Gtk.ListStore(str, str) for lang in SUPPORTED_DOWNLOAD_LANGUAGES: languages.append(lang) self.combobox_language.set_model(languages) self.combobox_language.set_entry_text_column(1) self.renderer_text = Gtk.CellRendererText() self.combobox_language.pack_start(self.renderer_text, False) self.combobox_language.add_attribute(self.renderer_text, "text", 1) # Set the active option current_lang = self.config.lang for key in range(len(languages)): if languages[key][:1][0] == current_lang: self.combobox_language.set_active(key) break def __set_view_list(self) -> None: views = Gtk.ListStore(str, str) for view in VIEWS: views.append(view) self.combobox_view.set_model(views) self.combobox_view.set_entry_text_column(1) self.renderer_text = Gtk.CellRendererText() self.combobox_view.pack_start(self.renderer_text, False) self.combobox_view.add_attribute(self.renderer_text, "text", 1) # Set the active option current_view = self.config.view for key in range(len(views)): if views[key][:1][0] == current_view: self.combobox_view.set_active(key) break def __save_locale_choice(self) -> None: new_locale = self.combobox_program_language.get_active_iter() if new_locale is not None: model = self.combobox_program_language.get_model() locale_choice = model[new_locale][-2] if locale_choice == '': default_locale = locale.getdefaultlocale()[0] locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) self.config.locale = locale_choice else: try: locale.setlocale(locale.LC_ALL, (locale_choice, 'UTF-8')) self.config.locale = locale_choice except locale.Error: self.parent.show_error(_("Failed to change program language. Make sure locale is generated on " "your system.")) def __save_language_choice(self) -> None: lang_choice = self.combobox_language.get_active_iter() if lang_choice is not None: model = self.combobox_language.get_model() lang, _ = model[lang_choice][:2] self.config.lang = lang def __save_view_choice(self) -> None: view_choice = self.combobox_view.get_active_iter() if view_choice is not None: model = self.combobox_view.get_model() view, _ = model[view_choice][:2] if view != self.config.view: self.parent.reset_library() self.config.view = view def __save_theme_choice(self) -> None: settings = Gtk.Settings.get_default() self.config.use_dark_theme = self.switch_use_dark_theme.get_active() if self.config.use_dark_theme is True: settings.set_property("gtk-application-prefer-dark-theme", True) else: settings.set_property("gtk-application-prefer-dark-theme", False) def __save_install_dir_choice(self) -> bool: choice = self.button_file_chooser.get_filename() old_dir = self.config.install_dir if choice == old_dir: return True if not os.path.exists(choice): try: os.makedirs(choice, mode=0o755) except Exception: return False else: write_test_file = os.path.join(choice, "write_test.txt") try: with open(write_test_file, "w") as file: file.write("test") file.close() os.remove(write_test_file) except Exception: return False # Remove the old directory if it is empty try: os.rmdir(old_dir) except OSError: pass self.config.install_dir = choice return True @Gtk.Template.Callback("on_button_save_clicked") def save_pressed(self, button): self.__save_locale_choice() self.__save_language_choice() self.__save_view_choice() self.__save_theme_choice() self.config.keep_installers = self.switch_keep_installers.get_active() self.config.stay_logged_in = self.switch_stay_logged_in.get_active() self.config.show_hidden_games = self.switch_show_hidden_games.get_active() self.config.create_applications_file = self.switch_create_applications_file.get_active() self.parent.library.filter_library() if self.switch_show_windows_games.get_active() != self.config.show_windows_games: if self.switch_show_windows_games.get_active() and not shutil.which("wine"): self.parent.show_error(_("Wine wasn't found. Showing Windows games cannot be enabled.")) self.config.show_windows_games = False else: self.config.show_windows_games = self.switch_show_windows_games.get_active() self.parent.reset_library() # Only change the install_dir is it was actually changed if self.button_file_chooser.get_filename() != self.config.install_dir: if self.__save_install_dir_choice(): self.download_manager.cancel_all_downloads() self.parent.reset_library() else: self.parent.show_error(_("{} isn't a usable path").format(self.button_file_chooser.get_filename())) self.destroy() @Gtk.Template.Callback("on_button_cancel_clicked") def cancel_pressed(self, button): self.response(Gtk.ResponseType.CANCEL) self.destroy() sharkwouter-minigalaxy-759382b/minigalaxy/ui/properties.py000066400000000000000000000153471455252417400240650ustar00rootroot00000000000000import os import shutil import subprocess from minigalaxy.paths import UI_DIR from minigalaxy.translation import _ from minigalaxy.launcher import config_game, regedit_game, winetricks_game from minigalaxy.ui.gtk import Gtk @Gtk.Template.from_file(os.path.join(UI_DIR, "properties.ui")) class Properties(Gtk.Dialog): __gtype_name__ = "Properties" gogBaseUrl = "https://www.gog.com" button_properties_regedit = Gtk.Template.Child() button_properties_winecfg = Gtk.Template.Child() button_properties_winetricks = Gtk.Template.Child() button_properties_open_files = Gtk.Template.Child() switch_properties_check_for_updates = Gtk.Template.Child() button_properties_wine = Gtk.Template.Child() button_properties_reset = Gtk.Template.Child() switch_properties_show_fps = Gtk.Template.Child() switch_properties_hide_game = Gtk.Template.Child() switch_properties_use_gamemode = Gtk.Template.Child() switch_properties_use_mangohud = Gtk.Template.Child() entry_properties_variable = Gtk.Template.Child() entry_properties_command = Gtk.Template.Child() button_properties_cancel = Gtk.Template.Child() button_properties_ok = Gtk.Template.Child() label_wine_custom = Gtk.Template.Child() def __init__(self, parent, game, api): Gtk.Dialog.__init__(self, title=_("Properties of {}").format(game.name), parent=parent.parent.parent, modal=True) self.parent = parent self.game = game self.api = api self.gamesdb_info = self.api.get_gamesdb_info(self.game) # Disable/Enable buttons self.button_sensitive(game) # Keep switch check for updates disabled/enabled self.switch_properties_check_for_updates.set_active(self.game.get_info("check_for_updates")) # Retrieve custom wine path each time Properties is open if self.game.get_info("custom_wine"): self.button_properties_wine.set_filename(self.game.get_info("custom_wine")) elif shutil.which("wine"): self.button_properties_wine.set_filename(shutil.which("wine")) # Keep switch FPS disabled/enabled self.switch_properties_show_fps.set_active(self.game.get_info("show_fps")) # Keep switch game shown/hidden self.switch_properties_hide_game.set_active(self.game.get_info("hide_game")) # Keep switch use GameMode disabled/enabled self.switch_properties_use_gamemode.set_active(self.game.get_info("use_gamemode")) # Keep switch use MangoHud disabled/enabled self.switch_properties_use_mangohud.set_active(self.game.get_info("use_mangohud")) # Retrieve variable & command each time properties is open self.entry_properties_variable.set_text(self.game.get_info("variable")) self.entry_properties_command.set_text(self.game.get_info("command")) # Center properties window self.set_position(Gtk.WindowPosition.CENTER_ALWAYS) @Gtk.Template.Callback("on_button_properties_cancel_clicked") def cancel_pressed(self, button): self.destroy() @Gtk.Template.Callback("on_button_properties_ok_clicked") def ok_pressed(self, button): if self.game.is_installed(): self.game.set_info("check_for_updates", self.switch_properties_check_for_updates.get_active()) self.game.set_info("show_fps", self.switch_properties_show_fps.get_active()) if self.switch_properties_use_gamemode.get_active() and not shutil.which("gamemoderun"): self.parent.parent.parent.show_error(_("GameMode wasn't found. Using GameMode cannot be enabled.")) self.game.set_info("use_gamemode", False) else: self.game.set_info("use_gamemode", self.switch_properties_use_gamemode.get_active()) if self.switch_properties_use_mangohud.get_active() and not shutil.which("mangohud"): self.parent.parent.parent.show_error(_("MangoHud wasn't found. Using MangoHud cannot be enabled.")) self.game.set_info("use_mangohud", False) else: self.game.set_info("use_mangohud", self.switch_properties_use_mangohud.get_active()) self.game.set_info("variable", str(self.entry_properties_variable.get_text())) self.game.set_info("command", str(self.entry_properties_command.get_text())) self.game.set_info("hide_game", self.switch_properties_hide_game.get_active()) self.game.set_info("custom_wine", str(self.button_properties_wine.get_filename())) self.parent.parent.filter_library() self.destroy() @Gtk.Template.Callback("on_button_properties_regedit_clicked") def on_menu_button_regedit(self, widget): regedit_game(self.game) @Gtk.Template.Callback("on_button_properties_reset_clicked") def on_menu_button_reset(self, widget): self.button_properties_wine.select_filename(shutil.which("wine")) @Gtk.Template.Callback("on_button_properties_winecfg_clicked") def on_menu_button_winecfg(self, widget): config_game(self.game) @Gtk.Template.Callback("on_button_properties_winetricks_clicked") def on_menu_button_winetricks(self, widget): if not shutil.which("winetricks"): self.parent.parent.parent.show_error(_("Winetricks wasn't found and cannot be used.")) else: winetricks_game(self.game) @Gtk.Template.Callback("on_button_properties_open_files_clicked") def on_menu_button_open_files(self, widget): subprocess.call(["xdg-open", self.game.install_dir]) def button_sensitive(self, game): if not game.is_installed(): self.button_properties_open_files.set_sensitive(False) self.button_properties_wine.set_sensitive(False) self.button_properties_reset.set_sensitive(False) self.button_properties_regedit.set_sensitive(False) self.button_properties_winecfg.set_sensitive(False) self.button_properties_winetricks.set_sensitive(False) self.button_properties_open_files.set_sensitive(False) self.switch_properties_check_for_updates.set_sensitive(False) self.switch_properties_show_fps.set_sensitive(False) self.switch_properties_use_gamemode.set_sensitive(False) self.switch_properties_use_mangohud.set_sensitive(False) self.entry_properties_variable.set_sensitive(False) self.entry_properties_command.set_sensitive(False) if game.platform == 'linux': self.button_properties_regedit.hide() self.button_properties_winecfg.hide() self.button_properties_winetricks.hide() self.button_properties_wine.hide() self.button_properties_reset.hide() self.label_wine_custom.hide() sharkwouter-minigalaxy-759382b/minigalaxy/ui/webkit.py000066400000000000000000000002521455252417400231430ustar00rootroot00000000000000import gi try: gi.require_version('WebKit2', '4.1') except ValueError: gi.require_version('WebKit2', '4.0') from gi.repository import WebKit2 # noqa: E402,F401 sharkwouter-minigalaxy-759382b/minigalaxy/ui/window.py000066400000000000000000000176531455252417400232020ustar00rootroot00000000000000import os import locale from minigalaxy.download_manager import DownloadManager from minigalaxy.logger import logger from minigalaxy.ui.categoryfilters import CategoryFilters from minigalaxy.ui.login import Login from minigalaxy.ui.preferences import Preferences from minigalaxy.ui.about import About from minigalaxy.api import Api from minigalaxy.paths import UI_DIR, LOGO_IMAGE_PATH, THUMBNAIL_DIR, COVER_DIR, ICON_DIR from minigalaxy.translation import _ from minigalaxy.ui.library import Library from minigalaxy.ui.gtk import Gtk, Gdk, GdkPixbuf, Notify from minigalaxy.config import Config @Gtk.Template.from_file(os.path.join(UI_DIR, "application.ui")) class Window(Gtk.ApplicationWindow): __gtype_name__ = "Window" HeaderBar = Gtk.Template.Child() header_sync = Gtk.Template.Child() header_installed = Gtk.Template.Child() header_search = Gtk.Template.Child() menu_about = Gtk.Template.Child() menu_preferences = Gtk.Template.Child() menu_logout = Gtk.Template.Child() window_library = Gtk.Template.Child() def __init__(self, config: Config, api: 'Api', download_manager: DownloadManager, name="Minigalaxy"): current_locale = config.locale default_locale = locale.getdefaultlocale()[0] if current_locale == '': locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) else: try: locale.setlocale(locale.LC_ALL, (current_locale, 'UTF-8')) except NameError: locale.setlocale(locale.LC_ALL, (default_locale, 'UTF-8')) Gtk.ApplicationWindow.__init__(self, title=name) self.api = api self.config = config self.download_manager = download_manager self.search_string = "" self.offline = False # Initialize notifications module Notify.init("minigalaxy") # Set library self.library = Library(self, config, api, download_manager) self.window_library.add(self.library) self.header_installed.set_active(self.config.installed_filter) # Set the icon icon = GdkPixbuf.Pixbuf.new_from_file(LOGO_IMAGE_PATH) self.set_default_icon_list([icon]) # Set theme settings = Gtk.Settings.get_default() if self.config.use_dark_theme is True: settings.set_property("gtk-application-prefer-dark-theme", True) else: settings.set_property("gtk-application-prefer-dark-theme", False) # Show the window if self.config.keep_window_maximized: self.maximize() self.show_all() self.make_directories() # Interact with the API logger.debug("Checking API connectivity...") self.offline = not self.api.can_connect() logger.debug("Done checking API connectivity, status: %s", "offline" if self.offline else "online") if not self.offline: try: logger.debug("Authenticating...") self.__authenticate() logger.debug("Authenticated as: %s", self.api.get_user_info()) self.HeaderBar.set_subtitle(self.api.get_user_info()) except Exception: logger.warning("Starting in offline mode after receiving exception", exc_info=1) self.offline = True self.sync_library() @Gtk.Template.Callback("filter_library") def filter_library(self, switch, _=""): self.library.filter_library(switch) if switch == self.header_installed: self.config.installed_filter = switch.get_active() @Gtk.Template.Callback("on_menu_preferences_clicked") def show_preferences(self, button): preferences_window = Preferences(parent=self, config=self.config, download_manager=self.download_manager) preferences_window.run() preferences_window.destroy() @Gtk.Template.Callback("on_menu_about_clicked") def show_about(self, button): about_window = About(self) about_window.run() about_window.destroy() @Gtk.Template.Callback("on_menu_category_filter_clicked") def show_categories(self, button): category_filters_window = CategoryFilters(self, self.library) category_filters_window.run() category_filters_window.destroy() @Gtk.Template.Callback("on_menu_logout_clicked") def logout(self, button): question = _("Are you sure you want to log out of GOG?") if self.show_question(question): # Unset everything which is specific to this user self.HeaderBar.set_subtitle("") self.config.username = "" self.config.refresh_token = "" self.hide() # Show the login screen self.__authenticate() self.HeaderBar.set_subtitle(self.api.get_user_info()) self.sync_library() self.show_all() @Gtk.Template.Callback("on_window_state_event") def on_window_state_event(self, widget, event): if event.new_window_state & Gdk.WindowState.MAXIMIZED: self.config.keep_window_maximized = True else: self.config.keep_window_maximized = False @Gtk.Template.Callback("on_header_sync_clicked") def sync_library(self, _=""): if self.library.offline: self.__authenticate() self.library.update_library() def make_directories(self): # Create the thumbnails directory if not os.path.exists(THUMBNAIL_DIR): os.makedirs(THUMBNAIL_DIR, mode=0o755) # Create the covers directory if not os.path.exists(COVER_DIR): os.makedirs(COVER_DIR, mode=0o755) # Create the icons directory if not os.path.exists(ICON_DIR): os.makedirs(ICON_DIR, mode=0o755) def reset_library(self): self.library.reset() def update_library(self): self.library.update_library() def show_error(self, text, secondary_text=""): dialog = Gtk.MessageDialog( parent=self, modal=True, destroy_with_parent=True, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=text ) if secondary_text: dialog.format_secondary_text(secondary_text) dialog.set_position(Gtk.WindowPosition.CENTER_ALWAYS) dialog.run() dialog.destroy() def show_question(self, text, secondary_text=""): dialog = Gtk.MessageDialog( parent=self, flags=Gtk.DialogFlags.MODAL, message_type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK_CANCEL, message_format=text ) if secondary_text: dialog.format_secondary_text(secondary_text) response = dialog.run() dialog.destroy() return response == Gtk.ResponseType.OK """ The API remembers the authentication token and uses it The token is not valid for a long time """ def __authenticate(self): url = None if self.config.stay_logged_in: token = self.config.refresh_token else: self.config.username = "" self.config.refresh_token = "" token = None # Make sure there is an internet connection if not self.api.can_connect(): return authenticated = self.api.authenticate(refresh_token=token, login_code=url) while not authenticated: login_url = self.api.get_login_url() redirect_url = self.api.get_redirect_url() login = Login(login_url=login_url, redirect_url=redirect_url, parent=self) response = login.run() login.hide() if response == Gtk.ResponseType.DELETE_EVENT: Gtk.main_quit() exit(0) if response == Gtk.ResponseType.NONE: result = login.get_result() authenticated = self.api.authenticate(login_code=result) self.config.refresh_token = authenticated sharkwouter-minigalaxy-759382b/minigalaxy/version.py000066400000000000000000000000221455252417400227210ustar00rootroot00000000000000VERSION = "1.2.6" sharkwouter-minigalaxy-759382b/pyproject.toml000066400000000000000000000004751455252417400214500ustar00rootroot00000000000000[project] name = "minigalaxy" description = "A simple GOG Linux client" version = "1.2.6" authors = [ { name = "Wouter Wijsman", email = "wwijsman@live.nl" } ] dependencies = [ "PyGObject", "requests" ] [build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta:__legacy__" sharkwouter-minigalaxy-759382b/requirements-testing.txt000066400000000000000000000000441455252417400234630ustar00rootroot00000000000000requests flake8 simplejson coverage sharkwouter-minigalaxy-759382b/requirements.txt000066400000000000000000000000221455252417400220040ustar00rootroot00000000000000requests PyGObjectsharkwouter-minigalaxy-759382b/screenshot.jpg000066400000000000000000010654621455252417400214230ustar00rootroot00000000000000JFIFC  !"$"$COx" k  !1AQ"#aq2TU5BRs$3br%4Stu &7CDVde'6EFc8(fvC !1AQSaq"2#BRr34b$C5D ?ї EoW2 tHDHSiJKk `Sm)#!N{APYZ(>'=Tuw9Tj oBJJm`%H#"C5>G* %V(⸺ h>wfĉ R H֯rQ44K!?!z3FJ?d<&OY7 T<8CS~^wO'^',2}6Uϑ>J>S OzhBw2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MFhBq2}',|њxL|;Gÿ44f'',2}7 DŽÿ4xL|;MXGH|].*bawf k!qM/ƇNjJ/ ΕVwf k&wf ihN<&OYdo3B h>wfЄd<&OY4!8>wf ihN<&OYdo3B h>wfЄd<&OY4!8>wf ihN<&OYdo3B h>wfЄd<&OY4!8>wf ihN<&OYdo3B h>wfЄd<&OY4!8>wf ihN<&OYdo3BȖRے\#FW'=U5}B(: U.'=Tuw9mYr\??$窎'=Um"\??$窎'=Um"\??$窎'=Um"\??$窎'=Um"\??$窎'=Um"\??$窎'=Um"\??$窎'=Um"\??$窎'=Um"ꢧe1+HM|wf!.6֐ABZ`k[|FjA&55'8Ԅ-Cs~IUM HB@ HWU!Aw9꠽pd窧5Qd(}Id:A',sp.A?qu8>wf ihN<&OYdo3B h>wfЄd<&OY4!8>wf ihN<&OYdo3B h>wfЅG鳣;I+*Tm aWvR*(嗄dVA¬:+'+1w\laCcs$պ:+|n6<- g."*:&A"#IQ*8v.U7egn80]B9 q OHpZ" Pys[`gLls5`U_R $u,oRz³:N;5T*C-@Ω=TF5YnZr;$l3`YrzI$] j2ՆT~!{I'%$>BKW$;G3#좀, Nv3ޛP;>'(?C[ן:5pRqnm<[WAmqEB\6AA2T@+vqC|E1B%ɜJN0$ c$;ÓnPn}Җւi՝Yiݶ[nͳ&%nDQZaIБ^(oFH/'ű,AmqKJJΐRX$fܮ~g:Tuen6wJp[}%Ǹ{iP#ݜ-ednqsp^vi]bUխzxXXcIs%6J9L ~ȣº}^z{j՜ooHqg[T3kE,%OLkmwl)u*za.uڟz![S*ZPX IQ'*#'d\%1fλ.B* }Hk!X'r3̍w5vϞhquzϞh^>z3BϞЅ>z394!zϞh^>z3BϞЅ>z394!zϞh^>z3BϞЅ>z394!zϞh^>z3BϞЅ>z394!zϞh^>z3BϞЅ>z394!zϞh^>z3BϞЅ>z394!zϞh^>z3BϞЅ>z394!zϞh^>z3Bꎎb_ nЂR[BU4D<'EeIN/l^DqĎC_};{V63Nnqek-xV ,Cd;!(paSsNEc "*)H#9p ܮX,γk0j1qKd#U{ ;įO0b-ᦞuJv}[,[Jzp 4ĀJ*ƠYC)ٕ AK}i/F,~FKq4[_Hn [ >4X)R1Ù;[E3N]Z£5?cPJkNEyJ H:.YLbJ#l2˃Rx}2 `Ps݃E,*˟*dqy *ܤ%$J5GR,` ~Ɨ+wR}JOyѿUƽZ߷8N.$S3NS2"| Ʊ<#] |g^sFk|g^sFhB=לњgF|4f/Yџ=y |g^sFhB=לњgF|4f/Yџ=y |g^sFhB=לњgF|4f/Yџ=y |g^sFhB=לњgF|4f/Yџ=y |g^sFhB=לњgF|4f+O }kUQxq/J MȫkiKfke!/=cTє"|xIXĕ9t \' ;o[ܐ̎#I[IS̅k#N=(]EQBEPQE!QEEQBEPQE!c⿪*<~/.RjfN6(O_}=_ӟ_mU\kO|cVkjggNvF8W.zиVmw&Vx\L]j~ᥟqƯ>v㚢]/uMܤDD+nxm]uPP$d٧''Q^3PR^>z |Cnnݤ!]iCIVe>6q`wP3=[855(۱[EsNH:t' q'zϞhT([W:xbړ=+-# Ž0R7lj?c!ţ>nR`I.c~bsV/Dqs"KDC !E*5 oL8O_@n0ݧFz395>zuY8mŎ9YA\t\՜F؜U5 5mcv˺x_C2XNpۛPI n1@AUxw\fm1i%$$`^훃Vn$}0e_AW.u`l2'lՃ5e}|W\]I)ZVۘ΅XԲn$ )~&Ux}T+;Y>>s¡JQP%zċ+ v% % Է֥$A*Wb|aT]K8KSNܭ'zY7kTG?6~&U5zE3x=2JZ}(Of_&}t~&U5zwֿm V]粛x|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NQM˜`gVu.Szu WKT]r-D mnDkbmϿ:)3d0HRz:vamfފ߄ɫG%M^eL.ALšY$M=c'8vHC`҈WwxF>7/jQ W=\}m..ڤ<sKiKΡM@?-- WKT]NFi%M^<>7/jW.OKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw3M<>7/jQ W;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zn*Brⴂ ؟MH! ~&U5zA8U읷Ov߄"~Y>x|o_~&U_~~d'mB'%M^<>7/jQU읷Ov߄"~Y>x|o_~&U_~~d'mB'%M^<>7/jQU읷Ov߄"~Y>x|o_~&U_~~d'mB'%M^<>7/jQU읷Ov߄"~Y>x|o_~&U_~~d'mB'%M^<>7/jQU읷Ov߄"~Y>x|o_~&U^[eKL]P!U)$'Ni/KTx|o_u,ZZT[WjV^tˈO5~I^P%zċ+;Y> ߏMW<*z$U}}TbEK}в r1_sL`)W|o_]u;5z߄ɫEНOKTx|o_NFi%M^<>7/jQt'y4~&U5zњi WKT] h4߄ɫG%M^.4fx|o_~&UBw)%M^(.'QkWܸGsS ((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(("((B(]~JTE\įM.q?ZJ?TS\\O#X65!xy$Z !#ՐdOK#$fi?ޭ~T:YadtH{ fc90ldGL%XOwd_ti]B.mN ?z: 8燚m#sO5OW[T)\ ERɴW&{ :`]l\CԳQקTv֯h*9RY *?[p;8\RtC #t]g# :WOQo.Be px te)Fsǣtxk $Qr'Dm~KWYZFފto~1{Ttc8[$glTd-{Ԑ ^E7oT*)Jظ;q^:/Oln"tRZt׀s5]O.GMFͦ il4ܔ:7xK}1N3ϑ7V tEҊ2|Je%J\0sx'z;`1 ў Jn#%XP IkIyVB]C (Fq:8j$l9oKdVz{kQ=YWu綾ǐPoΖ_:/О:YWuo}ҟA@'5'It_SJߊ̓Qy<<Q+MGN*|St:sH)G͛y XIxk9 N9WI#R0@ sU?5HsWJ'FRURbKPs&m 5coK:>eiΓxopK7NԷC(Dy1)}d;bE{Zn&*8C:ZWOM'#}]^:;M|2:e ҀFcX3N)'K9 d?ZU>=$=eAc\ (h(O:YHW+MHɊU:%#%;2wJ!!!wg㍻R:Q~l8)88"JvBG%;=꧖cVϡ Xr;_K]"&+opoM=$`Iy>m7Q.--nRwUgT jHүgѲm`Zmߦ.Z8FնF7[GN}.Ip'8;~'=^**嵦șd+ޡ%^4!H Q@)W=KT8.>ܝ-4zw":3E*}Nn 'd(Qy$lP3_uVY].xd|n7Q^g3nlXQ"q -7+F\u}gN."a;uJ>a鿤D&.nܛb9P{IjPFI9}@ `+M]%EYM*P1wbDİg9:6:`;M0z d~j^ خrf%F+zVntH0eEGtb 楇L]-sjcBJնg*U?a5+%JZOdhu4d\.5.m% )_WdKn<-! }ΊPav8J̑in֖ƣ(gV/f'?#dO pO&(!̅LEaXG[v+Y.J$ٸjR=`"DqNt-$iBg]Y81ړan}#f+$'6ޮ|SzAo$QW# 'l%)9d@w,u=e'.Vg q Xf3r~jn=0,q񎔛tB1꩟.m 4-.[[ $MWTߍXq,rq6+86JRӟLuNBq:/T*10U_$0i U㴅%t;w3G?h(_hDtͤaVd_L9Ց># V޼Î('FNG-{$cc_bOJ=$\-o#5G#O$٢y]]*8i_HTaHN{*7n8 Zx-J6TƤ`TZq[:ʗd'𓝒}s6;[h[Um9,JsuV9IW=sɶd\w٪Чtڛ}ʢ{}byMG'|m:yTV|oxQ; ĸ)Ƣ9!9&/D\)֕ZnAX$(\Hۥ3ϊ;:tx~uj .)[t8Tii) ǽ?#9)۳ >ԕ+O"&8MDwF!Qkx:x#^dt A=VV$-O! (R4rrioϚMvE@鏍8SyXB1dZ*I`}#?YmVyԲJ$ ]ySt97IIC<jnŔ-E$HrBe$b3ԤIj++O=kPChPuZ?JsuߥU u* IlY'>.STL1H]+EON,-ZQDnh?Q J+9IT "u-FyJvWVR{Swdޥ B~K]88HFpl# v #AzSe@18O$XBkʞuN6M}}lP mCR1i;dc$nb&z]%@Kq`~˙dvfƫW [e[JRugWSq#\'mK ,)DD*ZVFGlhuLa #dیem.ʁ;ttq@#/s-(>2k*Gj\*l64јLsZۥjdl$oN\Iw8ޓMpR%n) ՝9*]7V%Tƪ_nv- CDiNACg)PHpAv+ђ Q HRTq>-,F%<HeP38 POSR<,įܕ%y9Q]\!т:C%#*ՂIO>]KW1 QEgQ\-Nzy=ezt0?WhT?pR; Q]A+𝿣H+[uV@qw_Z*sR[Iƀ_c>[S Y 8$% VǛ^>X6i_#柎L6;ra]ZIC B$nI){jr.SUds< qnl&&WS9Q\mM)E.(2)$AW*W#ӆ')Dܷ[!isd(d)93qm˼JuH>nlsl@Q#5T`k.n"B=R;n{,0VǗl]TZ1[L9pBy*1峦2qԡ:jH)A<"INd[:J C!9=q4GXLZQ;;pbn= ;_w~N^nn?xuP;RWʟ7t"8 KY,Z} Մ(Gig<)=zbmk%ix-kP(.ٚђh0`2qJfRAH;>u  XOn yLlqN)եEH QH T7n7\t4Pa#;)%]9_~8]\g0]'Bp#fT+.CdQEJ+`M8VFBx?|Ztupn=[tMo_v)6("+4T5y;d<)VNN9IACm.GߣVG͊pEpi^sqIX%^|jNA]fY-ȏ&2!)Ia!S>2{\)*ΠJ75g)ZFo^pUۙ$!) s#5+7%/ J>WpLڗ1͎>QPiUϜ E bJ S 2ˠc|m+O1HRHd1ԷX9v.ף4UƓۣ^frڴid<;tS./PڭVRH5r" UGJ0VǧE(Blv';BCڽ%rpӏ)m77@A}('eJF <%ke϶ʖp PoUCZ_ |F~6[qG;orS.8iI JBR; ţCRR@=ܫx<@1uD+* ']EZܧacpYUc!bD˂F)Oy4FP#qwJ+] uiR6VTTxNk%$_%vq4/ZI#k~ܣKݪE2RH H6&\ncׂJ!9.y8Dq. + w}1SȐ|wO~:{7iCcC>5dbΑhuEIRR@' ૴c8wKs)$|Ki5vuʁMkŤ0=YmZIR5|U{PBq8ϼNCGkQE2 *Noqế}QGCwc`UOvw H-(XH*V$$VD9U&IJ;l%VTRĖ0×3t U-W)M<F3M"%k` jRBԝ Pr+KI&ZNڪx՞b!Lẍɵٙ ցܕשn5XI#% c;ޢ晅le;1kڻ .ܔ(\#I$.N e@7ޭJK.W{wQE(.vh[G Brpuc>)+%g|]!OY#Y׌j:co=s R5Ҧ6`P $oJʂB4hgsyhue(= dƗ4{#RQI(qX>~_.+Zh˳dm811sG ?ȵ0.3ڝ n2!C#CN7iNY,70\_ĦDRp-dvU7,Z_t-6 JM( {3)t=6Jc %8r{纜 9$6P>3w:DD+;=S />#j r58 WeUstA* b28!`d3j-R$(1T{Զ< IviqO:GQe1 A)PI8Rq!\T 54ӸDDoQSHR )n[ h i896oj- RȦu0Z-2ΟOi2|<4VI<<@2Km=/cΨEl!)ZH߿JЅtpeAqtp}NDS\^ܫVX$݄ = AI4aI+K2nVZIV#t=9^$Lq8U)(v(""(._W.r%hu}ˉ~4.'QԂg5tiXY"tw[ϳ]/Z |Ea47vV0!vu ntN **ڴrp)>ƩK$ S\ZA̍XsL|gdI)ʟuj҄'I'Xpwٙv֛yΒBk)-#,}zN ׍naǃoLb8pa,.T,|bqU"8./O#fⲩj"*@$\mIԕ$c*z)2BuA(s*^FRA|Um17I7{aXJA/!CZ|`"Z-zx-l+➣ZJ>gK#rhI 7, I IL Au4%@Z͗%\F[VcXVJOh iH{la\V)($@:+[U>@04hî!8mRITART)DS8 9wC;|.us )8eݠBGS- !*qds1HTX5M6U +Zz 7[Tu!dj}iwXr\&{cͥH- ?޶d0(r+X':;7Y1I n VHqRt*t'||y`ӑۜշixū3jRtg4TuOk7c@h (mX;Y^R8"(+,d%`*.~jvqq7ӷ)  j/%[ ]8ף+^!8QKyNx:ywWj_-EZRGo@XVݔ;UBr)9R >ө(mVRwAf~79.]x7#2ʂuc^/I7(?IQ3nB־,j^EE>\(& ^*V+<_ Gi,'J} Kx=[`ݢGZqIJ_xszS2tE{㪊g^RdGU* Xl7Fʦ-}VˣĵXNКp5m>KmK Hr@;QW-j,Q?(;jE7&Xyt3 aA%ZJsD p֭M 2#[ylEIpopN *Pp 3X(XԬ|YY>K`~4Ә(Bő򴶕BN S5SvJ>Aw;Vat^OKDlnO5 BV5~Zpm5ԯ$q*Qco㨏2jWř(d+4@eZKT9_߸]kEWQEPT=*񅹇EbR1%dK"=[`%Cާ8z5q<¯8/".MNhjIT^9`_65Α _[*t폾Hd([z.hVpA/5SOxK;Ve)O-Ax| -Vx ؋N[+gHZRH::J0Ղ:4t7AtkrZ }0)(IFjHQa,!ÍJAH\DE×a3@KH؀!>b{PmɊY #q֧]ӭa!O "D0h׏)e:>s?`ppnn+ppd ԊeSLnjOd6ع}N !ȍFet4 o얈ӚclGJ^a8! 8%%[w; :ytan4,Ʀ!\ia`rҵ$>`td;#sH\K)RNqy֛3Zdq'HBv] %ttkpeX2@__8Cg[O$YڞpvBٔTuaR V2)vX}SONsZƶgvW(I'`X=W6aEuBˈ eD|f[8#H@KHQBJF!:qdwe H^~ZZmQDCIh6Q#`-R3N[kJ8 L5d($R@RO,D[xtHccut)[!=$#݌D\’W<= RlEb]@Dg۪m( ]s#,7$fkp\f+=Ke/O~2>\TcL.'"ՂF{ GmvD}oMuw!!@?ͷuq -1 [dx!eLX!:2r;vb+nC"JXGk(pDN(7 ISℹPu 4ٔ=d̋/Ĺetr.dWh5tU?ˢЋf68iT%8zu`,n1tS\/'2iQmY9QFnͺ׀?9to.!I-h}-2P0wSY)yb 3TٶK' 75ƇBMxݹl'JFwV^ cOJR@n@?h~PjleR{dn_mɧZG.Ǵ^R$1/H]SYyQEREp/}2p/ޔtl 0[ )\s|>R(P6##j?"DsD~Eզ:Vl0))UӈT:$PUЇ9$I$ z)v{Zé bd +FwTfxV;ABd-)A>0X*Q![-[3jJVq[= {Mkk.'heĸ:Hl:SyuufL6X%&F IpR\ZP2RGXiDp] MbpXڕ)jX${k%"gܦ]#Xv JWֵ֭6ϓJV6JJOEFv7)+jߺcEPcu N3"8{)%‘)^Z}ƭX9-IS,JbmW9SpkJTOUu Ukv/H<<ס%eBCBq9'aاdi&7*:g}ѧ%^2HySzI%^8o-A* N|UDv9qRcFxۅ2Am!cI/s |⮋-f"&Q \cO*Ko%!: (8دrM4IBkyhͻK?\<|{۠81y ;syynf\)xxr4g#FCS>+Lx֩+bS:m* Hԟ( h:q۳77m $\ 'P2ҲZ7 =eu.!ٿmUUf3fMOZ8DJh|p9ϛzx~ZPmzP+9v/0npiHT3lro6nfGKdTp=FJcdK;v$rw G!=AFՄ䌟S 6]%O\J8)rJB܂ֿ~d2o ԕx9mԶ Z-F7=ٽ\.;^ ǕS4,Vc袊izVklgbiU,2=JA/j/J&7.\PK(qw&;aǛ·8VѲmYJҽ sw4+Ŧpf2ϬHӠr"mHxe@pq7S|(Bg#PҳB K+!z7=FuҤZw?%T[nՐN12зoJ m 8RʝZ[bT7Vhz*$'_)F8N4$cJˀ$G^e$-wI?;>JpYbWׄwm BBw%Q8}ցZ7 4[怐 ?\ uL}kl>,\)BWRJsȔ6C)ġD TU~cI>)[S8j ^=ܳPmȘ[--5PUrIQY'V\M'(_ 2\M𕼰N2N9|fuR# q˴\˘iP +2d)G ^ޒ)yLBڴ:H#Qm/m#R3Ct0BD7ӣsⓒَu%q:TR._) ` }sB6m;ږW?U(OiyF屑oN&ۛ\Fc&.WSAKPQ/tyz!3\cJ['OIA+98;z9֓9ݞb[ݶ\ 9!ŴBKmSFt|c,=lS}ngR\H!$!'e'<.ڶ6\aI\]qA~鳰&^ej:YBRR;̏I NC̢@ӷ3tDZqFz\[NIuY syTAOFVIpPWs4y/7S3QEIR#cʀwQO!\s!4]k`gy558uzUb[gҧ3N- .7R>rjѬ1eM>^`%0JZpGmHZlA+T }de*8I*IN##pwVm[kx=GX԰ ;s9NRl íLUeQ&n_K~pmSH!}Tt B}'*i ۣj$^rHHNg;nmS2v,>MUqjJA='w{bUd.ZYBC$W PRt|IlКT %'$ *{;{{ ݚ1󱭏8R J4WhE{o1gi Rq%jԥjJJKeC;dSWdp9s0an&3an$ 9:R qAiݸwwLY# 2ܪYA5/SJqHIIIY#ĥ]bdN+hj՜aG> ҡ1/CwO+Q8qʬEkjnܦYKsDJIF<0%̍,yHZ  ^)'v٬w'ա^@PZRH~3{0⤯Q #oZJڧB G'_[ ugrElV:HԮJ#P_ :Lgս9AW&.\OYXXET \įM]~JTQrQj(QNi\OģE9^ W%=&Ov$g>}qs/GLv?$V[lKLp$K+HO:QHG0Hׄi mHݳJ7t]u$r8]#|g F74\8ߢ8k'1寅Km@SPK)RRڊRtt!ѓ7X]#YoS&4Sm-8N #׎88 ,bbGr,KGX\Z/>1y >R!T@|C`te~"w?V)N ~0O!C̡Wat0d,ݞZ=P8Z/PeK qa))@s$@ 3R}t=k;UqLr^;iR•ԧN(yRb"u7 ů4/48.S,lw8cq#Jv5!ۉXFyˎp CHKvLi: Piʛ0̭nln=mMSs/l]|!m8TXC줥>\S) MJ$$nA 50ebJˊYKI;@ /2Zu57׹hy:/ /<57|#|-'> 2Ԭ>ԕ9UmÜak=[n{!lJZ:6<ûx-4cLem֐z}@nﰡ1Q((=aHԕI '?S{,¸tV¥.)[/ ^Q :2[]"RN$*ܭJ(NcLG{+Uc#%nljo.h 0OmY˵*iꇊAP$s8q:8_)yv\Mc1JO)s _$9!'.g쟣P@R=kEO3VP5疹Z0vp'cӉ㺋jeM6)p}Bbc.nGmnX0b2 m*ןrymcwȕSK#4ꕻJ%٘ nSBe F~).([s[?”U✤Yk|vp}, 9)JSE?!(6 y-g\' d8yoKKf-"[~-ΩZNf-{Y"Tx<䎮EyyCiA*߾λ[!o.iSIy-%>Ib[_!٩t6qİu; II!naLmn9VKR,'H ?si&KqBRpGxbxĐ_aQVan;g wQu|HCp;M퇈mWF_Ĝm0eZ@(䑧3Kp\2KeŅ-)A.5bI1j D p y7[<ﴇmcKnŝ)Z"4T4'őZ3 l0?k~Und#w['mf(R<1s^ܧy!YXe:ylCʮ2r0?EE[N'Ofznʠ%1j l9Rupݦj97HVje $tJΠ:n*}tˎ.<9q۷:iN5H MΒtHVy LP+\Z']:Bxu  ;Y@FB+\/J79|i3QtK6% A%HS:O~Ri^8c&}N5MOaŧh\r@{6!ŎIgߟ%Md 9jޫ|a"x,T,4qo^i&Qp%ld؏^o|1 غ&!&B %%!8my+R@f>mtJP04:IU߈Jo*_?NG-J9+̋!sKK'IR6RE_)D\պ+˙>tr骊JeXQwO|f$Cfiy@ꊚW;/.m7W.eMqo!-!%+8'ͅU#URK)Dg5 Z o)+JZNIVQCZZHt;zIvk캂]k?\>"=vqPm˱B?ƗEQT\)s+Y lG}xk[~Gjl{ iV20qN[4RƜ*yd,IRR냑O^13Ok+FPK Vb/t Aoq":RIAI9_"*j\&0JrR{7Kp-1MpoAn&PICnS[/We $ @1ή<oDęSS74%NIk8-L _  .|djKX :%dL/ERAiY Bq:o/.nZ!p_I6ֆն$yj  Gkn⬝Xeqp& kRXIJH=^Ppw{> ϴK!iRj-d$4ޱ5p7![ׅy..eLJ啾s~C5o\;pr]ՙW.rŽՍK =Տ& Q-'z9~!n ;ŻGu^*$9 Pڪ:D>9,& 0r'RϒӜ޳n+%熸 L"R·R f20j4 %g[X*:.O@@$'%r"tO'o!=t^\Q>ZvE4$'A#Jt3XgqsˬGh}+CZԢ:А$cήoD2S(rr|R#V2{+'b\81muBe/83CnmNC5Q;kǜslf*%^o|i*N+Hr dQ=ӹnUҧ\.>38G\&ˎ:CgpIJROoIqQ-T+.m-@A;sZ9*J+Nݿ* Qb(KؖNIV9 nQE2Y!}q"I i+Wī_MT_d?7.q"E1}U4Ƃtf3^@Y{e@lJpұsW0:j&:u4ELp(}Y骦p+ {J~*E ^6KIۿu][h+t;9lw:g3ָ m88Y!dqRnYK$䤔.Tl\\8W9Չ0̞>0.^18'#QjyDz6cU|6a)v(:eoZ# ?X~]ۑW&dtPa%aVhSk6ꓝmA vjH(Un^nLJ[Bin*=y–ִ!JN\'0Gn1T-eYR2=ZSfr< q:~6lg$W[Ye:Oe3˷W{n=S%H(;քpҥKBVqiՎD?=CH{3oچ%.ۗ1ġXNT(763 $%|ywr V*jnZS<h U/ITocTDg1R\t:­$cӏmrh1҆{u1V7vBJuֲ6J|~|65@*,(Ke!1e7ZI>ҖbrTh#aw6Msw{yf+#Vچ23fzSqc8-|c02vܞB3oQ{RS)Q #ԩSKj)N% l)l뼮=tCfTc*I aaH)ts⯵z?A|<ԆӉcfSd //1=QE Ϟ̲cpiNy#fإ3yvo[MuUϰ] |t$oJW}pO[܋oɆ$pss?Ef-7(ܹ BYC`J*; JtLiީșZF)~qX@9`','_КXmsٶiY${C!hu RuGb ge4 h=)D2:!XJ T }>O*%clu$|]bBH X{yQ:hx˳a3L-<)u:Ķb (⤐T!'pڳRyo1VPHlPڒ|&W=[*P穝rI r:|D8{tcsS2@Z-wxY q5X'1$q?9UGKMSHNLZT<J-=?[,ٓ=6x3n2[R}֢/&`}nErm'|rP<SO=lv'5v:ep8/02SqbBC\~H9!Dnjij1ҒBTR Y9S+Jj^z:qpi77%FD~:ˢ)୚^ƈANcGH=δ HLkG|a TrU UG_02 ><'.U`a=QEu"]~JTE\įM.q?ZJ?TS\\Wi+ W"tj絁Ea)Qʷ #iI ׄH rJJO/AK-tu0I- ̨G'§+h,B6;Mh&.rJDRˀ`)$U#H-cugWX$7\C̏":µm`cM8#j޸S6KPc8h.V}Is_\OHYѮ)V1r72|9 V%!Yh= p䘌dgk˛ZSnM&-6_mĄjS: J$$t炠p珬<~%dHL%lTPEnM[ωH/6Ck>  q`Nnu3>#>& s:eGX=\OlU83ZrB͘mM69$ӴATv ?  ~z%ҠVVnߊ)M Y ^f@HI!7 L{< %W'ia886n2 :X񓓂ri6uJKiuGIQta֜4i98<+þrs1dɅ!2a~+퓡\-(83.y%7+RΕq)I1T{ۙa4ݴJZTT̞tF͈G_+w螽sIvpPPO:jI B|>v[ F*-{&9:|gTkRtS* 39W.73].k=s9UM3ؠm~J{'Zk+ZӡK*N;N1毁IS-v@5I5Щp_DXN{Rm hB"RaU*=`KBF *oR] 8#-Be6@[Cjos`keX|4jÙu,QQ mm f7!&mTyiM^V3we,篈CJ>CJ%,UzU{BT(g$p|b)?\׎)w4%=h~Ug 5y=1؊BGoNQ\id*&s*(n(/ϞYZ:Yo%`s]BNJXRX$ ZR}'qI/2Tڡ㔹onRQTܑH#w^ug~ \3I01ֽzǣJ/MUB$ d0u))WqUe?EJGp_ Z.4XHJHk|&qIZٮcIXZΜp*kr]uC1uBFZsG$^D@8TQSf!U "c >Vof%DTzNyu2ӈ-mqSMqy ;+I=nV%6icfd\mB$gGl3@y96k:Y]F+ 9iPQhlG€$N4Z2*ˊw.6nmJrڂ[8ymnDnq*t28ʽH؊q `# N㲅,|JBn/yy:i(4^}tu:wucʮLc۴D%*!E[jp0u!;p(*Wts"\U9l%VO^&*:rKzœPklD>7<^7JW|!ka/<)ee@0NuK4$!¤tU2sXJH;KaRV,Z#$Kra$aJG#QIf4DF5(yyMz>I.8A!*1F7IY;JXmCIKV(Sj?PrBU|UOcJeo]{- 4M>ᅝ[(!`vڣ|% SH@S3PXZ2RHPk{ #X(C 熓jc֑ 73,6K0[ej4>u>_'JVcUQYxqneEݰ;j gq1A#`3eƕpr)t su%ST?(_);+pǴ [RCI]ְTVpIN*_IA^|[nݮ m(ŌXqHH$'%ŕ;XξG)~CQc֜cBJTJF;r+~)vqԺ !Zpz⾋ŮlPj% Bs5xk]e)WҚSύ;iy FyRrIQ 4ѡ>9B@ٵL۞qzG٤n@i}#9:Iاۋdp̖#!n9Ju(BAH'zغ{z~s t8}$#)%쑷g*Nyn)jq2wRIԯLLJhV; kI*ܐlʵq?Hki"JmNnZPҔ?<9,pݎvM 9Ete"I +YgoĈ 67yw `m){]6.Q7\Hڒ' Β3zD2֭YQO8ڱ.7c9l'K- 9";Q>֛ Bi lծ7G qi(:PֵsO*lLN T|q:RTY嚌+^UUmD#֖=XJ9P޵oaOiCU^MSH KjΥwZװ@+e>wiGдQE6QYogĿҙJegq&?Қ*q|m Tci9hKТ6 +H+鎄>J;.R|lT>v7<%:rqKYVe2t~ZcZT,.mι['Y筽ei璄K(Jy|I'⬿` YޭJDk pRʊwOKqRz H}5:Ưq{&-@?OR ݥArRmG!9>G]Oڽ) (%8O0FA*-2,:LۂVmR^tn% $+ IqoVL,! eh!82\[`)>&q'k-*R#qѥ$8#'}݅]Z#U˻z * %:x4)@%:=3q㔛Uώv5Nxpd!}h[AQ~*{Cކ-Q&׎8>@G)F‹  Nwj]<4)N$zϏBML4ϑ$20oIV/jj‰P氣%&sdz *\WGO5bVKCldTmTǣBܛ% Jgd ϘyÄjOU)o"ty1mBTH,?U_'uk2|i-q5@)K$@ 65nG[q$Vn %N,TL ^݀ ./4( He$T S'$Htm~awanҲqr2uWW)M?qLCAiʒqqP<5fAbP=q-#e ByRq禙502϶"N=R?9I6$Wg _ԃ'Oy4RL@jFL[(Rk Ɣ*@V{X.A-@( Z.ɹ+K~E%JTiZX">"XBZNe5j詛b$#Ӛ HW'm~k㯔2ABK*S;>:Ł ]־Z+-ֵ(e%:$ nE'Jujʚy+JIl RF`<Km2qiPCfZctטFMiDRCO4@Ugm>zĻ[۵K:̧֨Ρ$ 'jmEb㈖BQ^tz{ES$E]1Z-6p6][^BTj *ؐ 3PZj2 łRCy0h(>+,#ʔrSU;t8K*Y'iW88>$dALO\Pՠp Ž-0*KT\a )e9JIvMWPJU_[~&ln-oƉ6mhZӥ$-Hw.c8ǞDw]/SR(wؒ3ۊbV@UJԝ Is\ysdq̒|V*[RO|UZWZ4!^*NVݲsQL(u`% Qء*0qyiEPV''ר*Z\Vim3ug"[qZ͂H\ոNTRvH<򪜗%h'%(Hϣ$vX"(i$+ >MIkn(¼ K˲昶-LPXnc^h]5ອ\$tGTG;MCitvTNjA'M$ʝFqkԯrͳ5-jS,րs5UpE]KKlxxUN2$)䑾ߞ`{299Q3_JTeL51ds^Т0NEy $dU.Әw>R)GRHrMKiWB~ZTN^HuƕCjJNwТINly*5Yq4KRA#xC2vsښ}<)eN$8JC2,PԅeIXү6{ĀM+qzqźd9 >TRNFh$9+Ce>!@IGHkooW<+vdEQY W '#;~mǕ!N+ ςWtW'8'4 #}?kisk}B(#{gvnj8%2=fhm>Y QҝSVԬ}q$ܝW9AA;S|:uZӁ^dKbkbfLRViw;u}$U0+Olu$n~cGRӹG&]iu R{ [u )F*T= J%!ݟoTyk8m84-gR_0מuUD)IŠ{O ۨ,^0!;WPZ3IUYvWTfHO'3nHBsJ]9Mxz)f8K5ɿB7pG}"jYлDׇ2. ЃPʹ<둤nHP;U=h\JJސ9ϱޜx}!$N293K_Vc'ؤz|ҿUV1C+[+u+ ע( Cb(Wy Q8r,GŃC&JS! RYe8垹`b7BP’u+#1m]K:4GWu HuFTpuh~UOdWθJEdt=:7rExRRO!E_r!Rh(5HZ9 n)CX!CgzF+2қX* [DKQE ۘ?ν8PRsɩCH8R[HKi#QLn *c. eXՔc`3Q(dy퓜W\Srp;$9,ae8mk33Twf78CCiRJI9OpyWRd˵ǐԉ(hIG$g<,B$-VmR[o^HHywdf1<ʹU`)a#JI氡˒GƪB2 +BN(J[{4G B[gl vdvm NH JIԬmjr9 FzRROYQW:ڒՄDw9 r)R9ɓ iq 9P޽I|!;~`;KZSKAPV, YI!pR.!͊!,6*iG{;Åg[:𠡜"& ܄#PwWyF[ϟpwHhS7[|'eCa C MX-ДCmP;;o -rt(jB>8q.-K `+' Mc+X)ku&{:6)]y}{DzjΪ7_ C[`(V=cZ[m2Fs##z !8l-O5 c𛥵vM;26Pla?ir3 7)~pjB%T ^w .SjS3%2 _XϞ}]ILݾܵ ZR)$n8VZpM W[~LpFY1kfzޥv556$hyҤ=To[%'=3 x1h-=v>n &wmԤ) 9ۅ-KF 2X =jn7%NEnse%ìhRHO`KN5vF%I;n}-B )ϋx)»v+Ah"ey#%#_MVYuWoi|0|Yd-c۞*086$yAM7 `yMz,M~b%qG ySb Y,:;i~iiB\cΦTᖸu%WUjܷꪜn,S0ZSeƸuȆCSjH8W3se6˜.2甕TF)U>U:\GßrZpu]AO9m&TF9响 ynGTՋ\KRo\25 WI]L~oVY_${8doVT;)7jQETW쯸1%(wI. gF:\p̄/BN:5oٍ_ !]G{ksKgp1|u|.ŲoNtYKZ[Qҽh1lʸڀ*'U%Մl)X'9A,Z[a9y ҊcV`w^ڐ{9|U R#iC HH@\_HJ)U}Udϑe7Q%G6T}"棚nB i!R$ OVYMHGzҤQM+O X4o(I6˗ܹ_*.]j-_r~%)6}ˉ~5 nWH\q] O+kz^.о% _B_x;SFUߚ& ۚ֎~z2:a!XHUy$WεXble)OHJJNJ*Nה5&.P$_@7'u^ ^+XIʅ!9;n$m}I'sC'lTu'#n] 9;_X(J1z)2w89޻tI4j8AlѾ20kK^,5xI ^9ڦD%԰JvlڵvLnW5~BسP`FsM;e d%h<7Il9<E,N$szGɰ4nMN+5QaYS6ue?R/pQԷݨzc珊Dc Xכ*k2t=*BN>2F>3Q!,+cVEȄ(}*JRP'qXX`jxyF~T[U][\F{MÐϷUwj|j2'U|17mE.x8̌,!H 5"EDxݝ.ˊLygM%{ jNEL(棩&*XH}ZecP%=5N馬@"BFYp4gY4LY_5WЯ=,t{ia_;XT54i%G =SzdڬrcC(ojG3zN޳εbozCoW&j*B~NFQE|zQE!p7I:LI'ɩ>uH*Kmd$1JPJ|T(PRo3!uG jg)_fL8IMQC8 ?@eYA)Q~:%ejKP9imAhV5MޜZҒmʕ.$Ld$?NHJu;MZ@K2W:KII VsgNsQ( 4tlE\^ll5(oU*.|޾J[iA* r^ǐj}9KGXJB;P8[2ܚyrtר10[ddkBHwCZ:Cpd"9^FHD̕*M^ :!yp:b qwO`Hlm[-ѫ5{Mɮ`v cv9Fڏ1Ձ wCYBigGqz/D'f7l b1RqRJJaϘaIoJmC!C*Hv{DzZasn7}y ћQE;ҒiRUIyȮ!$$`͜V`%_uoMLzFpݒ{)VTpFw"훇eώS$ҒUwUF}rR;hB~!鬨w& S3L4[Bs۝ZR1ɂ[+XN m7U=o!+ǒX$wzXJلn5Hg#(Rp?MO]VK]w)WPH[*| GqF޾%CNTdd+}{dyg kTG`&KrDQʺحjR,_%,@JJRNA9􍥽rU)m IԭWXJU{-–~f>: ʡl$g|1ęvYzT7UxmaG!5k˕[$ABUR8QXI[aO(|vӶ8V[R>; m.7 mFJgW3QkJ=R\YbuHZ|M!#$;^+ ’;P;beh!`u, Gފ9 <}/3ÝpaeXfE$wL2GVqU8:J᳑Z"ԅI#pEWfTz ]T6{2Fmہ\%YS*Ozhe׾Twe;g'O@Nuq[R@L8fcr*umy) _*q-(~w^ǎ* R(nXmuȬ%IR2N5ij+l%QA1+5EV}{+գ%Q_L֣YWKf (+@覀`c⯊ Da>S & LUY@I8X"jLǜNՊ{@ͭj !!H"aIơ [PQJ;Ad96%=D͆Uz0FBOTW ܤ=Yα.P?E yD 2^N@T9G4(،הO8X5;i ###4r-jy)ᜠ6U$k*`響s\ v'zC„Rb:6\Xڹ9ێ^N:3N7dU6徐:ƆA2H5񦔜;e$vi@h 3ΙQo).A|)9:Be}mtƍ #nU_ Ny4cdTQ& E鹰5)"Pq';`sP%<4JjxTS˺#&V*TOyӏ#ċ ev/8VM%YHړF%gNMbvY(]UQȵ:2q Q1ov)GF24oM54۫ QӴ0^p0Kb譃:GpUn}WymΫ/i7p^- 44ܸTrM|SE mIRv ;T:/ո9Ѥ2S}0 zm␠@ ~\zk{E("]~JTE\įM.q?ZJ?TS\X_*4qKuii>0sYGO#Z46?Lz|v$M1jڲ8վzW{'͑{'͑ꤳFkWYW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳFh||<|K5!:~Gi+-p5 *H'`NF O|Q XKA6M<|<|ָ}˓ַGeGrJԄpvH97O0/3s Ԝ UٍT#\ۿE'+/q?%h6G%h6GQĐTwэmdgI#8pi, pQ.iJ/{D=Ty/{D=ThK\RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FeZ4˴qԢDccBC:68Րq@88;V'W]U= ]ˡx'B((r2gNl N p\l%h6G%h6G mnd2cq u)DJмdxKםERۊlbC)qDv% ZTc!'-i8Wict{'͑{'͑bgS ܵ`Rr*HǺy2ymE+N$lFA A"i#%QQRY5f||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑A bN)8o)mK dDyt0sҝGvڑ5r)ϑª;*+ 舣#B,zwKDie6P4 8$k{'͑1?uLl X}esq'yJ/{D=Ty/{D=Th3*1O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=U I&%+#R 8'fN%n\v" o:̺6p]hbqWGXe{]E/{D=Ty/{D=TED5͏n[P8!uZ‘yWnj~lUK>lU%3Fe+QRY4`RK>lUK>lU%3Fe+QRY6 7nn6ĥhNQ+ `3 n'd\f^dz^dzJ5P] Dƒ @:ө+AԭЮiv"xD\n_$%Jy^T*H㇚(ܛ/{D=Ty/{D=Tۛr^ u6V&)$`u di7SJ/{D=Ty/{D=ThOQRK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3Fe-Vq͛k U9O5f/c8q?KՇM;=žjI/mQE|{UW/GI\SR[i$HKe@HUE .!og>&BKV8B5]iV-ƒ((~3^<|K4f#r3+QRY5W{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳFh||<|sz0$@CΨ:d2J#;g .l(Nr{iR ԲNz\vRIO#GO#Ob"Dh괸^H0^#JвC#)*mY$~WI^3 o#}daAq̕%h6G%h6GV2^dz^dz,њ0c)_%h6G%h6GF2^dz^dz,њ0c)_%h6GiĸLm(25LpF3u7DYw:dQʗո‰$$UQz_$HrUZUJ~}(Lz bay,7sO$ ò׽|}L,;W4l8 x]#%||<|K4f0||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳR _U2$'#=`@^U#V1I| &K>lUK>lUI\,r\a3)A !*#R 2s˝4kFTɷ<Ɇe)D,p*px}Af.ғ!QQ"<_zí):y+bp"\ Q.iJ/{D=Ty/{D=ThK\RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FeH/2SQs(Fz9yJq' uR qdkWfo^Ǘ 9 ~W(rɵ\įM]~JTQrQj(QNi\OģE9ΞZfKK8A mBR뤏R~Z˵Wtw/m\$KjU#Uet5R:Et5R:Et5R:EtqsmGϋRң"q.!jRJSIQ'NU8Nq$u7K *\ s<(iA]b zA*N†M>:z/Z:;v7֠w}ުs|In; R R5%X9cigl.7;A߯ojR5 SS(95R:Za \IKjU#U.]-T5Qd]-T5Qd]-T5Qd]NpZƶZ\ʸ[q:6 N:;TO]9Wxg{fz( #k/c;UԵ]9]*^ CBO'Br>4WK뿴*T5W,UڨHUEڨHUEڨHUEڪW )szaĸڰpv;ڮXZ78]"\- wwK 8RXX^%Cxہ^lOp+Y|.=׮}H1›V^uRuN\9ZTd;-msߚpm5kFZk^j|S%>HU ֯&%~KmOս B QF;8)fDTMQ5ڨHU5dڨHUEڨHUEڨHUEڪСJ/ڤ꫷A8Jvu6\+NOƤڂSk9u(EB(<F.@Ecouxn_d/N)I? u H/`/e+-T5ST-T5Qd]-T5Qd]-T5Qd]Y;őĀj)C88 ij۳zSeC,-#e$,'`UtBTGo!E![)%*؂9)Fj49lꛊ3oEw=m9IjmŒ9 פ-%$! lP׋l==Ŕͷw%"H?OKNU>Jۋ[uO% x nf;5 A7gmܦ+k [÷ƪ5R:ZVH-T5Qd]-T5Qd]-T5Qd]-T5Qd]jӞZt]sǓ_J ijХv[)|*i?w1QEW[K˞FWv?Ev%` ֎JW;YWH mTjuQ[UTjȺ[UTjȺ[UTjȺ[UX_E"j_[eiA[BwqdUSsCDGp!2Y,4ۤn7ڨ`6 Do9kKsVe76RmNۡK)58H8+Z *%Pk3#-α'%IQ*Ӎ#JqTTjŇ1:OWKoz\x4'Bm,1?ʟ&Lhp)-AQQ7$SmT5?"8}>T7*AQFfʋQF,QF,QF,V\9ğĉ^SUmŖR TBUT'[:OyֳVEW׾EQ:{HnuGݧVSԬؔ(@S7*ȿtoi?-Tju 5Wkt5R:Et5R:Et5R:Et5R:Eun4][L\ZVX:{=^lga ڤ>ʂFZ[I cVJbMS ڛqpx2/%F $<66;\Mwցmw[}պus!-mHҍ*%HIRd@\(ң-( йXN;RAV }#4tLs^ [==!3%aSo!U#UdڨHUEڨHUEڨHUEڨHPȺy?Qiz,%uO-I? uxm%}UTh"(KQKK,5oe+NjOJ WT@UV'{:?;o*}[UTj,QF,QF,QF,Usm+]۫EMJ\떵G(H ƍP$'g&p-M2Pt a#aΔ.l3mh+x+S,C.C L^[Ll! 뒔@/N5Jvͅtr5ͭZZiuiJT!ġXOgeU9Oo1 mi -%*FvRٍ-q_KI9׹/#4HUhG@{\wQFnFGU.FGU.FGU.HWw?Ev~WܐDvPVB9G`k[G:?_:K_hKԦ_r4Qu/+SEE˭E\OģE9֯q?7𵋉"5Դ6J*%+A{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt>f-(jN8ZAJsZhWO$썑,*4Uwx+MlnK (t-MB*EN90(=xpik}%'+B 1Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtT[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|^EƲ[ڈ.JYTUs'Z,`(1ی2LN6|zQV4Ql&H,YGJOQ5>QW_Zy7V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|GC]2~<=d-ɷ!32Nt6ddd#EW-T HGi%Y{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt~౩e':]>dA~*\Ϫ裧c$2v HJ rRQEV;W[UoBfdeDr 7wG􌟬 b*BJx7{Ag]#)?YGGJOVEY}kcT[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩MtEz`quF8Ÿ`m`isS_CI.u >hJ_n8Jr}Z{5aȋN4HuXH@Ҥ)NpU6Ccu u%i8*+0~S.tp =ӑq_"Pj!$`Sb<ɮ2 }msΗVtS *]ִeI 1+wK\[1w3Ai 44My]Ժڦhox~ᛏZN2]w xCk\vʀŲe#9dok=qHmy k%uFK1Z.k?xRR=4a>"OQ>/W8ݽM6:R(?SY) UxKtO j\)RCZǡ9<; Q'+TvwU?q8?hWZ8[#^8,'PrBSDvGG}8ŖZ&nf;4Hy7?4|Ϫ3;!'o'QHYio->QNTt2VQ01yWq>ݺ5:TR^⣳'(4oʆRd$1[L6!%tպ8T^ 6Ƭ-2#LtÌ@V Qۗ-ki%ӕ7W q}V튢 *ʂu">$`zjS CꑷTqogP&L%A-^8:'o6oceuaLJJG,Sg VUSZҸW+^\b2%rZKH 09 l/s)ST:F&X8(+z{s.rcq/[C w4jڕ)G*Q;dF}rcdU.S4LWL׫'E&N/|T {QDU|&[G^FכFkXpӯK=_o'MӗL_m@_c"]EkJ[yJv;`z҃JjWlYK@hW׵tҡkС_| 6! Jj2u-)S`Ӻ1d:{x,ŤͶ/aubU.IƔ)hbw52s>T"F$Z^B?ػ _ꪃΧ >u_$CX^;BUr LHįCtŒb}UF̼ĩA@rEN5 SGp?6'R!]RTţP(e SN;E=ml4xvTZMGO1ؿU^J<]~Jx+ ƐI'4QA^dUbKL-O'R8]mx10TFr{1Va:֝KCg4ThSt.8-ڭ:yy@~I+YE)u]?tkV~E)h `ziޏ-*A:2yb ޫ};[[%HAJx]ys :@{![W(:bfIYmj:Uy$<ԡ2Y' { :\RH*G輿%Z@]0t=*XlWAф%G*沅p$)*Ekf_M<5#RtI> TZɁ-vZtQEI&(.Q㞘:ABq- ҄8O.M]-p1CT RO?T•_tP_TGvC#E/Xi\HՃ~( ޡ+F\)dkH]%ȟq+a;63& '{[mj!1xw4u_ N"Z]dE×숷 Omє/k9>i[vM_6N?}*78ٯٯ'26kjHG̪EI7 { c?_Ss՟٪@iHy/V HgffZ_t;1cff+4?Y) H﮶_>kƗoSNtƠmyjSw:I՟ب9Na:͍`I8{Me;4XH8 h m矖tҟN՝Xh1N?6ئOts6?b_˕zkf )k|c8󭭰()*;6q\v skUt^wkF Z,,EWVj+gB{lOZ9I/ t L[KȮ~j 6c"v3*f8̄#l}jE7 :lj.q"|<ϗkX["#a/hw1dMFAgȘUX6IYo!R*@#XGiMu =(&&,6ae•yu#[,Bu8sWnnlYqG'RBOɿOieהe\ÀMcamB&8{OE/6|XGTuc}w% : d`;6 –8#p|DzW{RPsN}3|Յ-tO҆ŀ{S^%a8#T+@i(Yo䌀ہΫ.!Ȓ\#Ki$㚲9&aۀ4%V7Np)xjje~#ʾxi`.N-2Űc-KV7ikOۓjFtC*B-|R;u%>}".Eŗ6S>myL)Z 9ҭ,jh)d~3@*NI7nkd=Hlds"ك]򧆎Wa N2~.Us\C|u j:vŽ 7krur`"AAYpn JGw1ʶˣ!HqJ*)Oӿjd;nnd pk;mQ ѕ.`)s@yjr}bT# \Fez8##)b;NqVMyd8r|wfGWhnVdU4襒I&᮪ֹh@p[BxzeB aDnRTFν:{ 8X\wKُ!)~JRpsmdqEٷ2 (BHˑ )$XD [5uCN rN粶cB{.lyǀ3ԟ^ߧu:xpZᄧҢ>JNwhrU0OZn3ʳhwGzQZ^w))SYKuJyHVےhHh-i w<\?OWh76U92!#uXߕQ$u[%\2kBc!CRTz*XJi:-{%zk~\?!޾?=_Y t'ym\8ji@ ukˮhHHYV^ sT=^ \JN#5aLpܟѽ#O%jI aCTU u`sᒄsC5#.`?48 Lffk1%ۥ)lY&lN@R^D)\|Ob[Au]:?7sӞ郌qmRTa:BSd>:r *'uI$ʙcZnؗt)'p率i~;"xockvu/9q$>?q_  Z!ldkҐr7GfjaBmgm;!GXߙ.)|7r RT m#U ʆ݀*"mhL@PJN65 7{4]nKyl< ,62UsګI @FٓQӽ"ݥ ll 흅ŽUC\WV$TN} Ӂ`6x8۾:tq7WVd{-nf|xCeǖ̏Y^]Q񯬌~EҪՋiD؇)!ҞLe޶[by 5̤ ˉKDEWR~B?=`8A%*҇cL&p/:cWɯ+]U<l6[)iN$(pIR '?($$ &m7i`oiͩW#aSv9DyqgJ~ ohFexee)܂qHOaASs^HZYB7$A1ʽ%~DcT@ pTy=jR+R );מl\=ena![nYjurހ].4Ag; ys4$ iO>H#?sS;? &Ei}dsV~~H<-؎gfUak@e+Ks9`LϤqWW/dO <3mM^kC?Yt8![6޹.^P@1:Z~S63U,.K]kq=g}#}]ZkNc&̞8qLXғUՁm +qH;At7Ɩ.ՆT !Х9oY;Zn)l`s~$eMG]gu $?2͍T~)mN]^ąFm4ЎHRTq?6*3s\G} p+|EmJTrpwqȝ`'_ų-n!Zm\>') q#}^x݋Q$%Rem y}soAhyVjCxpwN#qUI@ r+ݶs~Мg>ҙ>PIelV\X?*q})e'; 9 :pS8_iVG`6Ɇº*A'ts[yaF06])Y$WI7Eii%( x'(Hmn$aZzm&L_q( ~:խ\mƥA.^?HN~4 ԠU~O~Q vʪn)0$cXf{@I5IS#oLuD񤷟5QlEvG`mMR*Rxcs\Vmq|n_8O=.<ŦcA5Ƙ1QbL(޸eo>cQt({MK84O{gF-U5 >Kx:11׭mR)qKO$23Ju%є @ob@uGtEżIx]/vLK*[@ WYol.lCSYjܧP}#⯌$4E60HJR.W9"}N=<9BmؽӧI>6o:m9q7WX}(sSťĔj;Y1ve>np2"#e.R:r]3X oa^+yA۲Oٺ0ovjfֹtQE\įM]~JTQrQj(QNi\OģE9>,qX)7ʽY;v؎$w.[!B+'{[ccvhu-D5_b⾒; Td055Wr}c+L 6piOjv`si@2\'@3Wʒa%1Z&AxG$%iP0N>*6yj$Ca&N2RRx g.Q,i c^Op?]ĜAl+ JGv|9.ߦv5|3mvnrgmet;@)|'7嫇l.9}"q^m֘aa!">I$ʢj `ޕ%DXN])KᴋUFC`*>_Vm\EĿfkq$Rê8'H)Pd +UW3rRQ6r_mPݫ߹)tɊ|>['$HVr>:ObNI T[.+QtvVcntw̆ mF20S9K p5lȝ*l{j96]ЂRTƔ^01C637;ۜT)foQ=-϶=°~#_BTyvN;q櫏ExYd[k/$8\܃{U+z;ͶĐyliLCi}kOg{prl7̒w,ʲOadmrV5([Us 4(>WyѴG1ljEQOW$%]\um|W]#?ҕߵhlc@KRRRXqP| RNBRqIN6zޚI*є6 !Y+ Bܝlj3_[h!cNGWBJ@2*Fe'85mcZvEm*HPO C\Jԕ*U1%;9$Pμ!9Aҥswyo2+ϸjo~DQiCl8~7h]Z+H=ļ⩻uw `'*}- I +Z. o_6>7|UpM<"#Ӄ)R{>L Zh"&Aᒁ.9"EAb 4FiiV%ġ4N$sN5mƻݭkq cccI'twQe{SḜ3sv}1[ D"= A*J6Պ]ȕ"q!< :VR;qpk1嗻#4Q]G,NB+hRd6ZH7Yw}e@!v*y,{Հ3aIqjrX30J '˷5HIZt. 8rwp+,͈%LVl7h6 67ņ=6s2HJroiH;AZֿ~C.'?eT f2)r4NX-TN>aP8˫j2\iWGS_^tUVw+rT1νjOiZ8H2lצ['gV*&V]܂vq?I~z \xaGIv޹"J}b9=]¬H+^d +ɎX$4&eՍKWHzƶ;tRեI% cUgClmE.!ӥG1Z)qH”Q/).MˁU q˶aaX`r߂\NʡU1xg'P#?yIC%cKd`c;1KJ5z%qVyYS:&fAnϧ{$V϶,K뙮s1G t9_[Uє ך۶f(E~Ful<\l$8 m_U)KBLcp:j7H#s^ qKpŕ(NM}#lNZ' Ĭe1 E[tKNQ=Y OzM3ħb2Iw?{nEZ*V |bH 8}w۸62%JZA'mOzbc-2Vm#JVjFD+t٪La܅熌BkIFgޟEKcGFW)(1V90~=KVenr[MO)-!T;r;`Oq}]k6hak0lw<4h8/wH]Ŗe㼒=X*)ԜgpNF.\z*ZRTR);7wWV%4S%.1^1[ci;~͂*KK؍5~(JYJTոlW׽͍ɽ"d.ur%9?i=f-5%`:Mt \.,.9pHHRI3>zWɫ5n9%:)ojlVürn Bx+d) <;deWQӬp {>j}lp!u)]=Ђ;V*쒵K\Hm. e:I*Ɲ"O i)t)BZĨ ex) BrHQ8Wdo1p[oqJNyDyzfD],b n`yrXI2JS|ةڒʈkXJ› nʪGZ[Q Rt8Vw0mf[,(\&rR~MKx*;O*^] vQ₤̀pw <n%]xIRkp($Sƃ%<qC#Q<ݛ˥ r=kI汱5+`T\zsںpGF>ke$BbV;EQE UkS?zZCWFU ߐ)ZኖAr?|b/s=aJm8U'|xۥ(xx%̈q[ #`8!9;$|aq ./?QR7R$()8A;;3*l夶++;|@OZ'"jr#dTMς-1[w[҅*b<#߽ձTW͞WEeR줜CFƩ[<\mY_ɛfx"Vc(RpǼp N͊Ҍ +5:Zo')V1#i sYH<^ˑ8|%{OR5+ؘ^.9ҮBHH /WQչ1Q d'Vv ߸Cn;TV2|]+Fj/qΊިy廡j8F@8^ ۼM3d3%.\_֋;.vM\]ad`I8 ?Gk6]eu(Z6HCܐ2[ܤ$>7>}m)yZIq=VIHIHX 1myC ܸR5rX@ u)9'8K(b$TҶȕ# Q0kxe'h+m(5 3Z]zȌnKk%cH~ϩAFhǞХ!E,dܘJPbhUvWw:,Mxɢtd-en6li F;j :JeGSnM)IEv7:q [dBF)$+oYʐ\$ەml \d>,T`L78MBSF@''jBm>]M܁j #3嫶st)pY𵲠 \*.#~խ9&"ltK=ZҔ9rs\ 0i,lmW1KA)RQWlX߷1ZTqH/b Ïzla#+Q8SUB=\']:OH\nGXX _kPFcc 賦QE4YNdh^8'=H mwq)1:KSK{ gt_B\%h*oLiS\b씅(f)ؖ6elei2Zo ;֐G.k͖SpQqk$''&zqU=a'>aV8Λi]̤-AQ:rI1ʳi(excoZΥuL=Oӵ3P E)Ncleo 2-[ GJ~\ EI 6KMVL:2GW"kziOFЩ=S`Fr6 ;`j^-Ѐ%CluP)nӡZ:agt :'|`c;Pʍo%țji 6bTRBNSW+GE֬U};rdu,rduS9pM3rvl"%̱FgRt ;ָAqJ :|M~U#>aZwW !JT2tN:4sLzfIŠviZCm65vizBo`t7 SgI('Gܶ3W2  te%_e+uGXqU 1PJu}奅"Hm- ry1PRoF*X4w1 2$ r U!*-UqO{vr=CREiֱœdr'VX=褛^b# !) n~:q^%$\$x3s?+x1}wcbkSf5kc7 ފ(+}n`d͚ʮ.J:QӅN6R"OlaR}ʝJU Ͻ0YjX] _dV6EΆmSM&:M1Qg v*гrfַ؇8!RXtGդClj3*_fZZq'irؓs-%]ZT:ri Ͼ,Wv?I hS7X)N43j4LgK ҅$(9x (xML=XqHwS6OXR̊^ *]K{ڞ\PVJ rzns0x@pNZe+tdv PPAt ȩCT7&'Hn#M?xbN*JJRpCiR@)n*Yw \p%RHN sJ. T Nۏp[*ۑ\RӍh%:AKug8NHH&2-,&d8~>9HYDe\5AG PRD qN>:ʭpH)_Rˊ8Wh.}+xMַ۹ōlŵ˩ː|1ȈP sB(_6$1``L)ءPџ!o-!O? uĩ- '΅j;x6!f|e[y"< a ZS69{E*d]$h\ٷtS DjHVAZIWLrJwIn佦+n6X% RA ((, K:%JC"'*`bCV tk2IJq)>RnGZX2BvV5VSM&ۯ8sGq'<ۑ9M\cjذeoO?jQSH5u ր݋m I#EQVWbT_u E lO|lglҌKZҠ?#°O3'Vڭ89PoNpQL2YJJHub pj%X ,!M@.'H??+r5Z:tQE 2ޢPo8 ֖\F}5eHBiKhycJZ߆ZHi.:8P!*߶MSklD ,)nx5g6 0 }[*÷ rTq!bp`x<°>*E|ϱU-O\P۵J3QL2XmI>~rU.}c8uCɍK~蘌$&TfP*Yҥs+03f xFYQʤ$lTPYΞY$\˶ xK$0NpG`B8z=`=6rCmJ 'Qެdk$?fTJ2.nnp@[i.% Y4 +9'=p^1 ƒJRZJI T^3G0*Ü:OC >:EI'(;}j$@G ­nlI[Ue[ؓu$ ? KdlMx噷?M eQCIr4D ( m~K^kJ߁in3U4=D樞᫓|DsweZ ܥo:];R|=%e||DL/8Z;gT9}K GOimVo6,t>U7JԤ%u$  ƑnJ% YN fbiqOHrU0ds6-Qn"2. 'owwΘ2;2۷X/ZKa;}>JhM7I<۝8 n J'd)1JZrҬ_H}>#pۥC}n@ b'Emm=sK%hH|in3HV2cP;jIle(ee )J'y#jTG;䚁zʕ'iO!e8.({3W[peW,3FZ  G*̥J.1oH)@=^r*SkRzfu1d.sRTϡ]ZVIܬ;n0SY%;GYhAE2ZMrl-]RhC))'q񏊠IeI(߽||5vЉň(l \I`dh;v*мI iA^3+S/)*+Ci a̱y_<ՙeۃ[Yw]˅TQ @**H7#;WDZxmC :e6D>q˼ˋ*ʧqGYuN A9|tR8BL7Pf’ JbYAq iZZ{5Uuӽ龤fF n+Oeе"B h@똸~T{;v$$-e? OC@NI=~G YGJW ߇ JR^L#DŽ Pr8BxM̚{UaaԨI g$jjjyfrÞڍ`òhmqC[&CV-Dy +H(Q8.JLS[iÍ>38}yN!޳{-u0i+-n3/x \#$֕i%[RJh }>΅*[Ôn)G$cy6CH/օ<6ފlЦhVG)AD3*k.{Gb EQM*  `&J&'V9}mYk5pu^?zSJ3ݟXeNYlQn+v+\[l@R'C5TTC C.)sobc:^ Q%N c\qM!J}Ftl&Eh[JR9;==ddy^㐶gTeJGiG^AJxRo@4ް$JTܾ nGQ|ů}ݖi=^F5{oCn0T#Ɔe7֕ӐRTdtpf,VmlFRr32C;mDc' Zpt=, Ru.,,4bs3|?3*'BXh.2#Fg󓚤KpoˎF=ggۇ fGPvy'piq+ ^9O=:GN;B*@mȒ^NTw‰(Qvgcnq%&Mz%2YBZ)J±DeH'Nw t..1kc@!jBd9$y\e? 3 v\fvnȹ땗@9ǨX´%n)䐝d`)DdAۖ{"TV,%uo!I9B ّ<^:JkָЄՕ6ͤG (}ew dl"d4n 93doZ:hqň\n\$["{/s4g 2' o#!zVUz-];$]pV$ !Z?H68#Xmw $=E@c հ]Vf|O6M,}LrYlπ璌E*J 7rYy^޴Hrcm**h[zOj|K`bN\qq.cn:T^mkFR+ ۰Uućn-Ȓ[pۊ㶤 K}!G'P$a66RnN^p䴣KLʮWO~GKfx3Y'JKNƇfBŐJ* grv/c2e-쩎 ¾qVV$@=U/84'ZurCd_4%{೨ĎP0q)V OE\`Bm5RKr9)y9( mzwy-lsHF_eϱ7m8 Ev`im R[o3S$\{Vum$t7QEzmu/+SE_r4T\ZJ?TSmj(QNjAqrdz?ķCcՍt0i V6يGF|o-V Uo:ш *3J<%jyԗ7t#`PuCjЁOSr+v.1A+]FSI4u-IP;O?I立# 0`Dyx)JV]J%`n 5axbVJF쪯ld9ǢŝJȎZoNJ5%I k(` "qh+CkZG%Pgzֱ5>ULr]h7_`;!5=CΠqA {ڽpUWiMMS+QZH![n+lr4ɪm ԤŠ!cJ3Ȋqm<3. E;*9)=5ouӱb^.!r>Y Hߝ/zۯtbar#pt )=5Sl[5?:!-J}+?)?đ˛sxMT% dH#I3Rtmo/cHeo7)Zfqk|Xm6Nο]u_S1:0f,MɆОPT=TӉӀf`޸jiEP3w(s Xoe%'y 㶠v4SI晝n7yl*Ax6/$` Msx>?_^MiIAp{+O;%`һ(((B 飋BSx o:U?\*;تׇ<.#nx)Ȑc:=\uPAqR*P9>E`qICȨҀ*))"65@gR_9DO~0jBf bjMǗuĔHdAu.)+X!:Nh'Np냽]8{+_˸U%JXhKiK)qu-N% H%G}d[w+tJQJrPMGnxaS ͎b82.Uγ ̕XbG]z>81Mf;D&VPl(d$>cqCc=ߞpcBx-xODmFYqLu+>&jD;\H,>,H۲# kqk) u'NjJZ]cʁj[B[IDE8pX'rC&(n,O\5xC~NeIi`5*f )Xեo('dUSÜm<3–GڑHu<*Be+(אۨYRRIO"j) 5m#ÊIildiq-iKšY!KH2E%.-ƅǖ4J KL6)9Ju60TIٴRx krS='aV 1q*uv2.zp!T) Ӈb'@eX'IGIqX7]dJ:ꥪ)m6OzʂZPg_W%='::%!e-hcqWA8p;seqplrrum)n;Shr2uP:xޫ]a kDd!HBJrR zM\fwK..)QBs2;O\Z:krۗѕqYgЅ%cCZ59nl.BkbpW)u?N侐g~H8S^":J U<4pτ}X7+,CÉ[ua^U]oG(32k $WɸF"׭M8wJBDf!~,ȐM %)uRT $ P3&62X.ߌD;܂^krW!-!|#9JX #r 9QKq\6NG42A\l ('n͵׉? uo%%iVRmy@F>.h 6(l8:M+`(g I+8#NA2Qf&%>mSc" eJ$$d-J.-)Vԝ*+rwo[L&c]J)!Ԕrh:%G!"8JV R2d =1X.-Ttj`EQV_W~F㗚ztvn~zrN>*@rVkl0;iYdR )gtl{rZKL7D#]xO?`X$V<.rE)Ő9$KpksZJ ;um?%.Hm-49@88?)g?-0i,r}~n۫c„:o38J!s.(I| A+>H"ǹ6A(G7rH>mor6MX5 ;T|~!T7ßߕ.v[30|U)n0nBf~ NN犐l7.Sxo))8*9+Jp{D̩"#ˑ'$N vp1qno?dJ6%[s*;pߦ:։N!djRu-͔4v]z(qUu،62 p0 ְ֓t?Uۤx6 oCAiiqI#X':Uٜra R5( d g<A^2Үo^jc50ćXl/|168JY,q\،#pͶsi[҆\J)ZJ TBb;+=$*rQ,% ((yJ p䑧 HUvXpϪ>ܹ9+՜$ /v3"*zbW%! O|\c VMgԶ%:4ݶq9M`-gTSȆN iIi3Mrb$kgˊqHK v*{x4 쉶*v X'kNy 8ܑ{GÜGh?1Y*B:% VS_ A~ U"S-!s`KeQ(Tʒ9 pGu1<;V`+]QLʘZp{.I0N}lruJiMJ:_mVu'eG>)8SWW8 kq-dlZ WFSx:oaTRi :5cL\8xY e"t@S~:THIa!t&yٓ|7#68Z`$^,[wGh.e4PYP!:5!JB7vox\6měOCat\Q q,.&;1[elHK+J;BN{QRrk]ۮ7U۝aԲی!*A<\⩊:|#[1-$6fSpi9.}7 L”setlJQ6WHSrô[-\J[iiՀ vpx}XXp{g+xjoĤ)#CI y}FmM-N.H;: qDnZpU' yK  s:É?Zc{*{4sci E -}/|"'1/d٥'0zAyaVrݽ)ֈ-uzPliJQΖt]y!ŭ@>g~jj7;c[6ʐ#H 9$}turQp]X "p߅]zS&y. ܜjv, ĉ8Hͷ%NHBCd@ԲKDT"fLN;y{GS):Î> r<\vc ›ڸ ZF<XH.sdTǃfG.[ΰQd%Np'tUc1 ,:80?/Wwxh PEsz;W!- OXަ8': ҅)E ߖIp^ u Gn;ͨ(kq%4NRylv9#XgY5x})q7=թ:|@ ;g\m\chmnZwiU99|7'I<_e[ov-]R|a_VGp]OqG+yHoL`A @c+ۑyv٭bK/υ ,dHm -h@ :rJ>[KWܦDL2䀤e[L1HFI%nEtw*i"9ApGqWͷȈ3crr%EAi Fy·8+g hT<$`㲾BīCﰫT{7-{p zᙒ.I.J!(eLdV36C_֑m[3!uT2`r rw骬pkV]0mZSJ̋KS@~,VYnK%ƕI;k٥8[82TN|l~ ~'Xgr1e$ / $}<ЮM FtdW-ct⹮Gbh1ظv<1 ی۠|f&BƷ ) '$KM2 %($0 u06G5wgx^ېЩPiȾ;㒃`2H@WC3 P]3[^L6F<?QEBmu/+SE_r4T\ZJ?TSmj(QNjAqr IVU,mlcݩ9Jga+&[3_러A*a!P3lնK i>’J(պ݁SYjB5rk,b0i$$yv; &-^R:I!lFj1Jm%9Jk7ŕdQNM*)kuH5N:$)n()+.v"!Re%a @ Fu piqCTI̕:8ac% y FIwG9uGR-sc/u撖HRwlf8s8n*w:7ܭ ]i2#mnZ4+JRFWpċ9ސ3kW^Z\eFOcc:D`Āi)5djKW"א'^1!C/rnJ[je5<Ɇ[~b;ǐGp_xj̹+8}V%KҐ$x0[C.B256@[͹ԸYz,I+GV(X=R;pk] \ 2:Zƕgi -is&9ۋ%mkP呟=^߄?w2SJX1pZ3݀7ި}3 tUx>ji_ 9ѝ@cnG:mS !\3Tu9Ҳci4\_M_%dqUq4IW.cL-aJR}5WKj4# .J}WԿ YI4|* jD=^PEJNE2Kz *,z@>E~Y5u}'r.1dnVJѤmQESiW{!uΟ8-F6j]\ӫ鯈d0W-ħU&iUV,J x#d, j>LkZ!!HOJpbx'C@EMp$H: k;U=tGY Qq$k7nufC %$ϕ aR`6 9-{D ;( Zw?-EǼcM)H:A"Yxmvt̒r)uӈNumJ W9M1QIIN k}!![i&CjNT*Fdel,q tAzKLvM1t/þ,=5T+"N2XgcƔQPRVw`4AliMORP NUI-n@ .vmZWEW/psͼLVL%))IҰ1Q_3N(| '/Zh({cQ5KQޙ%:$ 6Ai8 ~aSl{ #]W 妋 Z]x?-z\WW^*Đ{;ät_}}<`-R0֭+JAN6sʤagIgeKSz*gC:^C<ڎZ1\6ڈɌ;]9oSǼd20۫h8H#ʼt;E<1lcLa8- ) `#sAmAơ . F\X!kNs<˦,x>x$^崲,x)vgf]53Ka "+35f*ijNq1,V]w [؍˹!ҝ Nuzh\ڈ$KS}wW״V9zWә!)'Xf3Bl!$t0├3N!GJLPaCj^"`<epkdYd*g'&ObpIZ Džo+FjWKV`8xz'JBD$ RDC{mEzEQBqLYéMFI wJ#XmHc8t (s}#~ :@o=>'0 EĜb:bS/ ̌_VCw]5E^X'򪄔3!@VusR/Ȗ@}RAȸJCAąxqIN& sS"jg]e6J`֩9ըCm$Tt˶-ϺPSe ܔ C;wZ\SML$%p%YVJl4|20BSXcTm_X()Cz^tbqe!%>b#-/RԤu5;x%DjVzډ%ɁqR@OjV&U xƷ*yT)nbZT()%Xlr1X(0j[:n\<9xG#erʲPrIIHP Yq\lIpͭ\xZaUR tp*lnǹ;nχ4]CIe$8 SaVjˍ&" .5,6QB BB=ТX!rb\7E9^ۙdYz~+9c&N2L/K<.Jaʌ VHGN79*E\hT\[R➞# 8)hNGXTүA)I>-zs9LbJ[$KQbPSoi̊͂-c\%2z,HijO6[}IJ"(JPS K?ėi7p^|Rؗ)䩕:)/%2HJT' ھj$<|]U;nwƭW&TqjnLi b&RԤ([gSB␥%Io $7dk wP %Oh >#%)ѐY*SIa/]r U ;S2>Enn3<Ӥ㺧-=4ci4!Ї E~cBE[ czи[[#.ㆠd=&@'NRRFyӕs29_s/ ݗa6LF^eYR.%Jn+:${ٶ=K-Y7o1&b]yK zBIִdryI>VG-Wa`1Vrh#?_YV3U;Hװ5~Xm}~~!'C*%OLaͻBP;UնD˄I- Zդ+;mʶx$_n"-' @JA<酃-Dq}D7mG;$|V#+֚8\o~{ɶ٢@u`T v Ҷ !*P9*br"J-Nʐb9i1mGh6 tQ{G-B:'TbCf1U-=b0q%@zr?!$6$8z8goq,Fԥk@Ry|]mϖ9K]=y⫮9} ;Uj.?R֕Z#'Q'$UXY蛄ZUٱ܉Bk1Ĝafu ]S^ I;mpX}%CP18Xn^ivD01K6i߽jUnpE|BͰ?ZJY[u2 >`ʫvkc 2P a)lyruÉA*XȈ5SR0+ĩBJ~˖76sH'vvd7 - q^nWs 9-'ZVNr;@(jQ9*99ޖmJOW^r7$t/I7 ɤ~\ #Zqϴ ZvaP $(I%$5%TĆVi#Vĝ!"GFΈ\shJa`yUcUdS4>ţOXjމ&@Em.JNJq*ǛRMZ0*l Xӵע8\KQEbOfsIq1m' JQ=}q2-7.(p xRc}`rIuvU^p&MxKJ C$HPv3YN]2`Dątw18)؍]LpAF_ŀՋ IG*ųTi0Ĉq]+B ;y b`[vdE)I|cil7Yw{|Sm G Jm|qam$}gU}O[JAOjiue4 PcvWnbωn٘NP"?@A*W#>LJԄ~‰^14b@爂rDd8kZ-EO6g|I|CaD# n_"0THߖqL;Cڦx:o} m.4;vm=%pFM׵fWkHr /ǵh]Jf'Cg2NI%Q҄<$|q<ى_^o VsYs+,[,l_ޣ2ZFPHjS(⫅TiM) {ILcfdf3DV?Iޕ%\7-Pkcmc<A]=AW Qӌ(9oT_q '?{C[[Py n7u3MۊIq:VҒ:Nw ]pTBz[O09|W;< ا+ic"-krSgC>2~7W iCהc MzRřBTi";d$7N ?,%"}fmg8Ji '܁C%"+ jVDZ[;o:%2VZYIKIz.A~:kJV 6[ P=QmzZFr-6޼ܳ?!c3ZBؔ]ѿYN[aqB'0`%}Oz sSn-;jB V-KGܸC=yWS{J#{Z.3cDb4fˎd%#+mf R|@@i8ݵnʈ1 Zj2:Ȣ+Y*]~JTE\įM.q?ZJ?TS\\xҝ?߬a)'9٬zT^ad)<Ѣ erILޜNX,D9\RHuڻM[0iܬ p0j`cS÷*t߉HޚĖP7}aQyxSr!-Y B]K]`FJHҟ$+`5cj0!Zն *Pp(,)DN_u$û<RQmFuY2)o9[a@-’{<ԹaPAGN˱uH$vg'@#=fȨ:mQ,j/ߟtd{-imajS1F74{'^[:^n5BEb c Njjd!TO%gqT,bggc[jrȴPUNs+#)Hi?`]'b+[#V穦ݟ "/nj?MNۚ$+ $:2Jn ՞k AZvVܢA)C6 6[;#zKAqA]By+!*$H:VFhΦқJ-l uHuО xe*]JFX[ZRi+'ӿuScxsVyg-=c:>Ғ7(䌃 VLc'>r4{J}W1ͿXK? pn/z#7jnD 4mMP95)nzWf#np@JC.'QYNI$1Y:!QOvskݙjΩ>SmA]:1zzg?vS `Od3K`!J@qY|1>GG[lמHSWqD*8+y vbhۛ!R֒I+?9Tݗxx+]韃- )㍚nMp-,9%X7$Nk7]K ԃjG?^v^~X u Xg+ v{S.2pfJS܍( 9FHjI!rM$9fE98~TΪ@+lS 6?-!8 ^|+;LZӆуT:RN|SGUFUdw1܌AeuJ*Y<s#y3.~Ϛp\G.U#5W=K$!rzX\J?c,'mp7m𜩼9;֏Yη?;Z zJbL,.dhkQEU kx+o+EфBvO Ɵ\ 1ԾG EeɁi9Cz nu7$.1+2P @ǣ:zŒ#IGPkYB҅lVvt2SwrG*\+Kh:vT|N)i5궦b5kA夤 =F4Iȩꮹ ˎϒ8*WSpyFv6PX-<ڔen jӱ=9;U-HƲkӏ Γ`lV5EH$\dVsޣNZ}:wV0;k#G@HN!'aZr C?b*r P 標/8O>I))e4S?Mִ] s@O͎Ҵ*/4=7%(|_|9֣Ya*qJuD(dg^hPGKlɺtu-Ej'QI*OniKr;`{tu^ Enb -D5R)${UtWؘIϛoMMϲo\vj)_-,c;?*5poEQ[I4._W.r%hu}ˉ~4.'QԂ?fV~ٸ=,[?mXrM: TkÌTA{oQ$LmM<fiqM{Rg}*L}/M('495sX YII]a$2krJm4.e8};((N9UNy^^$A T^JT{+AB ʗBs$ځzijJTv7i:+p8Hd|NShT)b+}6S>Ⱦ)CO)) ތ]~v140 :"Kx4Z:p rXy %-$`;D O λ81ٯy֔J[dnQj4r/Ӝ8sq'i-;} Xឹ{ ))nCi8@²sNX{:ڋI`IY яO*B#ԢJV[ XZ\bYV 0rC{,[mt jKt,xĄ~AVf[ H'U#%'=DOM7Ai6Kr> cRU|q` ێCONIr; \槠_:hH[!8OE|q ZI<_U #M[XKNP P7?b{!8a R{X9G>^+sV ERdQE!~otGKeCR.-^~!u҇7 ''oi{AO.t*5E(yes=.S((明\BZCrր6ϺeՍiY DOZpS?5-bR؁P,V,oHЧ OLM4$`\*aϿHҢΤ 5+{IÉ朾 *HWd5Fw(As:෤DJJB ],.QcI/ EmhH*9g& BRPpQR$ū9*Ӏ}9\F{h.SkSJ+I) :Ó4ѶƬ(V?5zBV) u,њfY(#c΅}%zG MFM/a[탊tZ \@Sm@ܜ%BԐy^O^rc=[.xA7e~OP/." }v\s|!k[t+qR9՘q15~kt}oWA4Wነ0RGj\pG>ؑ٥{ O's-)igQWكXÏ3?oE>< in hVN @'_> Ufio (0.N{y{+ %;LF?;[Ȩ׮ڟ'\;F{ =fRꓐP@۴ԕ|F|tfey1獹S1j p0ԭ8c7M#gӹqn"!j*;o0Yk%*)lg못2Vwp'5K j:@j48fv/A[mM O▋10[Dݎb tz%>6Um܆'Ɇ4+UԔ nRw;ְ21j[C72 >ډV=>6 rO2iɈ$R31;+Ŵ*TdH[-ԄhP#bNeWe8eR ʵjV eޝژmјe=\HF #^NT@2 Zf!'8 ?o9`迄]d['ʾ/#*۪BRFQkYܸɘ-J:uA$OMy)JkA8\CW.&/V2wOgp1ֽ0#H*K. AȠVA)]P:3ٹEC0{mR{Nbdo i*u!C84_mh(|p#+]q^uE.(U"_dq7JLtgSU\񻸨Iġ=@Sf\9'&Bw&뮳,H&G;T2NFj*Y;ө.<郤~5M*iHˆO*}J['Hŭ+:qBN|( 'ׇ\O}#`$Hޣ/Ӈ2C$j&&NI4HՐ:w'4*^ZP6粞۔[Sk)XP%IC#dG'Z2}yTE$r4$bPQ+;0;e$u vid1LG F9MdH;E;H:ƺlW04\}xͧɌvڈuj#| e⌅E ݹ]HiNҡTgYyƏ4(DԵx,}7$cC|~JӴE!B63Dl7O@#V;jE|*~ ;."4 -z':vcc66IJiO{'͑{'͑Ty1$*<c[j)%9H;;9K5sC\TKlRK>lUK>lU%3R1O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#V/2ii%(16Ї5dd+*N?cxOjkga2?+{:^ Т(( +sLǓ8UiQySiYQIث+HtoerL)߿v{-_Xq ~aҋHV^dz^dz,њf2^dz^dz,њ0c)_%h6G%h6GF2^dz^dz,њ0c)_%h6G%h6G)Hn4fϺ2H'\-h+ě{'͑{'͑H<9t7[|[0cLBee QR/!+v84=5QkԶ\QB-XIKs|!n;X3^dz^dz؅<p-e'X  0qaC^F[QJӨ+IH>HcqHTq2y/{D=Ty/{D=ThY(c)_%h6G%h6GF2^dz^dz,њ0c)_%h6G%h6GF2^dzed>=le˜N‚F@5f bcݤ6Mu~7pxʊB"(좃Ѕ=%^ki )9:rN2I5Z^dzCOe]EfGFVez\IRK>lUK>lU%3L e+QRY4`RK>lUK>lU%3Fe+QRY4`RK>lUK>lUzdIo+ԬN2@ 3ٓI[zs&b]Ȃ>N.Md%h+AWfAE{oU,-^Q~K>lUK>lU8bx scۖT!N)Hd!>]V``U6ۚ0/JohルxDcI1]aZrr>K>lUK>lU%3W e+QRY4`RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FekKs,yjjԅ>(䜅 +'=!=lk+n l7R QE`uG &y~y\ΔU׮{\[kWF6Ҹſ+F4F?sQRY5p屔O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=Ti;ۡ[q)ZTJrv?&<ٺ|<|bFBz$1m.Ju+t+q^WۗI RDW+d!++@$ 㾩mDx07&K>lUK>lU,ܗ9>]s͕ɷdi# I$C&faTmK>lUK>lU%3SqO#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#Ke{D=Uo3fûC,Ƶhk#6˳32%Ep%TB,ޗ3aQSXȑ:.#% :,PJqaURpW9'q2X_\s%n{'͑{'͑ꤳFjU||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑m:q.4--')[m% ̤Gihs]M%]dΩ.4rn){I T^??|R_FqulUK>lU%3R1O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#R6̻\CE!7ҠjI9]|\Y(w&lmkpq@NBߕ(2mu/+SE_r4T\ZJ?TSmj(QNjAqsD8ֽY89L1Trlp3o ]_?mJ&CoD\<PWXނ:tsJpOδ޸ć֩k3ά=A݅Mcnwß&#R[Be5ԽTIV##tXY) {nnu -{ڔz}D*T뇽J$i T5V`hHnRڨHUv˗KjU#TYKjU#TYKjU#TYSֱ?"r? }丕(@W`U཯1^ٯ_ފ(ZKu-rNmJ8PP$|U=?p=?}U#UK/t5R:Et5R:Et5R:Et?BjJz&F^q.6$㶫2N78WH e#*.#iV׾IP^6W= z6 u}o?8(R pA'$aDTSV(&jcg8{~AuіWĺqϹs'uɀ;p_RSo~nǐ8BFTQNq m=o =n|UTrhd5R:MY/t5R:Et5R:Et5R:Ett(sҥGv:cN;ҝM5->=Jӓ#6Nl]J9Q@D#E 8X[e]D^=m8ۨ(W9 SRO?BjR >5JKjU#Tŕ7KjU#TYKjU#TYKjU#TYVndtm}iiZZPzg#9UmT!LK. A q*!2X ꮂQÎjլM͇S,`J|MzN2Rfni\wEl[a-4JRT|<> 4i)4.FԠjH/r&=d>'$y»lOii_8IXdcw/:HUhY't5R:Et5R:Et5R:Et5R:(.?+O*T~q- u]r=SRO?^+G:rC4>Ȣ)$GsK5jJӚz@A(R>0|ꯨ{7Ώbʵ`FGUr&mTjuQ"mTjuQ"mTjuQ"mUzmxvSvFA:QA %@#IYq:tir\ SL aHo9*iyw }&`䁰 p 1-2BP) Ӎc$3a]8-<p3ku֣CZR`aq(V0@NmLGoh[aZB IJ;clK\ob~m{6iNjuK,6>$%"R:Z#hh$mTjuQeQF,QF,QF,R]߸$ЄjQ펰/WΒ~W(R)\įM]~JTQrQj(QNi\OģE9 |-bx|- ʉJO2$'5VyIIШ#&Bn{@YGJOQ5>QVt_Zs*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩V^OJ:yV('5{#dc b(M] )SkCJ Sn$sƴyQSGL6=c^0\,k}%'(yIIШzBw}UʛoYGJOQ5>QGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YVb8ҷJk%Xga%eD M`"(袊(BOG%ij:iC^mZ娶U>jk}%'+BemLma)wRMtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V W|1q! 9RG"(+&h{0ֆ4X"("x*rv>̓%өŲ= 9ޮTUM$Gn *䉒 <;V{5>{MteMշ,k}%'(yIIШ*|ʛoYGJOQ5>QGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V G􌟬Y-v rmLÌ ''>sHUU<{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩MtQVt_Z7V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|pF\cpfH}׊zz(mEDI)#=dbQER6˗ܹ_*.]j-_r~%)6}ˉ~5 .xl|c b~k 8RIt3Oꥺm?ڱCnC'ORhQw5*j^:ЉWۻ`):6%3ˀde'O!zTBN6;ds#NFPp{|uS49H ZZAh\=G)EN 6>3]5LqHOPOgW9qg885-FEÊ;*Q''$vjqLRBp$=YI9I5S{{F~[;}5DMprOAJqB[)~jΆY)h%js0䑱upGrK8,la(+ $2Lx4ӡIi B|eV\m`BWԶUN5ԓu] o{ $@&E}*p ;zJriJxN1f)鯤?|7VqW]xܹ׉sJl{G0*c5rc-4V*q x1E4Ӹ-Mbpxv1|]4@ G~gk4&?{?}r9jM,H=KJYH@=wY=eF)$8kHJ:N#FzN C/y:x>iV5-M:@?$v~zA+L""$־ Wv`U큮]ʩ'[.:Vb"*KrH1c+US-2]-i|1P%:[1zqł;ߚ#aa|Q4nJ7ޔb/}v;5gn rSpF7ZO "PRahk.e:Ub,?yO%_ +.&wC='QGzS9c7luVRBV )*p{#uS(nN*t'~;x6I54684ټ.hN})?WS1I3.=\$+@iJ1NGhnU=V \[n#=`Z7XUd{`Ǫˈ[Ia`I;j[S}&e79]&;G)Q:G,n1X!pMzȤȖ%rN$B$[ӥ!2; cv'58lwkϏf\3PL8nhj C=΢qr/Jl(F ])Mftt6CēAAِ~ CI2[i*N7% #V&/nvtq%pml&B9mgsrwV]>Ϲ9K#{*fEV] 9x\Tn(:%CB;+oi*\ c}]C^/7$^7gi 0;Ӑp9{{*̧QHkF+KfG 07D XMf|oVM&(ā^ouy[4.VkG]5ଠ+K+P(nv=qjJ֓ii6pR|'~gkRJ&_Gv}QJ'EP\o۸nEexJ⒑99闤$cStú/r"}g@(ܴ%tfwt:SG.'aꔕtCF.˱=ؚ?Ek=86\TԱ^-n]ާ/#t>56?bX+y%g*~jUWϝ1tg>4W٧q+?~R8cSkwG U?~(7U? TZt$Jh!Aj?xƗH%_SƜLf|L?fq&%HX8RT0A" 楉T1S6ږnxy2B>N[1{\U>4 SIAV=fkxY.)+i+pqyCrq+Wt(!-r]i:j%Βx \hJ'+">M4!DTŸ$)Ԓ< qq/\53#gZ$s3u]G/>e^$ 4Y!ģBpr"~Zk +I6`7[eTV{nnQETV+doLQn?ȟU_C/W}M"bnAY,=0v6'VII{RIHN5 124uZН0C؟U^S}I.'> KKZ;Szcs榡@/ckhprx[n/Sfxk6ظBI|/>lx; (,Žo𝙰8e9ҳY)2A#l1ܴy.<<û2 z[Jrxndo/L+((U'*v#*i~4^UdKl_؉?]쏀'~i_9΄*\/`8#?Q1(@ ]EEV:ETzc]l}nI)pc]RRO!'e97n'O%/.Jxg?*u'vcZZp\܏xX^zX)!\*b7MJQr_8%ERlRM,-9U\8~p!)<#r>+ wiS|n>-澰BLnƏjj*]o%ql'姖n Kq-%UE\l3!W7b-tdmvU9f(iC,:zY.x=}Vl;R}k|Ņ-C d^̈>^Us2ۉPIp+tr1 Vju[So-hd?S'mgkW5éys^Vx(qG*Xo.$^ml xFwƐ?n~#qvvt&QK]C( _fTy],[0RvNjx "nkloRACk.g{mG8 O_ED^0 rӾy&Z$\pcl@M^'O3&z-`xq.)]KyN\~oXDտ)d9W7;olye+[w}eGM|XRMp,y*zܺsäŎw) m"mWiQ.AZƖ:֣ WpoF= 3>o{RV-(Z ؊ $v~7Yl#;;J8ץ97u\m0 i{#zM079 JUA2}Uf fxCK%gt2|"o t%xi}W>+Bf.Ro'Mw^Ӈ%u NDcd|yKq(8'n H}FMS8:e&CS/:tw-*KJKJr4C>cN|Oجo|x9~.'bp 7%^:TYq w c)mfR:TW-[z\XRqNpS}>Ezn.3bF5#?'}-+ZAi* ܴ"AmCH-(6`7X{Gvsp i6cY@}43##;u%jKc8^Jg4$ wFp: gaM٤UÕ$@JTRymSFcb2Xu`M)&\}Lk'{['(T(n(qC>h<Ei$ qlwiqMdD  )rVҤ="9~T[UT'̾^'ywmYՅ\gЮW(ݕ^\Y)Rw#ӫ8[.2$;kjm.*I/"nV,QXҬ)AzTq[<֋ĜAMw a )H%I.yR^w6'VZM|p7NJw6'S}]--ou攊+ 55nxotzTq~|og8+o<q|<1:ڭ]zS.('WNN=((iA^+GCs0t{xt9F3Fk_zi=춰۪Zyywzvc$Y!W.&XW"ZjgKmᵐF67~."F}MZ w:[RHq!#^1YU *rqE +SjmeQhQ#N.]9IG 7e8 (CcL`N;IuN M v/c MӉwݭsˀ[FCO"3H҅nA+o1Z5YRڗH:2n()vW:Ւ`U[4T2$y[IBENؚXEW-K{0VdVG*FX~[K6S yL8JvaJ 6)(JSܩjI"B1mY@ AK9̊jZN(wUWW!E-I=Sej’R{iˣʑ[zfۂ;iA Ï6iqtQFɦ d8o$dl}88IRƥ#y M_lǹʤ l6**hl:⒗8V jpfXb;6=ZUT;q+i N|'`IHJ-+ E)Д>6ߧuuΚ*" ( lѤ&K)le)u8;c/6%DkD/gɍ@'ZkTu9@ n 2CqOj75njDC=Aު}2$-@HB[:ߞU%(J-!);o [kN i[fuIqq-=N6l0vjO>T1.4 !8QÎ7uXKH1 d򩮷+ CvQEi=;L/<ں)#cAK p\lHmL+invs 6ˍEXt}yve:):HFug}bW 3qZIaͽ)#8+"}>&$R)RV r ٵRzKG,V_mkZS+IJ INl75a$Xu\lj=Ŷ3!pV\JY(o[:HbWm>6*xHRH l+p.\v1kBÀ&iM!9l-Fx:BrF%ZA:S*0;4[@W Ee.S$'nV]_;uJ*JJu twUjK ;=8~=xuCO5֥-xn )bx.B=-0#KsyG|jQ3,m/,uͯhe'!k_w-rd~| >sKT;Wǚ(a=s*PO wЗ$(o@CۭJʖ94;Gk \vڙo`A.*kqŨ$9;d50ae@k܁嵝C'"K1k3,rpkJX}|K'nͫRR8Xa{[wKB4`WZc?4O;=䟷<:h]UқNETi/m˷qF5QF{x)ݚ5 Ő2JRZ,W 0e VQfy^N1G$Ud>$E~qTcXӯqZնk0Kn1WTվ[n6rr9:Σ~JOFRݹ$Yz@Hrb!wqZcE@8ҐqgLz䙋\,o{UO)?j^ $] C<rK;/wFP̱6.HBxfE b Vݝ3Us;C*Vpߌ W{g`hxwKw_>NN [Kuo:9y$| Gqtnv\f[TXޠ5ҽY5۸1!.ьZ*/Xkbg 5AH\aʏ>kVh$ 6W# U?ղl ԧFR_Ůp%1Tu!ä!O\j$]!#u{0E)wnd:ud2$6VLuw%fh gx.6EujUꎂ7q^0#<[>viL d3nGrGWmWkБɲN J^;7́mBd4BneIr8TyWo[0~ TYkBSJylfK/0ٷ4ږJӧw$skk7lmHt'%l gT+\)m$2ڛ*Pʇ#89Ǔޫ{q9E7Ѳw.ʰ<2St[@t]gwDt}ye2 g:dy+;i $T+*^܏G#7(.Z^% w1WR`T%ub_,n\FhP܃4fdFzXU)c%?0,H ]袊**K7E u3${!( 98~8\ϗWg Е-  =c"Jn|1螧ӫF| )Qvdq]m,Jyv )G%X/@.lUe=g6/Z_ӑ %)_d. 1 S1XsȜ w쮄 ߰giKq0#|  h?.ܢ2Ta %$rqm>v3Fָk$ {3'fkjIQ)ӵz^2?mOa6hJCdeלKi؜d'ZLn,ī9Lhi:]dd+[S'Eds⹡G}~ˑpkNTS"kT)CH~S'[ncb-)WHARJsxgze{n >ϾZ[P8 u-nܞ0ot}r:.dpRTR3F=|s&, Ba.DBe\qž@ !$o NZUnQdc=#P$27TK# 79.qk4怪~5Ϟ}~Ozoʭګ2INs %GzbUJKs[an-+u)Q!3f}SQ2q$}uti *hq*->Ѓa0ʇ!d%iМ X dIzH{OxҖیtߙCy*iIE Hbk '{+x}|-h/11DJIX3v Ɲ 9HWK}|c70= -@$`*Zou}侷XdlIhqZ0Q,x$CuRhNnvB۫rc)$@p`: S )$$ j&fbܕdȩrOԅNTHd6P_\pc!'`1jȒJztB>z:D?/EU\qQE._r4Qu/+SEE˭E\OģE9֯q?-B>CB4Vڂ:=HA7,ݶO8?X-<3.R0 JrJ[#l-y ڕ'T++Yo @:sy\jmI*mѲ Ϟ_ R%I$%zFt;~zb7Yn&*|ӌwR4Vq5,Geu`iP aU6f\ !8FJpσpRQ0KmSx8|FS]i\Ì)xaA`ό4OZ殰k~ 7iEQI'Q\UyMt~R&) I6c9;kteXJS:; ofie+RR xs F@Ԕu9+܍A`+M3uoh>PqF~R̅/J# < |AЂ6V eҝ_|T1}T .ѡSoCTui҅/ CC 禱J uĔU9p}ZThRm%<ҕ!):WdΔ:6q+8I;ΦWk2ڴ>zY!'VGX0>iER< HP BfM_0]]d:Jн`eɝ )xi޻2(TQE!pwIﺎxJ^?,yqitIڤzSO83/rꬱ.)~ L`81@ҡWГ N͑n d[@TbR3Zr8xtoKYia+ i;kP',fnW+Kicyn-; ł^qH-8.7;Ë4Ũ%(m8Oܔ3| U61;V"H8jV݅(Z:ܕj5fР vT 7[]Y%?mR5+;l6\u:fTBR dp>3HyY#laZ9fZ\<.q2W絈ds vbpŎES_Y; r9̃Y'ͤ[UF΍'[k(u) zAobj7-NVNu{7n)Τ[@|U&\;f"Jon-A-̄G(lh9_x[m"MʹGO]Ce[ǚӯxo2mjS)()t8AN8U B#3;iiF9B c|{:clDFR]U(Se%(JTU$$oW8AWGt4CH^䪯mRNsn]*&)7n8-KrCuJ@B9$%G,Up9rN/Yo)|gz-}IJtVYBnqT=ֿ]+l%QEqi"'_54-tAڝL|QN mLƪppAcm+nJns+բ#d ^JBʖ|7:Z,4.-vdLŧTXJTYM;eSN͎gTXKŽGcs=KqUە2#֜dj'섖ʰ[,LHHޒGuV3C`>1H)Βƭ&0f-pTsrv.%Pfޡ *, *\eĢS ^8N3.n܌IyԔ pwz8\hb|4:^yUl!4BԌv|ּ&4FOSrsJ$@8 NO/Ow Ycs3=kjFFFDY:BT/:i+;P8 u)Yt!W,cOԞc_)]>TuQ^}k"?eAFt6MVYײT~ՠdlϊsh{ og\ME7- s!Id/}^.Ш))Jj~LJ6H+KRp/iu/$$SdO^ kI(*jj8Rd~"5F>BA8{R/p> 7aq }um \ultpQnlĭ*窔 [}p%-8s)XZ29 KjDzC ؒ+7o+"j*reLiɺ1-JLke :N\[n$#%c rx9#̭QX$=|n! 䴷RrWfݕ"n~Wdt6ksm0nir ؏ݢC1>;Vocoαn-:{TOmH^>[[ HC'X'rBn3ea?eEi1XJUȺ2BqJ5ޝjJh (j=㙩;ct50rirFNQ5dyiN3:ҟO08pyj&ъXlARIPRsق;Mb=`Z8ɹ+i@%V~^z#bJe)z;毚Ib4E%u~s퉸Lvt+.Y?W\xJt%+e6ƪ;5"X!-ZBN2w\8j;-MdJocIv"OW- #4ۈ9rk:HUmlPU' #ۢ|X 2ҾX @VI.#=O\b(|wt>_Ÿtq2t5r(~.F]?~tnSk.4t\Rcݸ9cR}!^ DëmJqÁ|^(j+\}C(oWcqI5E]*Զy$ Q LJ+e\1[/.1bBqj*\mBV|`AN綘q*} q %k % HT| oL-3 "*`)';諁M.DT-<;?HE@#j`3'P!*-IqՀ 8L;lIR;n붻c]DU8Tڴ$'ro\[ \J6;i8m'Y_Tz2z{ʓ}jTlg$avwKYFzp&A:h6P(90,Ix_NĎRi %X 8T6Ke=&b_ D9NE-Y i+!*QzQ\9*֥rСT7eS8Cl5ZѼft / ޢ@lwYP+i`~HqWIz9rBa2RgqrjKKyz.r(*^]WVn_ꄮ'QyriXA9ww;F2em|5w=1Fu''cku(Z5mN=%Vxf nWjC`0yw$ro[ g~-m7w]”%mrmE`;*oW! 1؞[R돩S8oڱpݾ;ϰΏTɯKupࠦ]ݭobKl;o۳Yv䭹#k "Dpg.<*5 Vn8J?mWp<ń+HljA_uج\V6qe !*)#gͲiCɑāluo)vd&4{nzvR5r!4HIU9s˕m3 t䯬  5;uBTnq-_>ZvD6ˊnG}1]{44m3VTm82!w[q#)-%ԥK$('$E^d|9箲g[{``'mTۅݩ-xxSO"bxxx;aS#*JB ^r^+wcc+Ou.)P JSZ(q N9dlGR3i*(SJp{9wy$'5ݕ>9۩vѐƴ#3 xuQEmu/+SE_r4T\ZJ?TSmj(QNjAqa㸍fڥ[AAuCs:rCcc8@=Wn:+:F@*H~uG>p&A> g'm*xp 5Nzԩ$A Rv;`SA:Uc;g*#]GE eN`z$J#R|dVꮣt-h}+6\% ?l),n>v<;|7U*)TIIqj2.qI ,kBР$(cH8j3 e!]a i-0TdaAxz۱q1M; kOrѣsYp8ԆX'8o8$fzF +s^T N0U7رsLNRK%+ P;Wz뚙)JCxܟ>0IkƄ/3)]M#:g)ۯ63ͳKoSJגHŊx$˹(\^(J\QSH=1i!ia6nYÁg`#U^a=RBNe|DxRh+ :S6&hcJ{pqajpڣV,NP֖U-RW %wH;'~uU< + QZG{ZRd%ERiW{'_qM-hQLtG+`n"chQ*V1oi1wгˑ`oR*$nZWl 2R7zsʖdm`IFXR5gNgɹ!}5Gq@=ݔ1£-gk9uAUT^ˌ $TA7>Y{2Z5ڇgR[{͡ܒ NB zE'uͫHl#rE;s5 ;m4@kBrQd#8V0rG\Ғ|)/ޥkP+8I۞3Omu@!9 Z8-6(SxA Xӝ3[bIaLiҬ?b9qOMdl1nns6ɹmؽ7;je,܆C7ȟ -cwSnvٲN}x>wN5,+$3]+{`aZ\Fx-@vrL]Aտ.X\KJlGXZ |)U˘qY|ݦᐹG2kUUA4TRɈ.Z# HUʝjmKeNCa297 gR-!mǞˑʛXu p+ JH=Iܶ›!16d$8IZN:>zom?Ʀ^:f[i҇ yΖ1ԸV&pFwQJQ{v~m$fqk#qW" ǃ\}=jjSS C9J+$e'R5VdwP'#PРoZ8"ͅ52[P[*ym(J4poQL׆cNgK콩9:XFfy'wgnQlHm$:[KXLKRA ڻm+VwnyW(!p!F&!MJJsݑ~*3߽/N\b/A4GN?FqGYJqs o%r|Pגg>cq}#>,MK2RtD(aZ~ # q'͚ϒHFF~ ˍ_X\NJ;MЕi lcm!V PQ rryӈ*N^@%%hmv6+_L@ݪPq \9.?DyjJ|<;U5J{=kغ(UR(/) \8MX:RV:N˪桊vN+dPsV.&{cnhQ ֣ ։-e~"ÇpwGa5JtvS1{yRhswVӭ5"p RFƳ9ЏVl֕k|tؚY}Fx̔K.Jp@HrO-V8Gj-Ϻ^*uZ6ߝhS? !a.'3ao:6o8GQ095OjS,DJT I؜CcSds<B3;{oXY=FZ诡E9 ]%Ε(saTvJ;.x8ggZI\χu/\U ÑP gX/ےrB%HZH S0T{R=HEp'.R3+u&1E7骅hƯ3B PZ~/| ö3pՉr}/: >.4 ^t7ÎJ~|ǚ8FTY8V0gnK(*X_M$jZgj1PYVJOUlߣɔVUݩќQO|Gum2%-Tqeq%@Pp;M8g2E}Jٞ: BTsX%H$/$iΞ¿Z\WeBކƤ}iӔ$!ᓒN7M&kPfW^Sa`RΦNXؐ|,M$]FV 墾-"j#[a) a4ZД+b,DodpY&t1iXq=NI$$xۂMPl٢Ozmզ:ҕ.Iʝ)% H@$M;K\ r;cJ Jd$PsJ ո4-knG |&h1<,D <Mr?>"~3N!eZk'}ч:f܏oi XV { z˖]mm\p8TB9`X1\7.;έ*աNJ#$ IKljwHͶJSZr:O2Ov+RQu)ZCҥS4T[0]kxLf}e=A`P00ÞUNY#%d>yRib*"{[RJIKN6p |3{bx%ciܤs8"&ʺGѢLx>uͶ]pպ9qr4y+;dv`rkF}2TƐ̆ҠP^JJ9;U>U q-KydZWVbc'mGqYYv&e e sBT2Aդ4n,';.ƧQ!@] KJS@ N)k3:~Z.se+mN--%>hoZgf_lzaP\J]-Jy_YQ:N)L7+bxq5<;mZZA^|pHsqT6nz?+\ NeIRNcQ;cˍ~,&+cZHقTuP7):Z->8!1ӏK mώXT2RyT8y% Q3uǛ^|WXmK6+2Ou)oVN;?[tW8N9]'ps~ݚ/!,2#5+ci 3=Ax%[Y@%CkJhWI+s񺽳їG{)gX!|QK<IH)*Z)G*1 BVړʞƾij^IRDvCn2Opך^:4PU:G :ݪw._ -D~ҝp)l.۩"pN#DnZx"m˭vZJ>%ZB vrmk$Yy,#i=YZIǤӎ? :DHBJ4KFgp4r;ַ /3kf_Q5v&rgÄ$F/b~t5"8y-i<`w!8Z6^eƷv&*IqJgEI ڮH~#y ,(q,OK-')wPZFjR/F;-v˳cXpR5!O͑e{~ii%{@!X7~&hqPr%A2uqGJТw '"_ޣq%_;<ΔS-TU!x-:2#',VDO[c)nVge eX't4E7Olfp eJB$: nRO<]toh2l-گuAaX=2t;Hޚ6!\mSP@QV sR8BUcmF'us\8ru9|sOI*H,mQEAZdf%x9Nq,a=N.*|2$ ѾaDz;S9.~V^8߿MԤc\nw(mLI.G,u'\%ru"(IHX*o<9㗚9qxGg^Ms#! *!Üt4@Z;.۞ӊz9YUT&scp (^ l^Rێue]NǞ2SСiSBs9%.+>ѴE}!2tH0ϚPcDR_Y (IR5*Փo5igaq=~Įx{7p?0Zz3@ۅn7d!Zb\[%4 )$6[wT6C}CNGczg( :?yý.=:*1#|T!c 6*/$\g%4r0wWG,^$-ֈoȹt˲KHTzMᙶt̃Qeĺ(j$wˆ!C`v5ya{\\~)'s]' '#LqL.J0mRƒoCܞUR235Pdg!ƛCO @8=Y#8aU֗8ik QMoHk*ܳHTnا>ű b="'㌆lG$| }I6I/ )JԬ)HVN5aGlp܉_ưɮ[)7ZK`h8VxzÎl"$u+q':czN-sIGv +G%,-:+0KqWL:"I7%۱L bmVTn-etg"$"E G=X;/mn~L!)ԦA ZB)ʡu:]HFPo5_nKgG?6aom)BT7$c=ьaT`3nk䅤sI粎|B̌\sZ00p/f):RP'N÷a\k7 iMC-[/;V"˹LyW)H }Ĕq O4;=# LIvo[ T%Т 3xDd77$3fJ"%26HHsQf-OJ@Bb~6D $[sܮv-yS!*ĕrqƐ(:#Jw #G JȳD,x+XwN;T'ۂP6K.uJN6Iur4aPWYRƢwPCw XN?k=yuwDo* rpڈYSj Q wG\j;C2xEbN˸du{՜@Hש\zZON{F>,:!Ð\/N- \%p,+ 9BtΥ Ɠ)xŊq7=ų i۞Kq ;ADҚp2F1ttskqGHNEo\ĪZOA@:w|Tè?!hK~ʡ PQRTp9yVCIq:BUİ)꡻7n?MiT0刹pb٦ nZu}Xp՝#ڳXSmO!T9ћ3RR J0p[ֻ܎ KIIR\*9>oc6g^]}d{tXnq YpHu.iTꖔ95ĊSMqey`պ ߈o6XbHJT8sOm*ˊNOSӲDqOԤ%_!_%&S,m,y<ՔٌVu/yw Ų6Tq+Q9Xy!9/z;ZeGfv߳jrFg\F#D'Jr:Sڞʂs[#iiӾ3LҤ!A-) {ԬG43}S]_\\ucOܣ|cMuE-VnBbAEQI\/?1_eI>4e)#}zd[F{n5|Aŝq$F[Rn䭵6Yd4% I|۸DbCnkl$6l@-$>]E@۞+lu/ ;%.ͤ0t f=cs1Jx^fZ{ZV԰ ؁f&ʊWZ`)HP<scNoTWRSdcEz?c[]l\pY*^9igT]%c&QI6p9na)RBΤ2yv` xJj\RPp{W.esADm(sO=bF@d۴h榚+;)Wp *׎g7oH' Y ' %_jko$&{Sz cR]Ky^?Y',X 2dݡ[TnB SCԮh)w ;EVEQBtqX]VmS+(`]V +4;* 'zuh_},6V($[n{j10xm*ơq=O{fit\>J :&IyR:e. : IQGy QKq$F!k`XVF [͍ZW y9_b:Yz@ iE@pv_Zk|ೌ:{-7STK9Y {]#JF`&kADjY%;9uXMFג.8xT;Qܤ< K!Y<;W-`2i6FpB*90*0:R!+r+/Z_RR3%A9猍gG,W-{VY ycGװ9'K;HrՆ{l12HnHX*)JmQ@Ҡ4 19Y_p*οNyoTSdkl?/+ 06ZiqB ^c6Td%Է҅9#oھEy))QL^*v8S)JY̷tc8>ߊP;cwm%CCvz3"+O^X C ܈&[; ,TRT@ؕ@糕'LW x)Dw‚@;bK+lqF6ifRJ:TBtvWM$? !sH˸-ߖcQN)AJë+W)8#o^gmLr A` j*"684dvNqZԠߚi $eq5EyX6ϻ*F 0^z*=4:iuEŨPPnU']mbu* f aа 6tE9Xy-8$Ҷ@Ä_ԁ`39} bbu,[8y,xRCR8 `WseHA^~ ![NQ$% J %?Og9鐿f C#,l|F_3kΖv gRKQ'jn#({Q):iڵZZ})ޕcnخe'AjpgZt#RR\-']QE,E~H_ ]/dj I<4 y9|DJU{mXJ-)V)Y >ʐl$tckT TPK[j%HQI=JVsQϧ<4OAOqڬfmR<`4([9t) ⦧C)NA ݑYo]JK`ε \.pío&br hMLr#(GP|NdBR3ӋBHYNtL2*!?#%&ǘ!R&)jCN5!.({jrc,16dh² $iR/DSiAI2z+[9(:3ccpR+Jd.eҔj,plCxi%%J>=ķTky~{UZ*w= ޑ9Ա 0Hة_qU}JYظpz+'4dnVPо)]<Ϥ?Fwqe!XH8U72-K:<}ky=QZ'$+RקQVFy|U5j"F[Hަ(KՒd~ )I]Ryw 5+Cd4g=~+F՟|(+_h #r]ueQ^y<=Ei'h+W\$vj{!K]\^x?͢wqNl뚸M m~}ybrl=5fo%I*Y}ETYWv6h|a}Bhlgw_to-7Ѱ-%K(q v?!P1%{Si`8 *b1eU;ޕu|Ŧkq2$ɑ%L>ZZZ,%ʘV: PFR9EB)IZA\C>,<)X\ZGVhntSwgJJ+RPyo=~U-p[R_%`~ޢƔ<-O,pR+qGTq%qmͭ [&,.21\a|+܉K !Z @v B,ǜ,3)|L8XR]9.hNrO< [룄ևt%պF5fj:}"aN»3m^]R1RTió$/) g21غaW\ըC1-mqK}z\*'=p,'iJuƂN|wqe! W"L/b;!< ɈQ=wp%|'lqԀۤ%Ă@8_/|;pCw!%Im.{<yJq=͈}$!.-ZP\;(yycppFOF;"F~jK / kBYvR=#5\r%Vm}(4i믫 HN.}r\Kiq) Q}[V!l~{ӑެk;kVφ$H*}XɆ@-<3Ȃ9wu[1Rt+,NYiomJO(?<祐qװJK[]xOCG"()dGDRHR:51!Ӟg/*>rr~:ךyq#Ep7EQPV,/`a\3DC}U1H[Nf3,'Wc9{V 쬦J@) /<08RH ExP%<`we[-6+m.i6#@_#9ڲUũOpՑZ9Y%RmQ$aEX9'F7vʐCz3J%]-JED9R[kuS\ h<;heH !<$x5k#sL#7讗%seNAD9&u7aDe%H(5$EW U$M$z1ʽW*L[nZPՑ֧Pq;ȋ* ʼnT5wIa1g]"斜]g$+<ݝoOr) z^|?S͕R֫},D. {r:G^֖ Q!ˆB+mչoXK>eV0|!9 VN[}ǚ{{*ї12&q0%t%Ɖ$RIە}f?nnX0Kd~m$b( [ܧ#ytȺ VO Ht̘ӱ D `c}/fҧ1-[n9Y6Gvʖp^0 㦘 NNt;nطE\(!- z%i1h!jR4 8GJKF3V>nW}S!$UN7*._W.r%hu}ˉ~4.'QԂǺum4ң~.%kQ %J9w s"!c*RjZq؞2ssAEפ%?b{J׆|笾o#[|-n pڹ%I)9 XmVVcuR$o楔Tg#:VJoM::XS\im.M-tY{':AO׋kʹCThjP'|aIګhQgK,97bϼNlPm&+kO-1t[ viR?L}Ց~;{P'h.qJZ[&$wHpނN6.qnG)RFGӫR¹4#OE^U,J WǗ%:AC)%>0үIԯx22n1 tk*HeYN}ʴ6+ПqM8JHi. TTFēٚ?ӌ N* mJҢ<).n2趧B"ޠm9nb!ظmI S_'~4KTL[5(5%@uaU#JόI(*wv$ZL(a'`jWs6 un0T䢕+p;*dϏ;2 `RW ֜eYHOa4+P񎤓_s\I]\1Rr:F# cqLһ VÑ%ec^qNTrZ؊#؁@|g= eJB%)) :OSmA*QRz=5s]nA8Aj c IR) Rפ2;3R6EhpW>\x]Hv*ӺBL ٦-uܸN_AշZ!mCz(Gim=C'j9^ڄi%h9m@`yy,rC9%'*5P~ "NZ˯)4696*ot]+6ZJw#V%`lF2ڌ%K+_z: Tqt`ʝg Lc&<[u AUOP)8޸nlTGTˋ[j[}bU9k>~\%3QfbuG2V) qEnE) Ͻ4fk2ͦ/싉 *NAܣ'db:+ \v7rByJ $A#)}CIS#V’!CcgqC!sRII9$<=-e-y6>3yLmu{>#U[Co>˘L}F ;lHeF)IW4l|ԔT$% YBw8A;s6jLHHGUr _S$sXTo8 '͜Fי9;j(wpo=O?ֽgH+uKijاU? i (PxȮOd`,Į)(ug6Yllb(O""VU_V.TgFWҌp)YLfcL+YWP9ڛRPO"*m7Ӊ# ԕ}ƾD'$fj=z+ \OEK2ƕV'c2 [AOn2Aa^ֶ2b23u^&(oFwڑqS9cQSH}4OFǾHTT YmP%@ڽ;PDĭ9ϢMdܱWg2V9s/ꜿDkmir'0XreSR)`']]oyUQ0(_+ө$ oKZneHr;;S ԃ]2ҼvSBebky<՞mݛJՠ!ެ 9Tm Mgl}L- ,WC\ё2ڊa Xĸk|)7G@}h=廻4ԯF Z 9'g>~=%. 뗶8˚S.y *93IGQIǦ@I޹Oh%7LڋqJS߰潨IҦoAWy&|waH9>"ao/D Z}(iĭ)*jm`c'ӰޫovCu47In>ʑ>7ڭ fҰS:˵Q ]V99j o<Oz] pcf -`z+H2 R-Dofʽ {eiN|/koV J(EaGÜ.@82<5|xK8JD)E %.xy=/njZB3Nv?Fc9hu=9 %+N2> .'*q4S@جM],AQJQ_-9[82z(q㪕[ )̗PRo#JDiNxBc,o{Naѐ5ّ% A=4(V`.;yJPy*%$s(xΖH8ޝ8Y?A !ԅ!o%`I˵H-4T/q^.Og/Nj"4֝u:˩Z\?MEsmIBP_)qE n;E7 ]KisR2 &-%@N 9w'v羙ե$h5rJ@v*-Pbee@ }Jё zSv"K/uSa^ +B՜?6tO)d~d$7FXqMu!(V%vg5J-d QE,Mr%h._WZWܸGsM_r~%)H..Sd1 I \v%sێZ<ȒɆ @# ֍♶NmQYj;ˇZN"@8 Ղx$8RPI$O.|PlDk7-JW-vgI+ݔض[Wpԑ-!֢ҒPmϙlT ۄ Fiix!Iԕ]fҧMg姐Zr-ץ1H!)n.N9cÉq䵫Sel5% 0FaSE$^xoϏi:z|vWnbĝ$KIm:JIʶE=%Z&DpaI mVrP[Փtü_hkq ݣt39';)lr'9Qsŭ)p$OAڒee A#r:MM0 _ۚKt$=F.ƖԜ'AiґiKZ)Kq7l@*C`[8e)eJ KusBPǕ-!-&+*% ~z*KNV]'5kLD TkӖ˰.c(֔arVNV6]o.\.S'IQJ;3&-wgٜƔ@GTT@1:9,KiS[HbSrv˖K<\h7췪R$'R(\Q ƛ[GVX:nBbvtKitudc|n[ag@ umkHijN 6hlSZtAcoX2RPuW5;}µ*mCOSel'p6哟5,r &_oO ȠHp)'?;(z㶇Cd$ڬ  ZUxXbYn%O"$l*P {~%@6hwK+P#;S fk\42dJ'}Qnpԓتj?B+d .[GMqZZmtR:(v9k £M,hNܵR֢6PH# lL%+YS%ls>BjV>24]YQs XyTղjuRHG#IZÛyW{\P~J T04 5u=KWU-(Vޅ{p:(A$W`.] P4DDria7kj9WEc#jY8*H);k\3SY=/(5P"״,@U8\X ⽅ }z ԻT[zN4CլjqRTi~ J-JQʪٻ]zX^?#h{G٪'$"X7DQEJ+=j$U_ͶCzSw[rT>0=$TN$6y()$*a7EyM(q_ߟu;CJ ;w#'TY7R:kMv{2H5 'xI ޗ6W~6X+'Im^=mL*+ZBR6ɮ[pfS, a8Sa y-)G$pސqZkRvrDc 6T7J%MR6Jr<)6rS5kb%ugZe#cLBQWf7R kL88 /Nr0r)t`7$c`O餝9x tPJ\E'Q2{jk xQhڢi KN )c-)d& A TJne985տ{,ycEРqi}W_p(i-V[>q2pGKfJ}?OO?AVHq )h IŻpV78ګsIOex65Y^FX"@^ڽ)dWߠ1"ђ62Z#hHM;r` $-$vAb,s[*ʉ'ЂNH Gmd͸Rl.Hu-qJNԔu#}r9O8vSWvMX7P5,A=}3o+JOhϹ#⯢dfB²NO%$I;IJ֔&A:uRR4IDC d&9eR%&xCIҎ+I:Ǟ^ W5rAkREV-dR5o|cqTZ{Hh@ZNʘ$jhbU|'SZ_@R5"We$vy嵭w+/|W- NQO?}qouߢaIR|o;ˮ\!dXkp*5 uÏue5̪s'0[7* s^p%Rl1 :Fd2WEO+6h~(6'smpGH!+R4aѽ8v Jmԩ{9n rBOb2=<2AZeD8a*#?ޭ^zQEUj+e{݇%d2jY2;Ž WEx*ʩ1J|g#JDJH'W_w]Lae޳tr jm_z?C;V |(.՚:+Y9E$W~U$/P-eUFQEY6˗ܹ_*.]j-_r~%)6}ˉ~5 dUh(0 0!M.)kZ|aX 沏%h6G?diHlZfH(c/dq}O#GO#If֮񔯒O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=TjBuoeק* uEVZ$kJTND! ly/{D=Ty/{D=U+s٭qU'&#oʎʎ $Tsnua_gTmթ9GlUK>lUɉ!Q0QI)ΒFqY4*\b^dz^dz,њ||<|K4fW{'͑{'͑ꤳFh||<|K4fʵtiyhKO/DyƄ8u$lq ]L9W"pq~vOȮ{W[;^חBNQEyEQ\ӝdlUK>lUJAˡ`Ƀd+(R'Jx Xcy:\&ĆRJj6BO:[q qܯ&韒O#GO#^)Φk-P)=r Tt; $eղ4:ڊVAZH،AE\GJ{'͑{'͑ꤳFjCJ/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=Th1O#W/S!c.ĜmRpRږ6 5n`;!"kS #Tv;uTWGeF.Y*.Ɨo _\]N hm-iIӒqIגO#Rbߝ+/r+582/LN^dz^dz,њgTc)_%h6G%h6GF2^dz^dz,њ0c)_%h6G%h6GF2^dz^dz'LK|W%IX% 6F`q N9̝JӜ3DaZut/mK!+AZt2 /,p/k~B)dh^dz^dzĈkܷ qN"C%A J#$')xzS}ttkGoXW#Nq+ 2s^dz^dz,њUc)_%h6G%h6GF2^dz^dz,њ0c)_%h6G%h6GF2^dz^dz,њ0c+\:^#dST5TTt- G$/p6V\q9G)DWcY\#p^a\(+k8I57{̻.w&CϒHp Vtg rsا+_1Εf-^g70Y%h6G%h6Gk/-||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳNm.n rmcJН(V@$g}$AN)4'QTk.(5" %+mituRV[\ӌE5] .ܿHJ$2[$ YZ$T7Kj ;5iQ7^dz^dzg-6!lMk$+IRH$45s oͶ*coĕ^dz^dz,њ||<|K4fW{'͑{'͑ꤳFh||<|K4f[-'ꭿy6%be5@sX)&;2k l^ q~(v{}ؒ;4_[ۨHKWiV^ฦI‘8ʁ (Z]Cth}TMvqmlUK>lU%3W` e+QRY4`RK>lUK>lU%3Fe+QRY4`RK>lU{iӉqii9Ji(P>e$>#MFkgn.=['uN)q/qMHH'V I^ 85'RQE(Y/66;M34YIXo@ 8(Ie{D=U'ßYw5gbhpjGsNjoJ/{D=Ty/{D=Thn` +J/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifb"!C0dH+2OTFze9* !;8G.cdA6M<|<|X75Թ-nfRh'BTF$$`d:i"vIny Rn0E% \b^dz^dz,њ||<|K4fW{'͑{'͑ꤳFh||<|K4fʑ^eئnPr) RN0A:G+?i7xkck]O.rTQExkܹ_(˗֢.'QkWܸGsR ='!3uViq7Ԃrڄ%]Ij<"_<ڹVIޖFGUV:mTjuQ"mTjuQ"mTjuQ"mUz`8V95GDD2\BԤ 4IƒNYqr"9HyZPHNO~Rr5eؙcxd}UnjT2z"yP҂փT }u7$<^KYue9Xv*o@+sT1w)jJ rw,L 8\o`3kvv{v1k߿^ԥk'IV:\=Q$sHjuQE@rFGU\[UTjȺ[UTjȺ[UTjȺglqu`muw%ĩGrQEGerMG*_vksmƺT)zOR|i#QiQU?UTjYxQF,QF,QF,T+YX*U- S122>Éq`)' w\o q5rD[,)V)vqSM8J؞W8q\{\yqBc7 9# 2$JD|[[J@ ';$ ݜVvkq\܂o~gLW:#@@oe׍TjuQ[UTjȺ[UTjȺ[UTjȺ[UTjȺ==!;_c;N/&vաJNR>=* tU| lc(<=(~\Kո1AC I ?kv-6ڨHU{{/)t5R:Et5R:Et5R:Et3lOLD*Կ,ַ҃8*jHBdYtiR HoQQ 2mUtsV.fnl8ڝeC>BSkqp}fWB\UKׁf*F[ScNJUF:Jcfuߧt`Lc6Ě 6d<^J4@I9y$mmMVwG8:[u7=-A~xWX5tJC.[b l6TJe'IQG[ILP7F1r0w΃U0@Fi{7Θ[!!9'66ض{b{KNBgrJ#CyFGUB;QF,QF,QF,QFEu]9WӌYhK(*JZ~0A_ 9KEQI&8ßėYkUlVT BOU}Cټt~?vU5R:[Y7KjU#TYKjU#TYKjU#TYKj`nV÷V4B -jPI*O:SӤM[KZe @F|ϝ)SK˸\g~3G$ VVdX'K]lB%(:H^k OY mqk[Ҕ#CB0sn&'b8{B @ZJU ߘ߳bZ{/l5skp OsSr_F5ie!!)FЎ1CF$KjU#U;(-T5Qd]-T5Qd]-T5Qd]zv' 젭N$sRlxluwzftE◩Mr%h._WZWܸGsM_r~%)H.(n+kDk彩hl6TJVy$)?GJOVE1\7 o vCw}?GR~i *|CSum =yII=?GR~*( 1G2{5>{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{Mt7baP{g]#)?YGGJOVE3}kcT[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩MtT[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩ ¼/cላd\)ʔ9)D_53EN7)4YQEEIMcWd.N-gYPmIri"8qW$LYڳik}%'+B 1T*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩?de\xzk[oBfdm8<iĔ NRv RWWxT-G:Z!BR+ǴGJOVE66XWyT3ǐYGJOQ5>QGHU2{5>{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{Mt*EhUWyUGN?ǐIe!Cm6! H)J(+;v x!ZAoTi?YZUS-LHn}?GR~i *|W̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩MtcSIل6H\W- ŲK~PIjb>wfٚj-c1)#;vSvf~~wQT4-8nWZ#="q͗Uw wOV}-hxntS(芒0=JGVY\;#.H5."Ҳx7~+}6# h:Pu$Rr5rf4rme%˕c;[qqJ4#Bdl_}0aH>0 q#:F4Z>K4fU4%(ǍijNdʮwN$U>% K:))Cb8ѐ)I8/b^zhK~(J K6Bv)$sҎh%ǖFmlƳ N8r;Te[x?\I-C 5h{kU pR (9mp> uGZt(2c;N̅}`$R+ qYlke-!f O'H YoRS/j|X}B NpjIA HϸJ+qIBp}ܛ&3!%kY:[D$m3^..0+ˉ uAlo'<vsj澁MAH M;1oF_O8d] e:qxqfepPV4d y \',rfDw0:݂IO;_SmIВJV~ v޳EeN"]#ϪŠtўMyx[;+DJ[lRFRx8zBKy.tBrAc`ikz:dȟUY`{FSKmkPJ%Dy<n. MΘ2'Q_LꪢGlr տlb5m^L`rAOF=}1dO DžxC/ot6hVl~Jj7Av*p=]1\aT}2dO9wljf ,<!Gl+V^ʽUhn6'RCdOe}R=-gwа2F>J^SdODzov"}UI5!Mv<R|'aՖ'"J>jbcȥeGfN/H!vTQcĦxFC1҅ӌ!=}9‹i<2lXhu(Ԡ6QR`Uh^X/hcq)Dtܾ-&oqY^#.&[jH`}Q \ؿO;oD[C_x!Nri'l731EV6_ ħn`9WcqwUlm^#9ҞyQ!i)TFտ[xP9+uzPZ5W(\ERq8o+g:zNГ!Dn;jYs>CBuڛ䧢ۆ4$j ) T|EǍ4gA9 0:R3eZ{LmN J.@@s 닁f|=yz{Q$4PU$Nquä\u kАJ A(FN1_zP*o JGr?RP!dF9P떺I^x~utx>ǴsMɑ־h m|&qaw~d c8S'E>okoBr4f[9KiÏjXg{~Jނ@!xf~#g5g #.{55>kj5c˄%##$ vޘŦ\|ՙũlR; dկ!d̚m.ǖr`+: nO,iN5!q:^iTN2<×!J=+ 4.+x'_H/0xtcQK]buj=IM)Re#,' qQLf9]Uh)ZJRq $߽.+[48ĕFvvk;\e8GH#;Uu61=5!m%)'+#a:E^FWJYmKKJcBD(6 {PJr{G`# *@rg8,*t$RhU% 5!QEMEb^,-< xn&\eiqI,diƵrs۵5bӳwg޺[SZ#ZN>Jm޻ajm֕1hZN T Ȭo<.(nɅҔJUP.mn^c8ɠnѫ4Oiܤ% Y_!2*nIV2op{ zkNTwt_%(|b[#ŦXZB.|AHqWL,DWVGH*RP#[W 6nCplD1ow&;=EdqVV.}/WRB<3u |m9LR~r|-#'C]* Gp65ic-Mu=a+6t҃AZaٸ鿥xt?[uLoF6ڇQSǧ>-?F?FmfS~Un8E"S^lwWl#ޘy6'Q?Fx z+wYwVLGѱ>LTRGlMvˉ#uV7j)lU?-p˭MzcfH6Bmr*td/Sub@]*g(ѡS톙}v*>Qd,fC+O^TGTry92ɧ|?@QQQ7 Bқ6wo.":@ot\G:ScsRPa}gZb4t'N7J.Mu#Ynl0Hk]GGiiN?JUzJ֫nT!2NvRS(iD|잱% *< V7Id?*qP1ƛwڎeiil >~ƧCOZ&O[FpF TjK~Eől9˪<56QR0{j76$Rz'Xr35B[V i7ԡ^64.THNvD$7Ps ;8YA p+%1KDlA=݈!-K'zMqy{\Y,W_8٘]\O&9EQYˈی8bն&+I0EFͧ+?i? @sqJ{*`Ehu 'o5IDn# -݀'o=GGCA)et4(kϾn+c}e4pֹ3K wTdFp19|sMtRܑuLp!hP GYPDh 4Oe'`g+%{ )N,v’ m? 檜k!_.3 8!u[s1;+l<˷ޢ^ -)`N:-,F͵7TG B|H$̏RAHv3W2Ԇ<"jҎ7CC0N `)ib"6&-՗dI|$l6Lw CˎK䅒GnPwڬܻ*(5Z(=[԰\W - Uե 0 +AWdUa\x*_gxBW\7-RsrNo5 oss\nTZ8^-/D\㞴GX|b[4$-!d''s'ʬ'_,U#aHN?XjT!zps6CNjdn-l'͵%h uóRߖ`n aZuF9sX/{S"@(ε Il)Z 0*a udE SRRUqȃ*#iͳz8X.ڠ&fp5gcW~w&sQr~m C%N02@8#C)fڶ]8qdzQ'4]p};6T֌<o:̾{wyM:qIJrWzk2WGaKaK=bmWI4ݪvHYU)&!p#VTy''QK%Me5qJwWWRΏCE]Ẅ6eJnwh/p\bHB;o_WuwhnHAI$!\̜3Ib0XJzT Sj*-֯6ytS!-!NfCd)#AJ`1+F3(\/ qB˥|tl6Z2t;ü''+e [BP~uΒNpIK{諏om,W8} @銅9JǚYO䩋hPJ>_I?"=ˈ.c[qJ lMhYv:bBQq> -׎vcɂNZyy@Kd7kgÍ*;,I3lIuhc V2< #4ߏזhqJʎ n{OO_qXyRRTRt$d(A8Gم@eXs'qZYv\7B4*d;s9b@jtQ6CN êo=uQM.Rm%Y.x `{) t􈅵ܡ![Kp,)jgDe7Kcq uN̞&u[i*[nhQҟuʩ.$Y8j ۼyKRHmx jH) yk%wBj|V3APˬwLI3L*sP)ZQ"Nq`mZEL1  k^B6eoU^1[Xն2MŨ R2Gi] gVۗd7 Cj }HيpF%cw0!6qձInm1tn0)U VMYX5 |I'3|5vfBD`}]xVID~!ZL?8ܒ'85Q} ޭ;kWt~^.R]g(<|a)* rOieqo$E("ssw-tx̨/:9 36*6g/YLX;(n9-?I>~}z%U} T<6yg`I8&& r7<\Mյ2}Vϲ_H]%xӇE!᧦ye>Tk wBܛH#շ trAL+]PϙN8V3`lQ߈?PҢjih r.wA#IUd ƐWý$nH#!gb,gnW s8f:N\!U6ixSчy/ B?AgS#Ie 4IB)*TmF8,Z$`P骚.f@vF'-ӕ"#_dt6PӣIƔ Z|KYWM6`|l?~@{֗^9gu-'Fi.-VdbF qkAXyÌR?qSxiJTt-Մ8v G&4LG,뭶ָqEm-e&BB_hʒB?ūvarO3.:ˍ`mFdC1ҽԭ`VdhȌ:џS+ϥ$|`Tk 4#(PA9$WS6F481.0ed:K8םiN 3VH̭%ԥ2d6h>Uv'43auI#z gȨp8ZeY%ʌ -$G*<Y<,.o5"Q$6yrNp)iOS=ޣψ:X>m?T8r+2RQEnYkC~Z"dACo-Pc-DC He yG>|VeS"D Ob ղzq]I:чBN0'*&Pl 8^ߙ$w.$ NuR[H סu-H i5镨5֊#i¤8SG䴦f[Dn2rt*{Jոy[qm*2Oa9c{)qel@nnc̄J#};)@Wۤg4v(rii:<]:g^H .2^]뭪Fcv}fWweK#Q: hCϛ$S* SXP~;ԔtGhȎH|ڊ@=8"eԧÊSϼ 9cG+ 8}o3zkkM-l~: }C⑁٪Tiy-i񡸅- ڒ1V([=/4|TTF9['ksoſ+ c{iǷ'$)y8- R{M#16U5փhqI 3R%-q 1[rJɸܥr\MHe!HNc>~F-!(:Btr{#^)N wuw-8z˛5",7[ bt/+mQ:>\% xkZ.!0' r(oVŲk'9iә/R!zq$^Vb\3Z=Ө TKkScr@Zm8YJ>.r1{ږKUK(R ^I`WrZm-kJBAҁ˕e:GCi!V%T5yAfIO],$N0{q?OsCr:K w+>ʓqw + E%.%%G# s]˅n,+$4J}?A=S"njY$8cRXMtDc.֚|T`wg|{'멶] 26$nP1p3.ӤQx "jua[9R+r| î)2eL+7;d˚CI܆rkq}hgp#袊+*kܹ_(˗֢.'QkWܸGsR $hqтuCG뮳IpeY}ϩgې}>QUdwniz!pHl @nT.nuN;Vl۬q]6_jpFNOcEi&^.ܡyS Lf[ĝqK`r%_ޕvRla S'SʣEPl䜌f+ 7t\/YN02 a?{.}qo6&;MYNJH 8SA@#W-åܦx$k_W+rq. DS;)J=&l$R ] xm\%-ġ5hy)Iҟn7ZmQWK.!Jq`x=pLWZTJuи\["a?ٜx˄zB>bGӣ`)[󨱅_Ei^%XnRn(oZ6p)m,=!żsnk9EiK,Y~ڭşdsR"c=ЀpƠ4GJ+kmT0E;FKƼn­lܾ۲}J!ۺ)j'a;u-f}bQZ0EA^ F&ՓP%t%YZԀBIjU ]\K?R{oPעk QER m~)a:Xh$s(EhNDuy6i{  $VnkL#T2?!:K3e2$@!cRSq ʴ>@eK{F=W$Əq\BT$f".3ae=kmkm *RN+Ӂ:J21U\IDݷ2ԙdx+ĭD9ne˵IŠe]im'a椦- Ia:$g'cPp?\ J15.)H*N Q#JJ\K[E{ \1TE.r RZTI:Iʇv(u[u$猌nq7Sngݕ=rD gڀJJ]e{U `8;|RkO׎2Yc/\uAj: $iqMIpsa&KklvtDZhKjX^ HPՑsMKZ[mO6%Jy@:m$ 5=f ɾ=:J\[[ %#rG G>T[ Nx}FZvfR/ Ks SRQ(D3یN-jbe\e4D'Zp̅HIsl5"1j'VG>I baBDu7!,Ccyv=pXM"j:HWhn[RnQTyd %I*Ru+Wi!B8=WsyѰۤ3SIJҢ20wSE8o%ZOh(RKk#tƱi7)-]D:▢:LvyuzH5\u$D|x[Dcb&G̜˓#^Dna` XNw{fOqB (:~gs6ܙ) P-BG)=G=bN8QŃ>]?h{}ǂ)-> Ny_[Dd"2&6(.%ym:o{EQZW1R}5 \  gmMoM+)<ފD*q)N6,ʼq4(-) _XAst ,h-I),T mW&kUjKq v((l7}2u4%! Q`9W=)' Nj{MZ׸Z1 I \e7Ib2I P*ܫy^;.0s s Hό0|!('۠Z ܝvPԐGjAWxY1p;?dr!/BL̂WN0 'L. }ݣWeU7QӸZ\ $ODZҲw:@I=?E=k[K͖O\t4 AB73<-CVA^/nc~ش>RTOԢ@LakO[uU\GWYa*veߤ8ȗ51߼)~uNWEm3,Ǒ[mɇBy1ŽmAcSP{l1UMՅO -)!'cnOέ!e.~nSfmuqjB!C<Ҫ)|U# iSZZ*d,N*K g@)z J3uSDuyoRQ~EWTd KKUu t%'?n_-W.-, ;㶟KiRNܱΣV*PYNdFeCz)aIm.G'^0<jB?zeP}Gf%gQgf*й@Df"] fO q݌.T Aҧ@[P>}?kǵ{D2KͰic YtG9q=j F{NiH6D@Ŗ& SCaJ[zP##n2w F} p0jyVX`|Vj$O'S<3˳+$cᛒ} Tłl] ܘ 䥣pd)R)*d6ih2[`IP䗔e)i)l@J[Pն$0 L%/"`,م @]I V@(z;o]^ mKQP!$|T0:~V[tLF39On-0%QbNc2$rYJ3!Gnըo4Hqը--qilM "jb\k xQFvJ)ӽ&*`Y$U}Qu ~Mzix:d[W$W#PTR<#g>tGӒq;kFCm _袊*+ $m}KJX K9bMH)da)j9劚aI_ :TaNb.8:̺7"o\(BUK ĺ!n!JgVj@l8uvWp$Qo+ HlooK\">|TX[k6T`bqѣ_Sm"\ܘ2&πψyU'Tߥ)=ΡGh⎒|>S|Pn:VR ΠM1oxo. >!!¸$׏asp?K<)4)!]Ԭ džͤ!lvKlRG48Wqk)dz=u5\23aCfҖlGC2#JDKQޙܧ"P䖜i:𱂔㶾_:\ k3(rJNO䏋Nv$ZmO>Ph<{*j<s+a c"l[cklqYi]dDCN+>q>- п$sJ  rKmr2!JL9$L@Ƥ7fy| ^ƛ/y_Mky a5Kf Ew=R <@ۤh9"Hspu9+ё5;> :rZe$%''?$ۊзsiyӴd1efv~MQR^w@.qѬQXc9 Tu=r\N ԗp4KKn۰Et%!¢V=JN஦ ݭ.R܄ZGa֛eV6 NôG[jnF\ھIWmJmm,OPC1֠\IvӃ۵S_p%#f: 88º%EL㬶(-$d`_]bi.t?=`d7' 6;՘t 3ۯ')v1f]_Fvֵ&sۊvB5-;yǛs_qq!a58I#GWN3up:DnrT{O3R2=}g/RQ4yd٦ÉZuͽw-6շ pGZ̤$H5Wq?O!m.=S Vqmv{G >gh(Jmu/+SE_r4T\ZJ?TSmj(QNjAqsIucojR-rJT@ 0v&[V] 7❰zpsYBYu)V 8;T`sune*Duʎ\xenu8N# պt)c"xU=p+8.kS(N@;%QvTR{kta%ZO4bB޽ Rm8ks:a8rB5Ѓ ꎴv<}>ϵM.>H$޹{%Ivp?df$S` j=U%O ~#Vjyn%F@z}ϩ}-Bǃ|՘]^}$'gzнQ)=?*={uq'GmgMWGҴQUCG~ Z?YA_^^G~p YAVUdX_#;v3Cr ICEp%>%v3+QyXI>.u(VsSJֲġYbќl/*,8A5Ae|8.BeiKu(gffsM5u`KldԡMa4#Qt˧A!Xi"tV0wI)c<\V>?z`CYQ()%W49:L)ń!_ъjOe}n$1ߞc6;}SI s %11%@jmpz$`R(qJm{ŋ\,Km8ӧHH#NBA_l{8STw. 1}@6} )ޥz%A؁mѵcz]+wArcCvҎ7ۭR%y嶷߿)r$)g|SĶ_Y`aGDwǚR@YIΉ &huI+e`<\iԤjy>tkܤ<'[ @!+>2BAύcp'rU16WUʷ!#@hoI%*QR *3p&"ReRQ$N34_Fx[ر]q'v\Gmam*pi i' 8mG|5&'ao-]Лd%]bYR@8-B^-,#%ky a֟Si Υ$+:AR@4kGtr?(ذRx9i*'r**c{s} ㋬e6tWOIPl-9 Z[#. BЄ>/n#9qէwZ)JIB1S!Ţѹ o|[zQqR[Ԣ;*TeO8-G̀R @?Y^U9NBJH!KNylU&"e֓SZ䥁ȩ=S qUf1*ҿNƽ|t׹l#u !FI8Kk܍K"rh'ױb[ a䃏:J1C[}6qC#p(7.ע+QEQE_]0鏍JQ?A0:v¦za}ƺem #WQ1stӭ9*R(WUǝ.”ꃊH Fs^1&Hn3Are JA#¶Tl1ƇVT%NzHZ,4@! R85Ð9]jnN$sX{^SH67^QCQ\P. ?a~ː#q\dHbpV{ j G婥ʿp\tޕ:{J ' H6ֶ,X0.tRKԳդ+VT25tlb]ZQ !=5_PjVcq;iNw*Ƙlֶ/an9}o>-< J B3~jt/ks?sv˘KMqmH{EYlTe9~յAZ/Ww/߱izuzM΃!'+#;1U*bVwv= L` s݄uƠdU&?>=neHvA qnRR0 Rگs'\#Jq%BBvU8]a;ֹ݄JʐT'(*ŜG&f%ISҲA ]*tdweSm3Fos~iV#p5%-Z q9ۡճp!ctwb}'ɗy\#s VҖJQ] ~jNovt![V )}WTQ%P]ODrJe .@+}V WFiU)e|՝NI|xxt5G4Ghͻ/]bn 68),!ٍ)O`ʠ<%h1xa3&yKt:Rqv]P߉vY_>j(`).}{8[Qx˫"<8q%ă* sZvUf\\ -]{N1I҃F/+ZIB޷5-!mY΢])Js·ZV3$ *8m )a~:Ly9a+ \.);iM2Cl$e)}ES'\]u/8! sĥ9(m{m'N|O"S䍱X wS^p,#} $2 @d7;7[S-LT,-mmaUY_dx[ texNf `M64XJp@IT X~C- ,4:c Og̅"P[o$->eČb~9lvbbJAXXjJ I4l;=-k eZ%එy>zՠ] Ѕ3KF-lυh4TNMhutN]iOEiv8xQEj$__#8_u AGc餫{&iYZ{׶ LpEXuQ$)$`LZt$sx)z䃮)U㞓.gb(oΧ^.B7Wǟٸra^k*]OFqprm=,cGl>\@ INaͷtK: n"l7eP}c ST XܜR1c[uˤ!HqfnKS4Ѵsi\c:Y;b6`#|?wg-eq/uoD8-mV )#$(9qm+môFR=ѪFH*im2]{¤Jp)km R@:@5ywVn-LB Sj:מ]SK;c&k^ xZW7kvz%/9)hF 0 {-˅g^vUcSSurY.G}^t'>6I$QaĐ\Dif&b[%<@Ci*s;syRg(Ic\r;9ZaZz2RhќJ)+Z |`ia'[!S)(*+!K9%Zm'.qfB ǎʖN3M+t>$v֛T۩zRn>8IZV#Ck]9:~n{dخL IܣZSbJ3Zm$ +.gaL$Ly\en"K,iL5CnVÞEdSbQ˭Y$;ϺMؤ7!f A`8F=)lnvmo;? Vj3tP-mncMW W$=hqFT1I@ xźZXl sO3?QquG಑y P< ~ixݍ2Rr $ +Jj#|USD1ǖ6\Vђ3-͒ Hgz %' #ޫ/؎uTKm ;k2+.`HhATz}TւQ-)N橮+zv?]s|udQ}wgC|~E*EWTdq+6Nt'8E Bⶁ`y] nSV^ ;쩧Hi)U ]H A}%J `x$yo:dCCK!D tk9ԗR[@:3)\e]$#xEi*FiR^JN'LQ7$+Ev2TCN0$n2z"K.;S}@:S8q̡ RZ⇣qPɔe$ 68 I8v/ [>扷g\d4|G3,+_};l`_oʑ/Z|g̘f]RYo62HN{fƫFlqrb'F\H奓9Ehyt;Ę mĆl9!i8/6FNcsp94K֐Z%*Ma$jq Iq6h i.eA>Q܄I%Ā 2=LJg& PA-imJRqjUrI%ֿjMmؘ}$Ї{gM?XZ^bតo̝V\.&WKh H o<"kz\\-(Rc]Ԥa!MҶ 9H ;4#k^MHωn9`pl5.r!-hKm J9V^[~!{3F>irsOx2}pٗp"Y} nGQ*@SdDsgee%3vښV5vs;TVh $I¦rbphFTC jձ=j&SQN<){sD0GYbO`pJ*7H$O/kI̝],_/?/QE5َE* k̓}u.8t8u%[rn\S9IY5#p2 IDR?<,do9+1էl*@oeXn%5R,e'}vf)(|+'VrT! Yʕ3;)KA q @㲚|E9~a6EqިBƂH9ߏ먎8u=fn%6}vsU˄#A !lKq;@o!㕥.qzۈi rH$wm|f=,\i}`lmD6^TKR%^Ig|?l8A(J^WF1U:JYC]Y2Ͽ#vnW Cq FiDp{Sʭ00vjHX |{ByuGG|^*E}ˤAX|N*;&^mVt)IVΎ :Iu+ YY$SEKjh BRO-OɊRa2ֶNK:u+zڻnq @ܝUBc֙A'; { 㸌cJYS*) doT_/rgL,#g=jˤV;(R\I[vt5v:k2leuXiI#cȏ^IZ׻ޱJGޣx,uܬ8A/DӖՁRO+ț : O0}KS\z_9}v`==ꕅZf-r$mՊ?ms¬6 O A?N|\GiL&qMإR~̂w{=eZ-sRoz9p2mA!yqAr|eo'Wm,/;^uzv2-';U=hRqy D,[ՃG~ꞲlڏnN7i@NAȥ%qQGM約|͓֭Uu_鴪Kg >~uBS(TPNÑX>e6|h-%Jm5Ъu)O4$1%}B.1+CMG_K7V)wNzIL+`$a@*Éw ׾!R~,,)Isr#& RR VG#H|k{@x$!`疜ϣNW}n7..CAR|^8lwL涗\V_}4LP@Tw@+P:yd1{2-7)(䝀ޡw)4!6%!iQW xqsH 7[e?5`yD}MWӉZB2ΚJoxRn"aVrH @d T-s|*ck% *RrOeQx 6kr7k_e-4 O=~t[,W{w Ra[Lq.#ɂ a*;gaTrx=\&Sb2(R) | kFz7)"gt[}6?O:gF(\A*.\ܢ2azˊ:3曰J(ߗDf VDlVlV6'8:iR\JBVT(i;$lFn_Ŗ$h+Ŋ)ԧBV=bI `Kq/H߀ud(vLf}CzTKBU)Nm3{[}Unx7]čH%>W Oc-?|s!q|)jƵ-2ԀJx䕯$y ˋobͽ4i ml*)NA#pG#tr2po{pn%V6pkG CwyY#s=9sM_:aӗTJ|?ќrqU?O\Mo¶$PjK{BJXN@՜"M[k𽻉- @xu8VΠ $jNFt[6s8A}_53#-aڒkoj?m Ҭ-)Pj7hqK#<;cnjoܖ%:y (Hsj!.)`$o UiUk酥O4)H?0l0q< _rM\CjLav#+[m *ZJS?_xk[}9qs0xkF%1=Ӌ||鼎MKTc  3摖YɍhEH O+LX[ZHj Ya`4PΥg#~8⯳2.ZMպuK +)I #ӃDJ`4; S.$lq ̓5ʞKiYҗըTү4ND7!٢xngmfI]BTl50IFg]&O\˒mR,W-`FݵO905=1HZ!94 [-QJu-r B@]Z % tyǷۄ#Œ$+X#`rПMm*0ZnqYP$;NziJ=ķk_sn$S#.V_ pGm5z!re\hN*pn).2OKЌWOh؊l|3,m2]HQۅxȷ7T&"R[yEXb)J|D$;N)9-ݟLbi࡮2*ܠݹ*XJ_JHm+#Se\Dv{]B)*(͓UټCl-MJR%I3?}–gepΆ:# P\dvM?|"@ }줸{@K[H3>9[^+XI=u@G (6UrW͸lV{VO ۀ kB8EXv^GO *aw75Vk+cת*d5*j0z7xqJB:ҕv Os5:T$5=2@m`\ˮֽ}4 3+fs~ř<΄`Wo6a*\LpQ*֠{+^.;.vèF+xSZ^$`vyCGH\<aSƵqϛR>:&kͺ{Y5&: Z##0y.ay!n)5%n<ڗixZbʵ9 9IH)X s:iP)Զ˅9n,+܇eWˌW q*!|s%>ZR$axb9 }rٓrVN7#&Ԣ76PSrSd'_7Z0cԿhWٰҚPݷ+ ##V\@Ǜ՟/%D]ˣU~tkC$k#<(3~]Jz[WB1RNR@ls3ީO[*3 -0hiBpfSQO.$ojY~@ZDZyqI6qw2X.\q8-줡 9NF0l+bIE *ImMWA_h{azƭ{4F+_WeuG'.{SrI8Ϛܟ7 }}c#;m/Gg=wd)4xXH="BabANVJRT}A *įn[c;T%Ͷ~Gż08-V%p;*BZRW$cʦܐp9cjO'Ϧe++S!,FKؐʘt6`H<2g 1KiLµZi2ܝF;g$0{괃νdi9X.YZ&qgFbF:ӭ<+XRVl(UяE&sk2r`p+XGmy 1.Y+y=#F=v6V8;=B+5zX_A*MQ()d">dd/DfA+|zDfPZ{i$f5WFwYW',)“p1N"$X`(R(h-lmKNH͗ANb))PTBA9 LН88J:s .I:K#}*}o'z Y,k -8>z!i ٞԕ#!H'ƠS)NpNqK0|bsQ{)%$ E>~\pY8VJwl!; ˤ\nJRv8#WDW$\жg+͵xA|uPL4VG,ӨkZtt)0jB^7Ɠ^RhPr}mkyPPPw=G=6X?;Y_cF}xl?ѝGJ(Ub(5zaoocXn#N#.(CZZn9#d!9?zpŶiWhB'PF$jPۊ BֽD씱Asj +V$CW!Yf*9,ʏS췇7Eja5ɍU+n% Cp1ƵFqM%ZVGX~*8?IEjӄ6:׭D6t7RB6HO}gݘ5LWW%p0qzjYsPR :Ov*f@HԌ:FChP;Q7yE$k*7.=lwzh8-3ؑcY͍4 Eߪg%\CtC}-Y`Lfʈվ=X൫BپWH԰dM0qΛO#Ka8l.* տ}}vzjAQ8*2Cz.!_,= = |!ܨYq͞^x2T3R~qHϰ)f$c蚏hs] 7^n>pomtN+9ݟb:- J"D,⥬E:ؒ2U)@gUcIdA!k/5˰ S]xu;6wYP[eegJBH5ZYiRSk\lP:ABGu"͑SʆGTkş}bڷ2ԤI-ln_QۉeaZJQ#[QYe(NHȢ3!YNdy{.O7ֆFk=Pu`ާkC%I3uG*i *ñJnF4'SʄҚ(\&jW,Ӧ{HMxmIdne%nS6Vp{{5=h JjpRBuhmi<۰_57Kt.N9,1S1*^ mӟt4sKIARSVХ&p+HS9 =l [R=v?_R!  s2KD;+%7FF{1q%V\8Mz)^6io>KIՂҬmSD2:w[WbS xqcF%2 X:t'J}4V{mB3aĥ W"R,?-SHJAϽ;{{OuN%Jup>W 8Jy)(!)>6u]LF./K`j5 Z4uOŹ\KguIB;E"CA\%b=4HJ9 J\~K^x:BNg }"ˋɄ T#7S`M8m_T_9{%;\l2 G6|T&PP>2׏Q8HPWDUl`eBObA<:G+SeXߠ~*i"`K#ETkgҔLtpW^An x c6H˔5 ҈Qzd8JTHcuO/rQ DO!P'﹫=擼2:M*ʹ*˝T+:eARiRTHWXE٧,aږKN)IFJp~!SB6v*k E& ˏÎ]I yeTFVeGd=K 0s2bJ FIRJv4~,4ʧS!#T5%׸wۧݙ oWz8sBJ-%c>q 6st6r[[kP'qjMW q3Yy?_nU\-Qa`1K-f{+LΓL%jKNPTW[:'=ޑUW6ĎlxD4\ɼ 2 ԋI+A )"h 3]y߱Xڂl.ynlxi=a\qzr!J[w_No`7 kHkQE]~JTE\įM.q?ZJ?TS\\wR;~fdtj s{7?Zk{'ݕ@S]GmMte* Uj;-c!=B}eH .}/98O4qiUrq #RP!Be ,UYVjJ;B՚paFokY4R P줕ʽW5܃@r9Re: K#M)M#"4PRFqIkBU䪓ZQ^$uq\^z`]Y鋂`!qJj (]Ϗeϯe?Df[ɁPKZYV!AHyNQx Ϧ@8 dUQ @A9aK!K6`j)`N1Ur题#_J˿j|Pԛ՜'akE7YVz<J4v{biNs؈_loƮ|hWA{у?k跺(EeʵEc5Ze%%E @\ʏ.qhsKύE x0u9OS9Yea:NI*//j>^XH) Q¤ -jRUp7$ԣKvl hu#7#:9P٤9u}i;fB° QI: G%Dj\*Th;EW$wZ'҃ I^cO4ln9|͉mPqjN{m :HB#MSUL(W:B|ʈ$!#PmgIм%,N3+ˋJIJ!D N ^x7?ʿ=Z}e#l}Jz ׋_(.TtZGj^Wu?W'35k4I=RZYQ$5+^9F;Hm{w^V2 z7pռBF)' ,^PNpM&|ʍud[ TH S& NI5Q$I\q΢ ^M}i`u% |b@^,2C>(>#B@ HRv Q06 {qJ)gG$x7v"gKS{q=H'9'H[5|V;jfV._C9''<:\‹)rO,dlcLP9=3vWTMQ+ ;LF](Kpmh~ .[TO~]$ l6K!;r؞G v- ;>.=nRQ c{PH =Ƴ|Mp\wi.Dku8À03Yp7Te#K^;;}4ǭU{㭿£h1;鵳1*{X!qЯOV;p6irG{64s 1{>_H;k:RW2O*Ni(3Hմ˜(m8]~JTE\įM.q?ZJ?TS\X_*4qKuii>0sYGO#Z46?Lz|v$M1jڲ8վzW{'͑{'͑ꤳFkWYW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳFh||<|K5!:~Gi+-p5 *H'`NF O|Q XKA6M<|<|ָ}˓ַGeGrJԄpvH97O0/3s Ԝ UٍT#\ۿE'+/q?%h6G%h6GQĐTwэmdgI#8pi, pQ.iJ/{D=Ty/{D=ThK\RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FeZ4˴qԢDccBC:68Րq@88;V'W]U= ]ˡx'B((r2gNl N p\l%h6G%h6G mnd2cq u)DJмdxKםERۊlbC)qDv% ZTc!'-i8Wict{'͑{'͑bgS ܵ`Rr*HǺy2ymE+N$lFA A"i#%QQRY5f||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑A bN)8o)mK dDyt0sҝGvڑ5r)ϑª;*+ 舣#B,zwKDie6P4 8$k{'͑1?uLl X}esq'yJ/{D=Ty/{D=Th3*1O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=U I&%+#R 8'fN%n\v" o:̺6p]hbqWGXe{]E/{D=Ty/{D=TED5͏n[P8!uZ‘yWnj~lUK>lU%3Fe+QRY4`RK>lUK>lU%3Fe+QRY6 7nn6ĥhNQ+ `3 n'd\f^dz^dzJ5P] Dƒ @:ө+AԭЮiv"xD\n_$%Jy^T*H㇚(ܛ/{D=Ty/{D=Tۛr^ u6V&)$`u di7SJ/{D=Ty/{D=ThOQRK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3Fe-Vq͛k U9O5f/c8q?KՇM;=žjI/mQE|{UW/GI\SR[i$HKe@HUE .!og>&BKV8B5]iV-ƒ((~3^<|K4f#r3+QRY5W{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳFh||<|sz0$@CΨ:d2J#;g .l(Nr{iR ԲNz\vRIO#GO#Ob"Dh괸^H0^#JвC#)*mY$~WI^3 o#}daAq̕%h6G%h6GV2^dz^dz,њ0c)_%h6G%h6GF2^dz^dz,њ0c)_%h6GiĸLm(25LpF3u7DYw:dQʗո‰$$UQz_$HrUZUJ~}(Lz bay,7sO$ ò׽|}L,;W4l8 x]#%||<|K4f0||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳR _U2$'#=`@^U#V1I| &K>lUK>lUI\,r\a3)A !*#R 2s˝4kFTɷ<Ɇe)D,p*px}Af.ғ!QQ"<_zí):y+bp"\ Q.iJ/{D=Ty/{D=ThK\RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FeH/2SQs(Fz9yJq' uR qdkWfo^Ǘ 9 ~W(rɵ\įM]~JTQrQj(QNi\OģE9ΞZfKK8A mBR뤏R~Z˵Wtw/m\$KjU#Uet5R:Et5R:Et5R:EtqsmGϋRң"q.!jRJSIQ'NU8Nq$u7K *\ s<(iA]b zA*N†M>:z/Z:;v7֠w}ުs|In; R R5%X9cigl.7;A߯ojR5 SS(95R:Za \IKjU#U.]-T5Qd]-T5Qd]-T5Qd]NpZƶZ\ʸ[q:6 N:;TO]9Wxg{fz( #k/c;UԵ]9]*^ CBO'Br>4WK뿴*T5W,UڨHUEڨHUEڨHUEڪW )szaĸڰpv;ڮXZ78]"\- wwK 8RXX^%Cxہ^lOp+Y|.=׮}H1›V^uRuN\9ZTd;-msߚpm5kFZk^j|S%>HU ֯&%~KmOս B QF;8)fDTMQ5ڨHU5dڨHUEڨHUEڨHUEڪСJ/ڤ꫷A8Jvu6\+NOƤڂSk9u(EB(<F.@Ecouxn_d/N)I? u H/`/e+-T5ST-T5Qd]-T5Qd]-T5Qd]Y;őĀj)C88 ij۳zSeC,-#e$,'`UtBTGo!E![)%*؂9)Fj49lꛊ3oEw=m9IjmŒ9 פ-%$! lP׋l==Ŕͷw%"H?OKNU>Jۋ[uO% x nf;5 A7gmܦ+k [÷ƪ5R:ZVH-T5Qd]-T5Qd]-T5Qd]-T5Qd]jӞZt]sǓ_J ijХv[)|*i?w1QEW[K˞FWv?Ev%` ֎JW;YWH mTjuQ[UTjȺ[UTjȺ[UTjȺ[UX_E"j_[eiA[BwqdUSsCDGp!2Y,4ۤn7ڨ`6 Do9kKsVe76RmNۡK)58H8+Z *%Pk3#-α'%IQ*Ӎ#JqTTjŇ1:OWKoz\x4'Bm,1?ʟ&Lhp)-AQQ7$SmT5?"8}>T7*AQFfʋQF,QF,QF,V\9ğĉ^SUmŖR TBUT'[:OyֳVEW׾EQ:{HnuGݧVSԬؔ(@S7*ȿtoi?-Tju 5Wkt5R:Et5R:Et5R:Et5R:Eun4][L\ZVX:{=^lga ڤ>ʂFZ[I cVJbMS ڛqpx2/%F $<66;\Mwցmw[}պus!-mHҍ*%HIRd@\(ң-( йXN;RAV }#4tLs^ [==!3%aSo!U#UdڨHUEڨHUEڨHUEڨHPȺy?Qiz,%uO-I? uxm%}UTh"(KQKK,5oe+NjOJ WT@UV'{:?;o*}[UTj,QF,QF,QF,Usm+]۫EMJ\떵G(H ƍP$'g&p-M2Pt a#aΔ.l3mh+x+S,C.C L^[Ll! 뒔@/N5Jvͅtr5ͭZZiuiJT!ġXOgeU9Oo1 mi -%*FvRٍ-q_KI9׹/#4HUhG@{\wQFnFGU.FGU.FGU.HWw?Ev~WܐDvPVB9G`k[G:?_:K_hKԦ_r4Qu/+SEE˭E\OģE9֯q?7𵋉"5Դ6J*%+A{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt>f-(jN8ZAJsZhWO$썑,*4Uwx+MlnK (t-MB*EN90(=xpik}%'+B 1Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtT[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|^EƲ[ڈ.JYTUs'Z,`(1ی2LN6|zQV4Ql&H,YGJOQ5>QW_Zy7V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|GC]2~<=d-ɷ!32Nt6ddd#EW-T HGi%Y{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt~౩e':]>dA~*\Ϫ裧c$2v HJ rRQEV;W[UoBfdeDr 7wG􌟬 b*BJx7{Ag]#)?YGGJOVEY}kcT[|}?GR~i :Bw}Q̩Mt{MtQGHU97V ik}%'+B]Ts*nAg]#)?YGGJOVE!Wֻ>T[|}?GR~i :Bw}Q̩Mt~w Kr]iגAX V[Da,hGBi1 Bיפ88K%->6V|x5U)'tS{7nZ"xsOgpMt%)VM(Kqrk^~mc^x*N=]1 UQʂFu'zj x2\mNRtjZXːBnqdΗ&RfaIr88B#[G<:{4K%jTÚNN3۵4K(A׿];tGe8<˶>v$y=+crM/==_ۺ6E!@g޹cMl@r<"@/\sH +Ql+:@OS[s nxMJG'U)#ßYY:^В>&O!Ɣ:pGJgxxq a*SiTWV)Q99.#_ȳ{~1vO?54s.^SO=׃!m;7>+ׇb?r^$2;`@pX;Seᚶ) N=%8>Ҋ܊vRP~c^),/EQ%uHAO)I<6 >? 9<ԜUIgŀ_LׇI'b7)^ 3 xHcU. ''fT27ڟDT- Ra `aD#3TU}rlT5Z;>#JvԛAQ} 'Y#7&+R[9iJ;}ʈO*ZM ˰Z,BxV0F|W#~ꝋ3{ HjpXԙ!YJpH \pFs"KhAU*`T8S| ]eȢ8KMd48*DHs/w7BR hZbZ[m'WXH@zr+,,:Npc%f't,`weڭ g=d; R? r3lcǓ POI?KːXl)7Vfk)(A UgB}UJqQ;N ʪM…2/3؀ʖ[Js@jӊ]F]9JM۔,6HոUș,blxz撎!xuĂ u}C1oBN`7x'&*C2xg1"E.S.2̙0 y ]'3N5+nϵxD͙墿pgKT*uԶkJНyUˤK굟tHy8W殍5#:j؟{[QEVEsD<]MǏq ,E\L~*8NJNJAHZL I⸜{pڥեJajRJ) +NI$Ruo.7d$q8<ش.q (!WkBex-( ZJI8#n|kќ^#qdGR%Q ڔڒ~*P9RߥkĞKZ- pm!K^25dj*\9]%8d9M\7wֽʚ 6Ÿ\ sϞN%pZُd?nJkH;FGd'I>'#rc@$i5*$T͐Yk{ WqgWR:qaa0%P#onHX*VWIML5wJ<ּCz^8<ƚJc{fȑ_¾=]d(?VGMl}[(siK++ v{*Nvdž?r>I$k42wKkK:X y[+M=(<%.97[8xg?S[oNWHt}[IW@U?'5cCR*ol+VQC [xފ(U.3-<:K]^ \JNȚUK%Fe\ -[AZ}G 7>sZH˽Y 狀EVzF^?1ϫZ-)$ Ek=%P%Jmh iøq?o|doG1^9,qkn;OLcH:E'/SiyN%YINYk"Rwu[F|yrDvQY@ϟuG?mBgk  [!^I$'zܣڰ6B^ I *;$ ~v:Qrzy)văK q98e{T q7ʁg3jTi2u`mƫ# I*d(ui;=?-Umnz`D-譃+V>{V!\Rq(FPgR)̖̕1n#%\.h+MjiVUڢ>JNuJWBԥ+~DCjԠP]% _a2Ҵ6I; 6 u*B$ɈKRx 8'hS4:JJzJQ:y%eiRys34 H%m+Y9$+⹒UۭK.PA:1Tc^!f!i8!,HPI)3ڑ1вyJ ̟\:.A} Q$Vq>jd ].߯fE֡0[R A-cNsZ 8:ҖT~+cS̒vi}z[qx)5e9<:1?HÖ.p$pq3+lRY;[rwŰT[!+vU24HsU/~j}e,zBd(6B#YX[%/=iHm{=K\JRK{v9Պ&:|ι-:_  &8hBq:QVA!9=^HJ-CCV,(%?Ils䫷o4#+CCN T0ug'znar*-V>*iy(d^7mcAuK+:qOFUP*¸?PP_ZB)a<iݝ&_18O2\yKjn kN?ِUB譆ۑ![ ðBq0/x[Ma(s5/Ii8rی.)XߵO*ϸ{y)q)+€qLC6Վ^0A9W jRWr\|ɻHYol{xYz Y1N'u)Ǟlt=3t գN6}8k7ms#PRH0ՐNo#Ϊכ qec$S9;~H@[,5 ؾzRI8}6t鿥ωAꀷ]BZpy^KV Jc)7I\Mo=:Mc}]fIH;|u.8m-6iݝ&cW_=zN|#}]ghEXy9j*TPRNAH99N(.?` V=8tyĪ4ɔ%j'[8*B7WJ)h7GM'6:PjNGM^8*ZEWJ_r4Qu/+SEE˭E\OģE9֯q?6?L6lGj:2܈[lJ-$B?| WŊ eF~=eLpmy G$uZ"vCP޴]Dś(yx'`Db.1L`82y֫eHj RӆkH+d{jHޤn;*Y =rt (Q{.rV mm`n\#.%d%#}X;y>Z\qB4;gV'6-W'Vt*s~ACϡ(o#+W,mJB\h-NN^- :vT4B0OBEeD<߿EmCM\kv{+[w%eL8wA|ft} }Y>m$Kib`FЭf3q랣D"mk+Ilj=qf2é :ңuG$0ה!y5yS˛Ϥ|RrEU0{ji)9BW5~n@ IMl"HznWP߉Ȣ)4+}|0ӧn"}˨.Y3C]\e쑺AuҒc%YVk.n4p:>oٵ|~}k|۩߶^&0MS?gJtv`At87H_p) W6M(vqo£~CmO22n'ulOXH?Hyzﶛ1\} mE 8[T29&{U7G,пsgz #W \ZJ0{)щ(H4NsF9‡"G=qltY,ɲ; $1Q;͎&oY5uFdK׫loR8p{A Uxn,.9 6\͗CpYn! V^ ֆQN:%9lϕ"y:X3@<5 ǦKJw&u$}>]O_5$$vK#ԞO>A⚬4)5-}[kK[S u1mM8N9sޒ0gBYL[TVՁ%@W"\Hz8.tH2p9Ж u*j≥O ~WBsAmW2]w!N,Va m Ը!6r>P]#G!`#<=E@P@ :Z ICI%,DBPըpk-hm}Ė<qW$wŖ(9MRp69Vݘ써cxo?vswGZ[KR hjcjϊBqNKBi[{ '#o58gBAA 󫙭زb7)V|Tғ$8 `)^RV<3JJ°Φփ`Usez2%M8R9@jv+-Xn.Ʒ]X$6Gf<|[s@ gF~:D)/L?+C%*V8aU!-!2|o3m?szaU/Ѡ2pDRBdaGmH猍~+c-Z`Ddl'mW2$b聗:C pX /5\l\E")5"Lg#_VRB@Ǎm=eIRun͑JʊE'Me/bd>/u%иG R9*FVy弥-JiҜitR{U[/!gmJgSOɼ@EQV,V\5} "3{jV0Wk$)D%CW[rt[[lɎ'W%`qۖܫ/iذɝiyj1F qH;;`HJ`d,wH$lUl&m-R wvRЭ0Uzpe(t<*)BҦ)nǏII[[v^+ n!/ai χjfL ׳. rBFnIN).#f!^[uY MÀP䐡jw܆nIy0()$@?hִZݭR5 tHqZ4P@v?7xto$Բ)zHĚɸ*>$l#ɉ) SFwcL~\P9'./%Yy]ܯ''}{KJšW Nէ𗰭NknG:Q 0'jdB$#έ·+R$;Է7n7ևc9Qwҵu{ Ғū{jN}U,_Xuޭom J hD%,*T@)m*P*8器^iYxU1cu򓌍sTL ':y[qdN$fN@? wm%{Bp.l8PA$xm)  qZqիbj-iYZڗgҼ!2x,-. J;Ԩ睸`7[y/nҬ;5φN_Z$*4@洉ub[6lHP%+ǘҌ28}mw=H0wӿs+E dD|y@l R3_5OBt>3Cz>8r$n/Stͮ8_;jQm;֎'?ޟNqκr0( Xdw+m`Ev_p(QUt7g~}SjdJTOSAyr\t?%({_%έHe (*iR;oom5um% *wJQ|7'6KB$:^Sg + vE] sT0ke;=QGXQ#\~cJWyNG1%[R#Z8K`l};U]w(וeїNtۗhP6˳ʎ+-2)89Hz׶ӘQOX> H b|0Mp}ё԰]KNCۯ=TiIN8>ebmΕrIWT⎕̶ݷw-"K>\8/856vV|I©x nj7o0ҼIV=)$Ь$s'jߚu¯\uB $ٷ>*0w)𦮶x,iTF|.>*,5err[gú)-iBu!Gb Ag;wI.qߞ l7K$ovsɨ/- p,ό 4K.,dJH[9K!|I /415Z}}TCWSnL7)N0Ke @Jp f6^"kheaRR;)6I c,9Ĩ^*KrZc% Ц ZVn')+'d+)PW !2FBARвTN09TWk׍n-e#JE*BvZ@ /-ȑ*#b;6 6[QW1:ۅx%!JQ'|,aetk )$:QamCjVm+IqS(9O^BSbz(Z[9Ҳ;{k4޶TFD45 ;=Yb,iÆ\4* qJqdTG$v0.e٩Z }뒡>fܕ!DIyZ{FNNXҨ6F*Z6~5f%%e,ȏ!&"<cPA Y%IPFr9Ɵb˗!ϕJ)VJRIgjMu4Nc`v F4[օ'X)GR ФV`%7Z>SW8^1013IQ9΅iܡJ^xն! J%)4 J@*V΀|]Uu$'`"Iq8bd$KL(JHʎ,CĊqِq+a)$h *)呟5Cֆv(}6 A_P]rqbYu&)Qqso}i|yD%\w VJ#aߊmMײTq^gчWFfuM9g4Rm1a9Mk\ܕ5II*P# Dwנs VyR*I#x5D.. Xz)!@z]H*OV!r"%-(cI0 `N )`I.'lGwqň:U%*7;ڒ0~!S,q4wXJ\u0#'l۵hRV_Jςi $IAON($ CJ#[d-$w:A";H+sHI$ qi2yl!$ɭrs?;g}n%q{ۻBn7*9NnvW„4-)9)XDC#bXp,tjr\[*e5S{R/AB)E $ N" kxBf7&3lGRu$r[ӆKJB.{EtTFqkw!NIApIJbC9qsum9l.qKe (9bck"vx ,KAЍΕ۲>4فjhL^OjE2"W$%Iq|9\P| 줠#yT{A[MG9 }3ciit4AUFcQ/[ZpyGKN9jXSn6f9`y zGfYwhR:N~c7+[wX!2d8 PR hK/82 ,&;1NkIAH5Slfr ''O;c"#8J',%qS%/!;rnkRzGTWޫ"fN* o 9+n\ZD-]f FcQ' &tpvgАAa+Z =F* :v n%Ӡ)n:q[5}pӮ[!i 9Aco=gZf#>UR4(UhZ{ xzDfr'Ą"ZUɢYnO}|qx2e+mC T)L2\u)Ȫ{][ɾײCVs)vPȒQN3i 8ˁQa yp4|Ϟ?¶?8{ݢ,&"qUS1I+ܖTҋf%$Hsks؛opD+k!;r.2L ^0B osUW06 {w pDY.QRpk8^s֓mP*̞.&?pƳ2R SI.D;?WDt*H)U u bmSv^qSblcnWP=|2YPO IRi6_N9kIߙBa49WX Jr_u8Z<KfO.Đӆgү(9 peB}3IޟW؉ Lil*9bRio5j64ަa{l/U+4߿oE}{ܓU][f7 Ę8~ .FSQܕ\ $CHV[Fvg:'*uC)/\S~Ą ZP;tU  29R`ܓ:sZR7&WtWczPs3^ǸD{o3yfA.iמU΀<շ{orW %!{;BVkqUZmw5QSY(BøˉUr˺Xhўj.(gͲ=PU43\)„B-*=+ޞcqs㛼vdș(e¾i nҥmէEc:6KBCEI[8YHci|,=)k"~\HC )gl$G窍I9.5! ?zCkJ6QKi[*L4F{ӣɹ1r5Ub\K4Sm ^Y$$( 9ˬ g#qV k$X2--8܎9W[@״\!*iqn: GaF[%>6$$(YG\=ebqT#"bz J'6-^訌)>2ݥ mR1eQX6MCV5- jN' Yk~esZYJFgˇñ[Z|մmO(3qa1r# 9^~ Vt =LK>>ܙW/\>B i$j:jq"%c8>7*Qٱ[EYK W~[s;.LSix#HI -A"O^K(LlFuB@X5ѽ>˃&^,֩.7-7(Bii=`dI $c:x!gtS>8p12"̄e1ylʔ'8Ӷ2k?ĮfO]"Af5<8#]R +|NV9@orG\Yr0%+WoutI&o֞ٞƂ-ͦ Ju ,6B6mYM!5uz3! SdZVDxu2RBl s>۬~A1\yZu!J;bS?ֽ:d-"Pmn4C-ڎ)vސn'wrB@o: HGX[s`ۑ v72[aĘ=!)K[7Q{uGvklw% . `|l Y;/YVf_PRB(ڳ4##5vq e< 4&(IK9wq.,wz{95-$[Q~7Qmm|^ďT<JiWh8veEmn9﨎#],I\g/j,4IRJetXsSn}nqvBj(BZ $ĜTlslf:JH2nɵ%g}-qqe },a-%An.OX۾j.J3F) 'c N37hSU8;mii`k Bv42>v+c(J@:RB|a2F6ީQ=1.b&wT ʕ'wF0@$-,&􋸎i.4œDŽ@JׁG_w+ϴݵ9і|!6Nqϔ 7"Er D8fl\Mک1nrc2 D5Յdj p=0NM\V$E [RF' @9mV$pOjjJoRgJt{yl)8_hC*JBGVO.묭##㉶~w';&k/T BR H QKLÐA+bcx!GI<{@^=sjAR-1KŭoSd/m*Bt J0FypȰ7rTHo2*A H885l(nqOӅJ3J d9r-6w,i@nqw—$ƅw)t1$ļi>D%"5j*U)]dueH[aIV$:Fݵ"o2YgVu ҅,ηc4ۉbCN7%léq!IVwY:q ׀H$@-㟎\/ ,Ns qZ]qBQ;%WHp@lBV|a3쳖mqPͲ;~7^\ayMY[TҢ $-)#;d;ךLJ$ZE3|}(cd83fMF)ZqOo O/tN]='⛿)M$7gXF %jH (QR[j>/hW0v8bgo3-R-Ҧ6Od%=>)```|_WٻDVǴh'&^ַ;qmqg:C<2DyHv\!̊%GEwP&fS3'0xoFtKP1 qRT؀/E}@! Ig;#9=ݶ*EӕHȌhK,. 6ެoLuۛ&셁8R2T1*duyHRPu!Bߐy[pABU!d'*#tUrSm3S *SkIVr =摞i&6.9xm3;Ր s5 d۴h9꾕%$I=91\7n;d]-^qSA8`ߵ[ t,{ۅsE.F2 S1b H+1A6˗ܹ_*.]j-_r~%)6}ˉ~5 G«[yuvqӷr *aHZR#'q]uOIvֳj6ˮdaXp[.$5- l nryWiseuxYW=%JS+!KH,}*J[]޷i@jZNFTgi_VB{+B0ap7Nн; Kw(Vc>cJZܦ}Bb֠F!4Tj$T~79zyINGyVKCJ<wτCQCAgڿeZ-W-<:6Rթ$gUŊk$Uco\8?x┑9T S%2\BZx()IPF_^.W~}P(T^a)Z5("lk."ڗQ؛^(=dž-- _0soV&Czΰ9B9yr9Ӟ"'. y$RRA)ϊqٓtȖN'9you+k(ҢmCzR9L0j=;lGvd߳{SGW1q"u.vΐ7/Nq==hLxqZm)ܓ$I55r-T=D *uA @평 .B\Mv_]e)ʜRTq5X[k9ó-#%<:e}Ǐ'<_rmb:x sA׍XeXxo1o' zDmo<(585r>vrjJmr3 MšŸRrtTn'cզhMݥ\}!6 iJ$uRWlf6EΗ 6dNtGݰ?K}Um\aaӭ-* A=&.f' L1݁Nu2 Bxr,ťNC2YRQ#%5_sL,)K͕%/RJH>iy+c{"vMTDK[wu8R9'M\\y/ qGYfK(Kir#,!J s;ګ\SmIP-b؆Z)Jq !d d dg\xnf!\gEbS; D[ J\J9?$ol. 7#'[+E 6sIigq5 q-햣Fg$DDTNIRT VyϦö6vi2Di(q.%$ѶOLĶV+- +?A)m7|"z~\[u_ N{kڙ! xJpFu\RS@O*nWmBq+FKVR0'|>*dahlu$xؤ+{ߐ.c,-@L.2qs:53[S^ Hq>zOxa]JTNI{ur5x=fFcCMR (*;a}d]f<2=XUpr^$诇OpSļFDp|7*RN 8$\?qsqWIFxpZXjr%]HYX Nvge}ae #u^}`5?z| p؝:.T+ {SaVKm`n7N6F*?DZ|@iK߽IRNvrzZz>鏢u}!<}H?I\> [n 9\GTtӈVV^ʡfɲ*Z$Y _{^;==Tynp jK)kFQe jȌP`,?4sQS)~Czt82F#Ii qY?#)nm^0YRqE\anA1XNI2u)ױGmG5>pi946ԆB QY^=b-)*CH)cϹ89])r.mvs3W D^#M|2.u> rExO67'g4̔xe$c]zȦ y6Oo1C)KQ$1D婖!6Vr;r7<@wzGJMJe/0?s Tc|;5}(zK nոJBB ڳQl2|,{rw)up {#q%lveCr"#vU:V7Pg;Ym$w=5pёs|4DhՂFu9a<{-]5֮kD`j9PHPG~-4@٬KH_Mq׶OCg8$2@qNP>6m:rpZ[%8Pq%p A&7[ē1| A,Z5-`}].ѤAq]bRe% @!(ƤnImT{>w:& \ps [Y;E**ud'CVYX4I9T*@l7>5>8fuRظ)2ޠ >efqr5)jYRM7-%+XͭxUsH}ҤR0pn de%Ć_ch4g\LXhܕ訵Eܴ'`Q7G?)x:<;Z8פ)aN(4C(gԤ%#mMTZ$cqOxgDoSSiLH`-D(8T,iݤ:nVL:pe_bgmNUp`gXZG'HRG;S^$.{֥Pc'%}ۺ%!+PPHފK|7;ͩi $ 9yWϱ/$-lwQ5PjHYp9/}' öEjHJU&bT2BJ9𧻓~jiW }uON[ln|R)yԉN:4oeX@sLؙ&dwoiS$7v^Ԑ#Qn]%\L0.o es)i^zrH$ v󮐫X,,6|n*IW?1z i[n$rh55Y:R*I-.czog~oW8Y 섀 i0np}YY̭_[u!ZW 2sȷ`lOZmfcaO:ՠdTZqn uR>qQÈLp =U9UkgCec?OB}wp+ 77;JiaʎvY8ܘ7*˒{4m~J\jUd(pN.8uI)B|,4[RAҁՠ^/ClqoP 2|XWRxk5EujqEn)ʱL"FnywHi?nT DןWhCm ӓ桭;?6/I4͖G\c.ϳ%]no!ƐHEN<Z@-\Eݔ6+K5ko{ d`5.KI, 6+=4!j%~=ʿ%xN5G ܄ ,KI.t[soQq=}*XWra\i,\I۵#o8Lc֧5˚z+irh҂u)3R&0sڼKjpAЃ˞qR2ۭ1+Bh>[Z'`T pp6FbKm̔7ÖȊ8SJ{t2?S>zK*VI䰕(EX@p:T#! /'QP'N.D bd$2p'ȯoMVC6G6j> X~x=Fz&LjBipGTRSX9 !J=;snQ:go7suH;TGY!!AI 8U;z*hY+vKNiR8[ZY|V0%)[NvnI>|fךSNJS:Ѕ9)w~CqTVuI\{*|,P1ڶ`gY8 iQ+QJRV q[xqQ[%HK*V (*/wtNOwUt#kzɉqǢOpXfdrKI†0r5#ZE?VFq:-Nj97)#vYe(J{ꔍq豮)iM+:ҡY0hnTFl8;ert|a= NlPy 4@=5vwwˁK%)8m{+-%qm9-ƕE'Oc0EФŎzL)n%yks6L1h*UTJvh{:,4V? C -t' mJ|-K뒔F*iӍ϶.̝m&ދ=Kr.kFlj]n5N\2vkiFVߕtCz,-aM@JJrjD|p)fK[rNO}DΕ+ˡc%D'?!5Nql[L' %ZEQ%+!TA}n>\&W=Wo!mQZHբ_G̎:Vi9JIA Hg5 t ˄#Y˅q:s <=m80 A>dJi< poQ$>2⬾mi%%l`Z֭jlj(@I?G6se\bhIu*2jGomZl攷&SִCZmKQ 珖k!¼G/FW%#ZT '@(w斮hi$ 2]ua,+D7.sb? Mi})-N)'t֋phLHV`FrS[ !%@zI>:iNx4e YO.Qkw.1;.=kaU|2u7 #!;eEg" 'WHQJVkάVCyzO.mvیFz*89|-.JWN4v֩#.-> Kj=[XaRG>W7}f.BS~M Hr3hε5}ڗt)mU-8Vtnm>zPXm(m B1ց K~\?mKS,T)c)*{x"Em堥8T<&=kl"X-aC/EH'Mgcvr8ȗGmٹNҠj&Mļ3aDo.U+u'L_8+YhJЦ{y5-[1یps)XsY!*$` 5(6Fw0zcZFџ~+.cWW[ܤD[Z@ZBT4r@`:K8jk6;=5כ9Xp)D0{VNGq<|Y*mJrZ q|rnB't3q-5ȺO!E!x*Z=jY&hp l2J5syحm5-&:pq=^ jM6)[a&+* ېYoJ 1m6m0My%7m[|-<(8(Jԥ,.œ6 f'=5+Pnxߝ^lMkT_! VUR}5 Iٛh@⏳[\θ'RRmIJzۀzaIe/%hq#PVNv "Zޥ5dduOKyNu,2ZwjQ.6l{|dw-~gS:g@ wq6dqN"Km}i:\u*HSB]_]~5:)s glzc=e k)?RĤu2ZS*I)T )Ǎq֋{.,\^Rq¼iT ]9 T#e&3GPO.RZdFk >dp4lA99jۢm2 =^gLLIS 8 (Pnh ^ uy9W\O? ==E!,Oq'^@|Exi\XP,2 NGu-[5"DŽ\j2+k DWi)[UVKB+mJN>n2񨤂ysO9#T\RBB䓎G?>*8#Q2=7 ؅:rJ&YaƷ1҂̀*ympuߖ ZN& *G utqÑ\_F[gZHRr4J@el k)RFsgQ)n+>aF0Sנ gW6[9_;~ 9K Zn>R#ǎT^~CmQ$vjDouP]baƐtȑ9w 9r'J-B=DEYu }KdK3H:5zwTn`WVҝR9sĥؐ츣pahxwlTTAO7&5}[~9)Ӕce8 Y'3²?ıb4J-O<88.#Xijgǵ &RP8Y <}vm EE\tj[:5+S4+s..;RJzF+J:RHwvp}RoLRm/BNe, *rG`O .>4#6܌⩭Êy3ٴpTR y6dQN sdAM Gl|SH$',{SS.[yH`oV.,2֒VH1#*%P zBt4 Nc㯶[`u fJJ)JЂW ϣ5w2_ZykWv3mpR tdeF7 )e8[7i9-#!Quq8 da&tvB nF^]{jԅcpiQEq*Ṿ_w!)3_N[ D#x63y.Ƶ9򦹯+fRd7+#*mzTQ}6^[Iv"^Dr5^VAI虁F8ޟ[n AmaPK1ʳ}%)3Ԍ!B@ VF-6H};z:I]158^ZmtcE!V-7[ܩjm)K jʔyA=͒ 5ي|֨V*ud<9uo>J#J9=S OG\SrF^E~2 `d_`niZu+FVXmi |5pLL8.\f j&eJNBֵˈ!ŧ<7* n&\Ħ$$ {U\8_>n-q_GZT I>0>ʄBrrOڰZQq 3qӛlR@m.kBHP q}ٓ ɾċۿ,4nr[D-Q`[ +Qlp4:HϠ~:_g.5TGw(B└JbT+[#G(I! 2*ir\!pd0KLv{ٶϖ7nyt53A 4ZM8hpEO:/ qjp?x/!]r^-%!=b9QVr=ڐ&Hi>2Y+!]RJ{HrDZhXZ#_7gkNAM4QUm"Y/8~KZS +RRZ:F@$շDc[uT)Y:cv*vfKw\jc}CMYdR wc _^S%)>ZHA;QR;"ZvS9Bҭ-Ʌ Lq-l Ҷ))NAB2AqPE\Mz[)<ڨxF"ŒsԬ:B~dRFG roӰ0q'<໬mea'Nv*$T`Ķ2O3u<2};ZUe9-'QETTW49W3 'Aw]QJ3z.=iRDcM騢(ewo^ڣs;}<@*PaFrJ$`^]>ECW?ArUq}m| J,R)񔕫|!##"[F-NgGt6ݢٺ;ۣ>=I9 \gq/2D$EEŸc7#H%8*Jޮ6*}̦A(lT%r`VxvI1,v!IϻqJk*R HqvĨ6|C0,|:꼤>p#_e8CKi1^r+`Rۋy^N4RTJINP?5lG[1I$puܚnmBӺ~In xtSCZ@ N `j ^,ciBBv1 V$I$1Q3Ve )`Qnu*dwŽU쭃A$fZNcMrw&۱OK ݲg!q[Sk KhCHPRTVjsgܕ}8aǖR ׮aAFes)!gvґ-E#.׿b*0h%$oϿ<l36cF4`Y844ᾉQSn*1)N%EiH%Gu=´FxFп}Rrz!)!*81#6ܥ-<{^z17 R l7lk٪{Tvv9)[; hZҡaƮSupRuj dpMֿu Ǘ*r$۸')A] EDuݛuڕAS,/(#Z<Á7kҸ,ç/l-:Hy2t?kK8⼞F:k84඼gʤ#~ULymBu)~LtQ7喛h%D +QR C$8P3s*0V ƲH<m=\upXI;|u콐U6F4fds?n&Z~1'8IIAk^89;1]`cdl- #bQEe*kܹ_(˗֢.'QkWܸGsR _%^l DD+%|K [rGG*=2Z6GĊþ>vKŤa#J 9KoKJ`ԵIe$S,-ST]fA[6w@miVsbRV4/G#$jUr o +Bym)cjN K1-+$+:Hd{y4)ٔ5niuՅȞ PKCjy\]~T!(vʜHGF|2JSh U&)$+N Wyqh]q [ l]R⦚}sR\#h }0uy Uq ed@[Uopz("T69mZ0{y;Ա%<ՁҮv9Ayk~jV*g,Y Lf"4 sى K̷6IJ~.ʓH.֙ Rd -'?ߒ_㻧ZIQK`Xe* Mqfm',Ӊ8ElzpَKJqJ9R\I0B”<ꘖ%#K0{sR iog9svҰ 27;9oOdF ,6:ԡލ*5¦!ÀTu6iͲ=LHTsRM@,ۨUֵ۵R3nև}zFF.u]n.!$O2]BJH$F1e| eJpGw*aGeO-P;jֺ+{_I,U߳ҡ\+Ϣ9ΓxD}3E~T)EetQEQ>ʢ}8+U>ȋUOLWפmUTN#; S; ``9۞V;J>.kk(3njk,Wl #zO2*z;vSrk۹!J֯6{mL;$魟(x<0= x3KDM*:PIӞ_h+u+=MsV܌mvڮS.EqFڟ RpBJW¯߿?@W}Z%\h2[ e P>d8gdikA{|u36h,!o `aD~2T`#|,)ۚPTpU7eNJ(6 n7۶>0r .ΰy{ן"SsܱvT? v/l:Pp1NY9ԅ)!%RNNޜAW`Qi,iȩ Sn#*+ns%f\s)@\%$qw4T?%G! ᕺ8Sː&d tXIa6ڲeXݎ5 DBC`BU@=Fc!A99?&䱳Iަ P厺EPt-7+myr+~zp9uC@tA[>*rV8l`3IkHWB˩QAϡ;N*:Jr~!gNc)*S#Y#UNX|@5=k}ki沏0}㧭H !ha H'u(O]u5$$;#e`=`q%.SR 2-;NH˘mK C '|m .53MZ#<Ā~&:R4,9) 99TH7dr "yh!J jhęĆ/BT''Qpl8Kk^:? Q-ēg]7C)>.4y06;2fC YR'|7cQgj!]u Ժ<ʁ'J0TB9d!o,e1K%g_PIdҊtsܮxuL75 m^Zs I|ds6wT+AiGUu@GzYdIq !HF3dYN9!IijA$J2Rw9siY!hg#cg\F1'UX1 :Q!u+rE,ضĸ ">ZRR7Iy>5S.֨ҍҡEj ZioX%%Xcc1h֒]xQQ*I6aC4ja[C#=i緗eh\e^o':͏lÀaT<WY^>wDc{p?N֝YYƝdNN%gXQE^ϵi<_\mzt'i?z`vҲx>O[ssO%iIl(_O?Ssq`K (cvXD$hu-ai>b7_ZQ/J4*AVse"qKSi0ݔݥ(AQ8iJZRZ)8j 4}4*Q8Y4Bأ YO{* }?=7+=Hu(lk%0@é uQp.JKk)qp$%GsK/1h)b[V-!I[u+ׄ'giVx\hAZ ߖRV}٪>[hh\uY(԰ަHI o)EX`"MV'79I@=_PYc !JP^sgrN_lcOV?wTi,ޢC+e`d%C͍R:;pAqe)to%@ݳcU5䭗a!ƕa' 7k{ ni0H5hLQՉŎ~Wf$KX2wP?iLǼ0ƣVLNs+1T]~JTE\įM.q?ZJ?TS\\c&WKl,{?$VsBu;iIZo+;gn.cÉpJXHl2i:Ao*\ӆ.Ӎ_BFy׷խm%)9ebuޞlE6qҒ rH퇈zrXsڣƖAҗH9lNq %كLٜ#~fǻ-(J#T8š|Y97Fb IғD0Sf֗BW%|F]K}D&di82+ ;:5iӔI>HȋFCs[j81xN٤Ce10FYRGV5I z>k/uO!9SU#_RR\ K{cu)eQ;)9YO4^檒CF]j+L4=O1^))ndH'Nyb=I'8Rs׭i^i&ӉQT D)ZBx_s^uC NHE~W?C >|? !qSBE&4QE+i3\{#GL͂+_:g?Ut'}nĎMsS!{8Eᣟ.?瑱!Jq!*'c{3ڪl_5w)K% 9'HmX=[IBUhR2XdO%g$d:ӟvk4#'IAVeWQYڏ>k5R @2+vڊњGe|'jQ'xȯ=Ej|s۫6]48@R<Ԥ ִj~U+N37O_FLJ׭8`S6ؠG gRT۱e-$؜GO\0ΠJQ%?$G}UR .wQRK"( О8;_f=1 dnͱV쏫.+ˁzs/%V~ I:Uit3jƻdy5ۄ-y8I<1IQQJJA=Aϒ]k*i`/`w>wP<cmZ=򮱡S3"ݢ#RpybbV(Ln kPR{yTm:I'[[)YQ#nx=7csFj" ʒH 85gf-l5s'ͦ֗ucn=4Z=sd2d`FSF* I: TU|Ua#4R %G?=H)I$N3dM# ܏N?j ruXb9:k'5**3Lv]%$w+◒O`ښJ{KDeDo½Au$vrKqI%\ )N#rx܆}TGY!ʔώ}K:֖g$Sg.AhNs殐 16ąjC@p/! {ӵX~*{jj$`׫eZ!  %G~_%e\轗' d`!,]3OǮҮ?C}.[KNY N|o7vƻo!X%4EQ\J/4:Dfmb))㴮E!\8n6{IHX^&SNt:СB r]~9o|{*45JN[?5b11cJPt1sa2+RԠ{u<6ERr+,)+O.eDYqNAiFݫ6':arr ^k[ !y&\9PԆ R[uRNH^Cc& 7Y in;x@[z~~l (!!;ca0NCy|xd@{*ih߶>JZ֖NWaHNDn.- m۩pxnuLy)C)j1^p)XqԂE}+tu/qH- J mH7RJKg=FzDe`)%'e qH[8ձI'In꓀cc{.#d}KzZ=h(?p /cjtÍ+QEE 8H/)fГ![X x#G3T?ND}0^VNL)Bv`E:Ki[I;2| `J[,%uy(9Lp_B*m`AӜ`U+vϟ]0i6H2N}:]ˎ -#Zs㨒< J,dCqU3+,N-a?&AV6JFEJ[eeLjZ}cQ%j<ɯlsRo~jgT¤ ysv'5 9%}zCC);QXT2yzs.s3ҡr*/68 8]S<1- ē\qKQH'݊AŕjO c5󯚀 TTgVҢ;vga=+pAHsrN'/^WeI8X+]M Y]|KVLw1|PҮP#X:+009w4|a4> u[d--,CZ̷\@R^u lsv3s_;/G]7?TtܔQE]~JTE\įM.q?ZJ?TS\X_*4qKuii>0sYGO#Z46?Lz|v$M1jڲ8վzW{'͑{'͑ꤳFkWYW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳFh||<|K5!:~Gi+-p5 *H'`NF O|Q XKA6M<|<|ָ}˓ַGeGrJԄpvH97O0/3s Ԝ UٍT#\ۿE'+/q?%h6G%h6GQĐTwэmdgI#8pi, pQ.iJ/{D=Ty/{D=ThK\RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FeZ4˴qԢDccBC:68Րq@88;V'W]U= ]ˡx'B((r2gNl N p\l%h6G%h6G mnd2cq u)DJмdxKםERۊlbC)qDv% ZTc!'-i8Wict{'͑{'͑bgS ܵ`Rr*HǺy2ymE+N$lFA A"i#%QQRY5f||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑A bN)8o)mK dDyt0sҝGvڑ5r)ϑª;*+ 舣#B,zwKDie6P4 8$k{'͑1?uLl X}esq'yJ/{D=Ty/{D=Th3*1O#GO#Ifр#J/{D=Ty/{D=Th1O#GO#Ifр#J/{D=Ty/{D=U I&%+#R 8'fN%n\v" o:̺6p]hbqWGXe{]E/{D=Ty/{D=TED5͏n[P8!uZ‘yWnj~lUK>lU%3Fe+QRY4`RK>lUK>lU%3Fe+QRY6 7nn6ĥhNQ+ `3 n'd\f^dz^dzJ5P] Dƒ @:ө+AԭЮiv"xD\n_$%Jy^T*H㇚(ܛ/{D=Ty/{D=Tۛr^ u6V&)$`u di7SJ/{D=Ty/{D=ThOQRK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3Fe-Vq͛k U9O5f/c8q?KՇM;=žjI/mQE|{UW/GI\SR[i$HKe@HUE .!og>&BKV8B5]iV-ƒ((~3^<|K4f#r3+QRY5W{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳFh||<|sz0$@CΨ:d2J#;g .l(Nr{iR ԲNz\vRIO#GO#Ob"Dh괸^H0^#JвC#)*mY$~WI^3 o#}daAq̕%h6G%h6GV2^dz^dz,њ0c)_%h6G%h6GF2^dz^dz,њ0c)_%h6GiĸLm(25LpF3u7DYw:dQʗո‰$$UQz_$HrUZUJ~}(Lz bay,7sO$ ò׽|}L,;W4l8 x]#%||<|K4f0||<|K4fW{'͑{'͑ꤳFh||<|K4fW{'͑{'͑ꤳR _U2$'#=`@^U#V1I| &K>lUK>lUI\,r\a3)A !*#R 2s˝4kFTɷ<Ɇe)D,p*px}Af.ғ!QQ"<_zí):y+bp"\ Q.iJ/{D=Ty/{D=ThK\RK>lUK>lU%3Fe+QRY4`RK>lUK>lU%3FeH/2SQs(Fz9yJq' uR qdkWfo^Ǘ 9 ~W(rɵ\įM]~JTQrQj(QNjy;NIR[=Wͫ].Y`ZfYt>2beQT(Î0%'7'|گzHkh h\=WTu{" UX>8 ~hUWjzJo=R<ƫwUTj'|ڣ pQT]s~acU;*5U pQ_8k*.0G1}U_8kD⯂5Tt\ߘzuN>\\g3ԧ&舆\KZ#F8TIӲpj pSsӦ[Ve9= RM#\gm?.PUFm,qu7K *\ s<(iA]b zA*N†M>:z/Z:;v7֠w}?hUWjC.4Ԗۂ),*FG,wr*Nfkz{Me(M篪\f>"tjzCuޥO4}qW_>8 ~iBa3~a4Ud#VN*+_G'|ڮ\ߘzsGWQhUWjN*+_GIQu9WT#VN*+_G'|ڣƫwUTj'|ڣ pQT]s~acU;&ֱ?"r[nH\Sh*Ǐp:PRd*!}ckڽU⽩i1<;-{?D#Hx~ڽm^>ڽm^ ~Kk/c;Ugm^6U`=)pl)ӝjK(IDHWbxsphu oV&ކIijUX>8 ~hUWjIQu<ƫwUTj'|ڣ pQT]s~acU;*5U pQ_8k*.0G1}U_8kD⯂5Tt\ߘzuN>x9_N8 ~+ x&F}j8RNAV㶡.Т{ DPS1;/Jl\e[ۼwXauM4J * b{\Uqs  AB$(*=][Z򵨩Ghd'|ڤmP.u\Ӏֵl'-=eLK!W8+wZ u?/_ p5o~vHyV Gl pS4T1=囝hϊjz;/OK*5U pQ_8kT1}U_8kD⯂5Tt\ߘzuN>U`D⯂5T}qW_:Jo=QjyE_F}qW_>8 ~%E75]S꫇B=*Xwj? pUYz@].p|u:]sB啤lI@rУu$l$0{STTU-7x}{ڽT}{ڽU줨U`D⯂5T}qW_UjyE_F}qW_>8 ~%E75]SUX>8 ~hUWjTsGWQhUWjN*+_GIQu9WT#莎dt2$JD'|ڧ .4Uqm<:A$'d۳lW܂nܦeb9{=/ުVN*+_G'|ڭT1}U_8kD⯂5Tt\ߘzuN>U`D⯂5T}qW_:Jo=QjyE_F}qW_>8 ~%E75]SUX>8 ~hUWjTsGZ=!;8s^,e@qJd $'䭫_ꯟE4r֗FE`{= {H7:䤨߳}W}WFWv?Ev2jW-5|Vj$گ]TroR+C|IPZVN*+_G'|گ_T]s~a1}U_8kD⯂5Tt\ߘzuN>U`D⯂5T}qW_:Jo=QjyE_F}qW_>8 ~%E75]S6辴BKl(=kxN3hUWjC3nLK.նj+匰Nt18R]-Z͔qI;n }.נ%#$ ̮j&shiC!pT:T%DN4)GqW_>8 ~*z8@s:GI )kO "|&j:ʼ#J:+Lh3RdIQ99? hUWjr̓ ,R$ 5U pQ_8kUG1}U_8kD⯂5Tt\ߘzuN>U`D⯂5T}qW_:Jo=QjyE_F}qW_>8 ~%E75]Sꭗظs/?Y'|ڭ;+t~>%&8g դܓjO>ԲHi& -=IQc3lToWͫGWͫ_9^J=]7V}W}.ԯ-wV O:og6SW4jUX>8 ~hUWjT]s~ajyE_F}qW_>8 ~%E75]SUX>8 ~hUWjTsGWQhUWjN*+_GIQu9WT#VN*+_G'|ڣƫwN"E/Ch&!oV:NOn8BіH(Մ'ؓL񙶦my(dY䑶q7D⯂5U.}Cu6w [@ka: ;~smk{V֕̇t\Ŷm#J4!%J7O!ΓpJl*ncBaa8K]J @Gڜ\G.3ߝ2}en/e9'U`D⯂5T}qW_:Jo=QjyE_F}qW_>8 ~%E75]SUX>8 ~hUWjTsGo]]9WBvc*x5SP9"@{Ө<1D⯂5Um]K$<WV=a#-U_8kD⯂5UT]s~a9WT#VN*+_G'|ڣƫwUTj'|ڣ pQT]s~acU;*5U pQ_8k*.0G1}U^;rTݑeιkTrhuIP8y/'|ڧG步ɷP\0ҵ54S^v}ɘ Drw%`.tZf2efCaW\!zqdS=fl+ŵǖÑmnbHsJR !% {;!8 ~8p[LKpm l+eDX]Ķv ~r\Yp 6iQI9׹/#4VN*+_G'|ڭchhT*5U pQ_8k]%E7<ƫwUTj'|ڣ pQT]s~acU;*5U pQ_8k*.0G1}U_8kD⯂5Tt\ߘzuN>!_!\ZZErjXHWz{UU$bЃÂA,\(/mATvjQvjW^\įM>mv%#B$5)sharkwouter-minigalaxy-759382b/scripts/000077500000000000000000000000001455252417400202155ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/scripts/add-language.sh000077500000000000000000000003521455252417400230650ustar00rootroot00000000000000#!/bin/bash cd "$(dirname "$0")"/../data/po if [ -z "${1}" ]; then echo "Please select a language like: ${0} en_US" fi msginit --locale="${1}" --input=minigalaxy.pot LANGFILE="$(echo ${1}|cut -f1 -d'_').po" xdg-open "${LANGFILE}" sharkwouter-minigalaxy-759382b/scripts/check-changelog.sh000077500000000000000000000011771455252417400235640ustar00rootroot00000000000000#!/bin/bash cd "$(dirname "$0")"/.. n=0 while read line; do n=$((n+1)) if [ $n -eq 1 ];then if [[ ! "${line}" =~ ^\*\*[0-9]*\.[0-9]*\.[0-9]*\*\*$ ]]; then echo "First line in CHANGELOG.md doesn't match **1.0.0** format" exit 1 fi version="$(echo ${line}|tr -d "*")" echo "version: ${version}" continue fi if [ -z "${line}" ]; then continue fi if [[ "${line}" =~ ^\*\*[0-9]*\.[0-9]*\.[0-9]*\*\*$ ]]; then break fi echo "${line}" if [[ ! "${line}" =~ ^\ *-\ .* ]]; then echo "Error on line ${n} in CHANGELOG.md does not start with \"- \"" exit 2 fi done < CHANGELOG.md sharkwouter-minigalaxy-759382b/scripts/compile-translations.sh000077500000000000000000000003721455252417400247250ustar00rootroot00000000000000#!/bin/bash cd "$(dirname "$0")"/../data/po OUTPUTFILE="minigalaxy.mo" for langfile in *.po; do OUTPUTDIR="../mo/$(echo ${langfile}|cut -f1 -d '.')/LC_MESSAGES" mkdir -p "${OUTPUTDIR}" msgfmt -o "${OUTPUTDIR}/${OUTPUTFILE}" "${langfile}" done sharkwouter-minigalaxy-759382b/scripts/create-release.sh000077500000000000000000000063261455252417400234440ustar00rootroot00000000000000#!/bin/bash set -e # Set variables cd "$(dirname "$0")/.." WORK_DIR="${PWD}" SCRIPT_DIR="${WORK_DIR}/scripts" CHANGELOG_FILE="${WORK_DIR}/CHANGELOG.md" METADATA_FILE="${WORK_DIR}/data/io.github.sharkwouter.Minigalaxy.metainfo.xml" RELEASE_FILE="${WORK_DIR}/release.md" VERSION_FILE="${WORK_DIR}/minigalaxy/version.py" TOML_FILE="${WORK_DIR}/pyproject.toml" VERSION="$(head -1 "${CHANGELOG_FILE}"|tr -d "*")" check_changelog() { "${SCRIPT_DIR}/check-changelog.sh" > /dev/null } init_release_file() { echo "Minigalaxy version ${VERSION} is now available. For new users, Minigalaxy is a simple GOG client for Linux. The download and a breakdown of the changes can be found below." > "${RELEASE_FILE}" echo "" >> "${RELEASE_FILE}" echo "![screenshot](https://raw.githubusercontent.com/sharkwouter/minigalaxy/${VERSION}/screenshot.jpg)" >> "${RELEASE_FILE}" echo "" >> "${RELEASE_FILE}" echo "## Changes" >> "${RELEASE_FILE}" echo "" >> "${RELEASE_FILE}" } add_release_file_entry() { echo " - $@" >> "${RELEASE_FILE}" } finish_release_file() { echo "" >> "${RELEASE_FILE}" echo "As usual, a deb file for installing this release on Debian and Ubuntu can be found below. Packages most distributions will most likely become available soon. See the [website](https://sharkwouter.github.io/minigalaxy/) for installation instructions.">> "${RELEASE_FILE}" } init_metadata() { xmlstarlet ed -L \ -s /component/releases \ -t elem -n "release version=\"${VERSION}\" date=\"$(date -Idate)\"" \ "${METADATA_FILE}" xmlstarlet ed -L \ -s /component/releases/release[@version="'$VERSION'"] \ -t elem -n "description" \ -s /component/releases/release[@version="'$VERSION'"]/description \ -t elem -n "p" -v "Implements the following changes:" \ -s /component/releases/release[@version="'$VERSION'"]/description \ -t elem -n "ul" \ "${METADATA_FILE}" } add_metadata_entry() { xmlstarlet ed -L \ -s /component/releases/release[@version="'$VERSION'"]/description/ul \ -t elem -n li -v "$(echo $@|sed 's/^- //')" \ "${METADATA_FILE}" } sort_metadata() { xmlstarlet tr --xinclude "${SCRIPT_DIR}/sort-releases.xls" "${METADATA_FILE}" > "${METADATA_FILE}.tmp" mv "${METADATA_FILE}.tmp" "${METADATA_FILE}" sed -i '1s/^/\n/' "${METADATA_FILE}" } add_debian_changelog_entry() { dch -v "${VERSION}" -M "$(echo $@|sed 's/^- //')" } set_debian_changelog_release() { dch -r -D "$(lsb_release -cs)" "" } set_version() { echo "VERSION = \"${VERSION}\"" > "${VERSION_FILE}" sed -i "s/version = .*/version = \"${VERSION}\"/" "${TOML_FILE}" } return_version_info() { echo "::set-output name=VERSION::${VERSION}" } ############### # Actual code # ############### check_changelog set_version init_metadata init_release_file n=0 while read line; do n=$((n+1)) if [ $n -eq 1 ] || [ -z "${line}" ]; then continue fi # End the loop if we find the next version if [[ "${line}" =~ ^\*\*[0-9]*\.[0-9]*\.[0-9]*\*\*$ ]]; then break fi line="$(echo ${line}|sed 's/^- //')" add_metadata_entry "${line}" add_release_file_entry "${line}" add_debian_changelog_entry "${line}" done < "${CHANGELOG_FILE}" set_debian_changelog_release finish_release_file sort_metadata return_version_info sharkwouter-minigalaxy-759382b/scripts/missing-translations.sh000077500000000000000000000002541455252417400247450ustar00rootroot00000000000000#!/bin/bash cd "$(dirname "$0")"/.. # Update each po file for langfile in data/po/*.po; do echo "file: ${langfile}" msgattrib --untranslated "${langfile}" echo "" done sharkwouter-minigalaxy-759382b/scripts/sort-releases.xls000066400000000000000000000010111455252417400235260ustar00rootroot00000000000000 sharkwouter-minigalaxy-759382b/scripts/take-screenshot.sh000077500000000000000000000005421455252417400236540ustar00rootroot00000000000000# Variables cd "$(dirname "$0")"/.. IMAGE="screenshot.jpg" # Delete the old screenshot rm -f ${IMAGE} # Start Minigalaxy bin/minigalaxy & # Wait for Minigalaxy sleep 5s # Get the window id WID="$(xwininfo -tree -root|grep Minigalaxy|tail -1|awk '{print $1}')" # Make the screenshot import -window "${WID}" -strip -trim "${PWD}/${IMAGE}" && kill %1 sharkwouter-minigalaxy-759382b/scripts/update-translation-files.sh000077500000000000000000000007131455252417400254730ustar00rootroot00000000000000#!/bin/bash cd "$(dirname "$0")"/.. POTFILE="data/po/minigalaxy.pot" # Generate the pot file xgettext --from-code=UTF-8 --keyword=_ --sort-output --language=Python minigalaxy/*.py minigalaxy/ui/*.py bin/minigalaxy -o "${POTFILE}" xgettext --join-existing --from-code=UTF-8 --keyword=translatable --sort-output --language=Glade data/ui/*.ui -o "${POTFILE}" # Update each po file for langfile in data/po/*.po; do msgmerge -U "${langfile}" "${POTFILE}" done sharkwouter-minigalaxy-759382b/setup.py000066400000000000000000000057211455252417400202450ustar00rootroot00000000000000from setuptools import setup, find_packages from glob import glob import subprocess from minigalaxy.version import VERSION # Generate the translations subprocess.run(['bash', 'scripts/compile-translations.sh']) setup( name="minigalaxy", version=VERSION, packages=find_packages(exclude=['tests']), scripts=['bin/minigalaxy'], data_files=[ ('share/applications', ['data/io.github.sharkwouter.Minigalaxy.desktop']), ('share/icons/hicolor/128x128/apps', ['data/icons/128x128/io.github.sharkwouter.Minigalaxy.png']), ('share/icons/hicolor/192x192/apps', ['data/icons/192x192/io.github.sharkwouter.Minigalaxy.png']), ('share/minigalaxy/ui', glob('data/ui/*.ui')), ('share/minigalaxy/images', glob('data/images/*')), ('share/minigalaxy/', ['data/style.css']), ('share/metainfo', ['data/io.github.sharkwouter.Minigalaxy.metainfo.xml']), # Add translations ('share/minigalaxy/translations/de/LC_MESSAGES/', ['data/mo/de/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/es/LC_MESSAGES/', ['data/mo/es/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/fr/LC_MESSAGES/', ['data/mo/fr/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/nb_NO/LC_MESSAGES/', ['data/mo/nb_NO/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/nl/LC_MESSAGES/', ['data/mo/nl/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/nn_NO/LC_MESSAGES/', ['data/mo/nn_NO/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/pl/LC_MESSAGES/', ['data/mo/pl/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/pt_BR/LC_MESSAGES/', ['data/mo/pt_BR/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/ru_RU/LC_MESSAGES/', ['data/mo/ru_RU/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/tr/LC_MESSAGES/', ['data/mo/tr/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/zh_CN/LC_MESSAGES/', ['data/mo/zh_CN/LC_MESSAGES/minigalaxy.mo']), ('share/minigalaxy/translations/zh_TW/LC_MESSAGES/', ['data/mo/zh_TW/LC_MESSAGES/minigalaxy.mo']), ], # Project uses reStructuredText, so ensure that the docutils get # installed or upgraded on the target machine install_requires=[ 'PyGObject>=3.30', 'requests', ], # metadata to display on PyPI author="Wouter Wijsman", author_email="wwijsman@live.nl", description="A simple GOG Linux client", keywords="GOG gog client gaming gtk Gtk", url="https://github.com/sharkwouter/minigalaxy", # project home page, if any project_urls={ "Bug Tracker": "https://github.com/sharkwouter/minigalaxy/issues", "Documentation": "https://github.com/sharkwouter/minigalaxy/blob/master/README.md", "Source Code": "https://github.com/sharkwouter/minigalaxy", }, classifiers=[ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", ] ) sharkwouter-minigalaxy-759382b/tests/000077500000000000000000000000001455252417400176705ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/tests/__init__.py000066400000000000000000000000001455252417400217670ustar00rootroot00000000000000sharkwouter-minigalaxy-759382b/tests/test_api.py000066400000000000000000000773661455252417400220750ustar00rootroot00000000000000import http from unittest import TestCase from unittest.mock import MagicMock, Mock import copy import requests import time from minigalaxy.api import Api from minigalaxy.game import Game API_GET_INFO_TOONSTRUCK = {'downloads': {'installers': [ {'id': 'installer_windows_en', 'name': 'Toonstruck', 'os': 'windows', 'language': 'en', 'language_full': 'English', 'version': '1.0', 'total_size': 939524096, 'files': [{'id': 'en1installer0', 'size': 1048576, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en1installer0'}, {'id': 'en1installer1', 'size': 938475520, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en1installer1'}]}, {'id': 'installer_mac_en', 'name': 'Toonstruck', 'os': 'mac', 'language': 'en', 'language_full': 'English', 'version': 'gog-3', 'total_size': 975175680, 'files': [{'id': 'en2installer0', 'size': 975175680, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en2installer0'}]}, {'id': 'installer_linux_en', 'name': 'Toonstruck', 'os': 'linux', 'language': 'en', 'language_full': 'English', 'version': 'gog-2', 'total_size': 963641344, 'files': [{'id': 'en3installer0', 'size': 963641344, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en3installer0'}]}, {'id': 'installer_windows_fr', 'name': 'Toonstruck', 'os': 'windows', 'language': 'fr', 'language_full': 'français', 'version': '1.0', 'total_size': 985661440, 'files': [{'id': 'fr1installer0', 'size': 1048576, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr1installer0'}, {'id': 'fr1installer1', 'size': 984612864, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr1installer1'}]}, {'id': 'installer_mac_fr', 'name': 'Toonstruck', 'os': 'mac', 'language': 'fr', 'language_full': 'français', 'version': 'gog-3', 'total_size': 1023410176, 'files': [{'id': 'fr2installer0', 'size': 1023410176, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr2installer0'}]}, {'id': 'installer_linux_fr', 'name': 'Toonstruck', 'os': 'linux', 'language': 'fr', 'language_full': 'français', 'version': 'gog-2', 'total_size': 1011875840, 'files': [{'id': 'fr3installer0', 'size': 1011875840, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr3installer0'}]} ]}} API_GET_INFO_STELLARIS = [{'id': '51622789000874509', 'game_id': '51154268886064420', 'platform_id': 'gog', 'external_id': '1508702879', 'game': {'genres': [{'id': '51071904337940794', 'name': {'*': 'Strategy', 'en-US': 'Strategy'}, 'slug': 'strategy'}], 'summary': {'*': 'Stellaris description'}, 'visible_in_library': True, 'aggregated_rating': 78.5455, 'game_modes': [{'id': '53051895165351137', 'name': 'Single player', 'slug': 'single-player'}, {'id': '53051908711988230', 'name': 'Multiplayer', 'slug': 'multiplayer'}], 'horizontal_artwork': {'url_format': 'https://images.gog.com/742acfb77ec51ca48c9f96947bf1fc0ad8f0551c9c9f338021e8baa4f08e449f{formatter}.{ext}?namespace=gamesdb'}, 'background': {'url_format': 'https://images.gog.com/742acfb77ec51ca48c9f96947bf1fc0ad8f0551c9c9f338021e8baa4f08e449f{formatter}.{ext}?namespace=gamesdb'}, 'vertical_cover': {'url_format': 'https://images.gog.com/8d822a05746670fb2540e9c136f0efaed6a2d5ab698a9f8bd7f899d21f2022d2{formatter}.{ext}?namespace=gamesdb'}, 'cover': {'url_format': 'https://images.gog.com/8d822a05746670fb2540e9c136f0efaed6a2d5ab698a9f8bd7f899d21f2022d2{formatter}.{ext}?namespace=gamesdb'}, 'logo': {'url_format': 'https://images.gog.com/c50a5d26c42d84b4b884976fb89d10bb3e97ebda0c0450285d92b8c50844d788{formatter}.{ext}?namespace=gamesdb'}, 'icon': {'url_format': 'https://images.gog.com/c85cf82e6019dd52fcdf1c81d17687dd52807835f16aa938abd2a34e5d9b99d0{formatter}.{ext}?namespace=gamesdb'}, 'square_icon': {'url_format': 'https://images.gog.com/c3adc81bf37f1dd89c9da74c13967a08b9fd031af4331750dbc65ab0243493c8{formatter}.{ext}?namespace=gamesdb'}}}] GAMESDB_INFO_STELLARIS = {'cover': 'https://images.gog.com/8d822a05746670fb2540e9c136f0efaed6a2d5ab698a9f8bd7f899d21f2022d2.png?namespace=gamesdb', 'vertical_cover': 'https://images.gog.com/8d822a05746670fb2540e9c136f0efaed6a2d5ab698a9f8bd7f899d21f2022d2.png?namespace=gamesdb', 'background': 'https://images.gog.com/742acfb77ec51ca48c9f96947bf1fc0ad8f0551c9c9f338021e8baa4f08e449f.png?namespace=gamesdb', 'summary': {'*': 'Stellaris description'}, 'genre': {'*': 'Strategy', 'en-US': 'Strategy'}} API_GET_INFO_BLACKWELL = [{'id': '51437500425760439', 'game_id': '51295394952128810', 'platform_id': 'gog', 'external_id': '1207662883', 'dlcs_ids': [], 'dlcs': [], 'parent_id': None, 'supported_operating_systems': [{'slug': 'linux', 'name': 'Linux'}, {'slug': 'osx', 'name': 'macOS'}, {'slug': 'windows', 'name': 'Windows'}], 'available_languages': [{'code': 'en-US'}], 'first_release_date': '2006-12-23T00:00:00+0000', 'game': {'id': '51295394952128810', 'parent_id': None, 'dlcs_ids': [], 'first_release_date': '2006-12-23T00:00:00+0000', 'releases': [{'id': '51295394972780958', 'platform_id': 'steam', 'external_id': '80330', 'release_per_platform_id': 'steam_80330'}, {'id': '51437500425760439', 'platform_id': 'gog', 'external_id': '1207662883', 'release_per_platform_id': 'gog_1207662883'}, {'id': '52472176412391232', 'platform_id': 'humble', 'external_id': 'theblackwelllegacy', 'release_per_platform_id': 'humble_theblackwelllegacy'}, {'id': '52990950679797056', 'platform_id': 'humble', 'external_id': 'blackwelllegacy_steam', 'release_per_platform_id': 'humble_blackwelllegacy_steam'}, {'id': '54824915113880126', 'platform_id': 'amiga', 'external_id': 'blackwell1', 'release_per_platform_id': 'amiga_blackwell1'}, {'id': '56154123001776496', 'platform_id': 'humble', 'external_id': 'blackwelllegacy_bundle_steam', 'release_per_platform_id': 'humble_blackwelllegacy_bundle_steam'}, {'id': '51295394952128810', 'platform_id': 'generic', 'external_id': '51295394952128810', 'release_per_platform_id': 'generic_51295394952128810'}], 'title': {'*': 'The Blackwell Legacy', 'en-US': 'The Blackwell Legacy'}, 'sorting_title': {'*': 'Blackwell Legacy', 'en-US': 'Blackwell Legacy'}, 'type': 'game', 'developers_ids': ['51141380061810434'], 'developers': [{'id': '51141380061810434', 'name': 'Wadjet Eye Games', 'slug': 'wadjet-eye-games'}], 'publishers_ids': ['51141380061810434'], 'publishers': [{'id': '51141380061810434', 'name': 'Wadjet Eye Games', 'slug': 'wadjet-eye-games'}], 'genres_ids': ['51071842251704278', '51121492616405278', '51141224673762034', '51141224986801358'], 'genres': [{'id': '51071842251704278', 'name': {'*': 'Adventure', 'en-US': 'Adventure'}, 'slug': 'adventure'}, {'id': '51121492616405278', 'name': {'*': 'Indie', 'en-US': 'Indie'}, 'slug': 'indie'}, {'id': '51141224673762034', 'name': {'*': 'Puzzle', 'en-US': 'Puzzle'}, 'slug': 'puzzle'}, {'id': '51141224986801358', 'name': {'*': 'Point-and-click', 'en-US': 'Point-and-click'}, 'slug': 'point-and-click'}], 'themes_ids': ['51141224799400616', '51141227696328058', '51141227910729860'], 'themes': [{'id': '51141224799400616', 'name': {'*': 'Comedy', 'en-US': 'Comedy'}, 'slug': 'comedy'}, {'id': '51141227696328058', 'name': {'*': 'Mystery', 'en-US': 'Mystery'}, 'slug': 'mystery'}, {'id': '51141227910729860', 'name': {'*': 'Drama', 'en-US': 'Drama'}, 'slug': 'drama'}], 'screenshots': [{'url_format': 'https://images.gog.com/368f07e0edcce9191e60a004b0ef7bf8d9a97eac65b66e5e497f9e2728045255{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/075326e70912449e02c3d4593a26317ad8d7f8ba5f2d0d3aa216a5a467130f34{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/2c71a940d797c466a563ecfc132933300ce37bd996140dbaaf48333d2f628fb3{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/d8c490c12f0534adeb69fdeb467a4719d24765a85a58a880f9aab13008d14683{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/5724fb0d43b4b753e89a6dec52b40a8c401396d6b5ecaa228115a3b06c5fd374{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/7478e2b53e403ee04eb27331ecebad0d6e3403e76c747fffd6f5b2303005b174{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/bd6f6ba043f9ae0883c196086841891ac5ed11dcf933ab2ca3f104312be10802{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/c37db76ad0d3f6471a5bbac1cc5a17393ee5a2c8ae16150cc221aac5855dd788{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/9ec05f3b79a0e3bd9a2b4eb29788222b5723feb6273b6a0bbb711d53d6a93b25{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/7376efd38b2aa383e16d066c6cfbc78fefa9b870f91033666a08bef85bac6db9{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/78145ccd85eca1fec952a464b3b9aeea7b5288518282e8194e3a0991fd9aef77{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/d46d89dfabe116db2f428138d3a3481faf91e00759ef97e02645da8f2652df3a{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/01e93965ea0ce72fdb50472e08a11924a9c5aa45f86a6aed4deeff987010df34{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/c2f064852161738df48918111327c108cdb014bffff487caa13caa2f7c2d5750{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/5cedd80d5f46fa20d4f95e9b8305d608b44c098b4741d2347e0365c2c9932cda{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/b80f003398c1d4e63ae2054665ee90785a9ea526b371859269e92c51ac7840c1{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/702fe48f311ef0d398e219cf28eda78b64bf3104dda937983b7ec621d0845caa{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/73b9adb70ec1c2bfc75cd6286a4b1f5c81959a4e25ce4e0527dd9a61d28c7953{formatter}.{ext}?namespace=gamesdb'}], 'videos': [{'provider': 'youtube', 'video_id': 'j026WKDQhi8', 'thumbnail_id': 'j026WKDQhi8', 'name': 'Trailer'}], 'artworks': [{'url_format': 'https://images.gog.com/19eba3c8303ce3cdb45cc9f2f94993efd2620f7f12eeac814512e64d26e88f0f{formatter}.{ext}?namespace=gamesdb'}, {'url_format': 'https://images.gog.com/978a934689c303511c425450c62db88edd11ac4da9026d801159decf605cd93e{formatter}.{ext}?namespace=gamesdb'}], 'summary': {'*': "omitted for test", 'en-US': "omitted for test"}, 'visible_in_library': True, 'aggregated_rating': 70, 'game_modes': [{'id': '53051895165351137', 'name': 'Single player', 'slug': 'single-player'}], 'horizontal_artwork': {'url_format': 'https://images.gog.com/e6e6d9001ec0c990327bd8f20630b5e39d0b5d33f69778163025d9d7b04f3c44{formatter}.{ext}?namespace=gamesdb'}, 'background': {'url_format': 'https://images.gog.com/e6e6d9001ec0c990327bd8f20630b5e39d0b5d33f69778163025d9d7b04f3c44{formatter}.{ext}?namespace=gamesdb'}, 'vertical_cover': {'url_format': 'https://images.gog.com/87f87afa3a55ca5eb59df9a66c5063c202281432262b3f0fe8ef8e324270df61{formatter}.{ext}?namespace=gamesdb'}, 'cover': {'url_format': 'https://images.gog.com/87f87afa3a55ca5eb59df9a66c5063c202281432262b3f0fe8ef8e324270df61{formatter}.{ext}?namespace=gamesdb'}, 'logo': {'url_format': 'https://images.gog.com/a82f4f793fc7e981b7a8d429508b11ba7ec5783f3ae5db6a8d3508bd139ba122{formatter}.{ext}?namespace=gamesdb'}, 'square_icon': {'url_format': 'https://images.gog.com/978a934689c303511c425450c62db88edd11ac4da9026d801159decf605cd93e{formatter}.{ext}?namespace=gamesdb'}, 'global_popularity_all_time': 0, 'global_popularity_current': 0, 'series': {'id': '53060497266141230', 'name': 'Blackwell', 'slug': 'blackwell'}}, 'title': {'*': 'Blackwell Legacy', 'en-US': 'The Blackwell Legacy'}, 'sorting_title': {'*': 'Blackwell Legacy', 'en-US': 'Blackwell Legacy'}, 'type': 'game', 'summary': {'*': "When Rosa Blackwell's only relative dies after twenty years in a coma, she thinks the worst is over. This all changes when Joey Mallone, a sardonic ghost from the 1930s, blows into her life and tells her that she is a medium. Whether they like it or not, it is up to them to cure the supernatural ills of New York in this critically-acclaimed series of point-and-click adventure games. \r\nWhen three NYU students kill themselves one after the other, nobody thinks that a sinister force is at work. Nobody but fledgling medium Rosa Blackwell and her new spirit guide Joey Mallone. It's trial by fire as they set these troubled spirits to rest.", 'en-US': "When Rosa Blackwell's only relative dies after twenty years in a coma, she thinks the worst is over. This all changes when Joey Mallone, a sardonic ghost from the 1930s, blows into her life and tells her that she is a medium. Whether they like it or not, it is up to them to cure the supernatural ills of New York in this critically-acclaimed series of point-and-click adventure games.\nWhen three NYU students kill themselves one after the other, nobody thinks that a sinister force is at work. Nobody but fledgling medium Rosa Blackwell and her new spirit guide Joey Mallone. It's trial by fire as they set these troubled spirits to rest."}, 'videos': [{'provider': 'youtube', 'video_id': 'vkinYRD5sr4', 'thumbnail_id': 'vkinYRD5sr4', 'name': None}, {'provider': 'youtube', 'video_id': 'gLcoCPfc1zE', 'thumbnail_id': 'gLcoCPfc1zE', 'name': None}], 'game_modes': [{'id': '53051895165351137', 'name': 'Single player', 'slug': 'single-player'}], 'logo': {'url_format': 'https://images.gog.com/a82f4f793fc7e981b7a8d429508b11ba7ec5783f3ae5db6a8d3508bd139ba122{formatter}.{ext}?namespace=gamesdb'}, 'series': {'id': '53060497266141230', 'name': 'Blackwell', 'slug': 'blackwell'}}] GAMESDB_INFO_BLACKWELL = {'background': 'https://images.gog.com/e6e6d9001ec0c990327bd8f20630b5e39d0b5d33f69778163025d9d7b04f3c44.png?namespace=gamesdb', 'cover': 'https://images.gog.com/87f87afa3a55ca5eb59df9a66c5063c202281432262b3f0fe8ef8e324270df61.png?namespace=gamesdb', 'genre': {'*': 'Adventure, Indie, Puzzle, Point-and-click', 'en-US': 'Adventure, Indie, Puzzle, Point-and-click'}, 'summary': {'*': 'omitted for test', 'en-US': 'omitted for test'}, 'vertical_cover': 'https://images.gog.com/87f87afa3a55ca5eb59df9a66c5063c202281432262b3f0fe8ef8e324270df61.png?namespace=gamesdb'} class TestApi(TestCase): def test_get_login_url(self): session = MagicMock() config = MagicMock() api = Api(config, session) exp = "https://auth.gog.com/auth?client_id=46899977096215655&redirect_uri=https%3A%2F%2Fembed.gog.com%2Fon_login_success%3Forigin%3Dclient&response_type=code&layout=client2" obs = api.get_login_url() self.assertEqual(exp, obs) def test_get_redirect_url(self): session = MagicMock() config = MagicMock() api = Api(config, session) exp = "https://embed.gog.com/on_login_success?origin=client" obs = api.get_redirect_url() self.assertEqual(exp, obs) def test1_can_connect(self): session = MagicMock() config = MagicMock() api = Api(config, session) exp = True obs = api.can_connect() self.assertEqual(exp, obs) def test2_can_connect(self): session = MagicMock() config = MagicMock() api = Api(config, session) session.get.side_effect = requests.exceptions.ConnectionError(Mock(status="Connection Error")) exp = False obs = api.can_connect() self.assertEqual(exp, obs) def test1_get_download_info(self): session = MagicMock() config = MagicMock() api = Api(config, session) api.get_info = MagicMock() api.get_info.return_value = API_GET_INFO_TOONSTRUCK config.lang = "pl" test_game = Game("Test Game") exp = {'id': 'installer_linux_en', 'name': 'Toonstruck', 'os': 'linux', 'language': 'en', 'language_full': 'English', 'version': 'gog-2', 'total_size': 963641344, 'files': [{'id': 'en3installer0', 'size': 963641344, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en3installer0'}]} obs = api.get_download_info(test_game) self.assertEqual(exp, obs) def test2_get_download_info(self): session = MagicMock() config = MagicMock() api = Api(config, session) api.get_info = MagicMock() api.get_info.return_value = API_GET_INFO_TOONSTRUCK config.lang = "fr" test_game = Game("Test Game") exp = {'id': 'installer_linux_fr', 'name': 'Toonstruck', 'os': 'linux', 'language': 'fr', 'language_full': 'français', 'version': 'gog-2', 'total_size': 1011875840, 'files': [{'id': 'fr3installer0', 'size': 1011875840, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr3installer0'}]} obs = api.get_download_info(test_game) self.assertEqual(exp, obs) def test3_get_download_info(self): session = MagicMock() config = MagicMock() api = Api(config, session) api.get_info = MagicMock() api.get_info.return_value = {'downloads': {'installers': [ {'id': 'installer_windows_en', 'name': 'Toonstruck', 'os': 'windows', 'language': 'en', 'language_full': 'English', 'version': '1.0', 'total_size': 939524096, 'files': [{'id': 'en1installer0', 'size': 1048576, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en1installer0'}, {'id': 'en1installer1', 'size': 938475520, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en1installer1'}]}, {'id': 'installer_mac_en', 'name': 'Toonstruck', 'os': 'mac', 'language': 'en', 'language_full': 'English', 'version': 'gog-3', 'total_size': 975175680, 'files': [{'id': 'en2installer0', 'size': 975175680, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en2installer0'}]}, {'id': 'installer_windows_fr', 'name': 'Toonstruck', 'os': 'windows', 'language': 'fr', 'language_full': 'français', 'version': '1.0', 'total_size': 985661440, 'files': [{'id': 'fr1installer0', 'size': 1048576, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr1installer0'}, {'id': 'fr1installer1', 'size': 984612864, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr1installer1'}]}, {'id': 'installer_mac_fr', 'name': 'Toonstruck', 'os': 'mac', 'language': 'fr', 'language_full': 'français', 'version': 'gog-3', 'total_size': 1023410176, 'files': [{'id': 'fr2installer0', 'size': 1023410176, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/fr2installer0'}]} ]}} config.lang = "en" test_game = Game("Test Game") exp = {'id': 'installer_windows_en', 'name': 'Toonstruck', 'os': 'windows', 'language': 'en', 'language_full': 'English', 'version': '1.0', 'total_size': 939524096, 'files': [{'id': 'en1installer0', 'size': 1048576, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en1installer0'}, {'id': 'en1installer1', 'size': 938475520, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en1installer1'}]} obs = api.get_download_info(test_game) self.assertEqual(exp, obs) def test4_get_download_info(self): session = MagicMock() config = MagicMock() api = Api(config, session) dlc_test_installer = API_GET_INFO_TOONSTRUCK["downloads"]["installers"] config.lang = "en" test_game = Game("Test Game") exp = {'id': 'installer_linux_en', 'name': 'Toonstruck', 'os': 'linux', 'language': 'en', 'language_full': 'English', 'version': 'gog-2', 'total_size': 963641344, 'files': [{'id': 'en3installer0', 'size': 963641344, 'downlink': 'https://api.gog.com/products/1207666633/downlink/installer/en3installer0'}]} obs = api.get_download_info(test_game, dlc_installers=dlc_test_installer) self.assertEqual(exp, obs) def test1_get_library(self): session = MagicMock() config = MagicMock() api = Api(config, session) api.active_token = True response_dict = {'totalPages': 1, 'products': [{'id': 1097893768, 'title': 'Neverwinter Nights: Enhanced Edition', 'image': '//images-2.gog-statics.com/8706f7fb87a4a41bc34254f3b49f59f96cf13d067b2c8bbfd8d41c327392052a', 'url': '/game/neverwinter_nights_enhanced_edition_pack', 'worksOn': {'Windows': True, 'Mac': True, 'Linux': True}, "category": "Role-playing"}]} api.active_token_expiration_time = time.time() + 10.0 response_mock = MagicMock() response_mock.json.return_value = response_dict session.get.return_value = response_mock session.get().status_code = http.HTTPStatus.OK exp = "Neverwinter Nights: Enhanced Edition" retrieved_games, err_msg = api.get_library() obs = retrieved_games[0].name self.assertEqual(exp, obs) def test2_get_library(self): session = MagicMock() config = MagicMock() api = Api(config, session) api.active_token = False api.active_token_expiration_time = time.time() + 10.0 response_mock = MagicMock() response_mock.json.return_value = {} session.get.return_value = response_mock exp = "Couldn't connect to GOG servers" retrieved_games, obs = api.get_library() self.assertEqual(exp, obs) def test1_get_version(self): session = MagicMock() config = MagicMock() api = Api(config, session) test_game = Game("Test Game", platform="linux") exp = "gog-2" obs = api.get_version(test_game, gameinfo=API_GET_INFO_TOONSTRUCK) self.assertEqual(exp, obs) def test2_get_version(self): session = MagicMock() config = MagicMock() api = Api(config, session) test_game = Game("Test Game", platform="linux") dlc_name = "Test DLC" game_info = API_GET_INFO_TOONSTRUCK game_info["expanded_dlcs"] = [{"title": dlc_name, "downloads": {"installers": [{"os": "linux", "version": "1.2.3.4"}]}}] exp = "1.2.3.4" obs = api.get_version(test_game, gameinfo=API_GET_INFO_TOONSTRUCK, dlc_name=dlc_name) self.assertEqual(exp, obs) def test_get_download_file__info_md5(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK session.get().text = ''' 7e62ce101221ccdae2e9bff5c16ed9e0 b80960a2546ce647bffea87f85385535 5464b4499cd4368bb83ea35f895d3560 0261b9225fc10c407df083f6d254c47b ''' exp = "8acedf66c0d2986e7dee9af912b7df4f" obs = api.get_download_file_info("url").md5 self.assertEqual(exp, obs) def test_get_download_file_info_md5_returns_empty_string_on_empty_response(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK session.get().text = "" exp = "" obs = api.get_download_file_info("url").md5 self.assertEqual(exp, obs) def test_get_download_file_info_md5_returns_empty_string_on_response_error(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.NOT_FOUND exp = "" obs = api.get_download_file_info("url").md5 self.assertEqual(exp, obs) def test_get_download_file_info_md5_returns_empty_string_on_missing_md5(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK session.get().text = ''' 7e62ce101221ccdae2e9bff5c16ed9e0 b80960a2546ce647bffea87f85385535 5464b4499cd4368bb83ea35f895d3560 0261b9225fc10c407df083f6d254c47b ''' exp = "" obs = api.get_download_file_info("url").md5 self.assertEqual(exp, obs) def test_get_file_info_size(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK session.get().text = ''' 7e62ce101221ccdae2e9bff5c16ed9e0 b80960a2546ce647bffea87f85385535 5464b4499cd4368bb83ea35f895d3560 0261b9225fc10c407df083f6d254c47b ''' exp = 36717998 obs = api.get_download_file_info("url").size self.assertEqual(exp, obs) def test_get_file_info_size_returns_zero_on_empty_response(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK session.get().text = "" exp = 0 obs = api.get_download_file_info("url").size self.assertEqual(exp, obs) def test_get_file_info_size_returns_zero_on_response_error(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.NOT_FOUND exp = 0 obs = api.get_download_file_info("url").size self.assertEqual(exp, obs) def test_get_file_info_size_returns_zero_on_request_exception(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = requests.exceptions.RequestException("test") exp = 0 obs = api.get_download_file_info("url").size self.assertEqual(exp, obs) def test_get_file_info_size_returns_zero_on_request_timeout_exception(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = requests.exceptions.ReadTimeout("test") exp = 0 obs = api.get_download_file_info("url").size self.assertEqual(exp, obs) def test_get_file_info_size_returns_zero_on_missing_total_size(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"checksum": "url"} session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK session.get().text = ''' 7e62ce101221ccdae2e9bff5c16ed9e0 b80960a2546ce647bffea87f85385535 5464b4499cd4368bb83ea35f895d3560 0261b9225fc10c407df083f6d254c47b ''' exp = 0 obs = api.get_download_file_info("url").size self.assertEqual(exp, obs) def test1_get_gamesdb_info(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request_gamesdb = MagicMock() api._Api__request_gamesdb.side_effect = [{}] test_game = Game("Test Game") exp = {"cover": "", "vertical_cover": "", "background": "", "genre": {}, "summary": {}} obs = api.get_gamesdb_info(test_game) self.assertEqual(exp, obs) def test2_get_gamesdb_info(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request_gamesdb = MagicMock() api._Api__request_gamesdb.side_effect = API_GET_INFO_STELLARIS test_game = Game("Stellaris") exp = GAMESDB_INFO_STELLARIS obs = api.get_gamesdb_info(test_game) self.assertEqual(exp, obs) def test3_get_gamesdb_info_no_genre(self): api_info = copy.deepcopy(API_GET_INFO_STELLARIS) api_info[0]["game"]["genres"] = [] api_info[0]["game"]["genres_ids"] = [] session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request_gamesdb = MagicMock() api._Api__request_gamesdb.side_effect = api_info test_game = Game("Stellaris") exp = copy.deepcopy(GAMESDB_INFO_STELLARIS) exp['genre'] = {} obs = api.get_gamesdb_info(test_game) self.assertEqual(exp, obs) def test_get_gamesdb_info_with_multiple_genres(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request_gamesdb = MagicMock() api._Api__request_gamesdb.side_effect = API_GET_INFO_BLACKWELL test_game = Game("Blackwell Legacy") exp = GAMESDB_INFO_BLACKWELL obs = api.get_gamesdb_info(test_game) self.assertEqual(exp, obs) def test_get_user_info_from_api(self): username = "test" session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"username": username} config.username = "" session.get.side_effect = MagicMock() session.get().status_code = http.HTTPStatus.OK obs = api.get_user_info() self.assertEqual(username, obs) def test_get_user_info_from_config(self): username = "test" session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {"username": "wrong"} config.username = username obs = api.get_user_info() self.assertEqual(username, obs) def test_get_user_info_return_empty_string_when_nothing_is_returned(self): session = MagicMock() config = MagicMock() api = Api(config, session) api._Api__request = MagicMock() api._Api__request.return_value = {} config.username = "" exp = "" obs = api.get_user_info() self.assertEqual(exp, obs) sharkwouter-minigalaxy-759382b/tests/test_config.py000066400000000000000000000167401455252417400225560ustar00rootroot00000000000000from unittest import TestCase from unittest.mock import MagicMock, patch, mock_open from minigalaxy.config import Config from minigalaxy.paths import DEFAULT_INSTALL_DIR class TestConfig(TestCase): @patch('os.path.isfile') def test_read_config_file(self, mock_isfile: MagicMock): mock_isfile.return_value = True config_data = \ """ { "locale": "locale", "lang": "lang", "view": "view", "install_dir": "install_dir", "username": "username", "refresh_token": "refresh_token", "keep_installers": true, "stay_logged_in": false, "use_dark_theme": true, "show_hidden_games": true, "show_windows_games": true, "keep_window_maximized": true, "installed_filter": true, "create_applications_file": true, "current_downloads": [1, 2, 3] } """ with patch("builtins.open", mock_open(read_data=config_data)): config = Config() self.assertIsNotNone(config) self.assertEqual("locale", config.locale) self.assertEqual("lang", config.lang) self.assertEqual("view", config.view) self.assertEqual("install_dir", config.install_dir) self.assertEqual("username", config.username) self.assertEqual("refresh_token", config.refresh_token) self.assertEqual(True, config.keep_installers) self.assertEqual(False, config.stay_logged_in) self.assertEqual(True, config.use_dark_theme) self.assertEqual(True, config.show_hidden_games) self.assertEqual(True, config.show_windows_games) self.assertEqual(True, config.keep_window_maximized) self.assertEqual(True, config.installed_filter) self.assertEqual(True, config.create_applications_file) self.assertEqual([1, 2, 3], config.current_downloads) @patch('os.path.isfile') def test_defaults_if_file_does_not_exist(self, mock_isfile: MagicMock): mock_isfile.return_value = False config = Config() self.assertEqual({}, config._Config__config) self.assertEqual("", config.locale) self.assertEqual("en", config.lang) self.assertEqual("grid", config.view) self.assertEqual("", config.username) self.assertEqual(DEFAULT_INSTALL_DIR, config.install_dir) self.assertEqual("", config.refresh_token) self.assertEqual(False, config.keep_installers) self.assertEqual(True, config.stay_logged_in) self.assertEqual(False, config.use_dark_theme) self.assertEqual(False, config.show_hidden_games) self.assertEqual(False, config.show_windows_games) self.assertEqual(False, config.keep_window_maximized) self.assertEqual(False, config.installed_filter) self.assertEqual(False, config.create_applications_file) self.assertEqual([], config.current_downloads) @patch("os.remove") @patch("os.path.isfile") def test_invalid_config_file_is_deleted(self, mock_isfile: MagicMock, mock_remove: MagicMock): mock_isfile.return_value = True filename = "/this/is/a/test" config_data = \ """ { "locale": "locale", """ with patch("builtins.open", mock_open(read_data=config_data)): config = Config(filename) mock_remove.assert_called_once_with(filename) self.assertEqual("", config.locale) self.assertEqual("en", config.lang) self.assertEqual("grid", config.view) self.assertEqual("", config.username) self.assertEqual(DEFAULT_INSTALL_DIR, config.install_dir) self.assertEqual("", config.refresh_token) self.assertEqual(False, config.keep_installers) self.assertEqual(True, config.stay_logged_in) self.assertEqual(False, config.use_dark_theme) self.assertEqual(False, config.show_hidden_games) self.assertEqual(False, config.show_windows_games) self.assertEqual(False, config.keep_window_maximized) self.assertEqual(False, config.installed_filter) self.assertEqual(False, config.create_applications_file) self.assertEqual([], config.current_downloads) @patch("os.path.isfile") def test_config_property_setters(self, mock_isfile: MagicMock): mock_isfile.return_value = True filename = "/this/is/a/test" config_data = "{}" with patch("builtins.open", mock_open(read_data=config_data)): config = Config(filename) config._Config__write = MagicMock() self.assertEqual("", config.locale) config.locale = "en_US.UTF-8" self.assertEqual("en_US.UTF-8", config.locale) config._Config__write.assert_called_once() self.assertEqual("en", config.lang) config.lang = "pl" self.assertEqual("pl", config.lang) self.assertEqual("grid", config.view) config.view = "list" self.assertEqual("list", config.view) self.assertEqual("", config.username) config.username = "username" self.assertEqual("username", config.username) self.assertEqual(DEFAULT_INSTALL_DIR, config.install_dir) config.install_dir = "/install/dir" self.assertEqual("/install/dir", config.install_dir) self.assertEqual("", config.refresh_token) config.refresh_token = "refresh_token" self.assertEqual("refresh_token", config.refresh_token) self.assertEqual(False, config.keep_installers) config.keep_installers = True self.assertEqual(True, config.keep_installers) self.assertEqual(True, config.stay_logged_in) config.stay_logged_in = False self.assertEqual(False, config.stay_logged_in) self.assertEqual(False, config.use_dark_theme) config.use_dark_theme = True self.assertEqual(True, config.use_dark_theme) self.assertEqual(False, config.show_hidden_games) config.show_hidden_games = True self.assertEqual(True, config.show_hidden_games) self.assertEqual(False, config.show_windows_games) config.show_windows_games = True self.assertEqual(True, config.show_windows_games) self.assertEqual(False, config.keep_window_maximized) config.keep_window_maximized = True self.assertEqual(True, config.keep_window_maximized) self.assertEqual(False, config.installed_filter) config.installed_filter = True self.assertEqual(True, config.installed_filter) self.assertEqual(False, config.create_applications_file) config.create_applications_file = True self.assertEqual(True, config.create_applications_file) self.assertEqual([], config.current_downloads) config.current_downloads = [1, 2, 3] self.assertEqual([1, 2, 3], config.current_downloads) @patch("os.rename") @patch('os.path.isfile') @patch('os.makedirs') def test_create_config(self, mock_makedirs: MagicMock, mock_isfile: MagicMock, mock_rename: MagicMock): mock_isfile.return_value = False config = Config("/path/config.json") with patch("builtins.open", mock_open()): config.lang = "lang" self.assertEqual("lang", config.lang) mock_makedirs.assert_called_once_with("/path", mode=0o700, exist_ok=True) mock_rename.assert_called_once_with("/path/config.json.tmp", "/path/config.json") sharkwouter-minigalaxy-759382b/tests/test_download.py000066400000000000000000000037161455252417400231170ustar00rootroot00000000000000from unittest import TestCase from unittest.mock import MagicMock, Mock from minigalaxy.download import Download class TestDownload(TestCase): def test1_set_progress(self): mock_progress_function = MagicMock() download = Download("test_url", "test_save_location", progress_func=mock_progress_function) download.set_progress(50) kall = mock_progress_function.mock_calls[-1] name, args, kwargs = kall exp = 50 obs = args[0] self.assertEqual(exp, obs) def test2_set_progress(self): mock_progress_function = MagicMock() download = Download("test_url", "test_save_location", progress_func=mock_progress_function, out_of_amount=2) download.set_progress(32) kall = mock_progress_function.mock_calls[-1] name, args, kwargs = kall exp = 16 obs = args[0] self.assertEqual(exp, obs) def test1_finish(self): mock_finish_function = MagicMock() download = Download("test_url", "test_save_location", finish_func=mock_finish_function) download.finish() exp = 2 obs = len(mock_finish_function.mock_calls) self.assertEqual(exp, obs) def test2_finish(self): mock_finish_function = MagicMock() mock_finish_function.side_effect = FileNotFoundError(Mock(status="Connection Error")) mock_cancel_function = MagicMock() download = Download("test_url", "test_save_location", finish_func=mock_finish_function, cancel_func=mock_cancel_function) download.finish() exp = 2 obs = len(mock_cancel_function.mock_calls) self.assertEqual(exp, obs) def test_cancel(self): mock_cancel_function = MagicMock() download = Download("test_url", "test_save_location", cancel_func=mock_cancel_function) download.cancel() exp = 2 obs = len(mock_cancel_function.mock_calls) self.assertEqual(exp, obs) sharkwouter-minigalaxy-759382b/tests/test_download_manager.py000066400000000000000000000133621455252417400246070ustar00rootroot00000000000000import os import random import tempfile from string import ascii_uppercase from unittest import TestCase from unittest.mock import MagicMock from minigalaxy.constants import DOWNLOAD_CHUNK_SIZE from minigalaxy.download import Download, DownloadType from minigalaxy.download_manager import DownloadManager class TestDownloadManager(TestCase): def test_download_operation(self): session = MagicMock() download_request = MagicMock() session.get.return_value = download_request chunk1 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) chunk2 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) chunk3 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) download_request.iter_content.return_value = [chunk1, chunk2, chunk3] download_request.headers.get.return_value = len(chunk1) + len(chunk2) + len(chunk3) download_manager = DownloadManager(session) progress_func = MagicMock() finish_func = MagicMock() cancel_func = MagicMock() temp_file = tempfile.mktemp() download = Download("example.com", temp_file, DownloadType.GAME, finish_func, progress_func, cancel_func) download_manager._DownloadManager__download_operation(download, 0, "wb") expected = chunk1 + chunk2 + chunk3 with open(temp_file) as content: actual = content.read().encode('utf-8') self.assertEqual(expected, actual) # Clean up temp_file os.remove(temp_file) self.assertFalse(os.path.isfile(temp_file)) download_request.headers.get.assert_called_once() download_request.iter_content.assert_called_once() self.assertEqual(3 + 2, progress_func.call_count) self.assertEqual(0, finish_func.call_count) self.assertEqual(0, cancel_func.call_count) def test_download_operation_still_downloads_without_content_length(self): session = MagicMock() download_request = MagicMock() session.get.return_value = download_request chunk1 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) chunk2 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) chunk3 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) download_request.iter_content.return_value = [chunk1, chunk2, chunk3] download_request.headers.get.side_effect = TypeError download_manager = DownloadManager(session) progress_func = MagicMock() finish_func = MagicMock() cancel_func = MagicMock() temp_file = tempfile.mktemp() download = Download("example.com", temp_file, DownloadType.GAME, finish_func, progress_func, cancel_func) download_manager._DownloadManager__download_operation(download, 0, "wb") expected = chunk1 + chunk2 + chunk3 with open(temp_file) as content: actual = content.read().encode('utf-8') self.assertEqual(expected, actual) # Clean up temp_file os.remove(temp_file) self.assertFalse(os.path.isfile(temp_file)) download_request.headers.get.assert_called_once() download_request.iter_content.assert_called_once() self.assertEqual(2, progress_func.call_count) self.assertEqual(0, finish_func.call_count) self.assertEqual(0, cancel_func.call_count) def test_download_operation_cancel_download(self): session = MagicMock() download_request = MagicMock() session.get.return_value = download_request chunk1 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) chunk2 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) chunk3 = bytes(random.choices(ascii_uppercase.encode('utf-8'), k=DOWNLOAD_CHUNK_SIZE)) download_request.iter_content.return_value = [chunk1, chunk2, chunk3] download_request.headers.get.side_effect = TypeError download_manager = DownloadManager(session) progress_func = MagicMock() finish_func = MagicMock() cancel_func = MagicMock() temp_file = tempfile.mktemp() download = Download("example.com", temp_file, DownloadType.GAME, finish_func, progress_func, cancel_func) download_manager.active_downloads[download] = download download_manager.cancel_download(download) download_manager._DownloadManager__download_operation(download, 0, "wb") expected = chunk1 with open(temp_file) as content: actual = content.read().encode('utf-8') self.assertEqual(expected, actual) # Clean up temp_file os.remove(temp_file) self.assertFalse(os.path.isfile(temp_file)) download_request.headers.get.assert_called_once() download_request.iter_content.assert_called_once() self.assertEqual(1, progress_func.call_count) self.assertEqual(0, finish_func.call_count) self.assertEqual(0, cancel_func.call_count) def test_cancel_download(self): session = MagicMock() download_manager = DownloadManager(session) progress_func = MagicMock() finish_func = MagicMock() cancel_func = MagicMock() temp_file = tempfile.mktemp() download = Download("example.com", temp_file, DownloadType.GAME, finish_func, progress_func, cancel_func) download_manager.download(download) download_manager.cancel_download(download) cancel_func.assert_called_once() for queue in download_manager.queues: for i in queue: self.assertNotEqual(i, download) self.assertFalse(os.path.isfile(temp_file)) sharkwouter-minigalaxy-759382b/tests/test_game.py000066400000000000000000000400721455252417400222150ustar00rootroot00000000000000import unittest import os from unittest.mock import MagicMock, mock_open, patch from minigalaxy.game import Game class TestGame(unittest.TestCase): def test_strip_within_comparison(self): game1 = Game("!@#$%^&*(){}[]\"'_-<>.,;:") game2 = Game("") game3 = Game("hallo") game4 = Game("Hallo") game5 = Game("Hallo!") self.assertEqual(game1, game2) self.assertNotEqual(game2, game3) self.assertEqual(game3, game4) self.assertEqual(game3, game5) def test_local_and_api_comparison(self): larry1_api = Game("Leisure Suit Larry 1 - In the Land of the Lounge Lizards", game_id=1207662033) larry1_local_gog = Game("Leisure Suit Larry", install_dir="/home/user/Games/Leisure Suit Larry", game_id=1207662033) larry1_local_minigalaxy = Game("Leisure Suit Larry", install_dir="/home/wouter/Games/Leisure Suit Larry 1 - In the Land of the Lounge Lizards", game_id=1207662033) self.assertEqual(larry1_local_gog, larry1_local_minigalaxy) self.assertEqual(larry1_local_minigalaxy, larry1_api) self.assertEqual(larry1_local_gog, larry1_api) larry2_api = Game("Leisure Suit Larry 2 - Looking For Love (In Several Wrong Places)", game_id=1207662053) larry2_local_minigalaxy = Game("Leisure Suit Larry 2", install_dir="/home/user/Games/Leisure Suit Larry 2 - Looking For Love (In Several Wrong Places)", game_id=1207662053) larry2_local_gog = Game("Leisure Suit Larry 2", install_dir="/home/user/Games/Leisure Suit Larry 2", game_id=1207662053) self.assertNotEqual(larry1_api, larry2_api) self.assertNotEqual(larry2_local_gog, larry1_api) self.assertNotEqual(larry2_local_gog, larry1_local_gog) self.assertNotEqual(larry2_local_gog, larry1_local_minigalaxy) self.assertNotEqual(larry2_local_minigalaxy, larry1_api) self.assertNotEqual(larry2_local_minigalaxy, larry1_local_minigalaxy) def test_local_comparison(self): larry1_local_gog = Game("Leisure Suit Larry", install_dir="/home/user/Games/Leisure Suit Larry", game_id=1207662033) larry1_vga_local_gog = Game("Leisure Suit Larry VGA", install_dir="/home/user/Games/Leisure Suit Larry VGA", game_id=1207662043) self.assertNotEqual(larry1_local_gog, larry1_vga_local_gog) def test1_is_update_available(self): game = Game("Version Test game") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {'version': 'gog-2'} expected = True observed = game.is_update_available("gog-3") self.assertEqual(expected, observed) def test2_is_update_available(self): game = Game("Version Test game") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {'version': "91.8193.16"} expected = False observed = game.is_update_available("91.8193.16") self.assertEqual(expected, observed) def test3_is_update_available(self): game = Game("Version Test game") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {'version': "91.8193.16", "dlcs": {"Neverwinter Nights: Wyvern Crown of Cormyr": {"version": "82.8193.20.1"}}} expected = True observed = game.is_update_available("91.8193.16", dlc_title="Neverwinter Nights: Wyvern Crown of Cormyr") self.assertEqual(expected, observed) def test4_is_update_available(self): game = Game("Version Test game") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {'version': "91.8193.16", "dlcs": {"Neverwinter Nights: Wyvern Crown of Cormyr": {"version": "82.8193.20.1"}}} expected = False observed = game.is_update_available("82.8193.20.1", dlc_title="Neverwinter Nights: Wyvern Crown of Cormyr") self.assertEqual(expected, observed) def test1_get_install_directory_name(self): game = Game("Get Install Directory Test1") expected = "Get Install Directory Test1" observed = game.get_install_directory_name() self.assertEqual(expected, observed) def test2_get_install_directory_name(self): game = Game("Get\r Install\n Directory Test2!@#$%") expected = "Get Install Directory Test2" observed = game.get_install_directory_name() self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test1_fallback_read_installed_version(self, mock_isfile): mock_isfile.return_value = True gameinfo = """Beneath A Steel Sky gog-2 20150 en-US 1207658695 1207658695 664777434""" game = Game("Game Name test1") expected = "gog-2" with patch("builtins.open", mock_open(read_data=gameinfo)): observed = game.fallback_read_installed_version() self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test2_fallback_read_installed_version(self, mock_isfile): mock_isfile.return_value = False gameinfo = """Beneath A Steel Sky gog-2 20150 en-US 1207658695 1207658695 664777434""" game = Game("Game Name test2") expected = "0" with patch("builtins.open", mock_open(read_data=gameinfo)): observed = game.fallback_read_installed_version() self.assertEqual(expected, observed) @unittest.mock.patch('os.path.exists') def test1_set_info(self, mock_exists): mock_exists.return_value = True json_content = '{"version": "gog-2"}' with patch("builtins.open", mock_open(read_data=json_content)) as m: game = Game("Game Name test2") game.set_info("version", "gog-3") mock_c = m.mock_calls write_string = "" for kall in mock_c: name, args, kwargs = kall if name == "().write": write_string = "{}{}".format(write_string, args[0]) expected = '{"version": "gog-3"}' observed = write_string self.assertEqual(expected, observed) @unittest.mock.patch('os.path.exists') @unittest.mock.patch('os.makedirs') def test2_set_dlc_info(self, mock_makedirs, mock_exists): mock_exists.return_value = False dlc_name = "Neverwinter Nights: Wyvern Crown of Cormyr" with patch("builtins.open", mock_open()) as m: game = Game("Neverwinter Nights") game.set_dlc_info("version", "82.8193.20.1", dlc_name) mock_c = m.mock_calls write_string = "" for kall in mock_c: name, args, kwargs = kall if name == "().write": write_string = "{}{}".format(write_string, args[0]) expected = '{"dlcs": {"Neverwinter Nights: Wyvern Crown of Cormyr": {"version": "82.8193.20.1"}}}' observed = write_string self.assertEqual(expected, observed) def test_get_stripped_name(self): name_string = "Beneath A Steel Sky" game = Game(name_string) expected = "BeneathASteelSky" observed = game.get_stripped_name() self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test1_load_minigalaxy_info_json(self, mock_isfile): mock_isfile.side_effect = [True] json_content = '{"version": "gog-2"}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test2") jscon_dict = game.load_minigalaxy_info_json() expected = {"version": "gog-2"} observed = jscon_dict self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test2_load_minigalaxy_info_json(self, mock_isfile): mock_isfile.side_effect = [False] json_content = '{"version": "gog-2"}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test2") jscon_dict = game.load_minigalaxy_info_json() expected = {} observed = jscon_dict self.assertEqual(expected, observed) @unittest.mock.patch("minigalaxy.config.Config") @unittest.mock.patch('os.makedirs') def test_save_minigalaxy_info_json(self, mock_makedirs, mock_config): json_dict = {"version": "gog-2"} with patch("builtins.open", mock_open()) as m: game = Game("Neverwinter Nights") game.save_minigalaxy_info_json(json_dict) mock_c = m.mock_calls write_string = "" for kall in mock_c: name, args, kwargs = kall if name == "().write": write_string = "{}{}".format(write_string, args[0]) expected = '{"version": "gog-2"}' observed = write_string self.assertEqual(expected, observed) @unittest.mock.patch('os.path.exists') def test1_is_installed(self, mock_isfile): mock_isfile.side_effect = [False] game = Game("Game Name Test") game.load_minigalaxy_info_json = MagicMock() exp = False obs = game.is_installed() self.assertEqual(exp, obs) @unittest.mock.patch('os.path.exists') def test2_is_installed(self, mock_isfile): mock_isfile.side_effect = [True] game = Game("Game Name Test", install_dir="Test Install Dir") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {"dlcs": {"Neverwinter Nights: Wyvern Crown of Cormyr": {"version": "82.8193.20.1"}}} exp = True obs = game.is_installed(dlc_title="Neverwinter Nights: Wyvern Crown of Cormyr") self.assertEqual(exp, obs) @unittest.mock.patch('os.path.exists') def test3_is_installed(self, mock_isfile): mock_isfile.side_effect = [True] game = Game("Game Name Test", install_dir="Test Install Dir") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {"dlcs": {"Neverwinter Nights: Wyvern Crown of Cormyr": {"version": "82.8193.20.1"}}} game.legacy_get_dlc_status = MagicMock() game.legacy_get_dlc_status.return_value = "not-installed" exp = False obs = game.is_installed(dlc_title="Not Present DLC") self.assertEqual(exp, obs) @unittest.mock.patch('os.path.isfile') def test_get_info(self, mock_isfile): mock_isfile.side_effect = [True] json_content = '{"example_key": "example_value"}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game_get_status = game.get_info("example_key") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test_get_dlc_info(self, mock_isfile): mock_isfile.side_effect = [True, False] json_content = '{"dlcs": {"example_dlc" : {"example_key": "example_value"}}}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game_get_status = game.get_dlc_info("example_key", "example_dlc") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) def test_set_install_dir(self): install_directory = "/home/user/GOG Games" install_game_name = "Neverwinter Nights" game = Game(install_game_name) game.set_install_dir(install_directory) exp = os.path.join(install_directory, install_game_name) obs = game.install_dir self.assertEqual(exp, obs) @unittest.mock.patch('os.path.isfile') def test1_get_info_legacy(self, mock_isfile): mock_isfile.side_effect = [False, True] json_content = '{"example_key": "example_value"}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game.set_info = MagicMock() game_get_status = game.get_info("example_key") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test2_get_info_legacy(self, mock_isfile): mock_isfile.side_effect = [True, True] json_content = '{"example_key": "example_value"}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game.set_info = MagicMock() game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {} game_get_status = game.get_info("example_key") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test3_get_info_legacy(self, mock_isfile): mock_isfile.side_effect = [True, True] json_content = '{"example_key": "example_value_legacy"}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {"example_key": "example_value"} game_get_status = game.get_info("example_key") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test1_get_dlc_info_legacy(self, mock_isfile): mock_isfile.side_effect = [False, True] json_content = '{"dlcs": {"example_dlc" : {"example_key": "example_value"}}}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game.set_dlc_info = MagicMock() game_get_status = game.get_dlc_info("example_key", "example_dlc") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test2_get_dlc_info_legacy(self, mock_isfile): mock_isfile.side_effect = [True, True] json_content = '{"dlcs": {"example_dlc" : {"example_key": "example_value"}}}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game.set_dlc_info = MagicMock() game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {} game_get_status = game.get_dlc_info("example_key", "example_dlc") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) @unittest.mock.patch('os.path.isfile') def test3_get_dlc_info_legacy(self, mock_isfile): mock_isfile.side_effect = [True, True] json_content = '{"dlcs": {"example_dlc" : {"example_key": "example_value_legacy"}}}' with patch("builtins.open", mock_open(read_data=json_content)): game = Game("Game Name test") game.load_minigalaxy_info_json = MagicMock() game.load_minigalaxy_info_json.return_value = {"dlcs": {"example_dlc": {"example_key": "example_value"}}} game_get_status = game.get_dlc_info("example_key", "example_dlc") expected = "example_value" observed = game_get_status self.assertEqual(expected, observed) def test1_get_status_file_path(self): game = Game(name="Europa Universalis 2") expected = os.path.expanduser("~/.config/minigalaxy/games/Europa Universalis 2.json") observed = game.get_status_file_path() self.assertEqual(expected, observed) def test2_get_status_file_path(self): game = Game(name="Europa Universalis 2", install_dir="/home/user/GoG Games//Europa Universalis II") expected = os.path.expanduser("~/.config/minigalaxy/games/Europa Universalis II.json") observed = game.get_status_file_path() self.assertEqual(expected, observed) sharkwouter-minigalaxy-759382b/tests/test_installer.py000066400000000000000000000532331455252417400233040ustar00rootroot00000000000000import copy from unittest import TestCase, mock from unittest.mock import patch, mock_open, MagicMock from minigalaxy.game import Game from minigalaxy import installer from minigalaxy.translation import _ class Test(TestCase): @mock.patch('shutil.which') def test_install_game(self, mock_which): """[scenario: unhandled error]""" mock_which.side_effect = FileNotFoundError("Testing unhandled errors during install") game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift", platform="windows") exp = "Unhandled error." obs = installer.install_game(game, installer="", language="", install_dir="", keep_installers=False, create_desktop_file=True) self.assertEqual(exp, obs) @mock.patch('os.path.exists') @mock.patch('hashlib.md5') @mock.patch('os.listdir') def test1_verify_installer_integrity(self, mock_listdir, mock_hash, mock_is_file): md5_sum = "5cc68247b61ba31e37e842fd04409d98" installer_name = "beneath_a_steel_sky_en_gog_2_20150.sh" mock_is_file.return_value = True mock_hash().hexdigest.return_value = md5_sum mock_listdir.return_value = [installer_name] game = Game("Beneath A Steel Sky", install_dir="/home/makson/GOG Games/Beneath a Steel Sky", md5sum={installer_name: md5_sum}) installer_path = "/home/user/.cache/minigalaxy/download/" \ "Beneath a Steel Sky/{}".format(installer_name) exp = "" with patch("builtins.open", mock_open(read_data=b"")): obs = installer.verify_installer_integrity(game, installer_path) self.assertEqual(exp, obs) @mock.patch('os.path.exists') @mock.patch('hashlib.md5') @mock.patch('os.listdir') def test2_verify_installer_integrity(self, mock_listdir, mock_hash, mock_is_file): md5_sum = "5cc68247b61ba31e37e842fd04409d98" installer_name = "beneath_a_steel_sky_en_gog_2_20150.sh" corrupted_md5_sum = "99999947b61ba31e37e842fd04409d98" mock_is_file.return_value = True mock_hash().hexdigest.return_value = corrupted_md5_sum mock_listdir.return_value = [installer_name] game = Game("Beneath A Steel Sky", install_dir="/home/makson/GOG Games/Beneath a Steel Sky", md5sum={installer_name: md5_sum}) installer_path = "/home/user/.cache/minigalaxy/download/" \ "Beneath a Steel Sky/{}".format(installer_name) exp = _("{} was corrupted. Please download it again.").format(installer_name) with patch("builtins.open", mock_open(read_data=b"aaaa")): obs = installer.verify_installer_integrity(game, installer_path) self.assertEqual(exp, obs) @mock.patch('os.path.exists') @mock.patch('os.listdir') @mock.patch('subprocess.Popen') def test1_extract_installer(self, mock_subprocess, mock_listdir, mock_is_file): """[scenario: linux installer, unpack success]""" mock_is_file.return_value = True mock_subprocess().returncode = 0 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] mock_listdir.return_value = ["object1", "object2"] game = Game("Beneath A Steel Sky", install_dir="/home/makson/GOG Games/Beneath a Steel Sky") installer_path = "/home/makson/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" temp_dir = "/home/makson/.cache/minigalaxy/extract/1207658695" exp = "" obs = installer.extract_installer(game, installer_path, temp_dir, "en", use_innoextract=False) self.assertEqual(exp, obs) @mock.patch('os.path.exists') @mock.patch('os.listdir') @mock.patch('subprocess.Popen') def test2_extract_installer(self, mock_subprocess, mock_listdir, mock_is_file): """[scenario: linux installer, unpack failed]""" mock_is_file.return_value = True mock_subprocess().returncode = 2 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] mock_listdir.return_value = ["object1", "object2"] game = Game("Beneath A Steel Sky", install_dir="/home/makson/GOG Games/Beneath a Steel Sky") installer_path = "/home/makson/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" temp_dir = "/home/makson/.cache/minigalaxy/extract/1207658695" exp = "The installation of /home/makson/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh failed. Please try again." obs = installer.extract_installer(game, installer_path, temp_dir, "en", use_innoextract=False) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') @mock.patch('shutil.which') def test3_extract_installer(self, mock_which, mock_subprocess): """[scenario: innoextract, unpack success]""" mock_which.return_value = True mock_subprocess().returncode = 0 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift", platform="windows") installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "" obs = installer.extract_installer(game, installer_path, temp_dir, "en", use_innoextract=True) self.assertEqual(exp, obs) @mock.patch('os.path.exists') @mock.patch('os.listdir') @mock.patch('subprocess.Popen') def test_extract_linux(self, mock_subprocess, mock_listdir, mock_is_file): mock_is_file.return_value = True mock_subprocess().returncode = 1 mock_subprocess().communicate.return_value = [b"stdout", b"(attempting to process anyway)"] mock_listdir.return_value = ["object1", "object2"] installer_path = "/home/makson/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" temp_dir = "/home/makson/.cache/minigalaxy/extract/1207658695" exp = "" obs = installer.extract_linux(installer_path, temp_dir) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') def test_extract_windows(self, mock_subprocess): """[scenario: innoextract, unpack success]""" mock_subprocess().returncode = 0 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift", platform="windows") installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "" obs = installer.extract_windows(game, installer_path, temp_dir, "en", use_innoextract=True) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') def test1_extract_by_innoextract(self, mock_subprocess): """[scenario: success]""" mock_subprocess().returncode = 0 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "" obs = installer.extract_by_innoextract(installer_path, temp_dir, "en", use_innoextract=True) self.assertEqual(exp, obs) def test2_extract_by_innoextract(self): """[scenario: not installed/disabled]""" installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "Innoextract not installed." obs = installer.extract_by_innoextract(installer_path, temp_dir, "en", use_innoextract=False) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') def test3_extract_by_innoextract(self, mock_subprocess): """[scenario: unpack failed]""" mock_subprocess().returncode = 1 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "Innoextract extraction failed." obs = installer.extract_by_innoextract(installer_path, temp_dir, "en", use_innoextract=True) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') @mock.patch("os.path.exists") @mock.patch("os.unlink") @mock.patch("os.symlink") def test1_extract_by_wine(self, mock_symlink, mock_unlink, mock_path_exists, mock_subprocess): """[scenario: success]""" mock_path_exists.return_value = True mock_subprocess().returncode = 0 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift", platform="windows") installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "" obs = installer.extract_by_wine(game, installer_path, temp_dir) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') @mock.patch("os.path.exists") @mock.patch("os.unlink") @mock.patch("os.symlink") def test2_extract_by_wine(self, mock_symlink, mock_unlink, mock_path_exists, mock_subprocess): """[scenario: install failed]""" mock_path_exists.return_value = True mock_subprocess().returncode = 1 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift", platform="windows") installer_path = "/home/makson/.cache/minigalaxy/download/Absolute Drift/setup_absolute_drift_1.0f_(64bit)_(47863).exe" temp_dir = "/home/makson/.cache/minigalaxy/extract/1136126792" exp = "Wine extraction failed." obs = installer.extract_by_wine(game, installer_path, temp_dir) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') @mock.patch("os.path.isfile") def test1_postinstaller(self, mock_path_isfile, mock_subprocess): mock_path_isfile.return_value = False mock_subprocess().returncode = 1 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift") exp = "" obs = installer.postinstaller(game) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') @mock.patch("os.path.isfile") @mock.patch("os.chmod") def test2_postinstaller(self, mock_chmod, mock_path_isfile, mock_subprocess): mock_path_isfile.return_value = True mock_subprocess().returncode = 0 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift") exp = "" obs = installer.postinstaller(game) self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') @mock.patch("os.path.isfile") @mock.patch("os.chmod") def test3_postinstaller(self, mock_chmod, mock_path_isfile, mock_subprocess): mock_path_isfile.return_value = True mock_subprocess().returncode = 1 mock_subprocess().communicate.return_value = [b"stdout", b"stderr"] game = Game("Absolute Drift", install_dir="/home/makson/GOG Games/Absolute Drift") exp = "Postinstallation script failed: /home/makson/GOG Games/Absolute Drift/support/postinst.sh" obs = installer.postinstaller(game) self.assertEqual(exp, obs) @mock.patch('os.statvfs') def test_get_availablediskspace(self, mock_os_statvfs): frsize = 4096 bavail = 29699296 mock_os_statvfs().f_frsize = frsize mock_os_statvfs().f_bavail = bavail exp = frsize * bavail obs = installer.get_available_disk_space("/") self.assertEqual(exp, obs) @mock.patch('os.statvfs') def test1_check_diskspace(self, mock_os_statvfs): frsize = 4096 bavail = 29699296 mock_os_statvfs().f_frsize = frsize mock_os_statvfs().f_bavail = bavail exp = True obs = installer.check_diskspace(524288000, "/") self.assertEqual(exp, obs) @mock.patch('os.statvfs') def test2_check_diskspace(self, mock_os_statvfs): frsize = 4096 bavail = 29699 mock_os_statvfs().f_frsize = frsize mock_os_statvfs().f_bavail = bavail exp = False obs = installer.check_diskspace(524288000, "/") self.assertEqual(exp, obs) @mock.patch('os.statvfs') def test1_verify_disk_space(self, mock_os_statvfs): frsize = 4096 bavail = 29699 mock_os_statvfs().f_frsize = frsize mock_os_statvfs().f_bavail = bavail backup_installer = copy.deepcopy(installer.get_game_size_from_unzip) installer.get_game_size_from_unzip = MagicMock() installer.get_game_size_from_unzip.return_value = 524288000 game = Game("Beneath A Steel Sky", install_dir="/home/makson/GOG Games/Beneath a Steel Sky", platform="linux") installer_file = "/beneath_a_steel_sky_en_gog_2_20150.sh" exp = "Not enough space to extract game. Required: 524288000 Available: 121647104" obs = installer.verify_disk_space(game, installer_file) installer.get_game_size_from_unzip = backup_installer self.assertEqual(exp, obs) @mock.patch('subprocess.Popen') def test_get_game_size_from_unzip(self, mock_subprocess): stdout = b""" 550557 Defl:N 492111 11% 2018-04-19 15:01 48d4ab3f meta/gtk-2.0/pixmaps/background.png 0 Stored 0 0% 2018-04-19 15:01 00000000 scripts/ 212070 Defl:N 63210 70% 2017-10-25 11:07 a05c1728 scripts/localization.lua 21572 Defl:N 5592 74% 2017-06-27 13:02 e47c0968 scripts/mojosetup_init.lua 9171 Defl:N 3374 63% 2017-10-25 11:07 14ac8da7 scripts/app_localization.lua 4411 Defl:N 1247 72% 2018-04-19 15:01 2405a519 scripts/config.lua 76365 Defl:N 17317 77% 2017-10-25 11:07 27d8bdb2 scripts/mojosetup_mainline.lua -------- ------- --- ------- 159236636 104883200 34% 189 files """ mock_subprocess().communicate.return_value = [stdout, b"stderr"] installer_path = "/home/i/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" exp = 159236636 obs = installer.get_game_size_from_unzip(installer_path) self.assertEqual(exp, obs) @mock.patch('shutil.which') @mock.patch('os.listdir') def test_get_exec_line(self, mock_list_dir, mock_which): mock_which.return_value = True game1 = Game("Beneath A Steel Sky", install_dir="/home/test/GOG Games/Beneath a Steel Sky", platform="linux") mock_list_dir.return_value = ["data", "docs", "scummvm", "support", "beneath.ini", "gameinfo", "start.sh"] result1 = installer.get_exec_line(game1) self.assertEqual(result1, "scummvm -c beneath.ini") game2 = Game("Blocks That Matter", install_dir="/home/test/GOG Games/Blocks That Matter", platform="linux") mock_list_dir.return_value = ["data", "docs", "support", "gameinfo", "start.sh"] result2 = installer.get_exec_line(game2) self.assertEqual(result2, "./start.sh") @mock.patch('os.path.getsize') @mock.patch('os.listdir') @mock.patch('os.path.isdir') def test_compare_directory_true(self, mock_path_isdir, mock_list_dir, mock_os_path_getsize): mock_path_isdir.return_value = True mock_list_dir.return_value = ["beneath_a_steel_sky_en_gog_2_20150.sh", "beneath_a_steel_sky_en_gog_2_20150.part1"] mock_os_path_getsize.return_value = 100 obs = installer.compare_directories("/home/test/.cache/minigalaxy/installer/test", "/home/test/GOG Games/installer/test") self.assertEqual(obs, True) @mock.patch('os.path.getsize') @mock.patch('os.listdir') @mock.patch('os.path.isdir') def test_compare_directory_false(self, mock_path_isdir, mock_list_dir, mock_os_path_getsize): mock_path_isdir.return_value = True mock_list_dir.side_effect = [ ["beneath_a_steel_sky_en_gog_2_20150.sh", "beneath_a_steel_sky_en_gog_2_20150.part1"], ["beneath_a_steel_sky_en_gog_2_20150.sh"], ] obs = installer.compare_directories("/home/test/.cache/minigalaxy/installer/test", "/home/test/GOG Games/installer/test") self.assertEqual(obs, False) mock_list_dir.side_effect = [ ["beneath_a_steel_sky_en_gog_2_20150.sh", "beneath_a_steel_sky_en_gog_2_20150.part1"], ["beneath_a_steel_sky_en_gog_2_20150.sh", "beneath_a_steel_sky_en_gog_2_20150.part1"], ] mock_os_path_getsize.side_effect = [100, 200, 300, 400] obs = installer.compare_directories("/home/test/.cache/minigalaxy/installer/test", "/home/test/GOG Games/installer/test") self.assertEqual(obs, False) def test_remove_installer_no_installer(self): """ No installer present """ game1 = Game("Beneath A Steel Sky", install_dir="/home/test/GOG Games/Beneath a Steel Sky", platform="linux") installer_path = "/home/i/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" obs = installer.remove_installer(game1, installer_path, "/this/is/a/fake/directory", False) exp = "No installer directory is present: /home/i/.cache/minigalaxy/download/Beneath a Steel Sky" self.assertEqual(obs, exp) @mock.patch('os.path.isdir') @mock.patch('minigalaxy.installer.compare_directories') @mock.patch('shutil.rmtree') @mock.patch('os.remove') @mock.patch('os.listdir') def test_remove_installer_no_keep(self, mock_list_dir, mock_os_remove, mock_shutil_rmtree, mock_compare_directories, mock_os_path_isdir): """ Disabled keep_installer """ mock_os_path_isdir.return_value = True mock_compare_directories.return_value = False mock_list_dir.return_value = ["beneath_a_steel_sky_en_gog_2_20150.sh"] game1 = Game("Beneath A Steel Sky", install_dir="/home/test/GOG Games/Beneath a Steel Sky", platform="linux") installer_path = "/home/i/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" obs = installer.remove_installer(game1, installer_path, "/some/directory/test", False) assert mock_os_remove.called assert not mock_shutil_rmtree.called self.assertEqual(obs, "") @mock.patch('os.remove') @mock.patch('shutil.rmtree') @mock.patch('minigalaxy.installer.compare_directories') @mock.patch('os.path.isdir') def test_remove_installer_same_content(self, mock_os_path_isdir, mock_compare_directories, mock_shutil_rmtree, mock_os_remove): """ Same content of installer and keep dir """ mock_os_path_isdir.return_value = True mock_compare_directories.return_value = True game1 = Game("Beneath A Steel Sky", install_dir="/home/test/GOG Games/Beneath a Steel Sky", platform="linux") installer_path = "/home/i/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" obs = installer.remove_installer(game1, installer_path, "/home/i/GOG Games/installer", True) assert not mock_shutil_rmtree.called assert not mock_os_remove.called self.assertEqual(obs, "") @mock.patch('os.remove') @mock.patch('shutil.move') @mock.patch('shutil.rmtree') @mock.patch('minigalaxy.installer.compare_directories') @mock.patch('os.path.isdir') def test_remove_installer_keep(self, mock_os_path_isdir, mock_compare_directories, mock_shutil_rmtree, mock_shutil_move, mock_os_remove): """ Keep installer dir """ mock_os_path_isdir.return_value = True mock_compare_directories.return_value = False game1 = Game("Beneath A Steel Sky", install_dir="/home/test/GOG Games/Beneath a Steel Sky", platform="linux") installer_path = "/home/i/.cache/minigalaxy/download/Beneath a Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" obs = installer.remove_installer(game1, installer_path, "/home/i/GOG Games/installer", True) assert mock_shutil_rmtree.called assert mock_shutil_move.called assert not mock_os_remove.called self.assertEqual(obs, "") @patch("os.listdir") @mock.patch('os.remove') @mock.patch('shutil.move') @mock.patch('shutil.rmtree') @mock.patch('minigalaxy.installer.compare_directories') @mock.patch('os.path.isdir') def test_remove_installer_from_keep(self, mock_os_path_isdir, mock_compare_directories, mock_shutil_rmtree, mock_shutil_move, mock_os_remove, mock_os_listdir): """ Called from keep dir """ mock_os_path_isdir.return_value = True mock_compare_directories.return_value = False mock_os_listdir.return_value = [] game1 = Game("Beneath A Steel Sky", install_dir="/home/test/GOG Games/Beneath A Steel Sky", platform="linux") installer_path = "/home/i/GOG Games/installer/Beneath A Steel Sky/beneath_a_steel_sky_en_gog_2_20150.sh" obs = installer.remove_installer(game1, installer_path, "/home/i/GOG Games/installer", False) assert not mock_shutil_rmtree.called assert not mock_shutil_move.called assert not mock_os_remove.called self.assertEqual(obs, "") sharkwouter-minigalaxy-759382b/tests/test_launcher.py000066400000000000000000000316171455252417400231120ustar00rootroot00000000000000import subprocess from unittest import TestCase, mock from unittest.mock import MagicMock, mock_open from minigalaxy import launcher from minigalaxy.game import Game class Test(TestCase): def test1_determine_launcher_type(self): files = ['thumbnail.jpg', 'docs', 'support', 'game', 'start.sh', 'minigalaxy-dlc.json', 'gameinfo'] exp = "start_script" obs = launcher.determine_launcher_type(files) self.assertEqual(exp, obs) @mock.patch('shutil.which') def test2_determine_launcher_type(self, mock_shutil_which): mock_shutil_which.return_value = True files = ['thumbnail.jpg', 'data', 'docs', 'support', 'beneath.ini', 'scummvm', 'start.sh', 'gameinfo'] exp = "scummvm" obs = launcher.determine_launcher_type(files) self.assertEqual(exp, obs) def test3_determine_launcher_type(self): files = ['thumbnail.jpg', 'docs', 'support', 'unins000.exe', 'minigalaxy-dlc.json', 'gameinfo'] exp = "windows" obs = launcher.determine_launcher_type(files) self.assertEqual(exp, obs) @mock.patch('shutil.which') def test4_determine_launcher_type(self, mock_shutil_which): mock_shutil_which.return_value = True files = ['thumbnail.jpg', 'docs', 'support', 'dosbox', 'minigalaxy-dlc.json', 'gameinfo'] exp = "dosbox" obs = launcher.determine_launcher_type(files) self.assertEqual(exp, obs) def test5_determine_launcher_type(self): files = ['thumbnail.jpg', 'docs', 'support', 'game', 'minigalaxy-dlc.json', 'gameinfo'] exp = "final_resort" obs = launcher.determine_launcher_type(files) self.assertEqual(exp, obs) @mock.patch('glob.glob') def test1_get_windows_exe_cmd(self, mock_glob): mock_glob.return_value = ["/test/install/dir/start.exe", "/test/install/dir/unins000.exe"] files = ['thumbnail.jpg', 'docs', 'support', 'game', 'minigalaxy-dlc.json', 'start.exe', 'unins000.exe'] game = Game("Test Game", install_dir="/test/install/dir") exp = ["wine", "start.exe"] obs = launcher.get_windows_exe_cmd(game, files) self.assertEqual(exp, obs) @mock.patch('builtins.open', new_callable=mock_open, read_data="") @mock.patch('os.chdir') def test2_get_windows_exe_cmd(self, mock_os_chdir, mo): goggame_1414471894_info_content = """{ "buildId": "53350324452482937", "clientId": "53185732904249211", "gameId": "1414471894", "language": "Russian", "languages": [ "ru-RU" ], "name": "Metro Exodus - Sam's Story", "osBitness": [ "64" ], "playTasks": [], "rootGameId": "1407287452", "version": 1 }""" goggame_1407287452_info_content = """{ "buildId": "53350324452482937", "clientId": "53185732904249211", "gameId": "1407287452", "language": "Russian", "languages": [ "ru-RU" ], "name": "Metro Exodus", "osBitness": [ "64" ], "playTasks": [ { "category": "game", "isPrimary": true, "languages": [ "ru-RU" ], "name": "Metro Exodus", "osBitness": [ "64" ], "path": "MetroExodus.exe", "type": "FileTask" } ], "rootGameId": "1407287452", "version": 1 }""" handlers = (mock_open(read_data=goggame_1414471894_info_content).return_value, mock_open(read_data=goggame_1407287452_info_content).return_value) mo.side_effect = handlers files = ['thumbnail.jpg', 'docs', 'support', 'game', 'minigalaxy-dlc.json', 'MetroExodus.exe', 'unins000.exe', 'goggame-1407287452.info', 'goggame-1414471894.info'] game = Game("Test Game", install_dir="/test/install/dir") exp = ['wine', 'start', '/b', '/wait', '/d', '.', 'MetroExodus.exe'] obs = launcher.get_windows_exe_cmd(game, files) self.assertEqual(exp, obs) @mock.patch('builtins.open', new_callable=mock_open, read_data="") @mock.patch('os.chdir') def test3_get_windows_exe_cmd(self, mock_os_chdir, mo): goggame_1207658919_info_content = """{ "buildId": "52095557858882770", "clientId": "49843178982252086", "gameId": "1207658919", "language": "English", "languages": [ "en-US" ], "name": "Rayman Forever", "playTasks": [ { "arguments": "-conf \\"..\\\\dosboxRayman.conf\\" -conf \\"..\\\\dosboxRayman_single.conf\\" -noconsole -c \\"exit\\"", "category": "game", "isPrimary": true, "languages": [ "*" ], "name": "Rayman Forever", "path": "DOSBOX\\\\dosbox.exe", "type": "FileTask", "workingDir": "DOSBOX" }, { "arguments": "1207658919", "category": "tool", "languages": [ "*" ], "name": "Graphic Mode Setup", "path": "DOSBOX\\\\GOGDOSConfig.exe", "type": "FileTask", "workingDir": "DOSBOX" }, { "category": "document", "languages": [ "*" ], "link": "http://www.gog.com/support/rayman_forever", "name": "Support", "type": "URLTask" }, { "category": "document", "languages": [ "*" ], "name": "Manual", "path": "Manual.pdf", "type": "FileTask" }, { "category": "tool", "languages": [ "*" ], "name": "Mapper", "path": "RayKit\\\\Mapper.exe", "type": "FileTask", "workingDir": "RayKit" } ], "rootGameId": "1207658919", "version": 1 }""" mo.side_effect = (mock_open(read_data=goggame_1207658919_info_content).return_value,) files = ['goggame-1207658919.script', 'DOSBOX', 'thumbnail.jpg', 'game.gog', 'unins000.dat', 'webcache.zip', 'EULA.txt', 'Music', 'dosboxRayman_single.conf', 'Rayman', 'unins000.exe', 'support.ico', 'prefix', 'goggame-1207658919.info', 'Manual.pdf', 'gog.ico', 'unins000.msg', 'goggame-1207658919.hashdb', 'RayFan', 'dosboxRayman.conf', 'unins000.ini', 'thumbnail_100.jpg', 'RayKit', 'game.ins', 'goggame-1207658919.ico', 'goglog.ini', 'Launch Rayman Forever.lnk', 'cloud_saves', 'thumbnail_196.jpg'] game = Game("Test Game", install_dir="/test/install/dir") exp = ['wine', 'start', '/b', '/wait', '/d', 'DOSBOX', 'DOSBOX\\dosbox.exe', '-conf', '"..\\dosboxRayman.conf"', '-conf', '"..\\dosboxRayman_single.conf"', '-noconsole', '-c', '"exit"'] obs = launcher.get_windows_exe_cmd(game, files) self.assertEqual(exp, obs) def test_get_dosbox_exe_cmd(self): files = ['thumbnail.jpg', 'docs', 'support', 'dosbox_bbb_single.conf', 'dosbox_aaa.conf', 'dosbox'] game = Game("Test Game", install_dir="/test/install/dir") exp = ["dosbox", "-conf", "dosbox_aaa.conf", "-conf", "dosbox_bbb_single.conf", "-no-console", "-c", "exit"] obs = launcher.get_dosbox_exe_cmd(game, files) self.assertEqual(exp, obs) def test_get_scummvm_exe_cmd(self): files = ['thumbnail.jpg', 'data', 'docs', 'support', 'beneath.ini', 'scummvm', 'start.sh', 'gameinfo'] game = Game("Test Game", install_dir="/test/install/dir") exp = ["scummvm", "-c", "beneath.ini"] obs = launcher.get_scummvm_exe_cmd(game, files) self.assertEqual(exp, obs) def test_get_start_script_exe_cmd(self): exp = ["./start.sh"] obs = launcher.get_start_script_exe_cmd() self.assertEqual(exp, obs) @mock.patch('os.getcwd') @mock.patch('os.chdir') @mock.patch('subprocess.Popen') @mock.patch('minigalaxy.launcher.get_execute_command') def test1_run_game_subprocess(self, launcher_mock, mock_popen, mock_os_chdir, mock_os_getcwd): mock_process = "Mock Process" mock_popen.return_value = mock_process game = Game("Test Game", install_dir="/test/install/dir") exp = ("", mock_process) obs = launcher.run_game_subprocess(game) self.assertEqual(exp, obs) @mock.patch('os.getcwd') @mock.patch('os.chdir') @mock.patch('subprocess.Popen') @mock.patch('minigalaxy.launcher.get_execute_command') def test2_run_game_subprocess(self, launcher_mock, mock_popen, mock_os_chdir, mock_os_getcwd): mock_popen.side_effect = FileNotFoundError() game = Game("Test Game", install_dir="/test/install/dir") exp = ('No executable was found in /test/install/dir', None) obs = launcher.run_game_subprocess(game) self.assertEqual(exp, obs) @mock.patch('minigalaxy.launcher.check_if_game_start_process_spawned_final_process') def test1_check_if_game_started_correctly(self, mock_check_game): mock_process = MagicMock() mock_process.wait.side_effect = subprocess.TimeoutExpired("cmd", 1) game = Game("Test Game", install_dir="/test/install/dir") exp = "" obs = launcher.check_if_game_started_correctly(mock_process, game) self.assertEqual(exp, obs) @mock.patch('minigalaxy.launcher.check_if_game_start_process_spawned_final_process') def test2_check_if_game_started_correctly(self, mock_check_game): mock_process = MagicMock() mock_process.communicate.return_value = (b"Output message", None) game = Game("Test Game", install_dir="/test/install/dir") exp = "Output message" obs = launcher.check_if_game_started_correctly(mock_process, game) self.assertEqual(exp, obs) @mock.patch('os.getpid') @mock.patch('subprocess.check_output') def test1_check_if_game_start_process_spawned_final_process(self, mock_check_output, mock_getpid): mock_check_output.return_value = b"""UID PID PPID C STIME TTY TIME CMD root 1 0 0 lis24 ? 00:00:02 /sbin/init splash root 2 0 0 lis24 ? 00:00:00 [kthreadd] root 3 2 0 lis24 ? 00:00:00 [rcu_gp] root 4 2 0 lis24 ? 00:00:00 [rcu_par_gp] root 6 2 0 lis24 ? 00:00:00 [kworker/0:0H-kblockd] """ mock_getpid.return_value = 1000 err_msg = "Error Message" game = Game("Test Game", install_dir="/test/install/dir") exp = err_msg obs = launcher.check_if_game_start_process_spawned_final_process(err_msg, game) self.assertEqual(exp, obs) @mock.patch('os.getpid') @mock.patch('subprocess.check_output') def test2_check_if_game_start_process_spawned_final_process(self, mock_check_output, mock_getpid): mock_check_output.return_value = b"""UID PID PPID C STIME TTY TIME CMD root 1 0 0 lis24 ? 00:00:02 /sbin/init splash root 2 0 0 lis24 ? 00:00:00 [kthreadd] root 3 2 0 lis24 ? 00:00:00 [rcu_gp] root 4 2 0 lis24 ? 00:00:00 [rcu_par_gp] root 6 2 0 lis24 ? 00:00:00 [kworker/0:0H-kblockd] makson 1006 2 0 lis24 ? 00:00:00 /bin/sh /home/makson/.paradoxlauncher/launcher-v2.2020.15/Paradox Launcher --pdxlGameDir /home/makson/GOG Games/Stellaris/game --gameDir /home/makson/GOG Games/Stellaris/game """ mock_getpid.return_value = 1000 err_msg = "Error Message" game = Game("Stellaris", install_dir="/home/makson/GOG Games") exp = "" obs = launcher.check_if_game_start_process_spawned_final_process(err_msg, game) self.assertEqual(exp, obs) @mock.patch('os.getpid') @mock.patch('subprocess.check_output') def test3_check_if_game_start_process_spawned_final_process(self, mock_check_output, mock_getpid): mock_check_output.return_value = b"""UID PID PPID C STIME TTY TIME CMD root 12486 2 0 17:47 ? 00:00:00 [kworker/u17:3-kcryptd] root 12543 2 0 17:53 ? 00:00:00 [kworker/u17:1-kcryptd] root 12617 2 0 18:02 ? 00:00:00 [kworker/5:1-ata_sff] root 12652 2 0 18:07 ? 00:00:00 [kworker/0:0-events] root 12682 2 0 18:08 ? 00:00:00 [kworker/5:2-ata_sff] root 12699 2 0 18:08 ? 00:00:00 [kworker/u17:0-kcryptd] makson 12783 6690 1 18:09 pts/4 00:00:01 /usr/bin/python3 build/scripts-3.7/minigalaxy makson 12866 1378 0 18:09 pts/4 00:00:00 /bin/sh /home/makson/.paradoxlauncher/launcher-v2.2021.1/Paradox Launcher --pdxlGameDir /home/makson/GOG Games/Imperator Rome/game/launcher --gameDir /home/makson/GOG Games/Imperator Rome/game/launcher """ mock_getpid.return_value = 1000 err_msg = "Error Message" game = Game("Imperator: Rome", install_dir="/home/makson/GOG Games") exp = "" obs = launcher.check_if_game_start_process_spawned_final_process(err_msg, game) self.assertEqual(exp, obs) sharkwouter-minigalaxy-759382b/tests/test_ui_library.py000066400000000000000000000250471455252417400234520ustar00rootroot00000000000000import json import os import sys import uuid from unittest import TestCase, mock from unittest.mock import MagicMock, patch, mock_open import tempfile m_gtk = MagicMock() m_gi = MagicMock() m_window = MagicMock() m_preferences = MagicMock() m_gametile = MagicMock() m_gametilelist = MagicMock() m_categoryfilters = MagicMock() class UnitTestGtkTemplate: def __init__(self): self.Child = m_gtk def from_file(self, lib_file): def passthrough(func): def passthrough2(*args, **kwargs): return func(*args, **kwargs) return passthrough2 return passthrough class UnitTestGiRepository: class Gtk: Template = UnitTestGtkTemplate() Widget = m_gtk class Viewport: pass class Gdk: pass class GdkPixbuf: pass class Gio: pass class GLib: pass class Notify: pass u_gi_repository = UnitTestGiRepository() sys.modules['gi.repository'] = u_gi_repository sys.modules['gi'] = m_gi sys.modules['minigalaxy.ui.window'] = m_window sys.modules['minigalaxy.ui.preferences'] = m_preferences sys.modules['minigalaxy.ui.gametile'] = m_gametile sys.modules['minigalaxy.ui.gametilelist'] = m_gametilelist sys.modules['minigalaxy.ui.categoryfilters'] = m_categoryfilters from minigalaxy.game import Game # noqa: E402 from minigalaxy.ui.library import Library, get_installed_windows_games, read_game_categories_file, \ update_game_categories_file # noqa: E402 SELF_GAMES = {"Neverwinter Nights: Enhanced Edition": "1097893768", "Beneath A Steel Sky": "1207658695", "Stellaris (English)": "1508702879"} API_GAMES = {"Neverwinter Nights: Enhanced Edition": "1097893768", "Beneath a Steel Sky": "1207658695", "Dragonsphere": "1207658927", "Warsow": "1207659121", "Outlast": "1207660064", "Xenonauts": "1207664803", "Wasteland 2": "1207665783", "Baldur's Gate: Enhanced Edition": "1207666353", "Baldur's Gate II: Enhanced Edition": "1207666373", "Toonstruck": "1207666633", "Icewind Dale: Enhanced Edition": "1207666683", "Pillars of Eternity": "1207666813", "Grim Fandango Remastered": "1207667183", "Knights of Pen and Paper +1 Edition": "1320675280", "Sunless Sea": "1421064427", "Dungeons 2": "1436885138", "Wasteland 2 Director's Cut": "1444386007", "Stellaris": "1508702879", "Butcher": "1689871374", "Reigns: Game of Thrones": "2060365190"} class TestLibrary(TestCase): def test1_add_games_from_api(self): self_games = [] for game in SELF_GAMES: self_games.append(Game(name=game, game_id=int(SELF_GAMES[game]),)) api_games = [] for game in API_GAMES: api_games.append(Game(name=game, game_id=int(API_GAMES[game]),)) err_msg = "" config = MagicMock() api_mock = MagicMock() api_mock.get_library.return_value = api_games, err_msg test_library = Library(MagicMock(), config, api_mock, MagicMock()) test_library.games = self_games test_library._Library__add_games_from_api() exp = len(API_GAMES) obs = len(test_library.games) self.assertEqual(exp, obs) def test2_add_games_from_api(self): self_games = [] for game in SELF_GAMES: self_games.append(Game(name=game, game_id=int(SELF_GAMES[game]),)) api_games = [] for game in API_GAMES: api_games.append(Game(name=game, game_id=int(API_GAMES[game]),)) err_msg = "" config = MagicMock() api_mock = MagicMock() api_mock.get_library.return_value = api_games, err_msg test_library = Library(MagicMock(), config, api_mock, MagicMock()) test_library.games = self_games test_library._Library__add_games_from_api() exp = True obs = Game(name="Stellaris (English)", game_id=1508702879,) in test_library.games self.assertEqual(exp, obs) def test3_add_games_from_api(self): self_games = [] for game in SELF_GAMES: self_games.append(Game(name=game, game_id=int(SELF_GAMES[game]),)) self_games.append(Game(name="Game without ID", game_id=0)) api_games = [] for game in API_GAMES: api_games.append(Game(name=game, game_id=int(API_GAMES[game]),)) api_gmae_with_id = Game(name="Game without ID", game_id=1234567890) api_games.append(api_gmae_with_id) err_msg = "" config = MagicMock() api_mock = MagicMock() api_mock.get_library.return_value = api_games, err_msg test_library = Library(MagicMock(), config, api_mock, MagicMock()) test_library.games = self_games test_library._Library__add_games_from_api() exp = True obs = api_gmae_with_id in test_library.games self.assertEqual(exp, obs) exp = len(api_games) obs = len(test_library.games) self.assertEqual(exp, obs) def test4_add_games_from_api(self): self_games = [] for game in SELF_GAMES: self_games.append(Game(name=game, game_id=int(SELF_GAMES[game]),)) api_games = [] url_nr = 1 for game in API_GAMES: api_games.append(Game(name=game, game_id=int(API_GAMES[game]), url="http://test_url{}".format(str(url_nr)))) url_nr += 1 err_msg = "" config = MagicMock() api_mock = MagicMock() api_mock.get_library.return_value = api_games, err_msg test_library = Library(MagicMock(), config, api_mock, MagicMock()) test_library.games = self_games test_library._Library__add_games_from_api() exp = "http://test_url1" obs = test_library.games[0].url self.assertEqual(exp, obs) def test5_add_games_from_api(self): self_games = [] for game in SELF_GAMES: self_games.append(Game(name="{}_diff".format(game), game_id=int(SELF_GAMES[game]),)) api_games = [] for game in API_GAMES: api_games.append(Game(name=game, game_id=int(API_GAMES[game]))) err_msg = "" config = MagicMock() api_mock = MagicMock() api_mock.get_library.return_value = api_games, err_msg test_library = Library(MagicMock(), config, api_mock, MagicMock()) test_library.games = self_games test_library._Library__add_games_from_api() exp = "Neverwinter Nights: Enhanced Edition" obs = test_library.games[0].name self.assertEqual(exp, obs) def test6_add_games_from_api(self): self_games = [Game(name="Torchlight 2", game_id=0, install_dir="/home/user/GoG Games/Torchlight II")] api_games = [Game(name="Torchlight II", game_id=1958228073)] err_msg = "" config = MagicMock() api_mock = MagicMock() api_mock.get_library.return_value = api_games, err_msg test_library = Library(MagicMock(), config, api_mock, MagicMock()) test_library.games = self_games test_library._Library__add_games_from_api() exp = 1 obs = len(test_library.games) self.assertEqual(exp, obs) @mock.patch('os.listdir') def test1_get_installed_windows_game(self, mock_listdir): mock_listdir.return_value = ["goggame-1207665883.info"] game_json_data = '{ "gameId": "1207665883", "name": "Aliens vs Predator Classic 2000" }'.encode('utf-8') with patch("builtins.open", mock_open(read_data=game_json_data)): games = get_installed_windows_games("/example/path") exp = "Aliens vs Predator Classic 2000" obs = games[0].name self.assertEqual(exp, obs) @mock.patch('os.listdir') def test2_get_installed_windows_game(self, mock_listdir): mock_listdir.return_value = ["goggame-1207665883.info"] game_json_data = '{ "gameId": "1207665883", "name": "Aliens vs Predator Classic 2000" }'.encode('utf-8-sig') with patch("builtins.open", mock_open(read_data=game_json_data)): games = get_installed_windows_games("/example/path") exp = "Aliens vs Predator Classic 2000" obs = games[0].name self.assertEqual(exp, obs) def test_read_game_categories_file_should_return_populated_dict(self): with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as tmpfile: tmpfile.write('{"Test Game":"Adventure"}') tmpfile.flush() actual = read_game_categories_file(tmpfile.name) self.assertTrue(len(actual)) self.assertEqual(actual, {'Test Game': 'Adventure'}) @mock.patch('os.path.exists') def test_update_game_categories_file_should_skip_for_empty_dict(self, mock_path_exists: MagicMock): mock_path_exists.side_effect = Exception("Test error") update_game_categories_file({}, None) self.assertFalse(mock_path_exists.called) def test_update_game_categories_file_should_create_file_if_not_found(self): initially_non_existent_file = f'/tmp/{uuid.uuid4()}.json' self.assertFalse(os.path.exists(initially_non_existent_file)) expected = {'Test game': 'Adventure'} update_game_categories_file(expected, initially_non_existent_file) self.assertTrue(os.path.exists(initially_non_existent_file)) self.assertDictEqual(expected, read_game_categories_file(initially_non_existent_file)) def test_update_game_categories_file_should_skip_if_file_found_with_identical_contents(self): expected = {"Test Game": "Adventure"} with tempfile.NamedTemporaryFile(mode='r+t', delete=False) as tmpfile: json.dump(expected, tmpfile) tmpfile.flush() update_game_categories_file(expected, tmpfile.name) tmpfile.seek(os.SEEK_SET) actual = json.load(tmpfile) self.assertDictEqual(actual, expected) def test_update_game_categories_file_should_overwrite_file_if_contents_differ(self): with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as tmpfile: tmpfile.write('{"Test Game":"Adventure"}') tmpfile.flush() expected = {"Test Game": "Adventure", "Another Game": "Strategy"} update_game_categories_file(expected, tmpfile.name) tmpfile.seek(os.SEEK_SET) actual = json.load(tmpfile) self.assertDictEqual(actual, expected) del sys.modules['gi'] del sys.modules['gi.repository'] del sys.modules['minigalaxy.ui.window'] del sys.modules['minigalaxy.ui.preferences'] del sys.modules['minigalaxy.ui.gametile'] del sys.modules['minigalaxy.ui.gametilelist'] del sys.modules['minigalaxy.ui.categoryfilters'] sharkwouter-minigalaxy-759382b/tests/test_ui_window.py000066400000000000000000000062231455252417400233100ustar00rootroot00000000000000import sys from unittest import TestCase from unittest.mock import MagicMock, patch from simplejson.errors import JSONDecodeError m_gtk = MagicMock() m_gi = MagicMock() m_library = MagicMock() m_preferences = MagicMock() m_login = MagicMock() m_about = MagicMock() m_categoryfilters = MagicMock() class UnitTestGtkTemplate: def __init__(self): self.Child = m_gtk def from_file(self, lib_file): def passthrough(func): def passthrough2(*args, **kwargs): return func(*args, **kwargs) return passthrough2 return passthrough Callback = MagicMock() class UnitTestGiRepository: class Gtk: Template = UnitTestGtkTemplate() Widget = MagicMock() Settings = MagicMock() ResponseType = MagicMock() class ApplicationWindow: def __init__(self, title): pass set_default_icon_list = MagicMock() show_all = MagicMock() Gdk = MagicMock() GdkPixbuf = MagicMock() Gio = MagicMock() GLib = MagicMock Notify = MagicMock() u_gi_repository = UnitTestGiRepository() sys.modules['gi.repository'] = u_gi_repository sys.modules['gi'] = m_gi sys.modules['minigalaxy.ui.library'] = m_library sys.modules['minigalaxy.ui.preferences'] = m_preferences sys.modules['minigalaxy.ui.login'] = m_login sys.modules['minigalaxy.ui.about'] = m_about sys.modules['minigalaxy.ui.gtk'] = u_gi_repository sys.modules['minigalaxy.ui.categoryfilters'] = m_categoryfilters from minigalaxy.ui.window import Window # noqa: E402 class TestWindow(TestCase): def test1_init(self): with patch('minigalaxy.ui.window.Api.can_connect', return_value=False): config = MagicMock() config.locale = "en_US.UTF-8" config.keep_window_maximized = False api = MagicMock() api.can_connect.return_value = False test_window = Window(api=api, config=config, download_manager=MagicMock()) exp = True obs = test_window.offline self.assertEqual(exp, obs) def test2_init(self): config = MagicMock() config.locale = "en_US.UTF-8" config.keep_window_maximized = False api = MagicMock() api.authenticate.return_value = True test_window = Window(api=api, config=config, download_manager=MagicMock()) exp = False obs = test_window.offline self.assertEqual(exp, obs) api.authenticate.assert_called_once() def test3_init(self): config = MagicMock() config.locale = "en_US.UTF-8" config.keep_window_maximized = False api = MagicMock() api.authenticate.side_effect = JSONDecodeError(msg='mock', doc='mock', pos=0) test_window = Window(api=api, config=config, download_manager=MagicMock()) exp = True obs = test_window.offline self.assertEqual(exp, obs) del sys.modules['gi'] del sys.modules['gi.repository'] del sys.modules['minigalaxy.ui.library'] del sys.modules['minigalaxy.ui.preferences'] del sys.modules['minigalaxy.ui.login'] del sys.modules['minigalaxy.ui.about'] del sys.modules['minigalaxy.ui.gtk']