From 636fa42803ae0f9fc66900d2f7f0a2b2a65014b0 Mon Sep 17 00:00:00 2001 From: Jinash-Rouniyar Date: Thu, 27 Feb 2025 01:13:27 -0500 Subject: [PATCH] Fix: Record TOC navigations in history (#1248) --- .github/FUNDING.yml | 24 +- .github/stale.yml | 30 +- .github/workflows/ci.yml | 196 +- .github/workflows/package.yml | 236 +- .gitignore | 94 +- COPYING | 1352 +++++------ ChangeLog | 682 +++--- LICENSE | 1348 +++++------ README.md | 316 +-- debian/changelog | 10 +- debian/control | 72 +- debian/copyright | 2 +- debian/kiwix-desktop.1 | 20 +- debian/manpages | 2 +- debian/rules | 12 +- debian/source/format | 2 +- kiwix-desktop.pro | 522 ++-- resources/contentmanager.qrc | 10 +- resources/css/_contentManager.css | 162 +- resources/css/_settingsManager.css | 164 +- resources/css/choiceBox.css | 142 +- resources/css/contentmanagerside.css | 46 +- resources/css/localServer.css | 84 +- resources/css/messageBox.css | 78 +- resources/css/popup.css | 82 +- resources/css/style.css | 1112 ++++----- resources/fonts/Selawik/LICENSE.txt | 182 +- resources/generate_translation_qrc.py | 92 +- resources/i18n/ar.json | 280 +-- resources/i18n/bcl.json | 118 +- resources/i18n/bn.json | 90 +- resources/i18n/br.json | 226 +- resources/i18n/ca.json | 100 +- resources/i18n/cs.json | 232 +- resources/i18n/da.json | 150 +- resources/i18n/dag.json | 272 +-- resources/i18n/de.json | 312 +-- resources/i18n/diq.json | 212 +- resources/i18n/en.json | 384 +-- resources/i18n/eo.json | 126 +- resources/i18n/es.json | 394 +-- resources/i18n/fa.json | 128 +- resources/i18n/fi.json | 252 +- resources/i18n/fr.json | 406 ++-- resources/i18n/gsw.json | 104 +- resources/i18n/ha.json | 300 +-- resources/i18n/he.json | 386 +-- resources/i18n/hi.json | 296 +-- resources/i18n/hy.json | 96 +- resources/i18n/ia.json | 344 +-- resources/i18n/id.json | 386 +-- resources/i18n/ie.json | 104 +- resources/i18n/ig.json | 296 +-- resources/i18n/igl.json | 156 +- resources/i18n/io.json | 78 +- resources/i18n/it.json | 278 +-- resources/i18n/ja.json | 106 +- resources/i18n/ko.json | 396 +-- resources/i18n/ku-latn.json | 236 +- resources/i18n/lb.json | 274 +-- resources/i18n/mk.json | 380 +-- resources/i18n/ms.json | 106 +- resources/i18n/my.json | 134 +- resources/i18n/nb.json | 184 +- resources/i18n/nl.json | 376 +-- resources/i18n/nqo.json | 238 +- resources/i18n/or.json | 292 +-- resources/i18n/pl.json | 280 +-- resources/i18n/pt-br.json | 356 +-- resources/i18n/pt.json | 104 +- resources/i18n/qqq.json | 398 +-- resources/i18n/ro.json | 214 +- resources/i18n/roa-tara.json | 120 +- resources/i18n/ru.json | 382 +-- resources/i18n/sc.json | 292 +-- resources/i18n/scn.json | 64 +- resources/i18n/sdc.json | 68 +- resources/i18n/se.json | 104 +- resources/i18n/sk.json | 232 +- resources/i18n/skr-arab.json | 164 +- resources/i18n/sl.json | 296 +-- resources/i18n/sms.json | 114 +- resources/i18n/sq.json | 356 +-- resources/i18n/sr-ec.json | 134 +- resources/i18n/sro.json | 66 +- resources/i18n/sv.json | 390 +-- resources/i18n/sw.json | 294 +-- resources/i18n/ta.json | 218 +- resources/i18n/te.json | 294 +-- resources/i18n/th.json | 126 +- resources/i18n/tn.json | 292 +-- resources/i18n/tr.json | 308 +-- resources/i18n/uk.json | 264 +- resources/i18n/yo.json | 272 +-- resources/i18n/zgh.json | 76 +- resources/i18n/zh-hans.json | 404 ++-- resources/i18n/zh-hant.json | 356 +-- resources/icons/copy.svg | 14 +- resources/icons/history-left.svg | 114 +- resources/icons/history-right.svg | 114 +- resources/icons/home-button.svg | 34 +- resources/icons/kiwix-app-icons-square.svg | 150 +- resources/icons/kiwix-logo.svg | 40 +- resources/icons/kiwix/app_icon_source.svg | 150 +- .../icons/kiwix/scalable/kiwix-desktop.svg | 42 +- resources/icons/open-file.svg | 46 +- resources/icons/random.svg | 16 +- resources/icons/search.svg | 154 +- resources/icons/search_backward.svg | 116 +- resources/icons/search_forward.svg | 116 +- resources/icons/xmark-solid.svg | 2 +- resources/js.qrc | 10 +- resources/js/headerAnchor.js | 209 +- resources/kiwix.qrc | 144 +- resources/org.kiwix.desktop-mime.xml | 26 +- resources/org.kiwix.desktop.appdata.xml | 134 +- resources/org.kiwix.desktop.desktop | 20 +- resources/settingsmanager.qrc | 10 +- resources/style.qrc | 20 +- resources/texts/about.html | 58 +- resources/translations.qrc | 132 +- scripts/pkg-config-wrapper.py | 118 +- src/about.cpp | 64 +- src/about.h | 44 +- src/blobbuffer.cpp | 14 +- src/blobbuffer.h | 36 +- src/choiceitem.cpp | 64 +- src/choiceitem.h | 64 +- src/choiceitem.ui | 286 +-- src/contentmanager.cpp | 2150 ++++++++--------- src/contentmanager.h | 402 +-- src/contentmanagerdelegate.cpp | 600 ++--- src/contentmanagerdelegate.h | 54 +- src/contentmanagerheader.cpp | 40 +- src/contentmanagerheader.h | 36 +- src/contentmanagermodel.cpp | 556 ++--- src/contentmanagermodel.h | 140 +- src/contentmanagerside.cpp | 248 +- src/contentmanagerside.h | 90 +- src/contentmanagerside.ui | 442 ++-- src/contentmanagerview.cpp | 166 +- src/contentmanagerview.h | 70 +- src/contentmanagerview.ui | 98 +- src/contenttypefilter.cpp | 52 +- src/contenttypefilter.h | 50 +- src/css_constants.h | 160 +- src/descriptionnode.cpp | 100 +- src/descriptionnode.h | 52 +- src/downloadmanagement.cpp | 804 +++--- src/downloadmanagement.h | 418 ++-- src/findinpagebar.cpp | 138 +- src/findinpagebar.h | 68 +- src/findinpagebar.ui | 164 +- src/flowlayout.cpp | 458 ++-- src/flowlayout.h | 180 +- src/fullscreennotification.cpp | 94 +- src/fullscreennotification.h | 42 +- src/fullscreenwindow.cpp | 92 +- src/fullscreenwindow.h | 64 +- src/kconstants.h | 12 +- src/kiwixapp.cpp | 1206 ++++----- src/kiwixapp.h | 308 +-- src/kiwixchoicebox.cpp | 564 ++--- src/kiwixchoicebox.h | 124 +- src/kiwixlineedit.cpp | 58 +- src/kiwixlineedit.h | 40 +- src/kiwixlistwidget.cpp | 216 +- src/kiwixlistwidget.h | 72 +- src/kiwixloader.cpp | 154 +- src/kiwixloader.h | 56 +- src/kiwixmessagebox.cpp | 132 +- src/kiwixmessagebox.h | 160 +- src/kiwixwebchannelobject.h | 42 +- src/klistwidgetitem.cpp | 82 +- src/klistwidgetitem.h | 38 +- src/kprofile.cpp | 348 +-- src/kprofile.h | 102 +- src/library.cpp | 488 ++-- src/library.h | 132 +- src/localkiwixserver.cpp | 268 +- src/localkiwixserver.h | 62 +- src/main.cpp | 200 +- src/mainmenu.cpp | 150 +- src/mainmenu.h | 40 +- src/mainwindow.cpp | 476 ++-- src/mainwindow.h | 104 +- src/menuproxystyle.h | 38 +- src/multizimbutton.cpp | 334 +-- src/multizimbutton.h | 88 +- src/node.h | 40 +- src/opdsrequestmanager.cpp | 208 +- src/opdsrequestmanager.h | 82 +- src/portutils.h | 78 +- src/readinglistbar.cpp | 292 +-- src/readinglistbar.h | 66 +- src/readinglistbar.ui | 244 +- src/rownode.cpp | 168 +- src/rownode.h | 78 +- src/searchbar.cpp | 886 +++---- src/searchbar.h | 184 +- src/settingsmanager.cpp | 456 ++-- src/settingsmanager.h | 166 +- src/settingsview.cpp | 464 ++-- src/settingsview.h | 80 +- src/suggestionlistdelegate.cpp | 208 +- src/suggestionlistdelegate.h | 34 +- src/suggestionlistmodel.cpp | 214 +- src/suggestionlistmodel.h | 74 +- src/suggestionlistworker.cpp | 118 +- src/suggestionlistworker.h | 50 +- src/tabbar.cpp | 1102 ++++----- src/tabbar.h | 196 +- src/tableofcontentbar.cpp | 268 +- src/tableofcontentbar.h | 69 +- src/tableofcontentbar.ui | 260 +- src/texttospeechbar.cpp | 568 ++--- src/texttospeechbar.h | 130 +- src/texttospeechbar.ui | 286 +-- src/thumbnaildownloader.cpp | 96 +- src/thumbnaildownloader.h | 82 +- src/topwidget.cpp | 296 +-- src/topwidget.h | 78 +- src/translation.cpp | 104 +- src/translation.h | 46 +- src/urlschemehandler.cpp | 556 ++--- src/urlschemehandler.h | 42 +- src/webpage.cpp | 48 +- src/webpage.h | 32 +- src/webview.cpp | 949 ++++---- src/webview.h | 172 +- src/zimview.cpp | 310 +-- src/zimview.h | 96 +- subprojects/QtSingleApplication/common.pri | 28 +- .../QtSingleApplication/src/QtLockedFile | 2 +- .../src/QtSingleApplication | 2 +- .../QtSingleApplication/src/qtlocalpeer.cpp | 446 ++-- .../QtSingleApplication/src/qtlocalpeer.h | 154 +- .../QtSingleApplication/src/qtlockedfile.cpp | 386 +-- .../QtSingleApplication/src/qtlockedfile.h | 194 +- .../src/qtlockedfile_unix.cpp | 230 +- .../src/qtlockedfile_win.cpp | 422 ++-- .../src/qtsingleapplication.cpp | 694 +++--- .../src/qtsingleapplication.h | 210 +- .../src/qtsingleapplication.pri | 34 +- .../src/qtsinglecoreapplication.cpp | 298 +-- .../src/qtsinglecoreapplication.h | 142 +- .../src/qtsinglecoreapplication.pri | 20 +- ui/about.ui | 162 +- ui/kiwixchoicebox.ui | 230 +- ui/kiwixmessagebox.ui | 410 ++-- ui/localkiwixserver.ui | 436 ++-- ui/mainwindow.ui | 432 ++-- ui/settings.ui | 882 +++---- 253 files changed, 28046 insertions(+), 27767 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index f39dc2a96..830bd3512 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,12 +1,12 @@ -# These are supported funding model platforms - -github: kiwix # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # https://kiwix.org/support-us/ +# These are supported funding model platforms + +github: kiwix # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # https://kiwix.org/support-us/ diff --git a/.github/stale.yml b/.github/stale.yml index 08a39310b..69b95faab 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,15 +1,15 @@ -daysUntilClose: false -staleLabel: stale - -issues: - daysUntilStale: 60 - markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be now be reviewed manually. Thank you - for your contributions. -pulls: - daysUntilStale: 7 - markComment: > - This pull request has been automatically marked as stale because it has not had - recent activity. It will be now be reviewed manually. Thank you - for your contributions. +daysUntilClose: false +staleLabel: stale + +issues: + daysUntilStale: 60 + markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be now be reviewed manually. Thank you + for your contributions. +pulls: + daysUntilStale: 7 + markComment: > + This pull request has been automatically marked as stale because it has not had + recent activity. It will be now be reviewed manually. Thank you + for your contributions. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ebb35adf..f4271f493 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,98 +1,98 @@ -name: CI - -on: - pull_request: - push: - branches: - - main - -jobs: - Windows: - strategy: - fail-fast: false - matrix: - config: - - native_mixed - runs-on: windows-2022 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup python 3.8 - uses: actions/setup-python@v5 - with: - python-version: '3.8' - - - name: Install packages - run: | - choco.exe install pkgconfiglite - - - name: Install QT - uses: jurplel/install-qt-action@v4 - with: - version: 5.15.2 - modules: "qtwebengine" - setup-python: false - - - name: Setup MSVC compiler - uses: bus1/cabuild/action/msdevshell@v1 - with: - architecture: x64 - - - name: Install dependencies - uses: kiwix/kiwix-build/actions/dl_deps_archive@main - with: - target_platform: win-x86_64-mixed - - - name: Compile - shell: cmd - run: | - set PKG_CONFIG_PATH=%cd%\BUILD_win-amd64\INSTALL\lib\pkgconfig - qmake PREFIX=%cd%\BUILD_win-amd64\INSTALL - nmake release-all - - Linux: - strategy: - fail-fast: false - matrix: - target: - - jammy-qt5 - - noble-qt6.4 - - oracular-qt6.6 - include: - - target: jammy-qt5 - image_variant: jammy - lib_postfix: '/x86_64-linux-gnu' - qt_version: 5 - - target: noble-qt6.4 - image_variant: noble - lib_postfix: '/x86_64-linux-gnu' - qt_version: 6 - - target: oracular-qt6.6 - image_variant: oracular - lib_postfix: '/x86_64-linux-gnu' - qt_version: 6 - env: - HOME: /home/runner - runs-on: ubuntu-22.04 - container: - image: ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:2025-01-05 - - steps: - - name: Install dependencies - uses: kiwix/kiwix-build/actions/dl_deps_archive@main - with: - target_platform: linux-x86_64-dyn - - - name: Retrieve source code - uses: actions/checkout@v4 - - - name: Compile source code - shell: bash - env: - PKG_CONFIG_PATH: "/home/runner/BUILD_linux-x86_64/INSTALL/lib/pkgconfig:/home/runner/BUILD_linux-x86_64/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig" - run: | - export QT_SELECT=qt${{matrix.qt_version}} - qmake --version - qmake PREFIX=$HOME/BUILD_linux-x86_64/INSTALL - make +name: CI + +on: + pull_request: + push: + branches: + - main + +jobs: + Windows: + strategy: + fail-fast: false + matrix: + config: + - native_mixed + runs-on: windows-2022 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup python 3.8 + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install packages + run: | + choco.exe install pkgconfiglite + + - name: Install QT + uses: jurplel/install-qt-action@v4 + with: + version: 5.15.2 + modules: "qtwebengine" + setup-python: false + + - name: Setup MSVC compiler + uses: bus1/cabuild/action/msdevshell@v1 + with: + architecture: x64 + + - name: Install dependencies + uses: kiwix/kiwix-build/actions/dl_deps_archive@main + with: + target_platform: win-x86_64-mixed + + - name: Compile + shell: cmd + run: | + set PKG_CONFIG_PATH=%cd%\BUILD_win-amd64\INSTALL\lib\pkgconfig + qmake PREFIX=%cd%\BUILD_win-amd64\INSTALL + nmake release-all + + Linux: + strategy: + fail-fast: false + matrix: + target: + - jammy-qt5 + - noble-qt6.4 + - oracular-qt6.6 + include: + - target: jammy-qt5 + image_variant: jammy + lib_postfix: '/x86_64-linux-gnu' + qt_version: 5 + - target: noble-qt6.4 + image_variant: noble + lib_postfix: '/x86_64-linux-gnu' + qt_version: 6 + - target: oracular-qt6.6 + image_variant: oracular + lib_postfix: '/x86_64-linux-gnu' + qt_version: 6 + env: + HOME: /home/runner + runs-on: ubuntu-22.04 + container: + image: ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:2025-01-05 + + steps: + - name: Install dependencies + uses: kiwix/kiwix-build/actions/dl_deps_archive@main + with: + target_platform: linux-x86_64-dyn + + - name: Retrieve source code + uses: actions/checkout@v4 + + - name: Compile source code + shell: bash + env: + PKG_CONFIG_PATH: "/home/runner/BUILD_linux-x86_64/INSTALL/lib/pkgconfig:/home/runner/BUILD_linux-x86_64/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig" + run: | + export QT_SELECT=qt${{matrix.qt_version}} + qmake --version + qmake PREFIX=$HOME/BUILD_linux-x86_64/INSTALL + make diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index 52d60eb0e..12cff6962 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -1,118 +1,118 @@ -name: Packages - -on: - pull_request: - push: - branches: - - main - release: - types: [published] - -jobs: - build-deb: - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - matrix: - distro: -# - debian-unstable -# - debian-trixie -# - debian-bookworm -# - debian-bullseye - - ubuntu-noble - - ubuntu-jammy - - ubuntu-focal - steps: - - uses: actions/checkout@v4 - - # Determine which PPA we should upload to - - name: PPA - id: ppa - run: | - if [[ $REF == refs/tags* ]] - then - echo "ppa=kiwixteam/release" >> $GITHUB_OUTPUT - else - echo "ppa=kiwixteam/dev" >> $GITHUB_OUTPUT - fi - env: - REF: ${{ github.ref }} - - - uses: legoktm/gh-action-auto-dch@main - with: - fullname: Kiwix builder - email: release+launchpad@kiwix.org - distro: ${{ matrix.distro }} - -# - uses: legoktm/gh-action-build-deb@debian-unstable -# if: matrix.distro == 'debian-unstable' -# name: Build package for debian-unstable -# id: build-debian-unstable -# with: -# args: --no-sign -# -# - uses: legoktm/gh-action-build-deb@b47978ba8498dc8b8153cc3b5f99a5fc1afa5de1 # pin@debian-trixie -# if: matrix.distro == 'debian-trixie' -# name: Build package for debian-trixie -# id: build-debian-trixie -# with: -# args: --no-sign -# -# - uses: legoktm/gh-action-build-deb@1f4e86a6bb34aaad388167eaf5eb85d553935336 # pin@debian-bookworm -# if: matrix.distro == 'debian-bookworm' -# name: Build package for debian-bookworm -# id: build-debian-bookworm -# with: -# args: --no-sign -# -# - uses: legoktm/gh-action-build-deb@084b4263209252ec80a75d2c78a586192c17f18d # pin@debian-bullseye -# if: matrix.distro == 'debian-bullseye' -# name: Build package for debian-bullseye -# id: build-debian-bullseye -# with: -# args: --no-sign - - - uses: legoktm/gh-action-build-deb@9114a536498b65c40b932209b9833aa942bf108d # pin@ubuntu-noble - if: matrix.distro == 'ubuntu-noble' - name: Build package for ubuntu-noble - id: build-ubuntu-noble - with: - args: --no-sign - ppa: ${{ steps.ppa.outputs.ppa }} - - - uses: legoktm/gh-action-build-deb@ubuntu-jammy - if: matrix.distro == 'ubuntu-jammy' - name: Build package for ubuntu-jammy - id: build-ubuntu-jammy - with: - args: --no-sign - ppa: ${{ steps.ppa.outputs.ppa }} - - - uses: legoktm/gh-action-build-deb@ubuntu-focal - if: matrix.distro == 'ubuntu-focal' - name: Build package for ubuntu-focal - id: build-ubuntu-focal - with: - args: --no-sign - ppa: ${{ steps.ppa.outputs.ppa }} - - - uses: actions/upload-artifact@v4 - with: - name: Packages for ${{ matrix.distro }} - path: output - - - uses: legoktm/gh-action-dput@main - name: Upload dev package - if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' && startswith(matrix.distro, 'ubuntu-') - with: - gpg_key: ${{ secrets.LAUNCHPAD_GPG }} - repository: ppa:kiwixteam/dev - packages: output/*_source.changes - - - uses: legoktm/gh-action-dput@main - name: Upload release package - if: github.event_name == 'release' && startswith(matrix.distro, 'ubuntu-') - with: - gpg_key: ${{ secrets.LAUNCHPAD_GPG }} - repository: ppa:kiwixteam/release - packages: output/*_source.changes +name: Packages + +on: + pull_request: + push: + branches: + - main + release: + types: [published] + +jobs: + build-deb: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + distro: +# - debian-unstable +# - debian-trixie +# - debian-bookworm +# - debian-bullseye + - ubuntu-noble + - ubuntu-jammy + - ubuntu-focal + steps: + - uses: actions/checkout@v4 + + # Determine which PPA we should upload to + - name: PPA + id: ppa + run: | + if [[ $REF == refs/tags* ]] + then + echo "ppa=kiwixteam/release" >> $GITHUB_OUTPUT + else + echo "ppa=kiwixteam/dev" >> $GITHUB_OUTPUT + fi + env: + REF: ${{ github.ref }} + + - uses: legoktm/gh-action-auto-dch@main + with: + fullname: Kiwix builder + email: release+launchpad@kiwix.org + distro: ${{ matrix.distro }} + +# - uses: legoktm/gh-action-build-deb@debian-unstable +# if: matrix.distro == 'debian-unstable' +# name: Build package for debian-unstable +# id: build-debian-unstable +# with: +# args: --no-sign +# +# - uses: legoktm/gh-action-build-deb@b47978ba8498dc8b8153cc3b5f99a5fc1afa5de1 # pin@debian-trixie +# if: matrix.distro == 'debian-trixie' +# name: Build package for debian-trixie +# id: build-debian-trixie +# with: +# args: --no-sign +# +# - uses: legoktm/gh-action-build-deb@1f4e86a6bb34aaad388167eaf5eb85d553935336 # pin@debian-bookworm +# if: matrix.distro == 'debian-bookworm' +# name: Build package for debian-bookworm +# id: build-debian-bookworm +# with: +# args: --no-sign +# +# - uses: legoktm/gh-action-build-deb@084b4263209252ec80a75d2c78a586192c17f18d # pin@debian-bullseye +# if: matrix.distro == 'debian-bullseye' +# name: Build package for debian-bullseye +# id: build-debian-bullseye +# with: +# args: --no-sign + + - uses: legoktm/gh-action-build-deb@9114a536498b65c40b932209b9833aa942bf108d # pin@ubuntu-noble + if: matrix.distro == 'ubuntu-noble' + name: Build package for ubuntu-noble + id: build-ubuntu-noble + with: + args: --no-sign + ppa: ${{ steps.ppa.outputs.ppa }} + + - uses: legoktm/gh-action-build-deb@ubuntu-jammy + if: matrix.distro == 'ubuntu-jammy' + name: Build package for ubuntu-jammy + id: build-ubuntu-jammy + with: + args: --no-sign + ppa: ${{ steps.ppa.outputs.ppa }} + + - uses: legoktm/gh-action-build-deb@ubuntu-focal + if: matrix.distro == 'ubuntu-focal' + name: Build package for ubuntu-focal + id: build-ubuntu-focal + with: + args: --no-sign + ppa: ${{ steps.ppa.outputs.ppa }} + + - uses: actions/upload-artifact@v4 + with: + name: Packages for ${{ matrix.distro }} + path: output + + - uses: legoktm/gh-action-dput@main + name: Upload dev package + if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' && startswith(matrix.distro, 'ubuntu-') + with: + gpg_key: ${{ secrets.LAUNCHPAD_GPG }} + repository: ppa:kiwixteam/dev + packages: output/*_source.changes + + - uses: legoktm/gh-action-dput@main + name: Upload release package + if: github.event_name == 'release' && startswith(matrix.distro, 'ubuntu-') + with: + gpg_key: ${{ secrets.LAUNCHPAD_GPG }} + repository: ppa:kiwixteam/release + packages: output/*_source.changes diff --git a/.gitignore b/.gitignore index 7f672be87..78209d88a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,47 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app -/kiwix-desktop - -# QtCreator Environment Settings (not suitable for sharing) -kiwix-desktop.pro.user - -# Qt autogenerated files -/qrc_*.cpp -/ui_*.h -/moc_*.h -/moc_*.cpp -/Makefile -/.qmake.stash - -# IDE files -/.vscode +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +/kiwix-desktop + +# QtCreator Environment Settings (not suitable for sharing) +kiwix-desktop.pro.user + +# Qt autogenerated files +/qrc_*.cpp +/ui_*.h +/moc_*.h +/moc_*.cpp +/Makefile +/.qmake.stash + +# IDE files +/.vscode diff --git a/COPYING b/COPYING index 443254047..5b244d55a 100644 --- a/COPYING +++ b/COPYING @@ -1,676 +1,676 @@ - - 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 -. - + + 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 +. + diff --git a/ChangeLog b/ChangeLog index a3e27d0f6..992c0fc7a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,341 +1,341 @@ -kiwix-destkop 2.4.1 -=================== - -Compilation: -* FIX: PPA upload (@veloman-yunkan #1288) -* NEW: Qt6 in CI (@adamlamar #1287 #1289) - -kiwix-destkop 2.4.0 -=================== - -General: -* FIX: Fullscreen with Wayland on Linux (@adamlamar #1046) -* FIX: Fullscreen (auto-)scrolling (@sgourdas #1064) -* FIX: Main window title at start (@juuz0 #962) -* FIX: Proper fullscreen shortcut when run on WSL (@sgourdas #1080 #068) -* FIX: Settings directories display (@sgourdas #1139 #1125) -* FIX: More informative file-not-found page (@ShaopengLin #1132) -* FIX: UI padding/margin improvements (@ShaopengLin #1146) -* FIX: Stop crashing if ZIM file is somehow invalid (@veloman-yunkan #1086 #1174) -* FIX: Support of RTL (@ShaopengLin #1187) -* FIX: Improve support of high-density screens (@ShaopengLin #1198 #1208 #1209) -* FIX: Launching from a network drive on Windows (@sgourdas #1215 #1180 #1178 #1170, @veloman-yunkan #1266) -* FIX: App icons (@rgaudin @siemsie #1245) -* FIX: Scrollabel Settings tab (@heropj #1269) -* FIX: Bookmarks sidebar look & feel (@heropj #1256) -* CHANGE: Non-standart boomark icon changed to standart "star" and adding tooltip (@sgourdas #1066) -* CHANGE: Bookmarks sidebar shortcut to Ctrl+B (@vinurk #1090) -* CHANGE: Simplify & improve handling of portable version (@sgourdas #1179) -* NEW: Support of Qt6 (@adamlamar #1046) -* NEW: Handle Win+Arrow shortcuts on Windows (@juuz0 #878) -* NEW: Handle Ctrl+F4 and Ctrl+w tabs related shortcuts @juuz0 #968) -* NEW: "Random Article" button (@juuz0 #928 #955) -* NEW: Remember the current directory path to pen file over restart (@sgourdas #1058) -* NEW: Block any resource required within a ZIM page (@veloman-yunkan #1116) -* NEW: Remember window position over restart (@ShaopengLin #1126) -* NEW: Implement the "Home" button (@sgourdas #1138) -* NEW: import/export bookmarks (@ShaopengLin #1163) -* NEW: Support of IPv6 in server and improve IP propositions (@sgourdas #1220 #1121) -* NEW: Table of content sidebar (@ShaopengLin #1237) -* NEW: Text to Speech tool (ShaopengLin #1143) -* NEW: Open PDF files in ZIM via PDF reader (@juuz0 @veloman-yunkan #1123) - -Tabs: -* FIX: Buggy tab closing fixed (@veloman-yunkan #1057) -* FIX: Keep sidebars open by switching tabs (@sgourdas #1079) -* NEW: Remember (current) tabs over restart (@ShaopengLin #1048 #1073) - -Search(es): -* FIX: Keep vertical position after exiting find-in-page (@vinurk #1075) -* FIX: Proper search bar focus shortcuts: F6, Ctrl_I, Alt + d, ... (@ShaopengLin #1061) -* FIX: Fulltext search result handling (@sgourdas #1065) -* FIX: Improve handling of bookmark button in searchbar (@ShaopengLin #1060) -* NEW: Endless suggestion list (@ShaopengLin #1224) -* NEW: Pressing ENTER in the searchbar opens first suggestion (@ShaopengLin #1232) -* NEW: New search book chooser (@ShaopengLin #1218) - -Library: -* FIX: Book downloader stability (@veloman-yunkan #1024 #1035 #1036 #1038 #118) -* FIX: Properly check first if ZIM file can be saved to target filesystem (@veloman-yunkan #1188) -* FIX: More informative message if lack of space to download (@veloman-yunkan #1092) -* FIX: Detection of clicks on download control buttons (@veloman-yunkan #1101) -* FIX: Smarter/robuster directory monitoring... for ZIM files (@veloman-yunkan #1164) -* CHANGE: Moving library freetext search box to the sidebar (@juuz0 #554) -* CHANGE: Dyanmicaly populated library language & category filter (@juuz0 #960) -* CHANGE: Library revamping using Qt (@juuz0 #946 #965 #970, @ShaopengLin #1145) -* NEW: Library double-click opening a book (@juuz0 #946) -* NEW: Translated category label (@ShaopengLin #1175) -* NEW: New Library "Open folder" button (@juuz0 #970) -* NEW: Allow to nove book file to filesystem trash (@juuz0 #479) -* NEW: Switch automatically to online library if no local book (@juuz0 #989) -* NEW: Remember library filter values over restart (@juuz0 #974) -* NEW: Added ContextMenu Button for Previewing Online Books (@ShaopengLin #1110) - -Compilation: -* FIX: Better handle libzim and libkiwix dependencies (@kelson42 #1044) -* FIX: PPA publishing workflow (@kelson42 #953) -* FIX: Improve header includes order (@sgourdas #1077) -* FIX Many fixes around Appimage version (@veloman-yunkan #1016) -* CHANGE: Use latest libkiwix (@juuz0 #936, @kelson42 #1012, @mgautierfr ##1130, @aryanA101a #1112) -* NEW: Enable compilation on WSL (@sgourdas #1071) - -Documentation: -* CHANGE: [Proper documentation about where data are saved(/~https://github.com/kiwix/kiwix-desktop/wiki/Data-and-Configuration-Files) (@sgourdas #111]4) - -kiwix-destkop 2.3.1 -=================== - - * Fix the AppImage packaging. Now published AppImages work correctly on recent - distrubution (@mgautierfr #905) - * Improve zim file picker (@kelson42 #886) - * Do not show ServiceWorker zim file from the remote catalog (@kelson42 #887) - -kiwix-desktop 2.3.0 -=================== - - * Fix downloading of file bigger than 4G (@juzz0 #855) - * Hide top widget and topbar in fullscreen (@juzz0 #859) - * Add shortcuts for navigating tabs (Ctrl-tab, Ctrl-shift-tab) (@juzz0 #867) - * Do not use deprecated method `QWebEngineDownloadItem::setPath` (@juzz0 #853) - * Fix compilation with last version of libkiwix (@mgautierfr #858) - * Fix README.md (@psypherium #860) - * Update translations. - -kiwix-desktop 2.2.2 -=================== - - * Remove usage of deprecated libkiwix function (@juzz0 @mgautiefr #831) - * Adapt kiwix-desktop to last libkiwix api (@mgautierfr #842, @veloman-yunkan #844) - * Do not search (and crash) on a zim file without fulltext index (@mgautierfr #830) - * Update translations. - * [Flathub] Update link to screenshot - -kiwix-desktop 2.2.1 -=================== - - * [HOTFIX] Fix a bug making kiwix-desktop removing ALL file in the library directory - when a download fails (@juuz0 #816) - * Improve update of the library view when downloading a book (@juuz0 #811) - * Update translations. - -kiwix-desktop 2.2.0 -=================== - - * Force the request to be explicit about the fact we don't want a book count limit. - Fix case when server returns 0 books. (@juuz0 #769) - * Correctly initialize mainWindows/contentManager. Fix crash on Windows. (@mgautierfr #783) - * New Feature: Monitor a local directory for new/removed zim files (@juuz0 #743) - * Fix library filtering on "other" category (@mgautier #791 #798) - * Correctly set the zoom level on new pages (@juuz0 #774) - * Fix setting UI when zoom level may be hidden by QSpinBox arroys (@juuz0 #799) - * Remove filtering by fulltext index presence (@kelson42 #795) - * Refactor internal code for better signal management (@asashnov #751) - * Fix svg icon installation path (@xvitaly #773) - * Fix typos and translations - -Known bugs: - * AppImage build by our CI is buggy on recent Fedora, nothing is displayed - in the WebView. This works correctly on AppImage build on Fedora or rpm - packaged version. - * Kiwix-desktop fails to download huge zim files because of too big metadata - describing the download. - -kiwix-desktop 2.1.0 -=================== - - * Add a IPChooser in the server dialog windows and display a best IP address - on which the user can connect to (@juuz0 #735) - * Open multiple zim files from the command line (@juuz0 #720) - * Setting's zoom level change the zoom in all open tabs (@juuz0 #719) - * Properly display back and forward buttons on RTL environment (@juuz0 #721) - * Open new tabs just after the current tab (@juuz0 #716) - * Always open new url in background (@juuz0 #717) - * Make the server follow modification made in the library (@veloman-yunkan #714) - * Improved setting tab (@juuz0 #696) - * Display the shortcut of the action in its tootips (@juuz0 #682) - * Limit zoom changes to the current tab only (@juuz0 #689) - * Make the server use human-friendly name for zim files (@MananJethwany #676) - * Fix tooltips' position (@kelson42 #672) - * Improved server dialog (@HanYoung-uwu #638) - * Add shortcuts to navigate in web history : - - `BackButton`/`ForwardButton` (mouse buttons) (@Goutte #646) - - `Alt+Left`/`Alt+Right` (@kelson42 #621) - * Add `--version` and `--help` options (@HanYoung-uwu #631) - * Fix file dialog style on Windows (@HanYoung-uwu #634) - * Fix search result pagination (@maneeshpm #628) - * Add shortcut `Alt + ` to switch between tabs (@rishabh-soni #567) - * Fix fade-out tab when title is too long (@asashnov #573) - * Use secure connection (https) when possible (@kelson42 #589) - * Apply language filter to local library (@HanYoung-uwu #581) - * Make `Search` and `Main Menu` buttons visible to screen reader (@asashnov #583) - * Fix print support (@Ambrevar #557) - * Adpat to new libkiwix 10.0.0 API - * New SVG logo (@kelson42 #539) - * Various fixes (@legoktm @HanYoung-uwu @Saket-07 @rishabh-soni @asashnov @manneshpm @MananJethwany) - -kiwix-desktop 2.0.5 -=================== - - * Fix typos (@Abijeet) - * Better font in the contentManager (@vishal-1408) - * [CI] Add building and publication of deb package (@legoktm) - * Check the available size in the configured downloaded directory instea of - the default one (@mgautierfr) - * Use kiwix-lib internal server instead of launching a kiwix-serve - sub-process. (This remove dependency of kiwix-desktop on kiwix-tools) - (@mgautierfr) - * Update translation file. - -kiwix-desktop 2.0.4 -=================== - - * Add "open link in web browser" action in context menu. - * Improve setting UX (no apply button. Download directory dialog). - * Change fonts to the free "Selawik". - * Do not call `date` and `git describe` during build step to allow - reproducible build. - * Better about.html text. - * Update translations. - -kiwix-desktop 2.0.3 -=================== - - * Update logo - * Use new threadsafe api of kiwix-lib to do suggestions search. - -kiwix-desktop 2.0.2 -=================== - - * Add shortcut to set focus on the searchbar - * Udpate logo - * Create only one instance of the application. - * Add content type filter - * Fluid suggestion list UX - * Fix text and add several translations - -kiwix-desktop 2.0.1 -=================== - - * Automatically migrate data from the old data dir to the new one. - * Improve UX of the fulltext search/suggestions. - * Fix menu in fullscreen on Windows. - -kiwix-desktop 2.0 -================= - - * Readinglist doesn't close automatically - * Move the search bar (in article) at the bottom of the view. - * Fix zoom using mouse wheel. - * Make new tab empty (instead of reusing the previous url). - * Restore title tab when search bar loses focus. - * Allow user to set the download directory in the setting. - * Add search (in zim file) feature. - * Correctly handle redirection article in zimfile. - * Add a translation system using translatewiki. - * Use the new tag system of library.kiwix.org. - * Lot of UI tweaking and bug fixes. - -kiwix-desktop rc4 -================= - - * New "Settings" page - * Better aria2c error message - * Fix Reading list with bookmarks from missing books - * Support video fullscreen mode - * Support the default zoom level setting - * Support zoom level per content - * Remove page view source feature - * Stop crashing if a download fails - -kiwix-desktop rc3 -================= - - * Correctly display a book in the local library at the end of download. - * Do not display the context menu on the toolbar. - * Remove the status bar. - * Disable useless actions when displaying the library page. - * Allow user to download files (epub) from a zim files. - * Do not display obsolet bookmarks after removing a book from a library. - * Correctly display zim's icons in the local library page. - * Add "donate" action in the menu. - * Display a tooltip with the url when a link is "mouse over". - * Fix some debug information print on the console. - -kiwix-desktop rc2 -================= - - * Close tab on middle (scroll wheel) click. - * Do not display hidden zim tag (starting with `_`) in the content manager. - * Close all tabs displaying a book when we remove the book from the library. - * Fix icons around the top bar. - * Do not display the zim id in the searchbar. - * Add a icons to the windows executable. - * Remove the "Save page ..." action. - * Correctly run kiwix-serve with the correct library.xml. - -kiwix-desktop rc1 -================= - - * Fix random crash when opening article (race condition). - * Alert user if there is not enough place to download a zim file. - * Better searchbar completion and validation. - * Use the library.xml along the kiwix-desktop binary if present. - (For kiwix-desktop+content on a usb key) - * Better content manager design (css, icons, ...) - * Add few shortcuts (alt+home, ctrl+mousewheel) - * Refresh only information about downloading books instead of all books. - * Remove RPATH from the binaries. - * Add a new tag button. - * Remove categories without content. - * Integrate kiwix-serve (can run kiwix-serve from kiwix-desktop)7 - * Add book sorting. - - -kiwix-desktop beta5 -=================== - - * Bug fixes (numerous) - * Avoid to update the remote library when displaying the local library. - * Add filtering by category for the local content. - * Make zim files displayed with kiwix icon in the file explorer. - * Make the windows' title displaying the current article's title. - * Add an option to delete a book from the local library. - * Display a "load" icon when we are loading/updating the remote library. - * Add pause, resume and stop button for the downloads. - * Fix crash when trying to open a non existant file (from a link). - * Fix search bar reset. - * Search result doesn't open a new tab. - * Random article doesn't open a new tab. - -kiwix-desktop beta4 -=================== - - * Fixup appdata information (for flatpak). - * Add a filtering by language for the remote content. - * Add a filtering by category for the remote content. - -kiwix-desktop beta3 -=================== - - * Update README. - * Update to last version of kiwix-lib. - * Correctly install extra-files for flatpak. - * Update `about.html` files. - -kiwix-desktop beta2 -=================== - - * Do not crash in contentManager view and backend are out of sync. - * Reduce number of requests when we search in the contentManager - * Correctly handle the sideBar - * Make the searchBar update to tab change. - * Url encode the request to the server. - * Get the favicon from the book instead of the reader. - * Correctly style the first tab. - * Ensure reading list is displayed in only one column. - * Rename .desktop file to org.kiwix.desktop.desktop - * Add a appdata file - * Elide the title tab of the article. - * Update style - * Disable navigation buttons when needed - * Remove text highlight when we quit search +kiwix-destkop 2.4.1 +=================== + +Compilation: +* FIX: PPA upload (@veloman-yunkan #1288) +* NEW: Qt6 in CI (@adamlamar #1287 #1289) + +kiwix-destkop 2.4.0 +=================== + +General: +* FIX: Fullscreen with Wayland on Linux (@adamlamar #1046) +* FIX: Fullscreen (auto-)scrolling (@sgourdas #1064) +* FIX: Main window title at start (@juuz0 #962) +* FIX: Proper fullscreen shortcut when run on WSL (@sgourdas #1080 #068) +* FIX: Settings directories display (@sgourdas #1139 #1125) +* FIX: More informative file-not-found page (@ShaopengLin #1132) +* FIX: UI padding/margin improvements (@ShaopengLin #1146) +* FIX: Stop crashing if ZIM file is somehow invalid (@veloman-yunkan #1086 #1174) +* FIX: Support of RTL (@ShaopengLin #1187) +* FIX: Improve support of high-density screens (@ShaopengLin #1198 #1208 #1209) +* FIX: Launching from a network drive on Windows (@sgourdas #1215 #1180 #1178 #1170, @veloman-yunkan #1266) +* FIX: App icons (@rgaudin @siemsie #1245) +* FIX: Scrollabel Settings tab (@heropj #1269) +* FIX: Bookmarks sidebar look & feel (@heropj #1256) +* CHANGE: Non-standart boomark icon changed to standart "star" and adding tooltip (@sgourdas #1066) +* CHANGE: Bookmarks sidebar shortcut to Ctrl+B (@vinurk #1090) +* CHANGE: Simplify & improve handling of portable version (@sgourdas #1179) +* NEW: Support of Qt6 (@adamlamar #1046) +* NEW: Handle Win+Arrow shortcuts on Windows (@juuz0 #878) +* NEW: Handle Ctrl+F4 and Ctrl+w tabs related shortcuts @juuz0 #968) +* NEW: "Random Article" button (@juuz0 #928 #955) +* NEW: Remember the current directory path to pen file over restart (@sgourdas #1058) +* NEW: Block any resource required within a ZIM page (@veloman-yunkan #1116) +* NEW: Remember window position over restart (@ShaopengLin #1126) +* NEW: Implement the "Home" button (@sgourdas #1138) +* NEW: import/export bookmarks (@ShaopengLin #1163) +* NEW: Support of IPv6 in server and improve IP propositions (@sgourdas #1220 #1121) +* NEW: Table of content sidebar (@ShaopengLin #1237) +* NEW: Text to Speech tool (ShaopengLin #1143) +* NEW: Open PDF files in ZIM via PDF reader (@juuz0 @veloman-yunkan #1123) + +Tabs: +* FIX: Buggy tab closing fixed (@veloman-yunkan #1057) +* FIX: Keep sidebars open by switching tabs (@sgourdas #1079) +* NEW: Remember (current) tabs over restart (@ShaopengLin #1048 #1073) + +Search(es): +* FIX: Keep vertical position after exiting find-in-page (@vinurk #1075) +* FIX: Proper search bar focus shortcuts: F6, Ctrl_I, Alt + d, ... (@ShaopengLin #1061) +* FIX: Fulltext search result handling (@sgourdas #1065) +* FIX: Improve handling of bookmark button in searchbar (@ShaopengLin #1060) +* NEW: Endless suggestion list (@ShaopengLin #1224) +* NEW: Pressing ENTER in the searchbar opens first suggestion (@ShaopengLin #1232) +* NEW: New search book chooser (@ShaopengLin #1218) + +Library: +* FIX: Book downloader stability (@veloman-yunkan #1024 #1035 #1036 #1038 #118) +* FIX: Properly check first if ZIM file can be saved to target filesystem (@veloman-yunkan #1188) +* FIX: More informative message if lack of space to download (@veloman-yunkan #1092) +* FIX: Detection of clicks on download control buttons (@veloman-yunkan #1101) +* FIX: Smarter/robuster directory monitoring... for ZIM files (@veloman-yunkan #1164) +* CHANGE: Moving library freetext search box to the sidebar (@juuz0 #554) +* CHANGE: Dyanmicaly populated library language & category filter (@juuz0 #960) +* CHANGE: Library revamping using Qt (@juuz0 #946 #965 #970, @ShaopengLin #1145) +* NEW: Library double-click opening a book (@juuz0 #946) +* NEW: Translated category label (@ShaopengLin #1175) +* NEW: New Library "Open folder" button (@juuz0 #970) +* NEW: Allow to nove book file to filesystem trash (@juuz0 #479) +* NEW: Switch automatically to online library if no local book (@juuz0 #989) +* NEW: Remember library filter values over restart (@juuz0 #974) +* NEW: Added ContextMenu Button for Previewing Online Books (@ShaopengLin #1110) + +Compilation: +* FIX: Better handle libzim and libkiwix dependencies (@kelson42 #1044) +* FIX: PPA publishing workflow (@kelson42 #953) +* FIX: Improve header includes order (@sgourdas #1077) +* FIX Many fixes around Appimage version (@veloman-yunkan #1016) +* CHANGE: Use latest libkiwix (@juuz0 #936, @kelson42 #1012, @mgautierfr ##1130, @aryanA101a #1112) +* NEW: Enable compilation on WSL (@sgourdas #1071) + +Documentation: +* CHANGE: [Proper documentation about where data are saved(/~https://github.com/kiwix/kiwix-desktop/wiki/Data-and-Configuration-Files) (@sgourdas #111]4) + +kiwix-destkop 2.3.1 +=================== + + * Fix the AppImage packaging. Now published AppImages work correctly on recent + distrubution (@mgautierfr #905) + * Improve zim file picker (@kelson42 #886) + * Do not show ServiceWorker zim file from the remote catalog (@kelson42 #887) + +kiwix-desktop 2.3.0 +=================== + + * Fix downloading of file bigger than 4G (@juzz0 #855) + * Hide top widget and topbar in fullscreen (@juzz0 #859) + * Add shortcuts for navigating tabs (Ctrl-tab, Ctrl-shift-tab) (@juzz0 #867) + * Do not use deprecated method `QWebEngineDownloadItem::setPath` (@juzz0 #853) + * Fix compilation with last version of libkiwix (@mgautierfr #858) + * Fix README.md (@psypherium #860) + * Update translations. + +kiwix-desktop 2.2.2 +=================== + + * Remove usage of deprecated libkiwix function (@juzz0 @mgautiefr #831) + * Adapt kiwix-desktop to last libkiwix api (@mgautierfr #842, @veloman-yunkan #844) + * Do not search (and crash) on a zim file without fulltext index (@mgautierfr #830) + * Update translations. + * [Flathub] Update link to screenshot + +kiwix-desktop 2.2.1 +=================== + + * [HOTFIX] Fix a bug making kiwix-desktop removing ALL file in the library directory + when a download fails (@juuz0 #816) + * Improve update of the library view when downloading a book (@juuz0 #811) + * Update translations. + +kiwix-desktop 2.2.0 +=================== + + * Force the request to be explicit about the fact we don't want a book count limit. + Fix case when server returns 0 books. (@juuz0 #769) + * Correctly initialize mainWindows/contentManager. Fix crash on Windows. (@mgautierfr #783) + * New Feature: Monitor a local directory for new/removed zim files (@juuz0 #743) + * Fix library filtering on "other" category (@mgautier #791 #798) + * Correctly set the zoom level on new pages (@juuz0 #774) + * Fix setting UI when zoom level may be hidden by QSpinBox arroys (@juuz0 #799) + * Remove filtering by fulltext index presence (@kelson42 #795) + * Refactor internal code for better signal management (@asashnov #751) + * Fix svg icon installation path (@xvitaly #773) + * Fix typos and translations + +Known bugs: + * AppImage build by our CI is buggy on recent Fedora, nothing is displayed + in the WebView. This works correctly on AppImage build on Fedora or rpm + packaged version. + * Kiwix-desktop fails to download huge zim files because of too big metadata + describing the download. + +kiwix-desktop 2.1.0 +=================== + + * Add a IPChooser in the server dialog windows and display a best IP address + on which the user can connect to (@juuz0 #735) + * Open multiple zim files from the command line (@juuz0 #720) + * Setting's zoom level change the zoom in all open tabs (@juuz0 #719) + * Properly display back and forward buttons on RTL environment (@juuz0 #721) + * Open new tabs just after the current tab (@juuz0 #716) + * Always open new url in background (@juuz0 #717) + * Make the server follow modification made in the library (@veloman-yunkan #714) + * Improved setting tab (@juuz0 #696) + * Display the shortcut of the action in its tootips (@juuz0 #682) + * Limit zoom changes to the current tab only (@juuz0 #689) + * Make the server use human-friendly name for zim files (@MananJethwany #676) + * Fix tooltips' position (@kelson42 #672) + * Improved server dialog (@HanYoung-uwu #638) + * Add shortcuts to navigate in web history : + - `BackButton`/`ForwardButton` (mouse buttons) (@Goutte #646) + - `Alt+Left`/`Alt+Right` (@kelson42 #621) + * Add `--version` and `--help` options (@HanYoung-uwu #631) + * Fix file dialog style on Windows (@HanYoung-uwu #634) + * Fix search result pagination (@maneeshpm #628) + * Add shortcut `Alt + ` to switch between tabs (@rishabh-soni #567) + * Fix fade-out tab when title is too long (@asashnov #573) + * Use secure connection (https) when possible (@kelson42 #589) + * Apply language filter to local library (@HanYoung-uwu #581) + * Make `Search` and `Main Menu` buttons visible to screen reader (@asashnov #583) + * Fix print support (@Ambrevar #557) + * Adpat to new libkiwix 10.0.0 API + * New SVG logo (@kelson42 #539) + * Various fixes (@legoktm @HanYoung-uwu @Saket-07 @rishabh-soni @asashnov @manneshpm @MananJethwany) + +kiwix-desktop 2.0.5 +=================== + + * Fix typos (@Abijeet) + * Better font in the contentManager (@vishal-1408) + * [CI] Add building and publication of deb package (@legoktm) + * Check the available size in the configured downloaded directory instea of + the default one (@mgautierfr) + * Use kiwix-lib internal server instead of launching a kiwix-serve + sub-process. (This remove dependency of kiwix-desktop on kiwix-tools) + (@mgautierfr) + * Update translation file. + +kiwix-desktop 2.0.4 +=================== + + * Add "open link in web browser" action in context menu. + * Improve setting UX (no apply button. Download directory dialog). + * Change fonts to the free "Selawik". + * Do not call `date` and `git describe` during build step to allow + reproducible build. + * Better about.html text. + * Update translations. + +kiwix-desktop 2.0.3 +=================== + + * Update logo + * Use new threadsafe api of kiwix-lib to do suggestions search. + +kiwix-desktop 2.0.2 +=================== + + * Add shortcut to set focus on the searchbar + * Udpate logo + * Create only one instance of the application. + * Add content type filter + * Fluid suggestion list UX + * Fix text and add several translations + +kiwix-desktop 2.0.1 +=================== + + * Automatically migrate data from the old data dir to the new one. + * Improve UX of the fulltext search/suggestions. + * Fix menu in fullscreen on Windows. + +kiwix-desktop 2.0 +================= + + * Readinglist doesn't close automatically + * Move the search bar (in article) at the bottom of the view. + * Fix zoom using mouse wheel. + * Make new tab empty (instead of reusing the previous url). + * Restore title tab when search bar loses focus. + * Allow user to set the download directory in the setting. + * Add search (in zim file) feature. + * Correctly handle redirection article in zimfile. + * Add a translation system using translatewiki. + * Use the new tag system of library.kiwix.org. + * Lot of UI tweaking and bug fixes. + +kiwix-desktop rc4 +================= + + * New "Settings" page + * Better aria2c error message + * Fix Reading list with bookmarks from missing books + * Support video fullscreen mode + * Support the default zoom level setting + * Support zoom level per content + * Remove page view source feature + * Stop crashing if a download fails + +kiwix-desktop rc3 +================= + + * Correctly display a book in the local library at the end of download. + * Do not display the context menu on the toolbar. + * Remove the status bar. + * Disable useless actions when displaying the library page. + * Allow user to download files (epub) from a zim files. + * Do not display obsolet bookmarks after removing a book from a library. + * Correctly display zim's icons in the local library page. + * Add "donate" action in the menu. + * Display a tooltip with the url when a link is "mouse over". + * Fix some debug information print on the console. + +kiwix-desktop rc2 +================= + + * Close tab on middle (scroll wheel) click. + * Do not display hidden zim tag (starting with `_`) in the content manager. + * Close all tabs displaying a book when we remove the book from the library. + * Fix icons around the top bar. + * Do not display the zim id in the searchbar. + * Add a icons to the windows executable. + * Remove the "Save page ..." action. + * Correctly run kiwix-serve with the correct library.xml. + +kiwix-desktop rc1 +================= + + * Fix random crash when opening article (race condition). + * Alert user if there is not enough place to download a zim file. + * Better searchbar completion and validation. + * Use the library.xml along the kiwix-desktop binary if present. + (For kiwix-desktop+content on a usb key) + * Better content manager design (css, icons, ...) + * Add few shortcuts (alt+home, ctrl+mousewheel) + * Refresh only information about downloading books instead of all books. + * Remove RPATH from the binaries. + * Add a new tag button. + * Remove categories without content. + * Integrate kiwix-serve (can run kiwix-serve from kiwix-desktop)7 + * Add book sorting. + + +kiwix-desktop beta5 +=================== + + * Bug fixes (numerous) + * Avoid to update the remote library when displaying the local library. + * Add filtering by category for the local content. + * Make zim files displayed with kiwix icon in the file explorer. + * Make the windows' title displaying the current article's title. + * Add an option to delete a book from the local library. + * Display a "load" icon when we are loading/updating the remote library. + * Add pause, resume and stop button for the downloads. + * Fix crash when trying to open a non existant file (from a link). + * Fix search bar reset. + * Search result doesn't open a new tab. + * Random article doesn't open a new tab. + +kiwix-desktop beta4 +=================== + + * Fixup appdata information (for flatpak). + * Add a filtering by language for the remote content. + * Add a filtering by category for the remote content. + +kiwix-desktop beta3 +=================== + + * Update README. + * Update to last version of kiwix-lib. + * Correctly install extra-files for flatpak. + * Update `about.html` files. + +kiwix-desktop beta2 +=================== + + * Do not crash in contentManager view and backend are out of sync. + * Reduce number of requests when we search in the contentManager + * Correctly handle the sideBar + * Make the searchBar update to tab change. + * Url encode the request to the server. + * Get the favicon from the book instead of the reader. + * Correctly style the first tab. + * Ensure reading list is displayed in only one column. + * Rename .desktop file to org.kiwix.desktop.desktop + * Add a appdata file + * Elide the title tab of the article. + * Update style + * Disable navigation buttons when needed + * Remove text highlight when we quit search diff --git a/LICENSE b/LICENSE index 94a9ed024..818433ecc 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,674 @@ - 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 -. + 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 +. diff --git a/README.md b/README.md index 67f3cac1e..f3c9fd43a 100644 --- a/README.md +++ b/README.md @@ -1,158 +1,158 @@ - -Download on Flathub - - -Kiwix Desktop -============= - -The Kiwix Desktop is a viewer/manager of ZIM files for GNU/Linux and -Microsoft Windows OSes. - -[![Release](https://img.shields.io/github/v/tag/kiwix/kiwix-desktop?label=release&sort=semver)](https://download.kiwix.org/release/kiwix-desktop/) -[![Repositories](https://img.shields.io/repology/repositories/kiwix-desktop?label=repositories)](/~https://github.com/kiwix/kiwix-desktop/wiki/Repology) -[![Build Status](/~https://github.com/kiwix/kiwix-desktop/actions/workflows/ci.yml/badge.svg?branch=main)](/~https://github.com/kiwix/kiwix-desktop/actions?query=branch%3Amain) -[![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-desktop/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-desktop) -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) - -Disclaimer ----------- - -This document assumes you have a little knowledge about software -compilation. If you experience difficulties with the dependencies or -with the Kiwix library compilation itself, we recommend to have a look -at [kiwix-build](/~https://github.com/kiwix/kiwix-build). - -Dependencies ------------- - -The Kiwix Desktop application relies on many third party software libraries. -The following libraries need to be available: - -* [libkiwix](/~https://github.com/kiwix/libkiwix/) -* [Qt](https://www.qt.io/) -* [aria2](https://aria2.github.io/) - -These dependencies may or may not be packaged by your operating -system. They may also be packaged but only in an older version. The -compilation script will tell you if one of them is missing or too old. -In the worst case, you will have to download and compile a bleeding edge -version by hand. - -Libkiwix has to be compiled dynamically, the best way to have it is -to use [kiwix-build](/~https://github.com/kiwix/kiwix-build). - -Install needed packages (on Ubuntu 18.04+): - -```bash -sudo apt-get install libqt5gui5 qtbase5-dev qtwebengine5-dev \ - libqt5svg5-dev qt5-image-formats-plugins aria2 \ - qttools5-dev-tools qtchooser qt5-qmake \ - qtbase5-dev-tools -``` - -Compilation ------------ - -Once all dependencies are installed, you can compile Kiwix Desktop: - -```bash -qmake . -make -``` - -`qmake` will use pkg-config to locate libraries. Depending on where -you've installed libkiwix (and other libraries) you may have to -update the env variable `PKG_CONFIG_PATH`. It can be set as follows, -for example, for x86-64 native systems: - -```bash -export PKG_CONFIG_PATH="<...>/BUILD_native_dyn/INSTALL/lib/x86_64-linux-gnu/pkgconfig" -``` - -You may want to simply open the kiwix-desktop project in QtCreator and -then compile the project from there (don't forget to update -`PKG_CONFIG_PATH` if necessary). - -Compilation with Qt6 --------------------- - -There is initial support for Qt6. Additional packages are needed: - -```bash -sudo apt install qt6-base-dev qt6-base-dev-tools qt6-webengine-dev libqt6webenginecore6-bin libqt6svg6 -``` - -And `qmake` needs to be configured to use Qt6. First confirm `qmake` is using the right version: - -```bash -qtchooser -install qt6 $(which qmake6) # run once -export QT_SELECT=qt6 # set in environments where Qt6 builds are desired -qmake --version -``` - -produces this output: - -```bash -$ qmake --version -QMake version 3.1 -Using Qt version 6.2.4 in /usr/lib/aarch64-linux-gnu -``` - -then build as normal: - -```bash -qmake . -make -``` - -Compilation on Microsoft Windows --------------------------------- - -Here is the [online -documentation](/~https://github.com/kiwix/kiwix-build/wiki/Compile-on-Microsoft-Windows). - -Installation ------------- - -To install Kiwix Desktop on the system: -```bash -sudo make install -``` - -Run ---- - -To run Kiwix Desktop -```bash -kiwix-desktop -``` - -You might have to refresh the `ld` database before: -```bash -sudo ldconfig -``` - -If you face problems such as `library not found...`, add the following -environment variable: - -```bash -LD_LIBRARY_PATH="<...>/BUILD_native_dyn/INSTALL/lib/x86_64-linux-gnu" -``` - -## Communication - -Available communication channels: -* [Web Public Chat channel](https://chat.kiwix.org) -* [Email](mailto:contact+android@kiwix.org) -* [Mailing list](mailto:kiwix-developer@lists.sourceforge.net) -* [Slack](https://kiwixoffline.slack.com): #android channel [Get an invite](https://join.slack.com/t/kiwixoffline/shared_invite/zt-19s7tsi68-xlgHdmDr5c6MJ7uFmJuBkg) -* IRC: #kiwix on irc.freenode.net - -For more information, please refer to -[https://wiki.kiwix.org/wiki/Communication](https://wiki.kiwix.org/wiki/Communication). - -License -------- - -[GPLv3](https://www.gnu.org/licenses/gpl-3.0) or later, see -[LICENSE](LICENSE) for more details. + +Download on Flathub + + +Kiwix Desktop +============= + +The Kiwix Desktop is a viewer/manager of ZIM files for GNU/Linux and +Microsoft Windows OSes. + +[![Release](https://img.shields.io/github/v/tag/kiwix/kiwix-desktop?label=release&sort=semver)](https://download.kiwix.org/release/kiwix-desktop/) +[![Repositories](https://img.shields.io/repology/repositories/kiwix-desktop?label=repositories)](/~https://github.com/kiwix/kiwix-desktop/wiki/Repology) +[![Build Status](/~https://github.com/kiwix/kiwix-desktop/actions/workflows/ci.yml/badge.svg?branch=main)](/~https://github.com/kiwix/kiwix-desktop/actions?query=branch%3Amain) +[![CodeFactor](https://www.codefactor.io/repository/github/kiwix/kiwix-desktop/badge)](https://www.codefactor.io/repository/github/kiwix/kiwix-desktop) +[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) + +Disclaimer +---------- + +This document assumes you have a little knowledge about software +compilation. If you experience difficulties with the dependencies or +with the Kiwix library compilation itself, we recommend to have a look +at [kiwix-build](/~https://github.com/kiwix/kiwix-build). + +Dependencies +------------ + +The Kiwix Desktop application relies on many third party software libraries. +The following libraries need to be available: + +* [libkiwix](/~https://github.com/kiwix/libkiwix/) +* [Qt](https://www.qt.io/) +* [aria2](https://aria2.github.io/) + +These dependencies may or may not be packaged by your operating +system. They may also be packaged but only in an older version. The +compilation script will tell you if one of them is missing or too old. +In the worst case, you will have to download and compile a bleeding edge +version by hand. + +Libkiwix has to be compiled dynamically, the best way to have it is +to use [kiwix-build](/~https://github.com/kiwix/kiwix-build). + +Install needed packages (on Ubuntu 18.04+): + +```bash +sudo apt-get install libqt5gui5 qtbase5-dev qtwebengine5-dev \ + libqt5svg5-dev qt5-image-formats-plugins aria2 \ + qttools5-dev-tools qtchooser qt5-qmake \ + qtbase5-dev-tools +``` + +Compilation +----------- + +Once all dependencies are installed, you can compile Kiwix Desktop: + +```bash +qmake . +make +``` + +`qmake` will use pkg-config to locate libraries. Depending on where +you've installed libkiwix (and other libraries) you may have to +update the env variable `PKG_CONFIG_PATH`. It can be set as follows, +for example, for x86-64 native systems: + +```bash +export PKG_CONFIG_PATH="<...>/BUILD_native_dyn/INSTALL/lib/x86_64-linux-gnu/pkgconfig" +``` + +You may want to simply open the kiwix-desktop project in QtCreator and +then compile the project from there (don't forget to update +`PKG_CONFIG_PATH` if necessary). + +Compilation with Qt6 +-------------------- + +There is initial support for Qt6. Additional packages are needed: + +```bash +sudo apt install qt6-base-dev qt6-base-dev-tools qt6-webengine-dev libqt6webenginecore6-bin libqt6svg6 +``` + +And `qmake` needs to be configured to use Qt6. First confirm `qmake` is using the right version: + +```bash +qtchooser -install qt6 $(which qmake6) # run once +export QT_SELECT=qt6 # set in environments where Qt6 builds are desired +qmake --version +``` + +produces this output: + +```bash +$ qmake --version +QMake version 3.1 +Using Qt version 6.2.4 in /usr/lib/aarch64-linux-gnu +``` + +then build as normal: + +```bash +qmake . +make +``` + +Compilation on Microsoft Windows +-------------------------------- + +Here is the [online +documentation](/~https://github.com/kiwix/kiwix-build/wiki/Compile-on-Microsoft-Windows). + +Installation +------------ + +To install Kiwix Desktop on the system: +```bash +sudo make install +``` + +Run +--- + +To run Kiwix Desktop +```bash +kiwix-desktop +``` + +You might have to refresh the `ld` database before: +```bash +sudo ldconfig +``` + +If you face problems such as `library not found...`, add the following +environment variable: + +```bash +LD_LIBRARY_PATH="<...>/BUILD_native_dyn/INSTALL/lib/x86_64-linux-gnu" +``` + +## Communication + +Available communication channels: +* [Web Public Chat channel](https://chat.kiwix.org) +* [Email](mailto:contact+android@kiwix.org) +* [Mailing list](mailto:kiwix-developer@lists.sourceforge.net) +* [Slack](https://kiwixoffline.slack.com): #android channel [Get an invite](https://join.slack.com/t/kiwixoffline/shared_invite/zt-19s7tsi68-xlgHdmDr5c6MJ7uFmJuBkg) +* IRC: #kiwix on irc.freenode.net + +For more information, please refer to +[https://wiki.kiwix.org/wiki/Communication](https://wiki.kiwix.org/wiki/Communication). + +License +------- + +[GPLv3](https://www.gnu.org/licenses/gpl-3.0) or later, see +[LICENSE](LICENSE) for more details. diff --git a/debian/changelog b/debian/changelog index 554b9de80..0628a8d88 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -kiwix (0.0.0) unstable; urgency=medium - - * Initial release - - -- Kunal Mehta Wed, 22 Jul 2020 15:40:03 -0700 +kiwix (0.0.0) unstable; urgency=medium + + * Initial release + + -- Kunal Mehta Wed, 22 Jul 2020 15:40:03 -0700 diff --git a/debian/control b/debian/control index 5e6e722ab..3cbf08da0 100644 --- a/debian/control +++ b/debian/control @@ -1,36 +1,36 @@ -Source: kiwix -Section: misc -Priority: optional -Maintainer: Kiwix team -Build-Depends: debhelper-compat (= 13), - pkg-config, - qtbase5-dev, - qtwebengine5-dev, - libqt5texttospeech5-dev, - libkiwix-dev (>= 14.0), libkiwix-dev (<< 15.0), - libzim-dev (>= 9.0), libzim-dev (<< 10.0), -Standards-Version: 4.5.0 -Homepage: https://www.kiwix.org/ -Rules-Requires-Root: no - -Package: kiwix -Architecture: any -Depends: ${shlibs:Depends}, - ${misc:Depends} -Description: offline Wikipedia reader - Kiwix is an offline reader and manager for online content - like Wikipedia, Project Gutenberg, or TED Talks. - Kiwix allows you to read and search through offline content - as they were online. Similar to a browser, Kiwix works with - the highly compressed ZIM file format. - . - Features: - * Pure ZIM file reader - * Case and diacritics insensitive full text search engine - * Bookmarks & Notes - * ZIM base HTTP server - * PDF/HTML export - * Localized - * Search suggestions - * ZIM file Index capacity - * Tabs navigation +Source: kiwix +Section: misc +Priority: optional +Maintainer: Kiwix team +Build-Depends: debhelper-compat (= 13), + pkg-config, + qtbase5-dev, + qtwebengine5-dev, + libqt5texttospeech5-dev, + libkiwix-dev (>= 14.0), libkiwix-dev (<< 15.0), + libzim-dev (>= 9.0), libzim-dev (<< 10.0), +Standards-Version: 4.5.0 +Homepage: https://www.kiwix.org/ +Rules-Requires-Root: no + +Package: kiwix +Architecture: any +Depends: ${shlibs:Depends}, + ${misc:Depends} +Description: offline Wikipedia reader + Kiwix is an offline reader and manager for online content + like Wikipedia, Project Gutenberg, or TED Talks. + Kiwix allows you to read and search through offline content + as they were online. Similar to a browser, Kiwix works with + the highly compressed ZIM file format. + . + Features: + * Pure ZIM file reader + * Case and diacritics insensitive full text search engine + * Bookmarks & Notes + * ZIM base HTTP server + * PDF/HTML export + * Localized + * Search suggestions + * ZIM file Index capacity + * Tabs navigation diff --git a/debian/copyright b/debian/copyright index ff4636642..8d612eb20 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1 +1 @@ -See COPYING in the repository root. +See COPYING in the repository root. diff --git a/debian/kiwix-desktop.1 b/debian/kiwix-desktop.1 index e9bcf5251..b9f641142 100644 --- a/debian/kiwix-desktop.1 +++ b/debian/kiwix-desktop.1 @@ -1,10 +1,10 @@ -.TH KIWIX-DESKTOP "1" "July 2020" "Kiwix" "User Commands" -.SH NAME -kiwix-desktop \- viewer and manager for ZIM files -.SH SYNOPSIS -\fBkiwix\-desktop\fR [ZIMFILE]\fR -.SH DESCRIPTION -.TP -ZIMFILE -A ZIM file you wish to open. - +.TH KIWIX-DESKTOP "1" "July 2020" "Kiwix" "User Commands" +.SH NAME +kiwix-desktop \- viewer and manager for ZIM files +.SH SYNOPSIS +\fBkiwix\-desktop\fR [ZIMFILE]\fR +.SH DESCRIPTION +.TP +ZIMFILE +A ZIM file you wish to open. + diff --git a/debian/manpages b/debian/manpages index f694c748e..c240d33b3 100644 --- a/debian/manpages +++ b/debian/manpages @@ -1 +1 @@ -debian/kiwix-desktop.1 +debian/kiwix-desktop.1 diff --git a/debian/rules b/debian/rules index f02366303..9b1479894 100755 --- a/debian/rules +++ b/debian/rules @@ -1,6 +1,6 @@ -#!/usr/bin/make -f -export QT_SELECT=qt5 -export DEB_BUILD_MAINT_OPTIONS = hardening=+all - -%: - dh $@ +#!/usr/bin/make -f +export QT_SELECT=qt5 +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +%: + dh $@ diff --git a/debian/source/format b/debian/source/format index 89ae9db8f..cd029ffee 100755 --- a/debian/source/format +++ b/debian/source/format @@ -1 +1 @@ -3.0 (native) +3.0 (native) diff --git a/kiwix-desktop.pro b/kiwix-desktop.pro index c1f4f93e0..f668a8e50 100644 --- a/kiwix-desktop.pro +++ b/kiwix-desktop.pro @@ -1,261 +1,261 @@ -#------------------------------------------------- -# -# Project created by QtCreator 2018-04-11T15:26:46 -# -#------------------------------------------------- - -QT += core gui network -QT += webenginewidgets webchannel -QT += printsupport -qtHaveModule(texttospeech): QT += texttospeech - -win32 { - QT += gui-private -} - -# Avoid stripping incompatible files, due to false identification as executables, on WSL -DETECT_WSL = $$system(test -f /proc/sys/fs/binfmt_misc/WSLInterop && echo true || echo false) -equals(DETECT_WSL , "true"): CONFIG += nostrip -CONFIG += link_pkgconfig - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -TARGET = kiwix-desktop -TEMPLATE = app - -QMAKE_CXXFLAGS += -std=c++17 -QMAKE_LFLAGS += -std=c++17 - -!win32 { - QMAKE_CXXFLAGS += -Werror - equals(QT_MAJOR_VERSION, 6):equals(QT_MINOR_VERSION, 6) { - # Fail the build on errors, except for 'template-id-cdtor' due to a problem with Qt headers. - # This can be removed when the Ubuntu package is fixed. - # See - # /~https://github.com/RfidResearchGroup/proxmark3/issues/2382 - # https://bugreports.qt.io/browse/QTBUG-126989 - QMAKE_CXXFLAGS += -Wno-error=template-id-cdtor - } -} - -!win32:!static { - QMAKE_LFLAGS += -Wl,-rpath-link,\'$$PREFIX/lib/x86_64-linux-gnu\' -} - -# Also change resources/org.kiwix.desktop.appdata.xml -DEFINES += VERSION="2.4.1" - -# The following define makes your compiler emit warnings if you use -# any feature of Qt which has been marked as deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. -DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if you use deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - - -SOURCES += \ - src/choiceitem.cpp \ - src/contentmanagerdelegate.cpp \ - src/contentmanagerheader.cpp \ - src/contentmanagermodel.cpp \ - src/contenttypefilter.cpp \ - src/descriptionnode.cpp \ - src/downloadmanagement.cpp \ - src/findinpagebar.cpp \ - src/flowlayout.cpp \ - src/kiwixchoicebox.cpp \ - src/kiwixmessagebox.cpp \ - src/kiwixlineedit.cpp \ - src/kiwixlistwidget.cpp \ - src/kiwixloader.cpp \ - src/rownode.cpp \ - src/suggestionlistworker.cpp \ - src/suggestionlistmodel.cpp \ - src/suggestionlistdelegate.cpp \ - src/thumbnaildownloader.cpp \ - src/translation.cpp \ - src/main.cpp \ - src/mainwindow.cpp \ - src/kiwixapp.cpp \ - src/kprofile.cpp \ - src/blobbuffer.cpp \ - src/library.cpp \ - src/settingsmanager.cpp \ - src/settingsview.cpp \ - src/topwidget.cpp \ - src/urlschemehandler.cpp \ - src/webview.cpp \ - src/searchbar.cpp \ - src/mainmenu.cpp \ - src/webpage.cpp \ - src/about.cpp \ - src/contentmanager.cpp \ - src/contentmanagerview.cpp \ - src/tabbar.cpp \ - src/contentmanagerside.cpp \ - src/readinglistbar.cpp \ - src/tableofcontentbar.cpp \ - src/klistwidgetitem.cpp \ - src/opdsrequestmanager.cpp \ - src/localkiwixserver.cpp \ - src/fullscreenwindow.cpp \ - src/fullscreennotification.cpp \ - src/zimview.cpp \ - src/multizimbutton.cpp \ - -qtHaveModule(texttospeech): SOURCES += src/texttospeechbar.cpp \ - -HEADERS += \ - src/choiceitem.h \ - src/contentmanagerdelegate.h \ - src/contentmanagerheader.h \ - src/contentmanagermodel.h \ - src/contentmanagerview.h \ - src/contenttypefilter.h \ - src/descriptionnode.h \ - src/downloadmanagement.h \ - src/findinpagebar.h \ - src/flowlayout.h \ - src/kiwixchoicebox.h \ - src/kiwixmessagebox.h \ - src/kiwixlineedit.h \ - src/kiwixlistwidget.h \ - src/kiwixloader.h \ - src/node.h \ - src/rownode.h \ - src/suggestionlistworker.h \ - src/suggestionlistmodel.h \ - src/suggestionlistdelegate.h \ - src/thumbnaildownloader.h \ - src/translation.h \ - src/mainwindow.h \ - src/kiwixapp.h \ - src/kprofile.h \ - src/blobbuffer.h \ - src/library.h \ - src/settingsmanager.h \ - src/settingsview.h \ - src/topwidget.h \ - src/kconstants.h \ - src/urlschemehandler.h \ - src/webview.h \ - src/searchbar.h \ - src/mainmenu.h \ - src/webpage.h \ - src/about.h \ - src/contentmanager.h \ - src/tabbar.h \ - src/contentmanagerside.h \ - src/readinglistbar.h \ - src/tableofcontentbar.h \ - src/klistwidgetitem.h \ - src/opdsrequestmanager.h \ - src/localkiwixserver.h \ - src/fullscreenwindow.h \ - src/fullscreennotification.h \ - src/menuproxystyle.h \ - src/zimview.h \ - src/portutils.h \ - src/css_constants.h \ - src/multizimbutton.h \ - src/kiwixwebchannelobject.h \ - -qtHaveModule(texttospeech): HEADERS += src/texttospeechbar.h \ - -FORMS += \ - src/choiceitem.ui \ - src/contentmanagerview.ui \ - src/findinpagebar.ui \ - ui/kiwixchoicebox.ui \ - ui/kiwixmessagebox.ui \ - ui/mainwindow.ui \ - ui/about.ui \ - src/contentmanagerside.ui \ - src/readinglistbar.ui \ - ui/localkiwixserver.ui \ - ui/settings.ui \ - src/tableofcontentbar.ui \ - -qtHaveModule(texttospeech): FORMS += src/texttospeechbar.ui \ - -include(subprojects/QtSingleApplication/src/qtsingleapplication.pri) -CODECFORSRC = UTF-8 - -isEmpty(PREFIX) { - PREFIX = /usr/local -} -target.path = $$PREFIX/bin -INSTALLS += target - -static { - PKGCONFIG_OPTION = "--static" -} - -unix { - desktop_file.path = $$PREFIX/share/applications/ - desktop_file.files = resources/org.kiwix.desktop.desktop - INSTALLS += desktop_file - - metainfo_file.path = $$PREFIX/share/metainfo/ - metainfo_file.files = resources/org.kiwix.desktop.appdata.xml - INSTALLS += metainfo_file - - app_icon24.path = $$PREFIX/share/icons/hicolor/24x24/apps - app_icon24.files = resources/icons/kiwix/24/kiwix-desktop.png - app_icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps - app_icon32.files = resources/icons/kiwix/32/kiwix-desktop.png - app_icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps - app_icon48.files = resources/icons/kiwix/48/kiwix-desktop.png - app_icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps - app_icon128.files = resources/icons/kiwix/128/kiwix-desktop.png - app_icon256.path = $$PREFIX/share/icons/hicolor/256x256/apps - app_icon256.files = resources/icons/kiwix/256/kiwix-desktop.png - app_icon512.path = $$PREFIX/share/icons/hicolor/512x512/apps - app_icon512.files = resources/icons/kiwix/512/kiwix-desktop.png - app_svg.path = $$PREFIX/share/icons/hicolor/scalable/apps - app_svg.files = resources/icons/kiwix/scalable/kiwix-desktop.svg - INSTALLS += app_icon24 app_icon32 app_icon48 app_icon128 app_icon256 app_icon512 app_svg - - mime_icon48.path = $$PREFIX/share/icons/hicolor/48x48/mimetypes - mime_icon48.files = resources/icons/48/kiwix/org.kiwix.desktop.x-zim.png - mime_icon128.path = $$PREFIX/share/icons/hicolor/128x128/mimetypes - mime_icon128.files = resources/icons/128/kiwix/org.kiwix.desktop.x-zim.png - mime_icon256.path = $$PREFIX/share/icons/hicolor/256x256/mimetypes - mime_icon256.files = resources/icons/256/kiwix/org.kiwix.desktop.x-zim.png - mime_icon512.path = $$PREFIX/share/icons/hicolor/512x512/mimetypes - mime_icon512.files = resources/icons/512/kiwix/org.kiwix.desktop.x-zim.png - INSTALLS += mime_icon48 mime_icon128 mime_icon256 mime_icon512 - - mime_file.path = $$PREFIX/share/mime/packages/ - mime_file.files = resources/org.kiwix.desktop-mime.xml - INSTALLS += mime_file -} - -DEPS_DEFINITION = \"libkiwix >= 14.0.0 libkiwix < 15.0.0 libzim >= 9.0.0 libzim < 10.0.0\" - -PKGCONFIG_CFLAGS = $$system(pkg-config --cflags $$PKGCONFIG_OPTION $$DEPS_DEFINITION) - -QMAKE_CXXFLAGS += $$PKGCONFIG_CFLAGS -QMAKE_CFLAGS += $$PKGCONFIG_CFLAGS - -!win32 { - LIBS += $$system(pkg-config --libs $$PKGCONFIG_OPTION $$DEPS_DEFINITION) -} - -win32 { - LIBS += $$system(python scripts/pkg-config-wrapper.py --libs $$PKGCONFIG_OPTION $$DEPS_DEFINITION) -} - -RESOURCES += \ - resources/kiwix.qrc \ - resources/translations.qrc \ - resources/contentmanager.qrc \ - resources/settingsmanager.qrc \ - resources/style.qrc \ - resources/js.qrc - -RC_ICONS = resources/icons/kiwix/app_icon.ico +#------------------------------------------------- +# +# Project created by QtCreator 2018-04-11T15:26:46 +# +#------------------------------------------------- + +QT += core gui network +QT += webenginewidgets webchannel +QT += printsupport +qtHaveModule(texttospeech): QT += texttospeech + +win32 { + QT += gui-private +} + +# Avoid stripping incompatible files, due to false identification as executables, on WSL +DETECT_WSL = $$system(test -f /proc/sys/fs/binfmt_misc/WSLInterop && echo true || echo false) +equals(DETECT_WSL , "true"): CONFIG += nostrip +CONFIG += link_pkgconfig + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = kiwix-desktop +TEMPLATE = app + +QMAKE_CXXFLAGS += -std=c++17 +QMAKE_LFLAGS += -std=c++17 + +!win32 { + QMAKE_CXXFLAGS += -Werror + equals(QT_MAJOR_VERSION, 6):equals(QT_MINOR_VERSION, 6) { + # Fail the build on errors, except for 'template-id-cdtor' due to a problem with Qt headers. + # This can be removed when the Ubuntu package is fixed. + # See + # /~https://github.com/RfidResearchGroup/proxmark3/issues/2382 + # https://bugreports.qt.io/browse/QTBUG-126989 + QMAKE_CXXFLAGS += -Wno-error=template-id-cdtor + } +} + +!win32:!static { + QMAKE_LFLAGS += -Wl,-rpath-link,\'$$PREFIX/lib/x86_64-linux-gnu\' +} + +# Also change resources/org.kiwix.desktop.appdata.xml +DEFINES += VERSION="2.4.1" + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which has been marked as deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + + +SOURCES += \ + src/choiceitem.cpp \ + src/contentmanagerdelegate.cpp \ + src/contentmanagerheader.cpp \ + src/contentmanagermodel.cpp \ + src/contenttypefilter.cpp \ + src/descriptionnode.cpp \ + src/downloadmanagement.cpp \ + src/findinpagebar.cpp \ + src/flowlayout.cpp \ + src/kiwixchoicebox.cpp \ + src/kiwixmessagebox.cpp \ + src/kiwixlineedit.cpp \ + src/kiwixlistwidget.cpp \ + src/kiwixloader.cpp \ + src/rownode.cpp \ + src/suggestionlistworker.cpp \ + src/suggestionlistmodel.cpp \ + src/suggestionlistdelegate.cpp \ + src/thumbnaildownloader.cpp \ + src/translation.cpp \ + src/main.cpp \ + src/mainwindow.cpp \ + src/kiwixapp.cpp \ + src/kprofile.cpp \ + src/blobbuffer.cpp \ + src/library.cpp \ + src/settingsmanager.cpp \ + src/settingsview.cpp \ + src/topwidget.cpp \ + src/urlschemehandler.cpp \ + src/webview.cpp \ + src/searchbar.cpp \ + src/mainmenu.cpp \ + src/webpage.cpp \ + src/about.cpp \ + src/contentmanager.cpp \ + src/contentmanagerview.cpp \ + src/tabbar.cpp \ + src/contentmanagerside.cpp \ + src/readinglistbar.cpp \ + src/tableofcontentbar.cpp \ + src/klistwidgetitem.cpp \ + src/opdsrequestmanager.cpp \ + src/localkiwixserver.cpp \ + src/fullscreenwindow.cpp \ + src/fullscreennotification.cpp \ + src/zimview.cpp \ + src/multizimbutton.cpp \ + +qtHaveModule(texttospeech): SOURCES += src/texttospeechbar.cpp \ + +HEADERS += \ + src/choiceitem.h \ + src/contentmanagerdelegate.h \ + src/contentmanagerheader.h \ + src/contentmanagermodel.h \ + src/contentmanagerview.h \ + src/contenttypefilter.h \ + src/descriptionnode.h \ + src/downloadmanagement.h \ + src/findinpagebar.h \ + src/flowlayout.h \ + src/kiwixchoicebox.h \ + src/kiwixmessagebox.h \ + src/kiwixlineedit.h \ + src/kiwixlistwidget.h \ + src/kiwixloader.h \ + src/node.h \ + src/rownode.h \ + src/suggestionlistworker.h \ + src/suggestionlistmodel.h \ + src/suggestionlistdelegate.h \ + src/thumbnaildownloader.h \ + src/translation.h \ + src/mainwindow.h \ + src/kiwixapp.h \ + src/kprofile.h \ + src/blobbuffer.h \ + src/library.h \ + src/settingsmanager.h \ + src/settingsview.h \ + src/topwidget.h \ + src/kconstants.h \ + src/urlschemehandler.h \ + src/webview.h \ + src/searchbar.h \ + src/mainmenu.h \ + src/webpage.h \ + src/about.h \ + src/contentmanager.h \ + src/tabbar.h \ + src/contentmanagerside.h \ + src/readinglistbar.h \ + src/tableofcontentbar.h \ + src/klistwidgetitem.h \ + src/opdsrequestmanager.h \ + src/localkiwixserver.h \ + src/fullscreenwindow.h \ + src/fullscreennotification.h \ + src/menuproxystyle.h \ + src/zimview.h \ + src/portutils.h \ + src/css_constants.h \ + src/multizimbutton.h \ + src/kiwixwebchannelobject.h \ + +qtHaveModule(texttospeech): HEADERS += src/texttospeechbar.h \ + +FORMS += \ + src/choiceitem.ui \ + src/contentmanagerview.ui \ + src/findinpagebar.ui \ + ui/kiwixchoicebox.ui \ + ui/kiwixmessagebox.ui \ + ui/mainwindow.ui \ + ui/about.ui \ + src/contentmanagerside.ui \ + src/readinglistbar.ui \ + ui/localkiwixserver.ui \ + ui/settings.ui \ + src/tableofcontentbar.ui \ + +qtHaveModule(texttospeech): FORMS += src/texttospeechbar.ui \ + +include(subprojects/QtSingleApplication/src/qtsingleapplication.pri) +CODECFORSRC = UTF-8 + +isEmpty(PREFIX) { + PREFIX = /usr/local +} +target.path = $$PREFIX/bin +INSTALLS += target + +static { + PKGCONFIG_OPTION = "--static" +} + +unix { + desktop_file.path = $$PREFIX/share/applications/ + desktop_file.files = resources/org.kiwix.desktop.desktop + INSTALLS += desktop_file + + metainfo_file.path = $$PREFIX/share/metainfo/ + metainfo_file.files = resources/org.kiwix.desktop.appdata.xml + INSTALLS += metainfo_file + + app_icon24.path = $$PREFIX/share/icons/hicolor/24x24/apps + app_icon24.files = resources/icons/kiwix/24/kiwix-desktop.png + app_icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps + app_icon32.files = resources/icons/kiwix/32/kiwix-desktop.png + app_icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps + app_icon48.files = resources/icons/kiwix/48/kiwix-desktop.png + app_icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps + app_icon128.files = resources/icons/kiwix/128/kiwix-desktop.png + app_icon256.path = $$PREFIX/share/icons/hicolor/256x256/apps + app_icon256.files = resources/icons/kiwix/256/kiwix-desktop.png + app_icon512.path = $$PREFIX/share/icons/hicolor/512x512/apps + app_icon512.files = resources/icons/kiwix/512/kiwix-desktop.png + app_svg.path = $$PREFIX/share/icons/hicolor/scalable/apps + app_svg.files = resources/icons/kiwix/scalable/kiwix-desktop.svg + INSTALLS += app_icon24 app_icon32 app_icon48 app_icon128 app_icon256 app_icon512 app_svg + + mime_icon48.path = $$PREFIX/share/icons/hicolor/48x48/mimetypes + mime_icon48.files = resources/icons/48/kiwix/org.kiwix.desktop.x-zim.png + mime_icon128.path = $$PREFIX/share/icons/hicolor/128x128/mimetypes + mime_icon128.files = resources/icons/128/kiwix/org.kiwix.desktop.x-zim.png + mime_icon256.path = $$PREFIX/share/icons/hicolor/256x256/mimetypes + mime_icon256.files = resources/icons/256/kiwix/org.kiwix.desktop.x-zim.png + mime_icon512.path = $$PREFIX/share/icons/hicolor/512x512/mimetypes + mime_icon512.files = resources/icons/512/kiwix/org.kiwix.desktop.x-zim.png + INSTALLS += mime_icon48 mime_icon128 mime_icon256 mime_icon512 + + mime_file.path = $$PREFIX/share/mime/packages/ + mime_file.files = resources/org.kiwix.desktop-mime.xml + INSTALLS += mime_file +} + +DEPS_DEFINITION = \"libkiwix >= 14.0.0 libkiwix < 15.0.0 libzim >= 9.0.0 libzim < 10.0.0\" + +PKGCONFIG_CFLAGS = $$system(pkg-config --cflags $$PKGCONFIG_OPTION $$DEPS_DEFINITION) + +QMAKE_CXXFLAGS += $$PKGCONFIG_CFLAGS +QMAKE_CFLAGS += $$PKGCONFIG_CFLAGS + +!win32 { + LIBS += $$system(pkg-config --libs $$PKGCONFIG_OPTION $$DEPS_DEFINITION) +} + +win32 { + LIBS += $$system(python scripts/pkg-config-wrapper.py --libs $$PKGCONFIG_OPTION $$DEPS_DEFINITION) +} + +RESOURCES += \ + resources/kiwix.qrc \ + resources/translations.qrc \ + resources/contentmanager.qrc \ + resources/settingsmanager.qrc \ + resources/style.qrc \ + resources/js.qrc + +RC_ICONS = resources/icons/kiwix/app_icon.ico diff --git a/resources/contentmanager.qrc b/resources/contentmanager.qrc index 709a5068b..ced323022 100644 --- a/resources/contentmanager.qrc +++ b/resources/contentmanager.qrc @@ -1,5 +1,5 @@ - - - css/_contentManager.css - - + + + css/_contentManager.css + + diff --git a/resources/css/_contentManager.css b/resources/css/_contentManager.css index 289ce8c7c..2abc4e664 100644 --- a/resources/css/_contentManager.css +++ b/resources/css/_contentManager.css @@ -1,81 +1,81 @@ -#contentmanagerside { - background-color: red; -} - -QTreeView::branch:open:has-children { - image: url(:/icons/caret-down-solid.svg); - padding: 6px; -} - -QTreeView::branch:closed:has-children { - image: url(:/icons/caret-right-solid.svg); - padding: 7px; -} - -QTreeView::branch:has-children { - border-bottom: 1px solid #cccccc; -} - -QTreeView::branch:has-children:hover { - background-color: #eaecf0; -} - -QTreeView::item:has-children { - border-bottom: 1px solid #cccccc; -} - - -QTreeView { - font-family: 'Selawik'; - padding: 4px; /* XXX: duplicated in css_constants.h */ - border: none; - selection-color: black; - qproperty-iconSize: 30px; -} - -QTreeView::item:hover { - background-color: #eaecf0; -} - -QHeaderView::section { - color: #666666; - background-color: #fff; - border-width: 0px 0px 2px; - border-color: black; - border-style: plain; - font-size: 16px; - font-family: 'Selawik'; - padding: 4px; - margin-left: 10px; -} - -QHeaderView::down-arrow { - image: url(:/icons/caret-down-solid.svg); - height: 12px; - width: 12px; - padding-bottom: 6px; - subcontrol-position: left; -} - -QHeaderView::up-arrow { - image: url(:/icons/caret-up-solid.svg); - height: 12px; - width: 12px; - padding-bottom: 6px; - subcontrol-position: left; -} - -QMenu { - background-color: white; - margin: 2px; - font-family: 'Selawik'; -} - -QMenu::item { - padding: 2px 25px 2px 20px; - border-bottom: 1px solid #cccccc; -} - -QMenu::item:selected { - background-color: #cccccc; -} +#contentmanagerside { + background-color: red; +} + +QTreeView::branch:open:has-children { + image: url(:/icons/caret-down-solid.svg); + padding: 6px; +} + +QTreeView::branch:closed:has-children { + image: url(:/icons/caret-right-solid.svg); + padding: 7px; +} + +QTreeView::branch:has-children { + border-bottom: 1px solid #cccccc; +} + +QTreeView::branch:has-children:hover { + background-color: #eaecf0; +} + +QTreeView::item:has-children { + border-bottom: 1px solid #cccccc; +} + + +QTreeView { + font-family: 'Selawik'; + padding: 4px; /* XXX: duplicated in css_constants.h */ + border: none; + selection-color: black; + qproperty-iconSize: 30px; +} + +QTreeView::item:hover { + background-color: #eaecf0; +} + +QHeaderView::section { + color: #666666; + background-color: #fff; + border-width: 0px 0px 2px; + border-color: black; + border-style: plain; + font-size: 16px; + font-family: 'Selawik'; + padding: 4px; + margin-left: 10px; +} + +QHeaderView::down-arrow { + image: url(:/icons/caret-down-solid.svg); + height: 12px; + width: 12px; + padding-bottom: 6px; + subcontrol-position: left; +} + +QHeaderView::up-arrow { + image: url(:/icons/caret-up-solid.svg); + height: 12px; + width: 12px; + padding-bottom: 6px; + subcontrol-position: left; +} + +QMenu { + background-color: white; + margin: 2px; + font-family: 'Selawik'; +} + +QMenu::item { + padding: 2px 25px 2px 20px; + border-bottom: 1px solid #cccccc; +} + +QMenu::item:selected { + background-color: #cccccc; +} diff --git a/resources/css/_settingsManager.css b/resources/css/_settingsManager.css index 95e55bb0f..fb64b28a6 100644 --- a/resources/css/_settingsManager.css +++ b/resources/css/_settingsManager.css @@ -1,82 +1,82 @@ -QWidget#widget { - max-width: 600; -} - -QLabel, -QPushButton, -QSpinBox { - font-size: 16px; - line-height: 24px; -} - -QLabel, -QPushButton { - padding: 12px 8px; -} - -QFrame[frameShape="4"] { - /*frameShape 4 is HLine */ - border: 1px solid #eaecf0; - min-height: 0; - max-height: 1pt; -} - -QLabel#settingsLabel { - font-size: 32px; - line-height: 44px; - font-weight: bold; -} -QSpinBox, -QLabel#downloadDirPath, #monitorDirPath { - font: bold 12pt; -} -QPushButton { - background-color: white; - color: #3366cc; - padding: 4px; - font: bold; - border-radius: 4px; -} - -QPushButton:hover { - background-color: #3366cc; - color: white; -} - -#downloadDirPathCopy, #monitorDirPathCopy { - background-color: none; - border: 1px solid transparent; -} - -#downloadDirPathCopy:hover, #monitorDirPathCopy:hover { - background-color: #D9E9FF; - border: 1px solid #3366CC; - border-radius: 3px; -} - -#monitorDirLabel { - padding-right: 0; -} - -#monitorHelp { - padding: 1; - margin: 0; - background: #666666; - color: white; - border-radius: 2px; - max-height: 18px; - min-width: 14px; - font: bold; -} - -QCheckBox::indicator { - width: 16px; - height: 16px; - border: 2px solid black; - border-radius: 5px; - padding: 4px; -} - -QCheckBox::indicator:checked { - image: url(:/icons/check-solid.svg); -} +QWidget#widget { + max-width: 600; +} + +QLabel, +QPushButton, +QSpinBox { + font-size: 16px; + line-height: 24px; +} + +QLabel, +QPushButton { + padding: 12px 8px; +} + +QFrame[frameShape="4"] { + /*frameShape 4 is HLine */ + border: 1px solid #eaecf0; + min-height: 0; + max-height: 1pt; +} + +QLabel#settingsLabel { + font-size: 32px; + line-height: 44px; + font-weight: bold; +} +QSpinBox, +QLabel#downloadDirPath, #monitorDirPath { + font: bold 12pt; +} +QPushButton { + background-color: white; + color: #3366cc; + padding: 4px; + font: bold; + border-radius: 4px; +} + +QPushButton:hover { + background-color: #3366cc; + color: white; +} + +#downloadDirPathCopy, #monitorDirPathCopy { + background-color: none; + border: 1px solid transparent; +} + +#downloadDirPathCopy:hover, #monitorDirPathCopy:hover { + background-color: #D9E9FF; + border: 1px solid #3366CC; + border-radius: 3px; +} + +#monitorDirLabel { + padding-right: 0; +} + +#monitorHelp { + padding: 1; + margin: 0; + background: #666666; + color: white; + border-radius: 2px; + max-height: 18px; + min-width: 14px; + font: bold; +} + +QCheckBox::indicator { + width: 16px; + height: 16px; + border: 2px solid black; + border-radius: 5px; + padding: 4px; +} + +QCheckBox::indicator:checked { + image: url(:/icons/check-solid.svg); +} diff --git a/resources/css/choiceBox.css b/resources/css/choiceBox.css index 20d85dea1..1ad42d8ad 100644 --- a/resources/css/choiceBox.css +++ b/resources/css/choiceBox.css @@ -1,71 +1,71 @@ -QListWidget::item { - padding: 0; - padding-top: 6px; - padding-bottom: 6px; - padding-left: 2px; -} - -QListWidget { - padding: 0; - border: 1px solid #ccc; -} - -QListWidget::item::selected { - color: #444444; - background-color: transparent; -} - -QLineEdit { - padding: 4px; - border: 0; -} - -#choiceLabel { - font-size: 16px; -} - -#currentChoices { - margin: 0; - padding: 0; - border: 1px solid #ccc; - border-radius: 1px; -} - -#closeButton { - margin: 0; - background: transparent; - color: #eaecf0; - font-size: 12px; -} - -ChoiceItem > QFrame { - border: 1px solid #ccc; - border-radius: 2px; - padding: 0; - background-color: #666; - padding-left: 2px; - padding-right: 2px; - margin:0; -} - -#itemLabel { - margin: 0; - height: 6px; - background-color: #666; - color: #eaecf0; - font-size: 13px; -} - -#clearButton { - background-color: white; - color: #3366cc; - padding: 4px; - font: bold; - font-size: 14px; - border-radius: 4px; -} - -#clearButton:hover { - background-color: #3366cc; - color: white; -} +QListWidget::item { + padding: 0; + padding-top: 6px; + padding-bottom: 6px; + padding-left: 2px; +} + +QListWidget { + padding: 0; + border: 1px solid #ccc; +} + +QListWidget::item::selected { + color: #444444; + background-color: transparent; +} + +QLineEdit { + padding: 4px; + border: 0; +} + +#choiceLabel { + font-size: 16px; +} + +#currentChoices { + margin: 0; + padding: 0; + border: 1px solid #ccc; + border-radius: 1px; +} + +#closeButton { + margin: 0; + background: transparent; + color: #eaecf0; + font-size: 12px; +} + +ChoiceItem > QFrame { + border: 1px solid #ccc; + border-radius: 2px; + padding: 0; + background-color: #666; + padding-left: 2px; + padding-right: 2px; + margin:0; +} + +#itemLabel { + margin: 0; + height: 6px; + background-color: #666; + color: #eaecf0; + font-size: 13px; +} + +#clearButton { + background-color: white; + color: #3366cc; + padding: 4px; + font: bold; + font-size: 14px; + border-radius: 4px; +} + +#clearButton:hover { + background-color: #3366cc; + color: white; +} diff --git a/resources/css/contentmanagerside.css b/resources/css/contentmanagerside.css index df16f7027..34af07357 100644 --- a/resources/css/contentmanagerside.css +++ b/resources/css/contentmanagerside.css @@ -1,23 +1,23 @@ -#searcher { - font-family: 'Selawik'; - padding: 4px; - border: none; - border-bottom: 1px solid #cccccc; - color: #666666; - font-size: 16px; - height: 32px; - line-height: 24px; -} - -QScrollArea { - border: 0; -} - -#allFileButton, #localFileButton, #contentTypeButton { - padding-left: 2px; -} - -#allFileButton::indicator, #localFileButton::indicator, #contentTypeButton::indicator { - height: 0; - width: 0; -} +#searcher { + font-family: 'Selawik'; + padding: 4px; + border: none; + border-bottom: 1px solid #cccccc; + color: #666666; + font-size: 16px; + height: 32px; + line-height: 24px; +} + +QScrollArea { + border: 0; +} + +#allFileButton, #localFileButton, #contentTypeButton { + padding-left: 2px; +} + +#allFileButton::indicator, #localFileButton::indicator, #contentTypeButton::indicator { + height: 0; + width: 0; +} diff --git a/resources/css/localServer.css b/resources/css/localServer.css index 05f794e74..1c4390cbe 100644 --- a/resources/css/localServer.css +++ b/resources/css/localServer.css @@ -1,42 +1,42 @@ -QWidget { - background-color: white; -} - -QDialog { - border: 2px solid #cccccc; -} - -QPushButton { - opacity: 1; - border: none; - padding: 3px; - color: RoyalBlue; - outline: 0; -} - -QPushButton:hover { - color: rgba(65, 105, 225, 0.9); -} - -QLineEdit { - border: 1 solid #cccccc; - padding: 2px; -} - -QLabel { - margin-left: 8px; - margin-top: 4px; -} - -#IpChooser { - border-color: #cccccc; -} - -#stackedWidget { - margin-top: 8px; - margin-bottom: 4px; -} - -#KiwixServerButton { - margin-right:8px; -} +QWidget { + background-color: white; +} + +QDialog { + border: 2px solid #cccccc; +} + +QPushButton { + opacity: 1; + border: none; + padding: 3px; + color: RoyalBlue; + outline: 0; +} + +QPushButton:hover { + color: rgba(65, 105, 225, 0.9); +} + +QLineEdit { + border: 1 solid #cccccc; + padding: 2px; +} + +QLabel { + margin-left: 8px; + margin-top: 4px; +} + +#IpChooser { + border-color: #cccccc; +} + +#stackedWidget { + margin-top: 8px; + margin-bottom: 4px; +} + +#KiwixServerButton { + margin-right:8px; +} diff --git a/resources/css/messageBox.css b/resources/css/messageBox.css index af4c48bf1..d9d5e57bd 100644 --- a/resources/css/messageBox.css +++ b/resources/css/messageBox.css @@ -1,39 +1,39 @@ -* { - font-family: 'Selawik'; - background-color: white; - font-size: 16px; -} - -QDialog { - border: 2px solid #cccccc; -} - -#confirmTitle { - font-size: 18px; - line-height: 44px; - font-weight: bold; -} - -QPushButton { - opacity: 1; - padding: 6px; - outline: 0; - border-radius: 2px; - width: 24px; - border: 1px solid #3366cc; - color: #3366cc; -} - -QPushButton:hover { - background-color: #3366cc; - color: white; -} - -#closeButton { - border: 0; -} - -#closeButton:hover { - background-color: transparent; -} - +* { + font-family: 'Selawik'; + background-color: white; + font-size: 16px; +} + +QDialog { + border: 2px solid #cccccc; +} + +#confirmTitle { + font-size: 18px; + line-height: 44px; + font-weight: bold; +} + +QPushButton { + opacity: 1; + padding: 6px; + outline: 0; + border-radius: 2px; + width: 24px; + border: 1px solid #3366cc; + color: #3366cc; +} + +QPushButton:hover { + background-color: #3366cc; + color: white; +} + +#closeButton { + border: 0; +} + +#closeButton:hover { + background-color: transparent; +} + diff --git a/resources/css/popup.css b/resources/css/popup.css index cb8114dcc..cd9226a07 100644 --- a/resources/css/popup.css +++ b/resources/css/popup.css @@ -1,42 +1,42 @@ -QTreeView { - background-color: white; - border: none; - outline: none; -} - -QTreeView::item { - border: 1px solid transparent; -} - -QTreeView::item:selected { - border: 1px solid #3366CC; - background-color: #D9E9FF; - color: black; -} - -QHeaderView { - background-color: white; -} - -QHeaderView::section { - border: none; - color: #3b3b3b; - background-color: white; - - margin-top: 5px; /* XXX: duplicated in css_constants.h */ - padding: 5px 10px; /* XXX: duplicated in css_constants.h */ - - font-size: 16px; - line-height: 24px; /* XXX: duplicated in css_constants.h */ - font-weight: 400; -} - -QScrollBar { - width: 5px; - border: none; - outline: none; -} - -QScrollBar::handle { - background-color: grey; +QTreeView { + background-color: white; + border: none; + outline: none; +} + +QTreeView::item { + border: 1px solid transparent; +} + +QTreeView::item:selected { + border: 1px solid #3366CC; + background-color: #D9E9FF; + color: black; +} + +QHeaderView { + background-color: white; +} + +QHeaderView::section { + border: none; + color: #3b3b3b; + background-color: white; + + margin-top: 5px; /* XXX: duplicated in css_constants.h */ + padding: 5px 10px; /* XXX: duplicated in css_constants.h */ + + font-size: 16px; + line-height: 24px; /* XXX: duplicated in css_constants.h */ + font-weight: 400; +} + +QScrollBar { + width: 5px; + border: none; + outline: none; +} + +QScrollBar::handle { + background-color: grey; } \ No newline at end of file diff --git a/resources/css/style.css b/resources/css/style.css index fc1a1824e..263d3b87c 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -1,556 +1,556 @@ -QWidget { - background-color: #EAECF0; - color: #000000; -} - - -/* ------------------------------------ - The topBar -*/ - -TopWidget { - min-height: 48px; - max-height: 48px; - margin: 0; - padding: 5px; - border: none; - border-bottom: 1px solid #ccc; -} - -TopWidget::separator { - background: none; - border: none; - width: 40px; -} - -QToolButton { - background: none; - height: 40px; - width: 40px; - border: none; -} - -SearchBar { - background-color: white; - margin: 2px; /* XXX: duplicated in css_constants.h */ - border: 1px solid #ccc; /* XXX: duplicated in css_constants.h */ - border-radius: 3px; - - max-height: 40px; -} - -SearchBar > QLabel#searchIcon { - padding: 0; - border: none; - background-color: none; - margin: 0px 6px; - - max-height: 38px; - max-width: 38px; -} - -SearchBar > SearchBarLineEdit { - background-color: white; - margin: 0; - padding: 0; - color: #666; - border: none; - font-size: 16px; - - max-height: 38px; -} - -SearchBar > QToolButton { - padding: 0; - border: 0 solid #fff; - background-color: none; - max-height: 38px; - max-width: 38px; -} - -SearchBar > QToolButton:pressed, -SearchBar > QToolButton:hover { - border: 1px solid #3366CC; - background-color: #D9E9FF; - border-radius: 3px; -} - -MultiZimButton QListWidget { - border: 0px; - outline: 0px; - padding: 5px 0px; /* XXX: duplicated in css_constants.h */ - background-color: white; -} - -MultiZimButton QListWidget::item { - padding: 0px 5px; /* XXX: duplicated in css_constants.h */ - border: 1px solid transparent; /* XXX: duplicated in css_constants.h */ - background-color: white; -} - -MultiZimButton QListWidget::item:hover, -MultiZimButton QListWidget::item:selected:active { - border: 1px solid #3366CC; - background-color: #D9E9FF; -} - -MultiZimButton QScrollBar { - width: 5px; /* XXX: duplicated in css_constants.h */ - border: none; - outline: none; -} - -MultiZimButton QScrollBar::handle { - background-color: grey; -} - -ZimItemWidget * { - background-color: transparent; -} - -ZimItemWidget QLabel { - font-size: 16px; - line-height: 24px; /* XXX: duplicated in css_constants.h */ -} - -ZimItemWidget QRadioButton::indicator { - image: none; -} - -ZimItemWidget QRadioButton::indicator:checked { - image: url(:/icons/tick.svg); -} - -TopWidget QToolButton:pressed, -TopWidget QToolButton::hover { - border: 1px solid #3366CC; - background-color: #D9E9FF; - border-radius: 3px; -} - -TopWidget QToolButton::menu-button { - width: 100%; - background: rgba(0, 0, 0, 0); -} - -TopWidget QToolButton::menu-arrow { - image: none; -} - -TopWidget QToolButton::menu-indicator { - width: 0; /* it can be hidden at all by setting 0px */ - height: 0; - subcontrol-origin: padding; -} - -TopWidget QToolButton#backButton { - margin-left: 6px; /* XXX: duplicated in css_constants.h */ -} - -TopWidget QToolButton#fullScreenButton { - margin-right: 6px; -} - - - -/* ---------------------------------------- - Menu -*/ - -QMenu { - border: none; - min-width: 320px; -} - -QMenu::item { - min-width: 320px; - min-height: 40px; - max-height: 40px; - border: 1px solid transparent; - padding: 2px 12px 2px 40px; /* top right bottom left */ -} - -QMenu::icon { - min-width: 40px; - min-height: 40px; -} - -QMenu::item:selected { - background-color: #D9E9FF; - border: 1px solid #3366CC; -} - - -MainMenu::indicator { - color: #666666; - width: 13px; - height: 13px; -} - -WebViewForwardMenu::item, WebViewBackMenu::item { - padding: 2px 12px; -} - -WebViewForwardMenu::icon, WebViewBackMenu::icon { - min-width: 2px; - min-height: 2px; -} - - -/* ----------------------------------------- - TabWidget -*/ - -QTabBar { - font-size: 16px; - icon-size: 24px; - border-bottom: 1px solid #ccc; -} - -QTabBar::tear { - width: 0px; - height: 0px; - border: none; -} - -QTabWidget::pane { - top: -2px; - border: none; - border-top: 1px solid #ccc; -} - -QTabBar::tab { - border: 1px solid #ccc; /* XXX: duplicated in css_constants.h */ - border-radius: 0; - padding: 4px; /* XXX: duplicated in css_constants.h */ - padding-top: 6px; -} - -QTabBar::tab:selected { - background-color: white; - border-bottom: 1px solid white; -} - -QTabBar::tab:first { - padding-top: 6px; - padding-left: 5px; -} - -#closeTabButton, #newTabButton { - font-size: 30px; - width: 30px; min-width: 30px; max-width: 30px; - height: 30px; min-height:30px; max-height:30px; - border-radius: 3px; -} - -#nextTabButton, #prevTabButton, #newTabSideButton { - border: 1px solid #ccc; -} - -#closeTabButton:hover, #newTabButton:hover, -#nextTabButton:hover, #prevTabButton:hover, -#newTabSideButton:hover { - border: 1px solid #3366CC; - background-color: #D9E9FF; - border-radius: 3px; -} - -QTabBar::scroller { - width: 0px; - height: 0px; - - /* Last tab size is off by 1 if border not set. */ - border: 1px solid transparent; -} - -/* ----------------------------------------- - TabWidget -*/ - -#aboutText { - padding: 20px; - background-color: white; -} - -/* ---------------------------------------- - Find Search page -*/ - -#FindInPageBar { - border-top: 1px solid #ccc; -} - -#findEdit { - background-color: white; - padding: 2px; - max-height: 36px; - color: #666; - font-size: 16px; - border: 1px solid #ccc; - border-radius: 0; -} - -#fNextButton, -#fPreviousButton, -#hideButton { - outline: none; - max-height: 36px; - max-width: 36px; - border: 1px solid #ccc; - border-radius: 0; -} - -#fNextButton:pressed, -#fPreviousButton:pressed, -#hideButton:pressed { - background-color: #D9E9FF; - border: 1px solid #3366CC; -} - -/* ---------------------------------------- - Find Search page -*/ - -#contentmanagerside QWidget { - background-color: white; - outline: none; -} - -#contentmanagerside QRadioButton, -#contentmanagerside QCheckBox { - padding: 10px; -} - -#contentmanagerside QRadioButton:checked { - border: none; -} - -#contentmanagerside QRadioButton::indicator { - image: none; -} - -#contentmanagerside QCheckBox::indicator { - image: none; -} - -#contentmanagerside QListWidget { - border: none; - padding-left: 30px; - show-decoration-selected: 0; -} - -#contentTypeSelector QCheckBox { - padding: 0; -} - -#categorySelector QScrollBar, -#languageSelector QScrollBar { - width: 5px; - border: none; - outline: none; -} - -#categorySelector QScrollBar::handle, -#languageSelector QScrollBar::handle { - background-color: grey; -} - -#categorySelector::item:selected, -#languageSelector::item:selected { - background-color: white; - color: black; - outline: none; -} - -#contentTypeAllButton, -ContentTypeFilter { - spacing: 10; -} - - -#tableofcontentbar { - background-color: white; -} - -#tableofcontentbar QTreeWidget, -#tableofcontentbar QLabel, -#tableofcontentbar QFrame { - background-color: white; -} - -#tableofcontentbar QTreeWidget { - outline: none; -} - -#tableofcontentbar QTreeWidget::item { - height: 26px; - padding: 0px 10px; - outline: none; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; -} - -#tableofcontentbar QTreeWidget::item:selected, -#tableofcontentbar QTreeWidget::item:hover { - outline: none; - border-top: 1px solid #3366CC; - border-bottom: 1px solid #3366CC; - background-color: #D9E9FF; - color: black; -} - -#tableofcontentbar QTreeWidget::branch:selected, -#tableofcontentbar QTreeWidget::branch:hover { - outline: none; - border-top: 1px solid #3366CC; - border-bottom: 1px solid #3366CC; - background-color: #D9E9FF; -} - -#tableofcontentbar QTreeWidget::branch { - image: none; -} - -#tableofcontentbar #titleLabel { - padding: 0px; - margin: 10px; -} - -#tableofcontentbar #hideLabel { - margin: 13px 10px 10px; /* 3px to match bottom with titleLabel */ -} - -#tableofcontentbar QScrollBar { - width: 5px; - border: none; - outline: none; -} - -#tableofcontentbar QScrollBar::handle { - background-color: grey; -} - -/*this selector worked after changing class ReadingListBar to a QFrame instead of QWidget in readinglistbar.h file*/ -#readinglistbar { - background-color: white; -} - -#readinglistbar QPushButton::menu-indicator { - width: 0; - height: 0; -} - -#readinglistbar QTreeWidget, -#readinglistbar QLabel, -#readinglistbar QFrame, -#readinglistbar QListWidget, -#readinglistbar QPushButton { - background-color: white; -} - -#readinglistbar QPushButton{ - margin: 13px 10px 10px; -} - -#readinglistbar QListWidget { - outline: none; -} - -#readinglistbar QListWidget::item, -#readinglistbar QListWidget::item:selected{ - height: 26px; - padding: 0px 10px; - outline: none; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - color: black; -} - -#readinglistbar QListWidget::item:hover { - outline: none; - border-top: 1px solid #3366CC; - border-bottom: 1px solid #3366CC; - background-color: #D9E9FF; - color: black; -} - -#readinglistbar #titleLabel { - padding: 0px; - margin: 10px; -} - -#readinglistbar QScrollBar { - width: 5px; - border: none; - outline: none; -} - -#readinglistbar QScrollBar::handle { - background-color: grey; -} - -#readinglistbar QLabel#titleLabel { - font-family: "Selawik"; - font-size: 24px; /*equivalent to font-size 18*/ - font-weight: 500; /*equivalent to font-weight medium*/ -} -/* ---------------------------------------- - Text to Speech Page -*/ - -TextToSpeechBar { - border-top: 1px solid #ccc; -} - -TextToSpeechBar #closeButton { - outline: none; - max-height: 36px; - max-width: 36px; - border: 1px solid #ccc; - border-radius: 0; -} - -TextToSpeechBar #stopButton { - outline: none; - max-height: 36px; - padding: 0px 10px; - - background-color: white; - border: 1px solid #ccc; - border-radius: 0; -} - -TextToSpeechBar #stopButton:hover { - background-color: #D9E9FF; - border: 1px solid #3366CC; -} - -TextToSpeechBar #stopButton:disabled { - color: grey; - background-color: darkgrey; -} - -TextToSpeechBar QComboBox::drop-down { - height: 22px; - width: 15px; - padding: 0px 2px; - margin: 0px; - - image: url(":/icons/drop-down.svg"); - background-color: white; -} - -TextToSpeechBar QComboBox { - background-color: white; - border: 1px solid #ccc; -} - -TextToSpeechBar QAbstractItemView QScrollBar { - width: 5px; - border: none; - outline: none; -} - -TextToSpeechBar QAbstractItemView QScrollBar::handle { - background-color: grey; -} +QWidget { + background-color: #EAECF0; + color: #000000; +} + + +/* ------------------------------------ + The topBar +*/ + +TopWidget { + min-height: 48px; + max-height: 48px; + margin: 0; + padding: 5px; + border: none; + border-bottom: 1px solid #ccc; +} + +TopWidget::separator { + background: none; + border: none; + width: 40px; +} + +QToolButton { + background: none; + height: 40px; + width: 40px; + border: none; +} + +SearchBar { + background-color: white; + margin: 2px; /* XXX: duplicated in css_constants.h */ + border: 1px solid #ccc; /* XXX: duplicated in css_constants.h */ + border-radius: 3px; + + max-height: 40px; +} + +SearchBar > QLabel#searchIcon { + padding: 0; + border: none; + background-color: none; + margin: 0px 6px; + + max-height: 38px; + max-width: 38px; +} + +SearchBar > SearchBarLineEdit { + background-color: white; + margin: 0; + padding: 0; + color: #666; + border: none; + font-size: 16px; + + max-height: 38px; +} + +SearchBar > QToolButton { + padding: 0; + border: 0 solid #fff; + background-color: none; + max-height: 38px; + max-width: 38px; +} + +SearchBar > QToolButton:pressed, +SearchBar > QToolButton:hover { + border: 1px solid #3366CC; + background-color: #D9E9FF; + border-radius: 3px; +} + +MultiZimButton QListWidget { + border: 0px; + outline: 0px; + padding: 5px 0px; /* XXX: duplicated in css_constants.h */ + background-color: white; +} + +MultiZimButton QListWidget::item { + padding: 0px 5px; /* XXX: duplicated in css_constants.h */ + border: 1px solid transparent; /* XXX: duplicated in css_constants.h */ + background-color: white; +} + +MultiZimButton QListWidget::item:hover, +MultiZimButton QListWidget::item:selected:active { + border: 1px solid #3366CC; + background-color: #D9E9FF; +} + +MultiZimButton QScrollBar { + width: 5px; /* XXX: duplicated in css_constants.h */ + border: none; + outline: none; +} + +MultiZimButton QScrollBar::handle { + background-color: grey; +} + +ZimItemWidget * { + background-color: transparent; +} + +ZimItemWidget QLabel { + font-size: 16px; + line-height: 24px; /* XXX: duplicated in css_constants.h */ +} + +ZimItemWidget QRadioButton::indicator { + image: none; +} + +ZimItemWidget QRadioButton::indicator:checked { + image: url(:/icons/tick.svg); +} + +TopWidget QToolButton:pressed, +TopWidget QToolButton::hover { + border: 1px solid #3366CC; + background-color: #D9E9FF; + border-radius: 3px; +} + +TopWidget QToolButton::menu-button { + width: 100%; + background: rgba(0, 0, 0, 0); +} + +TopWidget QToolButton::menu-arrow { + image: none; +} + +TopWidget QToolButton::menu-indicator { + width: 0; /* it can be hidden at all by setting 0px */ + height: 0; + subcontrol-origin: padding; +} + +TopWidget QToolButton#backButton { + margin-left: 6px; /* XXX: duplicated in css_constants.h */ +} + +TopWidget QToolButton#fullScreenButton { + margin-right: 6px; +} + + + +/* ---------------------------------------- + Menu +*/ + +QMenu { + border: none; + min-width: 320px; +} + +QMenu::item { + min-width: 320px; + min-height: 40px; + max-height: 40px; + border: 1px solid transparent; + padding: 2px 12px 2px 40px; /* top right bottom left */ +} + +QMenu::icon { + min-width: 40px; + min-height: 40px; +} + +QMenu::item:selected { + background-color: #D9E9FF; + border: 1px solid #3366CC; +} + + +MainMenu::indicator { + color: #666666; + width: 13px; + height: 13px; +} + +WebViewForwardMenu::item, WebViewBackMenu::item { + padding: 2px 12px; +} + +WebViewForwardMenu::icon, WebViewBackMenu::icon { + min-width: 2px; + min-height: 2px; +} + + +/* ----------------------------------------- + TabWidget +*/ + +QTabBar { + font-size: 16px; + icon-size: 24px; + border-bottom: 1px solid #ccc; +} + +QTabBar::tear { + width: 0px; + height: 0px; + border: none; +} + +QTabWidget::pane { + top: -2px; + border: none; + border-top: 1px solid #ccc; +} + +QTabBar::tab { + border: 1px solid #ccc; /* XXX: duplicated in css_constants.h */ + border-radius: 0; + padding: 4px; /* XXX: duplicated in css_constants.h */ + padding-top: 6px; +} + +QTabBar::tab:selected { + background-color: white; + border-bottom: 1px solid white; +} + +QTabBar::tab:first { + padding-top: 6px; + padding-left: 5px; +} + +#closeTabButton, #newTabButton { + font-size: 30px; + width: 30px; min-width: 30px; max-width: 30px; + height: 30px; min-height:30px; max-height:30px; + border-radius: 3px; +} + +#nextTabButton, #prevTabButton, #newTabSideButton { + border: 1px solid #ccc; +} + +#closeTabButton:hover, #newTabButton:hover, +#nextTabButton:hover, #prevTabButton:hover, +#newTabSideButton:hover { + border: 1px solid #3366CC; + background-color: #D9E9FF; + border-radius: 3px; +} + +QTabBar::scroller { + width: 0px; + height: 0px; + + /* Last tab size is off by 1 if border not set. */ + border: 1px solid transparent; +} + +/* ----------------------------------------- + TabWidget +*/ + +#aboutText { + padding: 20px; + background-color: white; +} + +/* ---------------------------------------- + Find Search page +*/ + +#FindInPageBar { + border-top: 1px solid #ccc; +} + +#findEdit { + background-color: white; + padding: 2px; + max-height: 36px; + color: #666; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 0; +} + +#fNextButton, +#fPreviousButton, +#hideButton { + outline: none; + max-height: 36px; + max-width: 36px; + border: 1px solid #ccc; + border-radius: 0; +} + +#fNextButton:pressed, +#fPreviousButton:pressed, +#hideButton:pressed { + background-color: #D9E9FF; + border: 1px solid #3366CC; +} + +/* ---------------------------------------- + Find Search page +*/ + +#contentmanagerside QWidget { + background-color: white; + outline: none; +} + +#contentmanagerside QRadioButton, +#contentmanagerside QCheckBox { + padding: 10px; +} + +#contentmanagerside QRadioButton:checked { + border: none; +} + +#contentmanagerside QRadioButton::indicator { + image: none; +} + +#contentmanagerside QCheckBox::indicator { + image: none; +} + +#contentmanagerside QListWidget { + border: none; + padding-left: 30px; + show-decoration-selected: 0; +} + +#contentTypeSelector QCheckBox { + padding: 0; +} + +#categorySelector QScrollBar, +#languageSelector QScrollBar { + width: 5px; + border: none; + outline: none; +} + +#categorySelector QScrollBar::handle, +#languageSelector QScrollBar::handle { + background-color: grey; +} + +#categorySelector::item:selected, +#languageSelector::item:selected { + background-color: white; + color: black; + outline: none; +} + +#contentTypeAllButton, +ContentTypeFilter { + spacing: 10; +} + + +#tableofcontentbar { + background-color: white; +} + +#tableofcontentbar QTreeWidget, +#tableofcontentbar QLabel, +#tableofcontentbar QFrame { + background-color: white; +} + +#tableofcontentbar QTreeWidget { + outline: none; +} + +#tableofcontentbar QTreeWidget::item { + height: 26px; + padding: 0px 10px; + outline: none; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; +} + +#tableofcontentbar QTreeWidget::item:selected, +#tableofcontentbar QTreeWidget::item:hover { + outline: none; + border-top: 1px solid #3366CC; + border-bottom: 1px solid #3366CC; + background-color: #D9E9FF; + color: black; +} + +#tableofcontentbar QTreeWidget::branch:selected, +#tableofcontentbar QTreeWidget::branch:hover { + outline: none; + border-top: 1px solid #3366CC; + border-bottom: 1px solid #3366CC; + background-color: #D9E9FF; +} + +#tableofcontentbar QTreeWidget::branch { + image: none; +} + +#tableofcontentbar #titleLabel { + padding: 0px; + margin: 10px; +} + +#tableofcontentbar #hideLabel { + margin: 13px 10px 10px; /* 3px to match bottom with titleLabel */ +} + +#tableofcontentbar QScrollBar { + width: 5px; + border: none; + outline: none; +} + +#tableofcontentbar QScrollBar::handle { + background-color: grey; +} + +/*this selector worked after changing class ReadingListBar to a QFrame instead of QWidget in readinglistbar.h file*/ +#readinglistbar { + background-color: white; +} + +#readinglistbar QPushButton::menu-indicator { + width: 0; + height: 0; +} + +#readinglistbar QTreeWidget, +#readinglistbar QLabel, +#readinglistbar QFrame, +#readinglistbar QListWidget, +#readinglistbar QPushButton { + background-color: white; +} + +#readinglistbar QPushButton{ + margin: 13px 10px 10px; +} + +#readinglistbar QListWidget { + outline: none; +} + +#readinglistbar QListWidget::item, +#readinglistbar QListWidget::item:selected{ + height: 26px; + padding: 0px 10px; + outline: none; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + color: black; +} + +#readinglistbar QListWidget::item:hover { + outline: none; + border-top: 1px solid #3366CC; + border-bottom: 1px solid #3366CC; + background-color: #D9E9FF; + color: black; +} + +#readinglistbar #titleLabel { + padding: 0px; + margin: 10px; +} + +#readinglistbar QScrollBar { + width: 5px; + border: none; + outline: none; +} + +#readinglistbar QScrollBar::handle { + background-color: grey; +} + +#readinglistbar QLabel#titleLabel { + font-family: "Selawik"; + font-size: 24px; /*equivalent to font-size 18*/ + font-weight: 500; /*equivalent to font-weight medium*/ +} +/* ---------------------------------------- + Text to Speech Page +*/ + +TextToSpeechBar { + border-top: 1px solid #ccc; +} + +TextToSpeechBar #closeButton { + outline: none; + max-height: 36px; + max-width: 36px; + border: 1px solid #ccc; + border-radius: 0; +} + +TextToSpeechBar #stopButton { + outline: none; + max-height: 36px; + padding: 0px 10px; + + background-color: white; + border: 1px solid #ccc; + border-radius: 0; +} + +TextToSpeechBar #stopButton:hover { + background-color: #D9E9FF; + border: 1px solid #3366CC; +} + +TextToSpeechBar #stopButton:disabled { + color: grey; + background-color: darkgrey; +} + +TextToSpeechBar QComboBox::drop-down { + height: 22px; + width: 15px; + padding: 0px 2px; + margin: 0px; + + image: url(":/icons/drop-down.svg"); + background-color: white; +} + +TextToSpeechBar QComboBox { + background-color: white; + border: 1px solid #ccc; +} + +TextToSpeechBar QAbstractItemView QScrollBar { + width: 5px; + border: none; + outline: none; +} + +TextToSpeechBar QAbstractItemView QScrollBar::handle { + background-color: grey; +} diff --git a/resources/fonts/Selawik/LICENSE.txt b/resources/fonts/Selawik/LICENSE.txt index ac8fdb956..57e0bf2c4 100644 --- a/resources/fonts/Selawik/LICENSE.txt +++ b/resources/fonts/Selawik/LICENSE.txt @@ -1,91 +1,91 @@ -Copyright 2015, Microsoft Corporation (www.microsoft.com), with Reserved Font Name Selawik. All Rights Reserved. Selawik is a trademark of Microsoft Corporation in the United States and/or other countries. - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. +Copyright 2015, Microsoft Corporation (www.microsoft.com), with Reserved Font Name Selawik. All Rights Reserved. Selawik is a trademark of Microsoft Corporation in the United States and/or other countries. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/resources/generate_translation_qrc.py b/resources/generate_translation_qrc.py index 12957e626..38f5dc385 100755 --- a/resources/generate_translation_qrc.py +++ b/resources/generate_translation_qrc.py @@ -1,46 +1,46 @@ -#!/usr/bin/env python3 -# Copyright (c) 2020 Matthieu Gautier -# -# This file is part of kiwix-desktop. -# -# 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 . - -from pathlib import Path -import xml.etree.ElementTree as ET -import xml.etree - -script_path = Path(__file__) - -out_qrc_file = script_path.parent / "translations.qrc" -translation_dir = script_path.parent / "i18n" - -root = ET.Element("RCC") -root.text = "\n " -qresource = ET.SubElement(root, "qresource") -qresource.set("prefix", "/") -qresource.text = "\n" + " " * 8 -qresource.tail = "\n" -json_files = translation_dir.glob("*.json") - -node = None -for json in sorted(translation_dir.glob("*.json")): - if node is not None: - node.tail += " " * 8 - node = ET.SubElement(qresource, "file") - node.text = str(json.relative_to(script_path.parent)) - node.tail = "\n" - -node.tail += " "*4 - -out_qrc_file.write_bytes(ET.tostring(root, encoding='utf8')) +#!/usr/bin/env python3 +# Copyright (c) 2020 Matthieu Gautier +# +# This file is part of kiwix-desktop. +# +# 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 . + +from pathlib import Path +import xml.etree.ElementTree as ET +import xml.etree + +script_path = Path(__file__) + +out_qrc_file = script_path.parent / "translations.qrc" +translation_dir = script_path.parent / "i18n" + +root = ET.Element("RCC") +root.text = "\n " +qresource = ET.SubElement(root, "qresource") +qresource.set("prefix", "/") +qresource.text = "\n" + " " * 8 +qresource.tail = "\n" +json_files = translation_dir.glob("*.json") + +node = None +for json in sorted(translation_dir.glob("*.json")): + if node is not None: + node.tail += " " * 8 + node = ET.SubElement(qresource, "file") + node.text = str(json.relative_to(script_path.parent)) + node.tail = "\n" + +node.tail += " "*4 + +out_qrc_file.write_bytes(ET.tostring(root, encoding='utf8')) diff --git a/resources/i18n/ar.json b/resources/i18n/ar.json index da26e20e5..9a2917984 100644 --- a/resources/i18n/ar.json +++ b/resources/i18n/ar.json @@ -1,140 +1,140 @@ -{ - "@metadata": { - "authors": [ - "Hamoudak", - "Hanzo64", - "LaMagiaaa", - "McDutchie", - "Meno25", - "Mohammed Qays", - "Ravan", - "Tala Ali", - "بهتين شوكت", - "علاء", - "محمد أحمد عبد الفتاح" - ] - }, - "name": "الإنجليزية {اللغة الإنجليزية}", - "error-title": "خطأ", - "error-downloader-window-title": "لا يمكن إنشاء برنامج التنزيل", - "error-downloader-launch-message": "من المستحيل تشغيل برنامج التنزيل، سيبدأ تطبيق Kiwix-desktop لكن جميع وظائف التنزيل لن تعمل!", - "error-launch-server-message": "حدث خطأ!", - "error-archive": "لا يمكن الوصول إلي الأرشيف", - "open-zim": "افتح ملف ZIM", - "local-kiwix-server": "خادم Kiwix المحلي", - "random-article": "مقال عشوائي {غير محدد}", - "random-article-error": "فشل في فتح مقال عشوائي.", - "home-page": "الصفحة الرئيسية", - "main-menu": "القائمة الرئيسة", - "print": "اطبع", - "print-page-error": "حدث خطأ أثناء الطباعة.", - "new-tab": "تبويب جديد", - "close-tab": "إغلاق علامة التبويب", - "close": "إغلاق", - "reopen-closed-tab": "أعد فتح علامة التبويب المغلقة", - "browse-library": "تصفح المكتبة", - "open-file": "افتح ملفًا", - "open-recent": "فتح الأحدث", - "search-article": "البحث في المقالة", - "search-in-library": "ابحث في المكتبة", - "find-in-page": "البحث في الصفحة", - "set-fullscreen": "ضبط ملء الشاشة", - "quit-fullscreen": "غادر وضع الشاشة الكاملة", - "table-of-content": "قائمة المحتويات", - "reading-list": "قائمة القراءة", - "zoom-in": "تكبير", - "zoom-out": "تصغير", - "zoom-reset": "إعادة تعيين التكبير", - "help": "المساعدة", - "feedback": "تعليقات", - "report-a-bug": "الإبلاغ عن خطأ", - "request-a-feature": "طلب ميزة", - "about-kiwix": "عن كيويكس", - "donate-to-support-kiwix": "التبرع لدعم كيويكس", - "exit": "خروج {إنهاء}", - "save-file-as-window-title": "حفظ الملف باسم", - "download-finished-message": "تم تنزيل المستند.", - "file": "الملف", - "edit": "تعديل", - "view": "عرض", - "tools": "الأدوات", - "window-title": "مكتبة - كيويكس", - "search": "بحث", - "browse-directory": "تصفح الدليل", - "settings": "الإعدادات", - "back": "رجوع", - "forward": "إعادة توجيه", - "search-files": "البحث في الملفات", - "title": "العنوان", - "size": "الحجم", - "date": "التاريخ", - "content-type": "نوع المحتوى", - "reset-sort": "إعادة الترتيب", - "open": "فتح", - "delete": "حذف", - "download": "تنزيل", - "resume": "استئناف", - "pause": "إيقاف مؤقت", - "cancel": "إلغاء", - "apply": "تطبيق", - "port-for-local-kiwix-server-setting": "منفذ لخادم كيويكس المحلي", - "zoom-level-setting": "مستوى التكبير الافتراضي", - "download-directory-setting": "دليل التنزيل", - "monitor-directory-setting": "دليل المراقبة", - "reset": "إعادة الضبط", - "clear": "مسح", - "browse": "تصفح", - "about-kiwix-desktop-title": "سطح المكتب كيويكس", - "about-kiwix-desktop-description": "يتيح لك تطبيق Kiwix الوصول إلى كل ما يتعلق بموسوعة ويكيبيديا أينما ذهبت! سواء كنت على متن قارب أو في مكان ناءٍ أو في سجن، يتيح لك تطبيق Kiwix الوصول إلى كل المعارف البشرية. لست بحاجة إلى الإنترنت، فكل شيء مخزن على جهاز الكمبيوتر الخاص بك.", - "about-learn-more-about-kiwix": "لمعرفة المزيد عن كيويكس", - "about-release-title": "الإصدار", - "about-source-code": "تم إصدار هذا البرنامج بموجب شروط GNU General Public License الإصدار 3. اعرض الكود المصدري على Github .", - "about-version": "النسخة: {{VERSION}}", - "about-reporting-problem-title": "الإبلاغ عن المشاكل", - "about-report-issue": "لفتح موضوع، قم بزيارة متعقب مشاكل برنامج كيويكس - سطح المكتب Kiwix وافتح موضوع تصف فيه مشكلتك.", - "about-report-issue-2": "يرجي ذكر نسخة الإصدار في الموضوع", - "about-libraries-title": "مكتبات", - "kiwix-server-running-message": "خادم كيويكس قيد التشغيل يمكنك الإتصال به في الشبكة المحلية علي:", - "kiwix-server-description": "يسمح بدء تشغيل الخادم لأجهزة الكمبيوتر الأخرى في الشبكة المحلية بالوصول إلى مكتبة كيويكس الخاصة بك باستخدام مستعرض ويب قياسي.", - "fullscreen-notification": "أنت الآن في وضع ملء الشاشة. اضغط على ESC للإنهاء!", - "local-files": "ملفات محلية", - "hide": "إخفاء", - "open-in-browser": "افتح في المتصفح", - "start-kiwix-server": "{بدء} تشغيل خادم كيويكس", - "stop-kiwix-server": "إيقاف التشغيل \n{إيقاف خادم كيويكس}", - "all": "الكل", - "fulltext-search": "بحث في النص الكامل {كامل النص}", - "pictures": "الصور", - "videos": "مقاطع فيديو", - "ftindex": "فهرس النص الكامل {كامل النص}", - "details": "المقال كامل", - "yes": "نعم", - "no": "لا", - "no-filter": "لا يوجد مرشح", - "open-link-in-web-browser": "فتح الرابط في متصفح الشبكة", - "download-dir-dialog-title": "هل أنت متأكد أنك تريد تغيير دليل التنزيل؟", - "download-dir-dialog-msg": "سيكون مسار دليل التنزيل الجديد: {{DIRECTORY}}", - "invalid-port": "منفذ غير صالح", - "zim-open-fail-title": "ملف غير صالح { غير صحيح}", - "zim-open-fail-text": "لا يمكن فتح ملف ZIM {{ZIM}} بشكل صحيح. ستتم إزالته من مكتبتك.", - "monitor-dir-dialog-title": "هل أنت متأكد أنك تريد تغيير دليل الشاشة؟", - "monitor-dir-dialog-msg": "سيكون مسار دليل الشاشة الجديد هو:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "هل أنت متأكد أنك تريد مسح دليل الشاشة؟", - "path-was-copied": "تم نسخ المسار", - "monitor-clear-dir-dialog-msg": "سيؤدي هذا إلى إيقاف فحص دليل الشاشة لملفات ZIM الجديدة.", - "monitor-directory-tooltip": "ستتم إضافة جميع ملفات ZIM الموجودة في هذا الدليل تلقائيًا إلى المكتبة.", - "next-tab": "انتقل إلى علامة التبويب التالية", - "previous-tab": "انتقل إلى علامة التبويب السابقة", - "cancel-download": "إلغاء {التحميل} التنزيل", - "file-not-found-title": "لم يتم العثور علي ملف ZIM", - "file-not-found-text": "ملف ZIM غير موجود أو غير قابل للقراءة", - "zim-id": "معرف زيم", - "zim-name": "اسم زيم", - "zim-path": "مسار ملف ZIM", - "bad-zim-file-error-page-title": "ملف ZIM غير صالح", - "bad-zim-file-error-page-text": "ملف ZIM غير صالح أو تالف.", - "import-reading-list-error": "حدث خطأ أثناء استيراد قائمة القراءة.", - "disable-sandbox": "تم تشغيل kiwix من أحد محركات الشبكة، ومعلوم أن هذا قد يتسبب في حدوث مشاكل في التوافق مع بيئة المتصفح الآمن. ولذلك سيتم تعطيل التصفح الآمن. هل تريد المتابعة؟", - "stop": "إيقاف", - "voice": "صوت" -} +{ + "@metadata": { + "authors": [ + "Hamoudak", + "Hanzo64", + "LaMagiaaa", + "McDutchie", + "Meno25", + "Mohammed Qays", + "Ravan", + "Tala Ali", + "بهتين شوكت", + "علاء", + "محمد أحمد عبد الفتاح" + ] + }, + "name": "الإنجليزية {اللغة الإنجليزية}", + "error-title": "خطأ", + "error-downloader-window-title": "لا يمكن إنشاء برنامج التنزيل", + "error-downloader-launch-message": "من المستحيل تشغيل برنامج التنزيل، سيبدأ تطبيق Kiwix-desktop لكن جميع وظائف التنزيل لن تعمل!", + "error-launch-server-message": "حدث خطأ!", + "error-archive": "لا يمكن الوصول إلي الأرشيف", + "open-zim": "افتح ملف ZIM", + "local-kiwix-server": "خادم Kiwix المحلي", + "random-article": "مقال عشوائي {غير محدد}", + "random-article-error": "فشل في فتح مقال عشوائي.", + "home-page": "الصفحة الرئيسية", + "main-menu": "القائمة الرئيسة", + "print": "اطبع", + "print-page-error": "حدث خطأ أثناء الطباعة.", + "new-tab": "تبويب جديد", + "close-tab": "إغلاق علامة التبويب", + "close": "إغلاق", + "reopen-closed-tab": "أعد فتح علامة التبويب المغلقة", + "browse-library": "تصفح المكتبة", + "open-file": "افتح ملفًا", + "open-recent": "فتح الأحدث", + "search-article": "البحث في المقالة", + "search-in-library": "ابحث في المكتبة", + "find-in-page": "البحث في الصفحة", + "set-fullscreen": "ضبط ملء الشاشة", + "quit-fullscreen": "غادر وضع الشاشة الكاملة", + "table-of-content": "قائمة المحتويات", + "reading-list": "قائمة القراءة", + "zoom-in": "تكبير", + "zoom-out": "تصغير", + "zoom-reset": "إعادة تعيين التكبير", + "help": "المساعدة", + "feedback": "تعليقات", + "report-a-bug": "الإبلاغ عن خطأ", + "request-a-feature": "طلب ميزة", + "about-kiwix": "عن كيويكس", + "donate-to-support-kiwix": "التبرع لدعم كيويكس", + "exit": "خروج {إنهاء}", + "save-file-as-window-title": "حفظ الملف باسم", + "download-finished-message": "تم تنزيل المستند.", + "file": "الملف", + "edit": "تعديل", + "view": "عرض", + "tools": "الأدوات", + "window-title": "مكتبة - كيويكس", + "search": "بحث", + "browse-directory": "تصفح الدليل", + "settings": "الإعدادات", + "back": "رجوع", + "forward": "إعادة توجيه", + "search-files": "البحث في الملفات", + "title": "العنوان", + "size": "الحجم", + "date": "التاريخ", + "content-type": "نوع المحتوى", + "reset-sort": "إعادة الترتيب", + "open": "فتح", + "delete": "حذف", + "download": "تنزيل", + "resume": "استئناف", + "pause": "إيقاف مؤقت", + "cancel": "إلغاء", + "apply": "تطبيق", + "port-for-local-kiwix-server-setting": "منفذ لخادم كيويكس المحلي", + "zoom-level-setting": "مستوى التكبير الافتراضي", + "download-directory-setting": "دليل التنزيل", + "monitor-directory-setting": "دليل المراقبة", + "reset": "إعادة الضبط", + "clear": "مسح", + "browse": "تصفح", + "about-kiwix-desktop-title": "سطح المكتب كيويكس", + "about-kiwix-desktop-description": "يتيح لك تطبيق Kiwix الوصول إلى كل ما يتعلق بموسوعة ويكيبيديا أينما ذهبت! سواء كنت على متن قارب أو في مكان ناءٍ أو في سجن، يتيح لك تطبيق Kiwix الوصول إلى كل المعارف البشرية. لست بحاجة إلى الإنترنت، فكل شيء مخزن على جهاز الكمبيوتر الخاص بك.", + "about-learn-more-about-kiwix": "لمعرفة المزيد عن كيويكس", + "about-release-title": "الإصدار", + "about-source-code": "تم إصدار هذا البرنامج بموجب شروط GNU General Public License الإصدار 3. اعرض الكود المصدري على Github .", + "about-version": "النسخة: {{VERSION}}", + "about-reporting-problem-title": "الإبلاغ عن المشاكل", + "about-report-issue": "لفتح موضوع، قم بزيارة متعقب مشاكل برنامج كيويكس - سطح المكتب Kiwix وافتح موضوع تصف فيه مشكلتك.", + "about-report-issue-2": "يرجي ذكر نسخة الإصدار في الموضوع", + "about-libraries-title": "مكتبات", + "kiwix-server-running-message": "خادم كيويكس قيد التشغيل يمكنك الإتصال به في الشبكة المحلية علي:", + "kiwix-server-description": "يسمح بدء تشغيل الخادم لأجهزة الكمبيوتر الأخرى في الشبكة المحلية بالوصول إلى مكتبة كيويكس الخاصة بك باستخدام مستعرض ويب قياسي.", + "fullscreen-notification": "أنت الآن في وضع ملء الشاشة. اضغط على ESC للإنهاء!", + "local-files": "ملفات محلية", + "hide": "إخفاء", + "open-in-browser": "افتح في المتصفح", + "start-kiwix-server": "{بدء} تشغيل خادم كيويكس", + "stop-kiwix-server": "إيقاف التشغيل \n{إيقاف خادم كيويكس}", + "all": "الكل", + "fulltext-search": "بحث في النص الكامل {كامل النص}", + "pictures": "الصور", + "videos": "مقاطع فيديو", + "ftindex": "فهرس النص الكامل {كامل النص}", + "details": "المقال كامل", + "yes": "نعم", + "no": "لا", + "no-filter": "لا يوجد مرشح", + "open-link-in-web-browser": "فتح الرابط في متصفح الشبكة", + "download-dir-dialog-title": "هل أنت متأكد أنك تريد تغيير دليل التنزيل؟", + "download-dir-dialog-msg": "سيكون مسار دليل التنزيل الجديد: {{DIRECTORY}}", + "invalid-port": "منفذ غير صالح", + "zim-open-fail-title": "ملف غير صالح { غير صحيح}", + "zim-open-fail-text": "لا يمكن فتح ملف ZIM {{ZIM}} بشكل صحيح. ستتم إزالته من مكتبتك.", + "monitor-dir-dialog-title": "هل أنت متأكد أنك تريد تغيير دليل الشاشة؟", + "monitor-dir-dialog-msg": "سيكون مسار دليل الشاشة الجديد هو:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "هل أنت متأكد أنك تريد مسح دليل الشاشة؟", + "path-was-copied": "تم نسخ المسار", + "monitor-clear-dir-dialog-msg": "سيؤدي هذا إلى إيقاف فحص دليل الشاشة لملفات ZIM الجديدة.", + "monitor-directory-tooltip": "ستتم إضافة جميع ملفات ZIM الموجودة في هذا الدليل تلقائيًا إلى المكتبة.", + "next-tab": "انتقل إلى علامة التبويب التالية", + "previous-tab": "انتقل إلى علامة التبويب السابقة", + "cancel-download": "إلغاء {التحميل} التنزيل", + "file-not-found-title": "لم يتم العثور علي ملف ZIM", + "file-not-found-text": "ملف ZIM غير موجود أو غير قابل للقراءة", + "zim-id": "معرف زيم", + "zim-name": "اسم زيم", + "zim-path": "مسار ملف ZIM", + "bad-zim-file-error-page-title": "ملف ZIM غير صالح", + "bad-zim-file-error-page-text": "ملف ZIM غير صالح أو تالف.", + "import-reading-list-error": "حدث خطأ أثناء استيراد قائمة القراءة.", + "disable-sandbox": "تم تشغيل kiwix من أحد محركات الشبكة، ومعلوم أن هذا قد يتسبب في حدوث مشاكل في التوافق مع بيئة المتصفح الآمن. ولذلك سيتم تعطيل التصفح الآمن. هل تريد المتابعة؟", + "stop": "إيقاف", + "voice": "صوت" +} diff --git a/resources/i18n/bcl.json b/resources/i18n/bcl.json index d8b8c36c3..8e5c63055 100644 --- a/resources/i18n/bcl.json +++ b/resources/i18n/bcl.json @@ -1,59 +1,59 @@ -{ - "@metadata": { - "authors": [ - "Kunokuno" - ] - }, - "name": "Ingles", - "error-title": "Sala", - "error-downloader-window-title": "Dai makagibo nin downloader", - "error-downloader-launch-message": "Imposible an paglansar kan downloader, mapuon an Kiwix-desktop alagad gabos na download functions dai magana!", - "error-launch-server-message": "Igwa nin sala na nangyari!", - "error-archive": "Dai makua an archive", - "open-zim": "Bukaan an ZIM File", - "local-kiwix-server": "Lokal na Kiwix Server", - "random-article": "Dawa arin na Artikulo", - "random-article-error": "Dai mabukaan an dawa arin na artikulo.", - "home-page": "Harong na pahina", - "main-menu": "Mayor na pilian", - "print": "Iprint", - "print-page-error": "Igwa nin salang nangyari mantang nagpiprint.", - "new-tab": "Bagong tab", - "close-tab": "Saradong tab", - "close": "Sarado", - "reopen-closed-tab": "Bukaan an nakasaradong tab", - "browse-library": "Maghanap sa library", - "open-file": "Bukaan an file", - "open-recent": "Bukaan an pinakabago", - "search-article": "Maghanap nin artikulo", - "search-in-library": "Maghanap sa library", - "find-in-page": "Maghanap sa pahina", - "set-fullscreen": "Gibuhon na fullscreen", - "quit-fullscreen": "Maghali sa fullScreen", - "table-of-content": "Lista kan mga laog", - "reading-list": "Lista kan mga babasahon", - "zoom-in": "Padakulaon", - "zoom-out": "Pasaditon", - "zoom-reset": "Utruhon an pagpadakula", - "help": "Tabang", - "feedback": "Tuyaw", - "report-a-bug": "Magreport nin bug", - "request-a-feature": "Maghagad nin kakayahan", - "about-kiwix": "Manungod sa Kiwix", - "donate-to-support-kiwix": "Magdonar tangani na masuportahan na Kiwix", - "exit": "Maghali", - "save-file-as-window-title": "I-save an File bilang", - "download-finished-message": "Nadownload na an dokumento.", - "file": "Dokumento", - "edit": "Baguhon", - "view": "Hilingon", - "tools": "Mga gamit", - "window-title": "Library - Kiwix", - "search": "Hanapon", - "browse-directory": "Maghanap sa Directory", - "settings": "Mga Kagustuhan", - "back": "Magbalik", - "forward": "Magpadagos", - "search-files": "Hanapon an mga file", - "title": "Titulo" -} +{ + "@metadata": { + "authors": [ + "Kunokuno" + ] + }, + "name": "Ingles", + "error-title": "Sala", + "error-downloader-window-title": "Dai makagibo nin downloader", + "error-downloader-launch-message": "Imposible an paglansar kan downloader, mapuon an Kiwix-desktop alagad gabos na download functions dai magana!", + "error-launch-server-message": "Igwa nin sala na nangyari!", + "error-archive": "Dai makua an archive", + "open-zim": "Bukaan an ZIM File", + "local-kiwix-server": "Lokal na Kiwix Server", + "random-article": "Dawa arin na Artikulo", + "random-article-error": "Dai mabukaan an dawa arin na artikulo.", + "home-page": "Harong na pahina", + "main-menu": "Mayor na pilian", + "print": "Iprint", + "print-page-error": "Igwa nin salang nangyari mantang nagpiprint.", + "new-tab": "Bagong tab", + "close-tab": "Saradong tab", + "close": "Sarado", + "reopen-closed-tab": "Bukaan an nakasaradong tab", + "browse-library": "Maghanap sa library", + "open-file": "Bukaan an file", + "open-recent": "Bukaan an pinakabago", + "search-article": "Maghanap nin artikulo", + "search-in-library": "Maghanap sa library", + "find-in-page": "Maghanap sa pahina", + "set-fullscreen": "Gibuhon na fullscreen", + "quit-fullscreen": "Maghali sa fullScreen", + "table-of-content": "Lista kan mga laog", + "reading-list": "Lista kan mga babasahon", + "zoom-in": "Padakulaon", + "zoom-out": "Pasaditon", + "zoom-reset": "Utruhon an pagpadakula", + "help": "Tabang", + "feedback": "Tuyaw", + "report-a-bug": "Magreport nin bug", + "request-a-feature": "Maghagad nin kakayahan", + "about-kiwix": "Manungod sa Kiwix", + "donate-to-support-kiwix": "Magdonar tangani na masuportahan na Kiwix", + "exit": "Maghali", + "save-file-as-window-title": "I-save an File bilang", + "download-finished-message": "Nadownload na an dokumento.", + "file": "Dokumento", + "edit": "Baguhon", + "view": "Hilingon", + "tools": "Mga gamit", + "window-title": "Library - Kiwix", + "search": "Hanapon", + "browse-directory": "Maghanap sa Directory", + "settings": "Mga Kagustuhan", + "back": "Magbalik", + "forward": "Magpadagos", + "search-files": "Hanapon an mga file", + "title": "Titulo" +} diff --git a/resources/i18n/bn.json b/resources/i18n/bn.json index 947d6b125..2d23e771a 100644 --- a/resources/i18n/bn.json +++ b/resources/i18n/bn.json @@ -1,45 +1,45 @@ -{ - "@metadata": { - "authors": [ - "McDutchie", - "San1joy23", - "Sibabrata Banerjee", - "আজিজ", - "আফতাবুজ্জামান" - ] - }, - "name": "বাংলা", - "print": "মুদ্রিত করুন", - "new-tab": "নতুন ট্যাব", - "close-tab": "ট্যাব বন্ধ করুন", - "search-article": "নিবন্ধ অনুসন্ধান করুন", - "help": "সাহায্য", - "feedback": "প্রতিক্রিয়া", - "exit": "প্রস্থান", - "view": "দেখুন", - "tools": "সরঞ্জাম", - "search": "অনুসন্ধান", - "back": "পিছন", - "forward": "সামনে", - "title": "শিরোনাম", - "size": "আকার", - "date": "তারিখ", - "about-kiwix-desktop-title": "কিউইক্স ডেস্কটপ", - "about-privacy-policy": "গোপনীয়তার নীতি", - "about-release-title": "মুক্তি", - "about-version": "সংস্করণ: {{VERSION}}", - "about-report-issue-2": "দয়া করে সমস্যাটিতে সংস্করণের নামটি লিখুন।", - "all": "সব", - "fulltext-search": "পূর্ণপাঠ অনুসন্ধান", - "yes": "হ্যাঁ", - "no": "না", - "ok": "ঠিক আছে", - "open-link-in-web-browser": "ওয়েব ব্রাউজারে সংযোগ খুলুন", - "invalid-port": "পাতা অবৈধ", - "next-tab": "পরবর্তী ট্যাবে যান", - "previous-tab": "পূর্ববর্তী ট্যাবে যান", - "disable-sandbox": "কিউইক্স একটি নেটওয়ার্ক ড্রাইভ থেকে চালু করা হয়েছে। এটি ব্রাউজিং স্যান্ডবক্সিং-এর সাথে সামঞ্জস্যপূর্ণ সমস্যার কারণ হিসেবে পরিচিত। ফলস্বরূপ, স্যান্ডবক্স অক্ষম করা হবে। আপনি কি চালিয়ে যেতে চান?", - "portable-disabled-tooltip": "পোর্টেবল মোডে ফাংশন নিষ্ক্রিয় করা হয়েছে", - "scroll-next-tab": "পরবর্তী ট্যাবে স্ক্রোল করুন", - "scroll-previous-tab": "পূর্ববর্তী ট্যাবে স্ক্রোল করুন" -} +{ + "@metadata": { + "authors": [ + "McDutchie", + "San1joy23", + "Sibabrata Banerjee", + "আজিজ", + "আফতাবুজ্জামান" + ] + }, + "name": "বাংলা", + "print": "মুদ্রিত করুন", + "new-tab": "নতুন ট্যাব", + "close-tab": "ট্যাব বন্ধ করুন", + "search-article": "নিবন্ধ অনুসন্ধান করুন", + "help": "সাহায্য", + "feedback": "প্রতিক্রিয়া", + "exit": "প্রস্থান", + "view": "দেখুন", + "tools": "সরঞ্জাম", + "search": "অনুসন্ধান", + "back": "পিছন", + "forward": "সামনে", + "title": "শিরোনাম", + "size": "আকার", + "date": "তারিখ", + "about-kiwix-desktop-title": "কিউইক্স ডেস্কটপ", + "about-privacy-policy": "গোপনীয়তার নীতি", + "about-release-title": "মুক্তি", + "about-version": "সংস্করণ: {{VERSION}}", + "about-report-issue-2": "দয়া করে সমস্যাটিতে সংস্করণের নামটি লিখুন।", + "all": "সব", + "fulltext-search": "পূর্ণপাঠ অনুসন্ধান", + "yes": "হ্যাঁ", + "no": "না", + "ok": "ঠিক আছে", + "open-link-in-web-browser": "ওয়েব ব্রাউজারে সংযোগ খুলুন", + "invalid-port": "পাতা অবৈধ", + "next-tab": "পরবর্তী ট্যাবে যান", + "previous-tab": "পূর্ববর্তী ট্যাবে যান", + "disable-sandbox": "কিউইক্স একটি নেটওয়ার্ক ড্রাইভ থেকে চালু করা হয়েছে। এটি ব্রাউজিং স্যান্ডবক্সিং-এর সাথে সামঞ্জস্যপূর্ণ সমস্যার কারণ হিসেবে পরিচিত। ফলস্বরূপ, স্যান্ডবক্স অক্ষম করা হবে। আপনি কি চালিয়ে যেতে চান?", + "portable-disabled-tooltip": "পোর্টেবল মোডে ফাংশন নিষ্ক্রিয় করা হয়েছে", + "scroll-next-tab": "পরবর্তী ট্যাবে স্ক্রোল করুন", + "scroll-previous-tab": "পূর্ববর্তী ট্যাবে স্ক্রোল করুন" +} diff --git a/resources/i18n/br.json b/resources/i18n/br.json index 26cbd7334..549990d75 100644 --- a/resources/i18n/br.json +++ b/resources/i18n/br.json @@ -1,113 +1,113 @@ -{ - "@metadata": { - "authors": [ - "Adriendelucca", - "Fulup" - ] - }, - "name": "Brezhoneg", - "error-title": "Fazi", - "error-launch-server-message": "C'hoarvezet ez eus ur fazi!", - "error-archive": "N’haller ket kaout an diell", - "open-zim": "Digeriñ ar restr Zim", - "local-kiwix-server": "Servijer Kiwix lec’hel", - "random-article": "Pennad dre zegouezh", - "home-page": "Pajenn degemer", - "main-menu": "Lañser pennañ", - "print": "Moullañ", - "print-page-error": "Ur fazi zo bet en ur voullañ.", - "new-tab": "Ivinell nevez", - "close-tab": "Serriñ an ivinell", - "close": "Serriñ", - "reopen-closed-tab": "Addigeriñ ar steudenn bet serret", - "browse-library": "Ergerzhout al levraoueg", - "open-file": "Digeriñ ar restr", - "open-recent": "Digeriñ ar re nevez", - "search-article": "Klask ur pennad", - "search-in-library": "Klask el levraoueg", - "find-in-page": "Klask er bajenn", - "set-fullscreen": "Mod skramm a-bezh", - "quit-fullscreen": "Kuitaat ar skramm leun", - "reading-list": "Roll lenn", - "zoom-in": "Zoumañ", - "zoom-out": "Dizoumañ", - "zoom-reset": "Adderaouekaat ar zoum", - "help": "Skoazell", - "feedback": "Sonjoù", - "report-a-bug": "Disklêriañ un draen", - "about-kiwix": "Diwar-benn Kiwix", - "donate-to-support-kiwix": "Reiñ un donezon evit skoazellañ Kiwix", - "exit": "Kuitaat", - "save-file-as-window-title": "Enrollañ ar restr evel", - "download-finished-message": "Pellgarget eo bet an teul.", - "file": "Restr", - "edit": "Kemmañ", - "view": "Gwelet", - "tools": "Ostilhoù", - "window-title": "Levraoueg - Kiwix", - "search": "Klask", - "settings": "Arventennoù", - "back": "Distreiñ", - "forward": "Da-heul", - "search-files": "Klask restroù", - "title": "Titl", - "size": "Ment", - "date": "Deiziad", - "open": "Digeriñ", - "delete": "Dilemel", - "download": "Pellgargañ", - "resume": "Adstagañ ganti", - "pause": "Ehanañ", - "cancel": "Nullañ", - "apply": "Lakaat da dalvezout", - "port-for-local-kiwix-server-setting": "Porzh evit ar servijer Kiwix lec’hel", - "zoom-level-setting": "Live zoum dre ziouer", - "clear": "Riñsañ", - "browse": "Furchal", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-learn-more-about-kiwix": "Gouzout hiroc’h diwar-benn Kiwix", - "about-version": "Handelv: {{VERSION}}", - "about-reporting-problem-title": "Menegiñ ur gudenn", - "about-report-issue-2": "Menegit ar stumm Kiwix en ho tanevell mar plij.", - "about-libraries-title": "Levraouegoù", - "online-files": "Restroù enlinenn", - "local-files": "Restroù lec’hel", - "category": "Rummad", - "language": "Yezh", - "hide": "Kuzhat", - "open-in-browser": "Digeriñ er merdeer", - "start-kiwix-server": "Loc’hañ ar servijer Kiwix", - "stop-kiwix-server": "Ehanañ ar servijer Kiwix", - "all": "An holl", - "fulltext-search": "Klask en destenn glok", - "pictures": "Skeudennoù", - "videos": "Videoioù", - "details": "Pennad a-bezh", - "yes": "ya", - "no": "ket", - "ok": "mat eo", - "no-filter": "sil ebet", - "open-link-in-web-browser": "Digeriñ al liamm er merdeer", - "invalid-port": "Porzh direizh", - "zim-open-fail-title": "Restr direizh", - "next-tab": "Mont d'ar steudenn da-heul", - "previous-tab": "Mont d'ar steudenn gent", - "cancel-download": "Nullañ ar pellgargañ", - "cancel-download-text": "Sur oc’h e faot deoc’h nullañ pellgargañ ar restr {{ZIM}}?", - "delete-book": "Dilemel al levr", - "delete-book-text": "Sur oc’h e faot deoc’h dilemel {{ZIM}}?", - "open-book": "Digeriñ al levr", - "download-book": "Pellgargañ al levr", - "pause-download": "Ehanañ ar pellgargañ", - "resume-download": "Kenderc’hel da bellgargañ", - "open-folder": "Digeriñ ar restr", - "couldnt-open-location": "N’haller ket digeriñ al lec’hiadur", - "couldnt-open-location-text": "Kiwix n’eo ket gouest da zigeriñ ar restr {{FOLDER}}", - "move-files-to-trash-text": "Kement-se a lakay ar restr e-barzh ar pod-lastez.", - "perma-delete-files-text": "Kement-se a zilamo ar restr da viken.", - "language-searcher-placeholder": "Silañ dre yezh", - "category-searcher-placeholder": "Silañ dre rummad", - "no-details": "Digoradur hepken", - "no-pictures": "Skeudenn ebet", - "no-videos": "Video ebet" -} +{ + "@metadata": { + "authors": [ + "Adriendelucca", + "Fulup" + ] + }, + "name": "Brezhoneg", + "error-title": "Fazi", + "error-launch-server-message": "C'hoarvezet ez eus ur fazi!", + "error-archive": "N’haller ket kaout an diell", + "open-zim": "Digeriñ ar restr Zim", + "local-kiwix-server": "Servijer Kiwix lec’hel", + "random-article": "Pennad dre zegouezh", + "home-page": "Pajenn degemer", + "main-menu": "Lañser pennañ", + "print": "Moullañ", + "print-page-error": "Ur fazi zo bet en ur voullañ.", + "new-tab": "Ivinell nevez", + "close-tab": "Serriñ an ivinell", + "close": "Serriñ", + "reopen-closed-tab": "Addigeriñ ar steudenn bet serret", + "browse-library": "Ergerzhout al levraoueg", + "open-file": "Digeriñ ar restr", + "open-recent": "Digeriñ ar re nevez", + "search-article": "Klask ur pennad", + "search-in-library": "Klask el levraoueg", + "find-in-page": "Klask er bajenn", + "set-fullscreen": "Mod skramm a-bezh", + "quit-fullscreen": "Kuitaat ar skramm leun", + "reading-list": "Roll lenn", + "zoom-in": "Zoumañ", + "zoom-out": "Dizoumañ", + "zoom-reset": "Adderaouekaat ar zoum", + "help": "Skoazell", + "feedback": "Sonjoù", + "report-a-bug": "Disklêriañ un draen", + "about-kiwix": "Diwar-benn Kiwix", + "donate-to-support-kiwix": "Reiñ un donezon evit skoazellañ Kiwix", + "exit": "Kuitaat", + "save-file-as-window-title": "Enrollañ ar restr evel", + "download-finished-message": "Pellgarget eo bet an teul.", + "file": "Restr", + "edit": "Kemmañ", + "view": "Gwelet", + "tools": "Ostilhoù", + "window-title": "Levraoueg - Kiwix", + "search": "Klask", + "settings": "Arventennoù", + "back": "Distreiñ", + "forward": "Da-heul", + "search-files": "Klask restroù", + "title": "Titl", + "size": "Ment", + "date": "Deiziad", + "open": "Digeriñ", + "delete": "Dilemel", + "download": "Pellgargañ", + "resume": "Adstagañ ganti", + "pause": "Ehanañ", + "cancel": "Nullañ", + "apply": "Lakaat da dalvezout", + "port-for-local-kiwix-server-setting": "Porzh evit ar servijer Kiwix lec’hel", + "zoom-level-setting": "Live zoum dre ziouer", + "clear": "Riñsañ", + "browse": "Furchal", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-learn-more-about-kiwix": "Gouzout hiroc’h diwar-benn Kiwix", + "about-version": "Handelv: {{VERSION}}", + "about-reporting-problem-title": "Menegiñ ur gudenn", + "about-report-issue-2": "Menegit ar stumm Kiwix en ho tanevell mar plij.", + "about-libraries-title": "Levraouegoù", + "online-files": "Restroù enlinenn", + "local-files": "Restroù lec’hel", + "category": "Rummad", + "language": "Yezh", + "hide": "Kuzhat", + "open-in-browser": "Digeriñ er merdeer", + "start-kiwix-server": "Loc’hañ ar servijer Kiwix", + "stop-kiwix-server": "Ehanañ ar servijer Kiwix", + "all": "An holl", + "fulltext-search": "Klask en destenn glok", + "pictures": "Skeudennoù", + "videos": "Videoioù", + "details": "Pennad a-bezh", + "yes": "ya", + "no": "ket", + "ok": "mat eo", + "no-filter": "sil ebet", + "open-link-in-web-browser": "Digeriñ al liamm er merdeer", + "invalid-port": "Porzh direizh", + "zim-open-fail-title": "Restr direizh", + "next-tab": "Mont d'ar steudenn da-heul", + "previous-tab": "Mont d'ar steudenn gent", + "cancel-download": "Nullañ ar pellgargañ", + "cancel-download-text": "Sur oc’h e faot deoc’h nullañ pellgargañ ar restr {{ZIM}}?", + "delete-book": "Dilemel al levr", + "delete-book-text": "Sur oc’h e faot deoc’h dilemel {{ZIM}}?", + "open-book": "Digeriñ al levr", + "download-book": "Pellgargañ al levr", + "pause-download": "Ehanañ ar pellgargañ", + "resume-download": "Kenderc’hel da bellgargañ", + "open-folder": "Digeriñ ar restr", + "couldnt-open-location": "N’haller ket digeriñ al lec’hiadur", + "couldnt-open-location-text": "Kiwix n’eo ket gouest da zigeriñ ar restr {{FOLDER}}", + "move-files-to-trash-text": "Kement-se a lakay ar restr e-barzh ar pod-lastez.", + "perma-delete-files-text": "Kement-se a zilamo ar restr da viken.", + "language-searcher-placeholder": "Silañ dre yezh", + "category-searcher-placeholder": "Silañ dre rummad", + "no-details": "Digoradur hepken", + "no-pictures": "Skeudenn ebet", + "no-videos": "Video ebet" +} diff --git a/resources/i18n/ca.json b/resources/i18n/ca.json index 11f4f08ee..28fbb222f 100644 --- a/resources/i18n/ca.json +++ b/resources/i18n/ca.json @@ -1,50 +1,50 @@ -{ - "@metadata": { - "authors": [ - "Alan Mena", - "Fitoschido", - "Mguix" - ] - }, - "name": "Català", - "main-menu": "Menú principal", - "print": "Imprimeix", - "new-tab": "Pestanya nova", - "close-tab": "Tanca la pestanya", - "open-recent": "Obre els recents", - "find-in-page": "Cerca a la pàgina", - "reading-list": "Llista de lectura", - "zoom-in": "Apropa’t", - "zoom-out": "Allunya’t", - "help": "Ajuda", - "report-a-bug": "Informa d’un error", - "about-kiwix": "Quant al Kiwix", - "exit": "Surt", - "file": "Fitxer", - "edit": "Edita", - "view": "Visualitza", - "tools": "Eines", - "search": "Cerca", - "settings": "Configuració", - "back": "Enrere", - "title": "Títol", - "size": "Mida", - "date": "Data", - "content-type": "Tipus de contingut", - "open": "Obre", - "delete": "Suprimeix", - "download": "Baixa", - "resume": "Reprèn", - "pause": "Pausa", - "cancel": "Cancel·la", - "apply": "Aplica", - "download-directory-setting": "Directori de baixades:", - "reset": "Reinicialitza", - "browse": "Explora", - "about-libraries-title": "Biblioteques", - "local-files": "Fitxers locals", - "hide": "Amaga", - "all": "Tot", - "yes": "sí", - "no": "no" -} +{ + "@metadata": { + "authors": [ + "Alan Mena", + "Fitoschido", + "Mguix" + ] + }, + "name": "Català", + "main-menu": "Menú principal", + "print": "Imprimeix", + "new-tab": "Pestanya nova", + "close-tab": "Tanca la pestanya", + "open-recent": "Obre els recents", + "find-in-page": "Cerca a la pàgina", + "reading-list": "Llista de lectura", + "zoom-in": "Apropa’t", + "zoom-out": "Allunya’t", + "help": "Ajuda", + "report-a-bug": "Informa d’un error", + "about-kiwix": "Quant al Kiwix", + "exit": "Surt", + "file": "Fitxer", + "edit": "Edita", + "view": "Visualitza", + "tools": "Eines", + "search": "Cerca", + "settings": "Configuració", + "back": "Enrere", + "title": "Títol", + "size": "Mida", + "date": "Data", + "content-type": "Tipus de contingut", + "open": "Obre", + "delete": "Suprimeix", + "download": "Baixa", + "resume": "Reprèn", + "pause": "Pausa", + "cancel": "Cancel·la", + "apply": "Aplica", + "download-directory-setting": "Directori de baixades:", + "reset": "Reinicialitza", + "browse": "Explora", + "about-libraries-title": "Biblioteques", + "local-files": "Fitxers locals", + "hide": "Amaga", + "all": "Tot", + "yes": "sí", + "no": "no" +} diff --git a/resources/i18n/cs.json b/resources/i18n/cs.json index b7e30f0ac..0bea70428 100644 --- a/resources/i18n/cs.json +++ b/resources/i18n/cs.json @@ -1,116 +1,116 @@ -{ - "@metadata": { - "authors": [ - "Klaras", - "Spotter" - ] - }, - "name": "Čeština", - "error-title": "Chyba", - "error-downloader-window-title": "Nelze vytvořit stahovač", - "error-downloader-launch-message": "Stahovač nelze spustit, Kiwix se spustí, ale všechny funkce stahování budou přerušeny.", - "error-launch-server-message": "Došlo k chybě!", - "error-archive": "Archiv nelze získat", - "open-zim": "Otevřít Zim", - "local-kiwix-server": "Lokální Kiwix server", - "random-article": "Náhodný článek", - "random-article-error": "Nepodařilo se otevřít náhodný článek.", - "home-page": "Domovská stránka", - "main-menu": "Hlavní menu", - "print": "Vytisknout", - "print-page-error": "Při tisku došlo k chybě.", - "new-tab": "Nová karta", - "close-tab": "Zavřít kartu", - "close": "Zavřít", - "reopen-closed-tab": "Znovu otevřít zavřenou kartu", - "browse-library": "Procházet knihovnu", - "open-file": "Otevři soubor", - "open-recent": "Otevřít nedávný", - "search-article": "Hledat článek", - "search-in-library": "Hledat v knihovně", - "find-in-page": "Najít na stránce", - "set-fullscreen": "Nastavit režim celé obrazovky", - "quit-fullscreen": "Ukončit režim celé obrazovky", - "table-of-content": "Obsah", - "reading-list": "Seznam k přečtení", - "zoom-in": "Přiblížit", - "zoom-out": "Oddálit", - "zoom-reset": "Resetovat přiblížení", - "help": "Nápověda", - "feedback": "Zpětná vazba", - "report-a-bug": "Nahlásit chybu", - "request-a-feature": "Požádat o novou funkci", - "about-kiwix": "O Kiwix", - "donate-to-support-kiwix": "Darováním podpořte Kiwix", - "exit": "Opustit", - "save-file-as-window-title": "Uložit soubor jako", - "download-finished-message": "Dokument byl stažen.", - "file": "Soubor", - "edit": "Upravit", - "view": "Zobrazit", - "tools": "Nástroje", - "window-title": "Knihovna - Kiwix", - "search": "Hledat", - "browse-directory": "Procházet složku", - "settings": "Nastavení", - "back": "Zpět", - "forward": "Vpřed", - "search-files": "Hledat soubory", - "title": "Název", - "size": "Velikost", - "date": "Datum", - "content-type": "Typ obsahu", - "reset-sort": "Resetovat řazení", - "open": "Otevřít", - "delete": "Smazat", - "download": "Stáhnout", - "resume": "Obnovit", - "pause": "Pozastavit", - "cancel": "Zrušit", - "apply": "Použít", - "port-for-local-kiwix-server-setting": "Port pro místní server Kiwix", - "zoom-level-setting": "Výchozí stupeň přiblížení", - "download-directory-setting": "Složka stahování", - "monitor-directory-setting": "Sledování adresáře", - "reset": "Resetovat", - "clear": "Vymazat", - "browse": "Procházet", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix vám umožňuje mít po ruce celou Wikipedii, ať už jste kdekoli! Na lodi, uprostřed ničeho nebo ve vězení, Kiwix vám poskytuje přístup k celému lidskému poznání. Nepotřebujete internet, vše je uloženo v počítači.", - "about-learn-more-about-kiwix": "Dozvědět se více o Kiwix", - "about-release-title": "Vydání", - "about-source-code": "Tento software je vydáván za podmínek GNU General Public License verze 3. Zdrojový kód naleznete na webu Github.", - "about-version": "Verze: {{VERSION}}", - "about-reporting-problem-title": "Hlášení problému", - "about-report-issue": "Chcete-li problém otevřít, navštivte nástroj pro sledování problémů Kiwix-desktop a otevřete issue popisující váš problém.", - "about-report-issue-2": "Uveďte verzi v issue.", - "about-libraries-title": "Knihovny", - "kiwix-server-running-message": "Server Kiwix je spuštěn a lze k němu přistupovat v místní síti na:", - "kiwix-server-description": "Spuštění serveru umožňuje ostatním počítačům v místní síti přistupovat ke knihovně Kiwix pomocí standardního webového prohlížeče.", - "fullscreen-notification": "Jste v režimu celé obrazovky. Pro ukončení stiskněte klávesu ESC!", - "local-files": "Lokální soubory", - "hide": "Skrýt", - "open-in-browser": "Otevřít v prohlížeči", - "start-kiwix-server": "Spustit Kiwix server", - "stop-kiwix-server": "Zastavit Kiwix server", - "all": "Vše", - "fulltext-search": "Fulltextové vyhledávání", - "pictures": "Obrázky", - "videos": "Videa", - "ftindex": "Fulltextový index", - "details": "Celý článek", - "yes": "ano", - "no": "ne", - "no-filter": "žádný filtr", - "open-link-in-web-browser": "Otevřít odkaz ve webovém prohlížeči", - "download-dir-dialog-title": "Opravdu chcete změnit složku pro stahování?", - "download-dir-dialog-msg": "Nová cesta ke složce pro stahování bude:\n{{DIRECTORY}}", - "invalid-port": "Neplatný port", - "zim-open-fail-title": "Neplatný soubor", - "zim-open-fail-text": "Soubor ZIM {{ZIM}} nelze správně otevřít. Bude z vaší knihovny odstraněn.", - "monitor-dir-dialog-title": "Opravdu chcete změnit adresář monitorování?", - "monitor-dir-dialog-msg": "Nová cesta k adresáři monitoru bude:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Opravdu chcete vymazat adresář monitorování?", - "monitor-clear-dir-dialog-msg": "Tím se zastaví kontrola adresáře monitoru na nové soubory ZIM.", - "monitor-directory-tooltip": "Všechny soubory ZIM v tomto adresáři budou automaticky přidány do knihovny." -} +{ + "@metadata": { + "authors": [ + "Klaras", + "Spotter" + ] + }, + "name": "Čeština", + "error-title": "Chyba", + "error-downloader-window-title": "Nelze vytvořit stahovač", + "error-downloader-launch-message": "Stahovač nelze spustit, Kiwix se spustí, ale všechny funkce stahování budou přerušeny.", + "error-launch-server-message": "Došlo k chybě!", + "error-archive": "Archiv nelze získat", + "open-zim": "Otevřít Zim", + "local-kiwix-server": "Lokální Kiwix server", + "random-article": "Náhodný článek", + "random-article-error": "Nepodařilo se otevřít náhodný článek.", + "home-page": "Domovská stránka", + "main-menu": "Hlavní menu", + "print": "Vytisknout", + "print-page-error": "Při tisku došlo k chybě.", + "new-tab": "Nová karta", + "close-tab": "Zavřít kartu", + "close": "Zavřít", + "reopen-closed-tab": "Znovu otevřít zavřenou kartu", + "browse-library": "Procházet knihovnu", + "open-file": "Otevři soubor", + "open-recent": "Otevřít nedávný", + "search-article": "Hledat článek", + "search-in-library": "Hledat v knihovně", + "find-in-page": "Najít na stránce", + "set-fullscreen": "Nastavit režim celé obrazovky", + "quit-fullscreen": "Ukončit režim celé obrazovky", + "table-of-content": "Obsah", + "reading-list": "Seznam k přečtení", + "zoom-in": "Přiblížit", + "zoom-out": "Oddálit", + "zoom-reset": "Resetovat přiblížení", + "help": "Nápověda", + "feedback": "Zpětná vazba", + "report-a-bug": "Nahlásit chybu", + "request-a-feature": "Požádat o novou funkci", + "about-kiwix": "O Kiwix", + "donate-to-support-kiwix": "Darováním podpořte Kiwix", + "exit": "Opustit", + "save-file-as-window-title": "Uložit soubor jako", + "download-finished-message": "Dokument byl stažen.", + "file": "Soubor", + "edit": "Upravit", + "view": "Zobrazit", + "tools": "Nástroje", + "window-title": "Knihovna - Kiwix", + "search": "Hledat", + "browse-directory": "Procházet složku", + "settings": "Nastavení", + "back": "Zpět", + "forward": "Vpřed", + "search-files": "Hledat soubory", + "title": "Název", + "size": "Velikost", + "date": "Datum", + "content-type": "Typ obsahu", + "reset-sort": "Resetovat řazení", + "open": "Otevřít", + "delete": "Smazat", + "download": "Stáhnout", + "resume": "Obnovit", + "pause": "Pozastavit", + "cancel": "Zrušit", + "apply": "Použít", + "port-for-local-kiwix-server-setting": "Port pro místní server Kiwix", + "zoom-level-setting": "Výchozí stupeň přiblížení", + "download-directory-setting": "Složka stahování", + "monitor-directory-setting": "Sledování adresáře", + "reset": "Resetovat", + "clear": "Vymazat", + "browse": "Procházet", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix vám umožňuje mít po ruce celou Wikipedii, ať už jste kdekoli! Na lodi, uprostřed ničeho nebo ve vězení, Kiwix vám poskytuje přístup k celému lidskému poznání. Nepotřebujete internet, vše je uloženo v počítači.", + "about-learn-more-about-kiwix": "Dozvědět se více o Kiwix", + "about-release-title": "Vydání", + "about-source-code": "Tento software je vydáván za podmínek GNU General Public License verze 3. Zdrojový kód naleznete na webu Github.", + "about-version": "Verze: {{VERSION}}", + "about-reporting-problem-title": "Hlášení problému", + "about-report-issue": "Chcete-li problém otevřít, navštivte nástroj pro sledování problémů Kiwix-desktop a otevřete issue popisující váš problém.", + "about-report-issue-2": "Uveďte verzi v issue.", + "about-libraries-title": "Knihovny", + "kiwix-server-running-message": "Server Kiwix je spuštěn a lze k němu přistupovat v místní síti na:", + "kiwix-server-description": "Spuštění serveru umožňuje ostatním počítačům v místní síti přistupovat ke knihovně Kiwix pomocí standardního webového prohlížeče.", + "fullscreen-notification": "Jste v režimu celé obrazovky. Pro ukončení stiskněte klávesu ESC!", + "local-files": "Lokální soubory", + "hide": "Skrýt", + "open-in-browser": "Otevřít v prohlížeči", + "start-kiwix-server": "Spustit Kiwix server", + "stop-kiwix-server": "Zastavit Kiwix server", + "all": "Vše", + "fulltext-search": "Fulltextové vyhledávání", + "pictures": "Obrázky", + "videos": "Videa", + "ftindex": "Fulltextový index", + "details": "Celý článek", + "yes": "ano", + "no": "ne", + "no-filter": "žádný filtr", + "open-link-in-web-browser": "Otevřít odkaz ve webovém prohlížeči", + "download-dir-dialog-title": "Opravdu chcete změnit složku pro stahování?", + "download-dir-dialog-msg": "Nová cesta ke složce pro stahování bude:\n{{DIRECTORY}}", + "invalid-port": "Neplatný port", + "zim-open-fail-title": "Neplatný soubor", + "zim-open-fail-text": "Soubor ZIM {{ZIM}} nelze správně otevřít. Bude z vaší knihovny odstraněn.", + "monitor-dir-dialog-title": "Opravdu chcete změnit adresář monitorování?", + "monitor-dir-dialog-msg": "Nová cesta k adresáři monitoru bude:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Opravdu chcete vymazat adresář monitorování?", + "monitor-clear-dir-dialog-msg": "Tím se zastaví kontrola adresáře monitoru na nové soubory ZIM.", + "monitor-directory-tooltip": "Všechny soubory ZIM v tomto adresáři budou automaticky přidány do knihovny." +} diff --git a/resources/i18n/da.json b/resources/i18n/da.json index 9a2312eae..8497c221a 100644 --- a/resources/i18n/da.json +++ b/resources/i18n/da.json @@ -1,75 +1,75 @@ -{ - "@metadata": { - "authors": [ - "McDutchie", - "Saederup92", - "Zakawer2" - ] - }, - "name": "Dansk", - "error-title": "Fejl", - "error-launch-server-message": "Der opstod en fejl!", - "error-archive": "Kan ikke hente arkivet", - "open-zim": "Åbn ZIM-fil", - "local-kiwix-server": "Lokal Kiwix-server", - "random-article": "Tilfældig artikel", - "main-menu": "Hovedmenu", - "print": "Udskriv", - "print-page-error": "Der opstod en fejl under udskrivningen.", - "new-tab": "Ny fane", - "close-tab": "Luk fane", - "close": "Luk", - "reopen-closed-tab": "Genåbn lukket fane", - "browse-library": "Gennemse biblioteket", - "open-file": "Åbn fil", - "search-article": "Søg i artiklen", - "search-in-library": "Søg i biblioteket", - "find-in-page": "Find på siden", - "table-of-content": "Indholdsfortegnelse", - "reading-list": "Læseliste", - "zoom-in": "Zoom ind", - "zoom-out": "Zoom ud", - "help": "Hjælp", - "about-kiwix": "Om Kiwix", - "exit": "Afslut", - "save-file-as-window-title": "Gem fil som", - "file": "Fil", - "edit": "Redigér", - "tools": "Værktøjer", - "window-title": "Bibliotek - Kiwix", - "search": "Søg", - "settings": "Indstillinger", - "back": "Tilbage", - "title": "Titel", - "size": "Størrelse", - "date": "Dato", - "content-type": "Indholdstype", - "open": "Åbn", - "delete": "Slet", - "resume": "Fortsæt", - "pause": "Sæt på pause", - "reset": "Nulstil", - "clear": "Ryd", - "browse": "Gennemse", - "about-learn-more-about-kiwix": "Lær mere om Kiwix", - "about-version": "Version: {{VERSION}}", - "about-libraries-title": "Biblioteker", - "local-files": "Lokale filer", - "hide": "Skjul", - "open-in-browser": "Åbn i browser", - "start-kiwix-server": "Start Kiwix-server", - "stop-kiwix-server": "Stop Kiwix-server", - "all": "Alle", - "pictures": "Billeder", - "videos": "Videoer", - "details": "Fuld artikel", - "yes": "ja", - "no": "nej", - "no-filter": "intet filter", - "invalid-port": "Ugyldig port", - "zim-open-fail-title": "Ugyldig fil", - "zim-open-fail-text": "ZIM-filen {{ZIM}} kunne ikke åbnes korrekt. Den vil blive slettet fra dit bibliotek.", - "next-tab": "Flyt til næste fane", - "previous-tab": "Flyt til forrige fane", - "cancel-download": "Annuller download" -} +{ + "@metadata": { + "authors": [ + "McDutchie", + "Saederup92", + "Zakawer2" + ] + }, + "name": "Dansk", + "error-title": "Fejl", + "error-launch-server-message": "Der opstod en fejl!", + "error-archive": "Kan ikke hente arkivet", + "open-zim": "Åbn ZIM-fil", + "local-kiwix-server": "Lokal Kiwix-server", + "random-article": "Tilfældig artikel", + "main-menu": "Hovedmenu", + "print": "Udskriv", + "print-page-error": "Der opstod en fejl under udskrivningen.", + "new-tab": "Ny fane", + "close-tab": "Luk fane", + "close": "Luk", + "reopen-closed-tab": "Genåbn lukket fane", + "browse-library": "Gennemse biblioteket", + "open-file": "Åbn fil", + "search-article": "Søg i artiklen", + "search-in-library": "Søg i biblioteket", + "find-in-page": "Find på siden", + "table-of-content": "Indholdsfortegnelse", + "reading-list": "Læseliste", + "zoom-in": "Zoom ind", + "zoom-out": "Zoom ud", + "help": "Hjælp", + "about-kiwix": "Om Kiwix", + "exit": "Afslut", + "save-file-as-window-title": "Gem fil som", + "file": "Fil", + "edit": "Redigér", + "tools": "Værktøjer", + "window-title": "Bibliotek - Kiwix", + "search": "Søg", + "settings": "Indstillinger", + "back": "Tilbage", + "title": "Titel", + "size": "Størrelse", + "date": "Dato", + "content-type": "Indholdstype", + "open": "Åbn", + "delete": "Slet", + "resume": "Fortsæt", + "pause": "Sæt på pause", + "reset": "Nulstil", + "clear": "Ryd", + "browse": "Gennemse", + "about-learn-more-about-kiwix": "Lær mere om Kiwix", + "about-version": "Version: {{VERSION}}", + "about-libraries-title": "Biblioteker", + "local-files": "Lokale filer", + "hide": "Skjul", + "open-in-browser": "Åbn i browser", + "start-kiwix-server": "Start Kiwix-server", + "stop-kiwix-server": "Stop Kiwix-server", + "all": "Alle", + "pictures": "Billeder", + "videos": "Videoer", + "details": "Fuld artikel", + "yes": "ja", + "no": "nej", + "no-filter": "intet filter", + "invalid-port": "Ugyldig port", + "zim-open-fail-title": "Ugyldig fil", + "zim-open-fail-text": "ZIM-filen {{ZIM}} kunne ikke åbnes korrekt. Den vil blive slettet fra dit bibliotek.", + "next-tab": "Flyt til næste fane", + "previous-tab": "Flyt til forrige fane", + "cancel-download": "Annuller download" +} diff --git a/resources/i18n/dag.json b/resources/i18n/dag.json index 299071216..c75d72030 100644 --- a/resources/i18n/dag.json +++ b/resources/i18n/dag.json @@ -1,136 +1,136 @@ -{ - "@metadata": { - "authors": [ - "Dnshitobu", - "Kalakpagh", - "Munkaila Sulemana", - "Peggy" - ] - }, - "name": "Silimiinsili", - "error-title": "Chirimbu", - "error-downloader-window-title": "Di ku tooi nam deera", - "error-downloader-launch-message": "Haiwezekani kuzindua upakuaji, Kiwix-desktop itaanza lakini kazi zote za upakuaji hazitafanya\n kufanya kazi", - "error-launch-server-message": "Chiriŋ nyɛla din niŋ!", - "error-archive": "Ku tooi nya polo deei niŋ shee ŋɔ", - "open-zim": "Yoomi ZIM Fasara", - "local-kiwix-server": "Tiŋbia Kiwix tum tumda", - "random-article": "Gahim piigi lahabali", - "random-article-error": "Zaɣisiya ni di yoogi gahim piigi lahabali.", - "home-page": "Solɔɣu", - "main-menu": "Piligu shee", - "print": "Paɣibu", - "print-page-error": "Chiriŋ daa nyɛla din niŋ yaabu maa shɛɛ.", - "new-tab": "tab palli", - "close-tab": "kparimi a tab", - "close": "Kparima", - "reopen-closed-tab": "labiyooi tapi shɛli din kpari ma", - "open-file": "Yoomi Faali", - "open-recent": "Yoomi din na yoli niŋ", - "search-article": "Vihimi lahabali", - "search-in-library": "Vihimi kpanjɔɣu ni", - "find-in-page": "Bɔmili yaɣili maa ni", - "set-fullscreen": "Niŋmi diɣi'zaa", - "quit-fullscreen": "Yihimi diɣi'zaa", - "table-of-content": "Din be yaɣili ŋɔ ni", - "reading-list": "Binkarinda", - "zoom-in": "Muɣilim li", - "zoom-out": "Yɛliɣim li", - "zoom-reset": "Zoom labi'niŋ", - "help": "Sɔŋsim", - "feedback": "Labisigu", - "request-a-feature": "Suhi m-bo nahingbaŋ", - "about-kiwix": "Kiwix Lahabali", - "donate-to-support-kiwix": "Mali mi Kiwix sara", - "exit": "Yima", - "save-file-as-window-title": "Zaŋmi faali maa ni kpanjɔɣu ni kamani", - "download-finished-message": "Kundi maa nyɛla din luna.", - "file": "Lahabali kɔligu", - "edit": "Maliniŋ", - "view": "Lihima", - "tools": "Mazagaya", - "window-title": "Karimbu shee - Kiwix", - "search": "Vihima", - "settings": "Bintaɣindisi", - "back": "Nyaaŋa", - "forward": "Tooni", - "search-files": "Vihimi Lahabali kɔlisi", - "title": "Lahabali zuɣu", - "size": "Galisim", - "date": "Dali", - "content-type": "Lahabali balli", - "reset-sort": "labi'niŋ tabo", - "open": "Yooi", - "delete": "Yihima", - "download": "Deei", - "resume": "Tuɣi", - "pause": "Zanibu", - "cancel": "Kpihim", - "apply": "Niŋ", - "port-for-local-kiwix-server-setting": "Yaɣili zaŋ n-ti Kiwix tiŋbia tum tumda", - "zoom-level-setting": "Chiriŋ zoom fahirili", - "download-directory-setting": "Deebu wuhibu", - "monitor-directory-setting": "Lihigu wuhibu", - "reset": "Labi'bari", - "clear": "Neei", - "browse": "Browsɛ", - "about-learn-more-about-kiwix": "Bɔhimmi pam zaŋ jɛndi Kiwix", - "about-release-title": "Gaai", - "about-source-code": "\"Software\" ŋɔ nyɛla bɛ ni daa bahi shɛli tam GNU zalikpana zuɣu 3. Lihimi Kundivihira jia ŋɔ Github.", - "about-reporting-problem-title": "Yɛlli zaŋ wuhi salo", - "about-report-issue-2": "Pahimi suɣilo bolimi yaɣa shɛli yɛlli'gola ŋɔ ni be", - "about-libraries-title": "Karimbu shɛhi", - "fullscreen-notification": "A pa be la diɣi yɛlinli zaa ni saha ŋɔ. Dihimi ESC yi!", - "online-files": "Pohim zuɣu Lahabali kɔlisi", - "local-files": "Dundɔŋ ni Lahabali kɔlisi", - "category": "Pubu", - "language": "Balli", - "hide": "Sɔɣi", - "open-in-browser": "Yooi browzɛr ni", - "start-kiwix-server": "Piligi Kiwix tum tumda", - "stop-kiwix-server": "Taɣimi Kiwix tum tumda", - "all": "Zaa", - "fulltext-search": "Lahabali pali vihibu", - "pictures": "Anfooninima", - "videos": "Siniinima", - "details": "Lahabali pali", - "yes": "iin", - "no": "aayi", - "ok": "tɔr", - "no-filter": "Lɔŋ kani", - "download-dir-dialog-title": "A niŋ dihitabili ni a borimi ni a taɣi deebu wuhibu?", - "download-dir-dialog-msg": "Deebu wuhibu palli soli yɛn nyɛla :\n{{DIRECTORY}}", - "zim-open-fail-title": "Bara fasara", - "zim-open-fail-text": "ZIM fasara ŋɔ {{ZIM}} ku tooi yoogi viɛnyɛla. Bɛ ni yihi li a karimbu shee.", - "monitor-dir-dialog-title": "A niŋ dihitabili ni a borimi ni a taɣi lihigu wuhibu?", - "monitor-dir-dialog-msg": "Lihigu wuhibu palli ŋɔ soli yɛn nyɛla :\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "A niŋ dihitabili ni a borimi ni a neei lihigu wuhibu ŋɔ?", - "monitor-clear-dir-dialog-msg": "Din bɔŋɔ nyɛla din yɛn taɣi ka chɛ lihigu wuhibu kaabu zaŋ n-ti ZIM fasara palli.", - "monitor-directory-tooltip": "ZIM fasara nima zaa din be wuhibu ŋɔ ni nyɛla din daa pahi karimbu shee.", - "next-tab": "Vuui chaŋ binshɛɣ'kam din paya ni.", - "previous-tab": " Vuui chaŋ binshɛɣ'kam kurili", - "cancel-download": "Kpihimi deebu", - "cancel-download-text": "A niŋ dihitabili ni a borimi ni a kpihim deebu zaŋ n-ti {{ZIM}}?", - "delete-book": "Yihimi buku", - "delete-book-text": "A niŋ dihitabili ni a borimi ni a yihi {{ZIM}}?", - "download-storage-error": "Deei niŋ shee Chiriŋ", - "download-unavailable": "Deebu kani", - "download-unavailable-text": "Din bɔŋɔ deebu nyɛla din kani", - "open-book": "Yoomi buku", - "download-book": "Deemi buku", - "pause-download": "Cheli deebu", - "resume-download": "Tuɣimi deebu", - "open-folder": "Yoomi gbaŋ", - "couldnt-open-location": "Bi tooi yoogi dooshee", - "couldnt-open-location-text": "Kiwix bi tooi yoogi gbaŋ {{FOLDER}}", - "move-files-to-trash": "Daami Lahabali kɔlisi shɛŋa a ni nyahi n zaŋ chaŋ saɣari guŋgɔŋ ni", - "move-files-to-trash-text": "Niŋsim ŋɔ nyɛla bin shɛli din ni tooi zaŋ lahabali kɔligu n chaŋ saɣari guŋgɔŋ ni.", - "perma-delete-files-text": "Niŋsim ŋɔ nyɛla bin shɛli din ni tooi nyahi lahabali kɔligu kuli mali wara.", - "clear-filter": "Neemi saha ŋɔ lɔŋsi", - "language-searcher-placeholder": "Lɔŋ balli", - "category-searcher-placeholder": "Lɔŋ pubu", - "content-type-searcher-placeholder": "Lɔŋ lahabali ballibu", - "no-details": "Piligu koŋko", - "no-pictures": "Anfooni nima kani", - "no-videos": "Sinii nima kani" -} +{ + "@metadata": { + "authors": [ + "Dnshitobu", + "Kalakpagh", + "Munkaila Sulemana", + "Peggy" + ] + }, + "name": "Silimiinsili", + "error-title": "Chirimbu", + "error-downloader-window-title": "Di ku tooi nam deera", + "error-downloader-launch-message": "Haiwezekani kuzindua upakuaji, Kiwix-desktop itaanza lakini kazi zote za upakuaji hazitafanya\n kufanya kazi", + "error-launch-server-message": "Chiriŋ nyɛla din niŋ!", + "error-archive": "Ku tooi nya polo deei niŋ shee ŋɔ", + "open-zim": "Yoomi ZIM Fasara", + "local-kiwix-server": "Tiŋbia Kiwix tum tumda", + "random-article": "Gahim piigi lahabali", + "random-article-error": "Zaɣisiya ni di yoogi gahim piigi lahabali.", + "home-page": "Solɔɣu", + "main-menu": "Piligu shee", + "print": "Paɣibu", + "print-page-error": "Chiriŋ daa nyɛla din niŋ yaabu maa shɛɛ.", + "new-tab": "tab palli", + "close-tab": "kparimi a tab", + "close": "Kparima", + "reopen-closed-tab": "labiyooi tapi shɛli din kpari ma", + "open-file": "Yoomi Faali", + "open-recent": "Yoomi din na yoli niŋ", + "search-article": "Vihimi lahabali", + "search-in-library": "Vihimi kpanjɔɣu ni", + "find-in-page": "Bɔmili yaɣili maa ni", + "set-fullscreen": "Niŋmi diɣi'zaa", + "quit-fullscreen": "Yihimi diɣi'zaa", + "table-of-content": "Din be yaɣili ŋɔ ni", + "reading-list": "Binkarinda", + "zoom-in": "Muɣilim li", + "zoom-out": "Yɛliɣim li", + "zoom-reset": "Zoom labi'niŋ", + "help": "Sɔŋsim", + "feedback": "Labisigu", + "request-a-feature": "Suhi m-bo nahingbaŋ", + "about-kiwix": "Kiwix Lahabali", + "donate-to-support-kiwix": "Mali mi Kiwix sara", + "exit": "Yima", + "save-file-as-window-title": "Zaŋmi faali maa ni kpanjɔɣu ni kamani", + "download-finished-message": "Kundi maa nyɛla din luna.", + "file": "Lahabali kɔligu", + "edit": "Maliniŋ", + "view": "Lihima", + "tools": "Mazagaya", + "window-title": "Karimbu shee - Kiwix", + "search": "Vihima", + "settings": "Bintaɣindisi", + "back": "Nyaaŋa", + "forward": "Tooni", + "search-files": "Vihimi Lahabali kɔlisi", + "title": "Lahabali zuɣu", + "size": "Galisim", + "date": "Dali", + "content-type": "Lahabali balli", + "reset-sort": "labi'niŋ tabo", + "open": "Yooi", + "delete": "Yihima", + "download": "Deei", + "resume": "Tuɣi", + "pause": "Zanibu", + "cancel": "Kpihim", + "apply": "Niŋ", + "port-for-local-kiwix-server-setting": "Yaɣili zaŋ n-ti Kiwix tiŋbia tum tumda", + "zoom-level-setting": "Chiriŋ zoom fahirili", + "download-directory-setting": "Deebu wuhibu", + "monitor-directory-setting": "Lihigu wuhibu", + "reset": "Labi'bari", + "clear": "Neei", + "browse": "Browsɛ", + "about-learn-more-about-kiwix": "Bɔhimmi pam zaŋ jɛndi Kiwix", + "about-release-title": "Gaai", + "about-source-code": "\"Software\" ŋɔ nyɛla bɛ ni daa bahi shɛli tam GNU zalikpana zuɣu 3. Lihimi Kundivihira jia ŋɔ Github.", + "about-reporting-problem-title": "Yɛlli zaŋ wuhi salo", + "about-report-issue-2": "Pahimi suɣilo bolimi yaɣa shɛli yɛlli'gola ŋɔ ni be", + "about-libraries-title": "Karimbu shɛhi", + "fullscreen-notification": "A pa be la diɣi yɛlinli zaa ni saha ŋɔ. Dihimi ESC yi!", + "online-files": "Pohim zuɣu Lahabali kɔlisi", + "local-files": "Dundɔŋ ni Lahabali kɔlisi", + "category": "Pubu", + "language": "Balli", + "hide": "Sɔɣi", + "open-in-browser": "Yooi browzɛr ni", + "start-kiwix-server": "Piligi Kiwix tum tumda", + "stop-kiwix-server": "Taɣimi Kiwix tum tumda", + "all": "Zaa", + "fulltext-search": "Lahabali pali vihibu", + "pictures": "Anfooninima", + "videos": "Siniinima", + "details": "Lahabali pali", + "yes": "iin", + "no": "aayi", + "ok": "tɔr", + "no-filter": "Lɔŋ kani", + "download-dir-dialog-title": "A niŋ dihitabili ni a borimi ni a taɣi deebu wuhibu?", + "download-dir-dialog-msg": "Deebu wuhibu palli soli yɛn nyɛla :\n{{DIRECTORY}}", + "zim-open-fail-title": "Bara fasara", + "zim-open-fail-text": "ZIM fasara ŋɔ {{ZIM}} ku tooi yoogi viɛnyɛla. Bɛ ni yihi li a karimbu shee.", + "monitor-dir-dialog-title": "A niŋ dihitabili ni a borimi ni a taɣi lihigu wuhibu?", + "monitor-dir-dialog-msg": "Lihigu wuhibu palli ŋɔ soli yɛn nyɛla :\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "A niŋ dihitabili ni a borimi ni a neei lihigu wuhibu ŋɔ?", + "monitor-clear-dir-dialog-msg": "Din bɔŋɔ nyɛla din yɛn taɣi ka chɛ lihigu wuhibu kaabu zaŋ n-ti ZIM fasara palli.", + "monitor-directory-tooltip": "ZIM fasara nima zaa din be wuhibu ŋɔ ni nyɛla din daa pahi karimbu shee.", + "next-tab": "Vuui chaŋ binshɛɣ'kam din paya ni.", + "previous-tab": " Vuui chaŋ binshɛɣ'kam kurili", + "cancel-download": "Kpihimi deebu", + "cancel-download-text": "A niŋ dihitabili ni a borimi ni a kpihim deebu zaŋ n-ti {{ZIM}}?", + "delete-book": "Yihimi buku", + "delete-book-text": "A niŋ dihitabili ni a borimi ni a yihi {{ZIM}}?", + "download-storage-error": "Deei niŋ shee Chiriŋ", + "download-unavailable": "Deebu kani", + "download-unavailable-text": "Din bɔŋɔ deebu nyɛla din kani", + "open-book": "Yoomi buku", + "download-book": "Deemi buku", + "pause-download": "Cheli deebu", + "resume-download": "Tuɣimi deebu", + "open-folder": "Yoomi gbaŋ", + "couldnt-open-location": "Bi tooi yoogi dooshee", + "couldnt-open-location-text": "Kiwix bi tooi yoogi gbaŋ {{FOLDER}}", + "move-files-to-trash": "Daami Lahabali kɔlisi shɛŋa a ni nyahi n zaŋ chaŋ saɣari guŋgɔŋ ni", + "move-files-to-trash-text": "Niŋsim ŋɔ nyɛla bin shɛli din ni tooi zaŋ lahabali kɔligu n chaŋ saɣari guŋgɔŋ ni.", + "perma-delete-files-text": "Niŋsim ŋɔ nyɛla bin shɛli din ni tooi nyahi lahabali kɔligu kuli mali wara.", + "clear-filter": "Neemi saha ŋɔ lɔŋsi", + "language-searcher-placeholder": "Lɔŋ balli", + "category-searcher-placeholder": "Lɔŋ pubu", + "content-type-searcher-placeholder": "Lɔŋ lahabali ballibu", + "no-details": "Piligu koŋko", + "no-pictures": "Anfooni nima kani", + "no-videos": "Sinii nima kani" +} diff --git a/resources/i18n/de.json b/resources/i18n/de.json index a7f635ae1..5bacc69a7 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -1,156 +1,156 @@ -{ - "@metadata": { - "authors": [ - "Brettchenweber", - "DraconicDark", - "Falwye", - "IMayBeABitShy", - "Justman10000", - "Magiczocker", - "Rofiatmustapha12", - "Susann Schweden", - "ThisCarthing", - "Umlaut", - "Wolfdietmann" - ] - }, - "name": "Deutsch", - "error-title": "Fehler", - "error-downloader-window-title": "Erstellen vom Downloader nicht möglich", - "error-downloader-launch-message": "Der Downloader konnte nicht gestartet werden, Kiwix-desktop wird gestartet, aber alle Funktionen zum Herunterladen werden nicht funktionieren!", - "error-launch-server-message": "Es ist ein Fehler aufgetreten!", - "error-archive": "Das Archiv konnte nicht geöffnet werden.", - "open-zim": "Öffne ZIM-Datei", - "local-kiwix-server": "Lokaler Kiwix-Server", - "random-article": "Zufälliger Artikel", - "random-article-error": "Ein zufälliger Artikel kann nicht geöffnet werden.", - "home-page": "Startseite", - "main-menu": "Hauptmenü", - "print": "Drucken", - "print-page-error": "Beim Drucken ist ein Fehler aufgetreten.", - "new-tab": "Neue Registerkarte", - "close-tab": "Registerkarte schließen", - "close": "Schließen", - "reopen-closed-tab": "Geschlossene Registerkarte wieder öffnen", - "browse-library": "Bibliothek durchsuchen", - "open-file": "Datei öffnen", - "open-recent": "Öffne zuletzt Benutztes", - "search-article": "Such Artikel", - "search-in-library": "Such in der Bibliothek", - "find-in-page": "In Seite suchen", - "set-fullscreen": "Vollbild aktivieren", - "quit-fullscreen": "Vollbild beenden", - "table-of-content": "Inhaltsverzeichnis", - "reading-list": "Leseliste", - "zoom-in": "Vergrößern", - "zoom-out": "Verkleinern", - "zoom-reset": "Zoom zurücksetzen", - "help": "Hilfe", - "feedback": "Rückmeldung", - "report-a-bug": "Einen Fehler melden", - "request-a-feature": "Eine Funktion anfordern", - "about-kiwix": "Über Kiwix", - "donate-to-support-kiwix": "Spenden zur Unterstützung von Kiwix", - "exit": "Beenden", - "save-file-as-window-title": "Datei speichern als", - "download-finished-message": "Das Dokument wurde heruntergeladen.", - "file": "Datei", - "edit": "Bearbeiten", - "view": "Ansehen", - "tools": "Werkzeuge", - "window-title": "Bibliothek - Kiwix", - "search": "Suchen", - "browse-directory": "Verzeichnis durchsuchen", - "settings": "Einstellungen", - "back": "Zurück", - "forward": "Vorwärts", - "search-files": "Dateien durchsuchen", - "title": "Titel", - "size": "Größe", - "date": "Datum", - "content-type": "Inhaltstyp", - "reset-sort": "Sortierung zurücksetzen", - "open": "Öffnen", - "delete": "Löschen", - "download": "Herunterladen", - "resume": "Fortfahren", - "pause": "Pausieren", - "cancel": "Abbrechen", - "apply": "Anwenden", - "port-for-local-kiwix-server-setting": "Port für den lokalen Kiwix-Server:", - "zoom-level-setting": "Zoom-Einstellung", - "download-directory-setting": "Verzeichnis für Downloads", - "monitor-directory-setting": "Zu beobachtender Ordner", - "reset": "Zurücksetzen", - "clear": "Leeren", - "browse": "Durchsuchen", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix ermöglicht es Ihnen, die gesamte Wikipedia immer und überall zur Hand zu haben! Auf einem Boot, mitten im Nirgendwo oder im Gefängnis, Kiwix ermöglicht Ihnen den Zugang zum gesamten menschlichen Wissen. Sie brauchen kein Internet, alles ist auf Ihrem Computer gespeichert.", - "about-learn-more-about-kiwix": "Mehr über Kiwix erfahren", - "about-release-title": "Veröffentlichung", - "about-source-code": "Diese Software unterliegt einer Lizenz, der GNU General Public License Version 3. Anzeige des Quellcodes unter Github.", - "about-version": "Version: {{VERSION}}", - "about-reporting-problem-title": "Meldung von Problemen", - "about-report-issue": "Um ein Problem zu melden, bitte Kiwix-Desktop Issue tracker aufrufen und dort ein Thema erstellen, das Ihr Problem beschreibt.", - "about-report-issue-2": "Bitte im Thema die Version angeben.", - "about-libraries-title": "Bibliotheken", - "kiwix-server-running-message": "Der Kiwix-Server ist aktiv und ein Zugriff ist möglich im lokalen Netzwerk unter:", - "kiwix-server-description": "Wenn ein Server gestartet wird, eröffnet das anderen Rechnern im lokalen Netzwerk die Möglichkeit, mit einem normalen Browser auf Ihre Kiwix-Bibliothek zuzugreifen.", - "fullscreen-notification": "Sie sind im Vollbildmodus. Drücken Sie ESC, um ihn zu verlassen!", - "online-files": "Online Dateien", - "local-files": "Lokale Dateien", - "category": "Kategorie", - "language": "Sprache", - "hide": "Verbergen", - "open-in-browser": "Im Browser öffnen", - "start-kiwix-server": "Kiwix-Server starten", - "stop-kiwix-server": "Kiwix-Server stoppen", - "all": "Alle", - "fulltext-search": "Volltextsuche", - "pictures": "Bilder", - "videos": "Videos", - "ftindex": "Volltextindex", - "details": "Ganzer Artikel", - "yes": "ja", - "no": "nein", - "ok": "Ok", - "no-filter": "kein Filter", - "open-link-in-web-browser": "Link im Browser öffnen", - "download-dir-dialog-title": "Sind Sie sicher, dass Sie das Download-Verzeichnis wechseln möchten?", - "download-dir-dialog-msg": "Der neue Download-Verzeichnis-Pfad ist:\n{{DIRECTORY}}", - "invalid-port": "Ungültiger Port", - "zim-open-fail-title": "Ungültige Datei", - "zim-open-fail-text": "Die ZIM-Datei {{ZIM}} kann nicht ordnungsgemäß geöffnet werden. Sie wird aus Ihrer Bibliothek entfernt.", - "monitor-dir-dialog-title": "Sind Sie sicher, dass Sie das zu beobachtende Verzeichnis ändern wollen?", - "monitor-dir-dialog-msg": "Der Pfad des zu beobachtenden Ordners wird auf Folgendes geändert: {{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Sind Sie sicher, dass Sie den zu beobachtenden Ordner leeren wollen?", - "monitor-clear-dir-dialog-msg": "Hierdurch wird der zu beobachtende Ordner nicht mehr nach neu hinzugekommenen ZIM-Dateien durchsucht.", - "monitor-directory-tooltip": "Alle ZIM-Dateien in diesem Verzeichnis werden automatisch zur Bibliothek hinzugefügt.", - "next-tab": "Zur nächsten Registerkarte wechseln", - "previous-tab": "Zur vorherigen Registerkarte wechseln", - "cancel-download": "Download abbrechen", - "cancel-download-text": "Sind Sie sicher, dass Sie den Download von {{ZIM}} abbrechen wollen?", - "delete-book": "Buch löschen", - "delete-book-text": "Sind Sie sicher, dass Sie {{ZIM}} löschen wollen?", - "download-storage-error": "Speicherfehler", - "download-storage-error-text": "Das System hat nicht genug Speicherplatz zur Verfügung.", - "download-unavailable": "Download nicht verfügbar", - "download-unavailable-text": "Dieser Download ist nicht verfügbar.", - "open-book": "Buch öffnen", - "download-book": "Buch herunterladen", - "pause-download": "Download pausieren", - "resume-download": "Download fortsetzen", - "open-folder": "Ordner öffnen", - "couldnt-open-location": "Pfad konnte nicht geöffnet werden", - "couldnt-open-location-text": "Es ist Kiwix nicht möglich, den Ordner {{FOLDER}} zu öffnen", - "move-files-to-trash": "Gelöschte Dateien in den Papierkorb verschieben", - "move-files-to-trash-text": "Durch diese Aktion wird die Datei in den Papierkorb verschoben.", - "perma-delete-files-text": "Durch diese Aktion wird die Datei unwiderruflich gelöscht.", - "clear-filter": "Aktuell eingestellte Filter Löschen", - "language-searcher-placeholder": "Nach Sprache filtern", - "category-searcher-placeholder": "Nach Kategorie filtern", - "content-type-searcher-placeholder": "Nach Inhaltstyp filtern", - "no-details": "Nur Einführung", - "no-pictures": "Keine Bilder", - "no-videos": "Keine Videos" -} +{ + "@metadata": { + "authors": [ + "Brettchenweber", + "DraconicDark", + "Falwye", + "IMayBeABitShy", + "Justman10000", + "Magiczocker", + "Rofiatmustapha12", + "Susann Schweden", + "ThisCarthing", + "Umlaut", + "Wolfdietmann" + ] + }, + "name": "Deutsch", + "error-title": "Fehler", + "error-downloader-window-title": "Erstellen vom Downloader nicht möglich", + "error-downloader-launch-message": "Der Downloader konnte nicht gestartet werden, Kiwix-desktop wird gestartet, aber alle Funktionen zum Herunterladen werden nicht funktionieren!", + "error-launch-server-message": "Es ist ein Fehler aufgetreten!", + "error-archive": "Das Archiv konnte nicht geöffnet werden.", + "open-zim": "Öffne ZIM-Datei", + "local-kiwix-server": "Lokaler Kiwix-Server", + "random-article": "Zufälliger Artikel", + "random-article-error": "Ein zufälliger Artikel kann nicht geöffnet werden.", + "home-page": "Startseite", + "main-menu": "Hauptmenü", + "print": "Drucken", + "print-page-error": "Beim Drucken ist ein Fehler aufgetreten.", + "new-tab": "Neue Registerkarte", + "close-tab": "Registerkarte schließen", + "close": "Schließen", + "reopen-closed-tab": "Geschlossene Registerkarte wieder öffnen", + "browse-library": "Bibliothek durchsuchen", + "open-file": "Datei öffnen", + "open-recent": "Öffne zuletzt Benutztes", + "search-article": "Such Artikel", + "search-in-library": "Such in der Bibliothek", + "find-in-page": "In Seite suchen", + "set-fullscreen": "Vollbild aktivieren", + "quit-fullscreen": "Vollbild beenden", + "table-of-content": "Inhaltsverzeichnis", + "reading-list": "Leseliste", + "zoom-in": "Vergrößern", + "zoom-out": "Verkleinern", + "zoom-reset": "Zoom zurücksetzen", + "help": "Hilfe", + "feedback": "Rückmeldung", + "report-a-bug": "Einen Fehler melden", + "request-a-feature": "Eine Funktion anfordern", + "about-kiwix": "Über Kiwix", + "donate-to-support-kiwix": "Spenden zur Unterstützung von Kiwix", + "exit": "Beenden", + "save-file-as-window-title": "Datei speichern als", + "download-finished-message": "Das Dokument wurde heruntergeladen.", + "file": "Datei", + "edit": "Bearbeiten", + "view": "Ansehen", + "tools": "Werkzeuge", + "window-title": "Bibliothek - Kiwix", + "search": "Suchen", + "browse-directory": "Verzeichnis durchsuchen", + "settings": "Einstellungen", + "back": "Zurück", + "forward": "Vorwärts", + "search-files": "Dateien durchsuchen", + "title": "Titel", + "size": "Größe", + "date": "Datum", + "content-type": "Inhaltstyp", + "reset-sort": "Sortierung zurücksetzen", + "open": "Öffnen", + "delete": "Löschen", + "download": "Herunterladen", + "resume": "Fortfahren", + "pause": "Pausieren", + "cancel": "Abbrechen", + "apply": "Anwenden", + "port-for-local-kiwix-server-setting": "Port für den lokalen Kiwix-Server:", + "zoom-level-setting": "Zoom-Einstellung", + "download-directory-setting": "Verzeichnis für Downloads", + "monitor-directory-setting": "Zu beobachtender Ordner", + "reset": "Zurücksetzen", + "clear": "Leeren", + "browse": "Durchsuchen", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix ermöglicht es Ihnen, die gesamte Wikipedia immer und überall zur Hand zu haben! Auf einem Boot, mitten im Nirgendwo oder im Gefängnis, Kiwix ermöglicht Ihnen den Zugang zum gesamten menschlichen Wissen. Sie brauchen kein Internet, alles ist auf Ihrem Computer gespeichert.", + "about-learn-more-about-kiwix": "Mehr über Kiwix erfahren", + "about-release-title": "Veröffentlichung", + "about-source-code": "Diese Software unterliegt einer Lizenz, der GNU General Public License Version 3. Anzeige des Quellcodes unter Github.", + "about-version": "Version: {{VERSION}}", + "about-reporting-problem-title": "Meldung von Problemen", + "about-report-issue": "Um ein Problem zu melden, bitte Kiwix-Desktop Issue tracker aufrufen und dort ein Thema erstellen, das Ihr Problem beschreibt.", + "about-report-issue-2": "Bitte im Thema die Version angeben.", + "about-libraries-title": "Bibliotheken", + "kiwix-server-running-message": "Der Kiwix-Server ist aktiv und ein Zugriff ist möglich im lokalen Netzwerk unter:", + "kiwix-server-description": "Wenn ein Server gestartet wird, eröffnet das anderen Rechnern im lokalen Netzwerk die Möglichkeit, mit einem normalen Browser auf Ihre Kiwix-Bibliothek zuzugreifen.", + "fullscreen-notification": "Sie sind im Vollbildmodus. Drücken Sie ESC, um ihn zu verlassen!", + "online-files": "Online Dateien", + "local-files": "Lokale Dateien", + "category": "Kategorie", + "language": "Sprache", + "hide": "Verbergen", + "open-in-browser": "Im Browser öffnen", + "start-kiwix-server": "Kiwix-Server starten", + "stop-kiwix-server": "Kiwix-Server stoppen", + "all": "Alle", + "fulltext-search": "Volltextsuche", + "pictures": "Bilder", + "videos": "Videos", + "ftindex": "Volltextindex", + "details": "Ganzer Artikel", + "yes": "ja", + "no": "nein", + "ok": "Ok", + "no-filter": "kein Filter", + "open-link-in-web-browser": "Link im Browser öffnen", + "download-dir-dialog-title": "Sind Sie sicher, dass Sie das Download-Verzeichnis wechseln möchten?", + "download-dir-dialog-msg": "Der neue Download-Verzeichnis-Pfad ist:\n{{DIRECTORY}}", + "invalid-port": "Ungültiger Port", + "zim-open-fail-title": "Ungültige Datei", + "zim-open-fail-text": "Die ZIM-Datei {{ZIM}} kann nicht ordnungsgemäß geöffnet werden. Sie wird aus Ihrer Bibliothek entfernt.", + "monitor-dir-dialog-title": "Sind Sie sicher, dass Sie das zu beobachtende Verzeichnis ändern wollen?", + "monitor-dir-dialog-msg": "Der Pfad des zu beobachtenden Ordners wird auf Folgendes geändert: {{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Sind Sie sicher, dass Sie den zu beobachtenden Ordner leeren wollen?", + "monitor-clear-dir-dialog-msg": "Hierdurch wird der zu beobachtende Ordner nicht mehr nach neu hinzugekommenen ZIM-Dateien durchsucht.", + "monitor-directory-tooltip": "Alle ZIM-Dateien in diesem Verzeichnis werden automatisch zur Bibliothek hinzugefügt.", + "next-tab": "Zur nächsten Registerkarte wechseln", + "previous-tab": "Zur vorherigen Registerkarte wechseln", + "cancel-download": "Download abbrechen", + "cancel-download-text": "Sind Sie sicher, dass Sie den Download von {{ZIM}} abbrechen wollen?", + "delete-book": "Buch löschen", + "delete-book-text": "Sind Sie sicher, dass Sie {{ZIM}} löschen wollen?", + "download-storage-error": "Speicherfehler", + "download-storage-error-text": "Das System hat nicht genug Speicherplatz zur Verfügung.", + "download-unavailable": "Download nicht verfügbar", + "download-unavailable-text": "Dieser Download ist nicht verfügbar.", + "open-book": "Buch öffnen", + "download-book": "Buch herunterladen", + "pause-download": "Download pausieren", + "resume-download": "Download fortsetzen", + "open-folder": "Ordner öffnen", + "couldnt-open-location": "Pfad konnte nicht geöffnet werden", + "couldnt-open-location-text": "Es ist Kiwix nicht möglich, den Ordner {{FOLDER}} zu öffnen", + "move-files-to-trash": "Gelöschte Dateien in den Papierkorb verschieben", + "move-files-to-trash-text": "Durch diese Aktion wird die Datei in den Papierkorb verschoben.", + "perma-delete-files-text": "Durch diese Aktion wird die Datei unwiderruflich gelöscht.", + "clear-filter": "Aktuell eingestellte Filter Löschen", + "language-searcher-placeholder": "Nach Sprache filtern", + "category-searcher-placeholder": "Nach Kategorie filtern", + "content-type-searcher-placeholder": "Nach Inhaltstyp filtern", + "no-details": "Nur Einführung", + "no-pictures": "Keine Bilder", + "no-videos": "Keine Videos" +} diff --git a/resources/i18n/diq.json b/resources/i18n/diq.json index cb108b0fa..db5d62dbd 100644 --- a/resources/i18n/diq.json +++ b/resources/i18n/diq.json @@ -1,106 +1,106 @@ -{ - "@metadata": { - "authors": [ - "1917 Ekim Devrimi", - "Mirzali", - "Orbot707" - ] - }, - "name": "Zazaki", - "error-title": "Xeta", - "error-downloader-window-title": "Ronayoğ nêvırazeno", - "error-downloader-launch-message": "Kewtışê ronayoği mımkın niyo ke serê masaya Kiwixi bıguriyo, feqet karê ronayışi pêro nêguriyeno!", - "error-launch-server-message": "Yew xeta biye!", - "open-zim": "Zim'i akerê", - "local-kiwix-server": "Mehali Serverê Kiwixi", - "random-article": "Raştameye meqale", - "home-page": "Pela Seri", - "main-menu": "Menuyo esas", - "print": "Çap ke", - "new-tab": "Taba newiye", - "close-tab": "Tab racıne", - "close": "Kip ke", - "reopen-closed-tab": "Racınayi tabi reyna akerê", - "browse-library": "Çımberze kıtıbxane", - "open-file": "Dosya ake", - "open-recent": "Bahdoyên ake", - "search-article": "Meqale cı geyre", - "search-in-library": "Kıtıbxaney de cıgeyrayış", - "find-in-page": "Pela vinayış", - "set-fullscreen": "Tam ekran eyar kerê", - "quit-fullscreen": "Tam ekran ra bıvıciyê", - "table-of-content": "Tabloy zerreki", - "reading-list": "Lista wanayışi", - "zoom-in": "Nezdı fiyê", - "zoom-out": "Duri fiyê", - "zoom-reset": "Zoomi reset ke", - "help": "Pheşti", - "feedback": "Peyderıştış", - "report-a-bug": "Xeta rapor ke", - "request-a-feature": "Xısusiyetê bıwazê", - "about-kiwix": "Heqa Kiwixi", - "donate-to-support-kiwix": "Pheştiya Kiwixi bec bıdê", - "exit": "Veciyayış", - "save-file-as-window-title": "Ciya qeyd kereu", - "download-finished-message": "Dokuman roniya", - "file": "Dosya", - "edit": "Bıvurnê", - "view": "Bıvêne", - "tools": "Haceti", - "window-title": "Kıtıbxane - Kiwix", - "search": "Cıgeyrayış", - "browse-directory": "Çımberze Rêze", - "settings": "Eyari", - "back": "Peyser", - "forward": "Şıkıtış", - "search-files": "Dosya cıgeyrayış", - "title": "Serey", - "size": "Ebad", - "date": "Tarix", - "content-type": "Tewrê zerreki", - "reset-sort": "Rêzer reset ke", - "open": "Ake", - "delete": "Bestere", - "download": "Ronê", - "resume": "Dewam kerê", - "pause": "Bıvındarne", - "cancel": "Bıtexelne", - "apply": "Tetbiq bıkerê", - "port-for-local-kiwix-server-setting": "Nuxta gıreyê ardoğê Kiwixiyo mehali", - "zoom-level-setting": "Sewiyaya nêzdikerdışiya hesebiyayiye", - "download-directory-setting": "Zerrekê ronayışi", - "reset": "Reset kerê", - "clear": "Pak ke", - "browse": "Çımberze", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix şıma koti ke vanê se şırê wikipedia daba şıma desti bın! Yee kelek de, warte dê cayê de ya zi hebısxane de, Kiwik şıma rê resayışê insanan pêroyın kena. İhtiyacê şıma interneti ra zi çıniyo, pêro komputerê şıma de nımneyêno.", - "about-learn-more-about-kiwix": "Heqa Kiwixi teyna melumat", - "about-release-title": "Ramate", - "about-source-code": "No software GNU şertanê Umumiyê Lisansê Şari 3ın bın de neşr biyo. Kodê çımey Github sero bımusê.", - "about-version": "Versiyon: {{VERSION}}", - "about-reporting-problem-title": "Xırabey rapor kerê", - "about-report-issue": "Yew mewzu akerdışi rê Kiwix Desktopi Seyriyan mewzu ziyaret kerê u persê ho izah kerdê yew mewzu akerê.", - "about-report-issue-2": "Xerpiyaye versiyoni bınısne", - "about-libraries-title": "Kıtabxaney", - "kiwix-server-running-message": "Serverê kiwixiyo guriyeno, pirıka mehaliye de be adrese ra şenê cı resê:", - "kiwix-server-description": "Serveri sıftekerdış, tora mehalli de komputeranê binan ra standard yew rovıteri ra kıtabxaney Kiwixê şıma resayışi re mısade dano.", - "fullscreen-notification": "Enewke modê tam ekranı derê.veciyayışi rê ESC bıpıloğne", - "local-files": "Dosya mehali", - "hide": "Bınımnê", - "open-in-browser": "Browseri de akerê", - "start-kiwix-server": "Serverê Kiwixi bıgureynê", - "stop-kiwix-server": "Serverê Kiwixi vındarne", - "all": "Pêro", - "fulltext-search": "Tam Metin cıgeyrayış", - "pictures": "Resımi", - "videos": "Videoy", - "ftindex": "Tammetin index", - "details": "Meqale pêro", - "yes": "eya", - "no": "nê", - "no-filter": "parzûn çıniyo", - "open-link-in-web-browser": "Browserê webi de gırey akerê", - "download-dir-dialog-title": "Şıma qayılê ke zerrekê ronayışi bıvurnê?", - "download-dir-dialog-msg": "Zerrekê ronayışiyo newe do wına bo:\n{{DIRECTORY}}", - "invalid-port": "Nuqtaya xırab" -} +{ + "@metadata": { + "authors": [ + "1917 Ekim Devrimi", + "Mirzali", + "Orbot707" + ] + }, + "name": "Zazaki", + "error-title": "Xeta", + "error-downloader-window-title": "Ronayoğ nêvırazeno", + "error-downloader-launch-message": "Kewtışê ronayoği mımkın niyo ke serê masaya Kiwixi bıguriyo, feqet karê ronayışi pêro nêguriyeno!", + "error-launch-server-message": "Yew xeta biye!", + "open-zim": "Zim'i akerê", + "local-kiwix-server": "Mehali Serverê Kiwixi", + "random-article": "Raştameye meqale", + "home-page": "Pela Seri", + "main-menu": "Menuyo esas", + "print": "Çap ke", + "new-tab": "Taba newiye", + "close-tab": "Tab racıne", + "close": "Kip ke", + "reopen-closed-tab": "Racınayi tabi reyna akerê", + "browse-library": "Çımberze kıtıbxane", + "open-file": "Dosya ake", + "open-recent": "Bahdoyên ake", + "search-article": "Meqale cı geyre", + "search-in-library": "Kıtıbxaney de cıgeyrayış", + "find-in-page": "Pela vinayış", + "set-fullscreen": "Tam ekran eyar kerê", + "quit-fullscreen": "Tam ekran ra bıvıciyê", + "table-of-content": "Tabloy zerreki", + "reading-list": "Lista wanayışi", + "zoom-in": "Nezdı fiyê", + "zoom-out": "Duri fiyê", + "zoom-reset": "Zoomi reset ke", + "help": "Pheşti", + "feedback": "Peyderıştış", + "report-a-bug": "Xeta rapor ke", + "request-a-feature": "Xısusiyetê bıwazê", + "about-kiwix": "Heqa Kiwixi", + "donate-to-support-kiwix": "Pheştiya Kiwixi bec bıdê", + "exit": "Veciyayış", + "save-file-as-window-title": "Ciya qeyd kereu", + "download-finished-message": "Dokuman roniya", + "file": "Dosya", + "edit": "Bıvurnê", + "view": "Bıvêne", + "tools": "Haceti", + "window-title": "Kıtıbxane - Kiwix", + "search": "Cıgeyrayış", + "browse-directory": "Çımberze Rêze", + "settings": "Eyari", + "back": "Peyser", + "forward": "Şıkıtış", + "search-files": "Dosya cıgeyrayış", + "title": "Serey", + "size": "Ebad", + "date": "Tarix", + "content-type": "Tewrê zerreki", + "reset-sort": "Rêzer reset ke", + "open": "Ake", + "delete": "Bestere", + "download": "Ronê", + "resume": "Dewam kerê", + "pause": "Bıvındarne", + "cancel": "Bıtexelne", + "apply": "Tetbiq bıkerê", + "port-for-local-kiwix-server-setting": "Nuxta gıreyê ardoğê Kiwixiyo mehali", + "zoom-level-setting": "Sewiyaya nêzdikerdışiya hesebiyayiye", + "download-directory-setting": "Zerrekê ronayışi", + "reset": "Reset kerê", + "clear": "Pak ke", + "browse": "Çımberze", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix şıma koti ke vanê se şırê wikipedia daba şıma desti bın! Yee kelek de, warte dê cayê de ya zi hebısxane de, Kiwik şıma rê resayışê insanan pêroyın kena. İhtiyacê şıma interneti ra zi çıniyo, pêro komputerê şıma de nımneyêno.", + "about-learn-more-about-kiwix": "Heqa Kiwixi teyna melumat", + "about-release-title": "Ramate", + "about-source-code": "No software GNU şertanê Umumiyê Lisansê Şari 3ın bın de neşr biyo. Kodê çımey Github sero bımusê.", + "about-version": "Versiyon: {{VERSION}}", + "about-reporting-problem-title": "Xırabey rapor kerê", + "about-report-issue": "Yew mewzu akerdışi rê Kiwix Desktopi Seyriyan mewzu ziyaret kerê u persê ho izah kerdê yew mewzu akerê.", + "about-report-issue-2": "Xerpiyaye versiyoni bınısne", + "about-libraries-title": "Kıtabxaney", + "kiwix-server-running-message": "Serverê kiwixiyo guriyeno, pirıka mehaliye de be adrese ra şenê cı resê:", + "kiwix-server-description": "Serveri sıftekerdış, tora mehalli de komputeranê binan ra standard yew rovıteri ra kıtabxaney Kiwixê şıma resayışi re mısade dano.", + "fullscreen-notification": "Enewke modê tam ekranı derê.veciyayışi rê ESC bıpıloğne", + "local-files": "Dosya mehali", + "hide": "Bınımnê", + "open-in-browser": "Browseri de akerê", + "start-kiwix-server": "Serverê Kiwixi bıgureynê", + "stop-kiwix-server": "Serverê Kiwixi vındarne", + "all": "Pêro", + "fulltext-search": "Tam Metin cıgeyrayış", + "pictures": "Resımi", + "videos": "Videoy", + "ftindex": "Tammetin index", + "details": "Meqale pêro", + "yes": "eya", + "no": "nê", + "no-filter": "parzûn çıniyo", + "open-link-in-web-browser": "Browserê webi de gırey akerê", + "download-dir-dialog-title": "Şıma qayılê ke zerrekê ronayışi bıvurnê?", + "download-dir-dialog-msg": "Zerrekê ronayışiyo newe do wına bo:\n{{DIRECTORY}}", + "invalid-port": "Nuqtaya xırab" +} diff --git a/resources/i18n/en.json b/resources/i18n/en.json index e0d54e121..504963a8b 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -1,192 +1,192 @@ -{ - "@metadata": { - "authors": [ - ] - }, - "name":"English", - "error-title":"Error", - "error-downloader-window-title":"Cannot create downloader", - "error-downloader-launch-message":"Impossible to launch downloader. Kiwix-desktop will start, but all download functions will not work.", - "error-launch-server-message":"An error has occured!", - "error-archive":"Cannot get the archive", - "error-opening-file": "There was an error opening the file.", - "open-zim":"Open ZIM File", - "local-kiwix-server":"Local Kiwix Server", - "random-article":"Random Article", - "random-article-error":"Failed to open a random article.", - "home-page":"Home page", - "main-menu":"Main menu", - "print":"Print", - "print-page-error":"An error has occured while printing.", - "new-tab":"New tab", - "close-tab":"Close tab", - "close":"Close", - "reopen-closed-tab":"Reopen closed tab", - "browse-library":"Browse library", - "open-file":"Open file", - "save-file": "Save file", - "open-recent":"Open recent", - "search-article":"Search article", - "search-in-library":"Search in library", - "find-in-page":"Find in page", - "set-fullscreen":"Set fullscreen", - "quit-fullscreen":"Quit fullScreen", - "table-of-content":"Table of content", - "add-bookmark": "Add to the reading list", - "remove-bookmark": "Remove from the reading list", - "reading-list":"Reading List", - "zoom-in":"Zoom in", - "zoom-out":"Zoom out", - "zoom-reset":"Zoom reset", - "help":"Help", - "feedback":"Feedback", - "report-a-bug":"Report a bug", - "request-a-feature":"Request a feature", - "about-kiwix":"About Kiwix", - "donate-to-support-kiwix":"Donate to support Kiwix", - "exit":"Exit", - "save-file-as-window-title":"Save File as", - "download-finished": "Download Finished", - "download-finished-message":"The document has been downloaded.", - "file":"File", - "edit":"Edit", - "view":"View", - "tools":"Tools", - "window-title":"Library - Kiwix", - "search":"Search", - "browse-directory":"Browse Directory", - "settings":"Settings", - "back":"Back", - "forward":"Forward", - "search-files":"Search files", - "title":"Title", - "size":"Size", - "date":"Date", - "content-type":"Content type", - "reset-sort":"reset sort", - "open":"Open", - "delete":"Delete", - "download":"Download", - "resume":"Resume", - "pause":"Pause", - "cancel":"Cancel", - "apply":"Apply", - "port-for-local-kiwix-server-setting":"Port for local Kiwix server", - "zoom-level-setting":"Default zoom level", - "download-directory-setting":"Download directory", - "monitor-directory-setting":"Monitor directory", - "reset":"Reset", - "clear":"Clear", - "browse":"Browse", - "about-kiwix-desktop-title":"Kiwix Desktop", - "about-kiwix-desktop-description":"Kiwix allows you to have the entirety of Wikipedia at hand wherever you go! On a boat, in the middle of nowhere, or in jail, Kiwix gives you access to all of human knowledge. You don't need Internet, everything is stored on your computer.", - "about-learn-more-about-kiwix":"Learn more about Kiwix", - "about-privacy-policy":"Privacy policy", - "about-release-title":"Release", - "about-source-code":"This software is released under the terms of the GNU General Public License version 3. View the source code on Github.", - "about-version":"Version: {{VERSION}}", - "about-reporting-problem-title":"Reporting problems", - "about-report-issue":"To open an issue, visit Kiwix-desktop issue tracker and open an issue describing your problem.", - "about-report-issue-2":"Please mention the version in the issue.", - "about-libraries-title":"Libraries", - "kiwix-server-running-message":"The Kiwix Server is running and can be accessed in the local network at:", - "kiwix-server-description":"Starting a server allows other computers in the local network to access your Kiwix library with a standard web browser.", - "fullscreen-notification":"You are now in full screen mode. Press ESC to quit!", - "online-files":"Online Files", - "local-files":"Local Files", - "category":"Category", - "language":"Language", - "hide":"Hide", - "open-in-browser":"Open in browser", - "start-kiwix-server":"Start Kiwix Server", - "stop-kiwix-server":"Stop Kiwix Server", - "all":"All", - "all_ips":"All - Dual Stack Mode", - "ipv4":"IPv4 Only Mode", - "ipv6":"IPv6 Only Mode", - "fulltext-search":"Fulltext search", - "pictures":"Pictures", - "videos":"Videos", - "ftindex":"Fulltext index", - "details":"Full article", - "yes":"yes", - "no":"no", - "ok":"ok", - "no-filter":"no filter", - "open-link-in-web-browser":"Open link in web browser", - "open-link-new-tab":"Open link in new tab", - "download-dir-dialog-title":"Are you sure you want to change the download directory?", - "download-dir-dialog-msg":"The new download directory path will be:\n{{DIRECTORY}}", - "invalid-port":"Invalid port", - "zim-open-fail-title":"Invalid file", - "zim-open-fail-text":"The ZIM file {{ZIM}} cannot be opened properly. It will be removed from your library.", - "monitor-dir-dialog-title":"Are you sure you want to change the monitor directory?", - "monitor-dir-dialog-msg":"The new monitor directory path will be:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title":"Are you sure you want to clear the monitor directory?", - "path-was-copied": "Path was copied", - "monitor-clear-dir-dialog-msg":"This will stop checking the monitor directory for new ZIM files.", - "monitor-directory-tooltip":"All ZIM files in this directory will be automatically added to the library.", - "next-tab":"Move to next tab", - "previous-tab":"Move to previous tab", - "cancel-download": "Cancel download", - "cancel-download-text": "Are you sure you want to cancel the download of {{ZIM}}?", - "delete-book": "Delete book", - "delete-book-text": "Are you sure you want to delete {{ZIM}}?", - "download-storage-error": "Storage Error", - "download-storage-error-text": "The system doesn't have enough storage available.", - "download-exceeds-max-file-size": "Download size exceeds the max file size supported by the target filesystem.", - "download-dir-missing": "Download directory doesn't exist.", - "download-dir-not-writable": "Download directory is not writable.", - "download-unavailable": "Download Unavailable", - "download-unavailable-text": "This download is unavailable.", - "open-book": "Open book", - "download-book": "Download book", - "pause-download": "Pause download", - "resume-download": "Resume download", - "open-folder": "Open folder", - "couldnt-open-location": "Couldn't open location", - "couldnt-open-location-text": "Kiwix is not able to open folder {{FOLDER}}", - "move-files-to-trash": "Move deleted files to trash", - "move-files-to-trash-text": "This action will move the file to trash.", - "perma-delete-files-text": "This action will permanently delete the file.", - "clear-filter": "Clear the currently set filters", - "language-searcher-placeholder": "Filter by language", - "category-searcher-placeholder": "Filter by category", - "content-type-searcher-placeholder": "Filter by content type", - "no-details": "Introduction only", - "no-pictures": "No Pictures", - "no-videos": "No Videos", - "open-previous-tabs-at-startup": "Open previous tabs at startup", - "preview-book-in-web-browser": "Preview book in web browser", - "file-not-found-title": "ZIM File Not Found", - "file-not-found-text": "ZIM file doesn't exist or is not readable", - "zim-id": "ZIM Id", - "zim-name": "ZIM Name", - "zim-path": "ZIM File Path", - "bad-zim-file-error-page-title": "Invalid ZIM File", - "bad-zim-file-error-page-text": "ZIM file is invalid or corrupted.", - "zim-entry-path": "ZIM Entry Path", - "export-reading-list": "Export reading list", - "export-reading-list-error": "An error has occured during export of the reading list.", - "import-reading-list": "Import reading list", - "import-reading-list-error": "An error has occured during import of the reading list.", - "disable-sandbox": "Kiwix has been launched from a network drive. This is known to cause compatibility issues with the browsing sandboxing. As a result, the sandbox will be disabled. Do you want to continue?", - "save-page-as": "Save As...", - "portable-disabled-tooltip": "Function disabled in portable mode", - "scroll-next-tab": "Scroll to next tab", - "scroll-previous-tab": "Scroll to previous tab", - "kiwix-search": "Kiwix search", - "search-options": "Search Options", - "read-article": "Read article", - "read-text": "Read selected text", - "read-stop": "Stop reading", - "stop": "Stop", - "voice": "Voice", - "select-read-voice": "Select reading voice", - "select-read-language": "Select reading language", - "save-or-open": "Save or Open file", - "save-or-open-text": "What should Kiwix do with this file?", - "speed": "Speed", - "increase-tts-speed": "Increase TTS speed.", - "decrease-tts-speed": "Decrease TTS speed." -} +{ + "@metadata": { + "authors": [ + ] + }, + "name":"English", + "error-title":"Error", + "error-downloader-window-title":"Cannot create downloader", + "error-downloader-launch-message":"Impossible to launch downloader. Kiwix-desktop will start, but all download functions will not work.", + "error-launch-server-message":"An error has occured!", + "error-archive":"Cannot get the archive", + "error-opening-file": "There was an error opening the file.", + "open-zim":"Open ZIM File", + "local-kiwix-server":"Local Kiwix Server", + "random-article":"Random Article", + "random-article-error":"Failed to open a random article.", + "home-page":"Home page", + "main-menu":"Main menu", + "print":"Print", + "print-page-error":"An error has occured while printing.", + "new-tab":"New tab", + "close-tab":"Close tab", + "close":"Close", + "reopen-closed-tab":"Reopen closed tab", + "browse-library":"Browse library", + "open-file":"Open file", + "save-file": "Save file", + "open-recent":"Open recent", + "search-article":"Search article", + "search-in-library":"Search in library", + "find-in-page":"Find in page", + "set-fullscreen":"Set fullscreen", + "quit-fullscreen":"Quit fullScreen", + "table-of-content":"Table of content", + "add-bookmark": "Add to the reading list", + "remove-bookmark": "Remove from the reading list", + "reading-list":"Reading List", + "zoom-in":"Zoom in", + "zoom-out":"Zoom out", + "zoom-reset":"Zoom reset", + "help":"Help", + "feedback":"Feedback", + "report-a-bug":"Report a bug", + "request-a-feature":"Request a feature", + "about-kiwix":"About Kiwix", + "donate-to-support-kiwix":"Donate to support Kiwix", + "exit":"Exit", + "save-file-as-window-title":"Save File as", + "download-finished": "Download Finished", + "download-finished-message":"The document has been downloaded.", + "file":"File", + "edit":"Edit", + "view":"View", + "tools":"Tools", + "window-title":"Library - Kiwix", + "search":"Search", + "browse-directory":"Browse Directory", + "settings":"Settings", + "back":"Back", + "forward":"Forward", + "search-files":"Search files", + "title":"Title", + "size":"Size", + "date":"Date", + "content-type":"Content type", + "reset-sort":"reset sort", + "open":"Open", + "delete":"Delete", + "download":"Download", + "resume":"Resume", + "pause":"Pause", + "cancel":"Cancel", + "apply":"Apply", + "port-for-local-kiwix-server-setting":"Port for local Kiwix server", + "zoom-level-setting":"Default zoom level", + "download-directory-setting":"Download directory", + "monitor-directory-setting":"Monitor directory", + "reset":"Reset", + "clear":"Clear", + "browse":"Browse", + "about-kiwix-desktop-title":"Kiwix Desktop", + "about-kiwix-desktop-description":"Kiwix allows you to have the entirety of Wikipedia at hand wherever you go! On a boat, in the middle of nowhere, or in jail, Kiwix gives you access to all of human knowledge. You don't need Internet, everything is stored on your computer.", + "about-learn-more-about-kiwix":"Learn more about Kiwix", + "about-privacy-policy":"Privacy policy", + "about-release-title":"Release", + "about-source-code":"This software is released under the terms of the GNU General Public License version 3. View the source code on Github.", + "about-version":"Version: {{VERSION}}", + "about-reporting-problem-title":"Reporting problems", + "about-report-issue":"To open an issue, visit Kiwix-desktop issue tracker and open an issue describing your problem.", + "about-report-issue-2":"Please mention the version in the issue.", + "about-libraries-title":"Libraries", + "kiwix-server-running-message":"The Kiwix Server is running and can be accessed in the local network at:", + "kiwix-server-description":"Starting a server allows other computers in the local network to access your Kiwix library with a standard web browser.", + "fullscreen-notification":"You are now in full screen mode. Press ESC to quit!", + "online-files":"Online Files", + "local-files":"Local Files", + "category":"Category", + "language":"Language", + "hide":"Hide", + "open-in-browser":"Open in browser", + "start-kiwix-server":"Start Kiwix Server", + "stop-kiwix-server":"Stop Kiwix Server", + "all":"All", + "all_ips":"All - Dual Stack Mode", + "ipv4":"IPv4 Only Mode", + "ipv6":"IPv6 Only Mode", + "fulltext-search":"Fulltext search", + "pictures":"Pictures", + "videos":"Videos", + "ftindex":"Fulltext index", + "details":"Full article", + "yes":"yes", + "no":"no", + "ok":"ok", + "no-filter":"no filter", + "open-link-in-web-browser":"Open link in web browser", + "open-link-new-tab":"Open link in new tab", + "download-dir-dialog-title":"Are you sure you want to change the download directory?", + "download-dir-dialog-msg":"The new download directory path will be:\n{{DIRECTORY}}", + "invalid-port":"Invalid port", + "zim-open-fail-title":"Invalid file", + "zim-open-fail-text":"The ZIM file {{ZIM}} cannot be opened properly. It will be removed from your library.", + "monitor-dir-dialog-title":"Are you sure you want to change the monitor directory?", + "monitor-dir-dialog-msg":"The new monitor directory path will be:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title":"Are you sure you want to clear the monitor directory?", + "path-was-copied": "Path was copied", + "monitor-clear-dir-dialog-msg":"This will stop checking the monitor directory for new ZIM files.", + "monitor-directory-tooltip":"All ZIM files in this directory will be automatically added to the library.", + "next-tab":"Move to next tab", + "previous-tab":"Move to previous tab", + "cancel-download": "Cancel download", + "cancel-download-text": "Are you sure you want to cancel the download of {{ZIM}}?", + "delete-book": "Delete book", + "delete-book-text": "Are you sure you want to delete {{ZIM}}?", + "download-storage-error": "Storage Error", + "download-storage-error-text": "The system doesn't have enough storage available.", + "download-exceeds-max-file-size": "Download size exceeds the max file size supported by the target filesystem.", + "download-dir-missing": "Download directory doesn't exist.", + "download-dir-not-writable": "Download directory is not writable.", + "download-unavailable": "Download Unavailable", + "download-unavailable-text": "This download is unavailable.", + "open-book": "Open book", + "download-book": "Download book", + "pause-download": "Pause download", + "resume-download": "Resume download", + "open-folder": "Open folder", + "couldnt-open-location": "Couldn't open location", + "couldnt-open-location-text": "Kiwix is not able to open folder {{FOLDER}}", + "move-files-to-trash": "Move deleted files to trash", + "move-files-to-trash-text": "This action will move the file to trash.", + "perma-delete-files-text": "This action will permanently delete the file.", + "clear-filter": "Clear the currently set filters", + "language-searcher-placeholder": "Filter by language", + "category-searcher-placeholder": "Filter by category", + "content-type-searcher-placeholder": "Filter by content type", + "no-details": "Introduction only", + "no-pictures": "No Pictures", + "no-videos": "No Videos", + "open-previous-tabs-at-startup": "Open previous tabs at startup", + "preview-book-in-web-browser": "Preview book in web browser", + "file-not-found-title": "ZIM File Not Found", + "file-not-found-text": "ZIM file doesn't exist or is not readable", + "zim-id": "ZIM Id", + "zim-name": "ZIM Name", + "zim-path": "ZIM File Path", + "bad-zim-file-error-page-title": "Invalid ZIM File", + "bad-zim-file-error-page-text": "ZIM file is invalid or corrupted.", + "zim-entry-path": "ZIM Entry Path", + "export-reading-list": "Export reading list", + "export-reading-list-error": "An error has occured during export of the reading list.", + "import-reading-list": "Import reading list", + "import-reading-list-error": "An error has occured during import of the reading list.", + "disable-sandbox": "Kiwix has been launched from a network drive. This is known to cause compatibility issues with the browsing sandboxing. As a result, the sandbox will be disabled. Do you want to continue?", + "save-page-as": "Save As...", + "portable-disabled-tooltip": "Function disabled in portable mode", + "scroll-next-tab": "Scroll to next tab", + "scroll-previous-tab": "Scroll to previous tab", + "kiwix-search": "Kiwix search", + "search-options": "Search Options", + "read-article": "Read article", + "read-text": "Read selected text", + "read-stop": "Stop reading", + "stop": "Stop", + "voice": "Voice", + "select-read-voice": "Select reading voice", + "select-read-language": "Select reading language", + "save-or-open": "Save or Open file", + "save-or-open-text": "What should Kiwix do with this file?", + "speed": "Speed", + "increase-tts-speed": "Increase TTS speed.", + "decrease-tts-speed": "Decrease TTS speed." +} diff --git a/resources/i18n/eo.json b/resources/i18n/eo.json index 635c67b67..a41c5c9c8 100644 --- a/resources/i18n/eo.json +++ b/resources/i18n/eo.json @@ -1,63 +1,63 @@ -{ - "@metadata": { - "authors": [ - "Jakub Fabijan", - "Robin van der Vliet" - ] - }, - "name": "Esperanto", - "error-title": "Eraro", - "error-launch-server-message": "Eraro okazis!", - "local-kiwix-server": "Loka Kiwix-servilo", - "home-page": "Ĉefpaĝo", - "main-menu": "Ĉefa menuo", - "print": "Presi", - "new-tab": "Nova langeto", - "close-tab": "Fermi langeton", - "close": "Fermi", - "reopen-closed-tab": "Remalfermi fermitan langeton", - "open-file": "Malfermi la dosieron", - "search-article": "Serĉi artikolon", - "search-in-library": "Serĉi en biblioteko", - "help": "Helpo", - "about-kiwix": "Pri Kiwix", - "exit": "Ĉesi", - "save-file-as-window-title": "Konservi Dosieron kiel", - "file": "Dosiero", - "edit": "Redakti", - "view": "Vido", - "tools": "Iloj", - "window-title": "Biblioteko - Kiwix", - "search": "Serĉi", - "settings": "Agordoj", - "back": "Reveni", - "search-files": "Serĉi dosierojn", - "title": "Titolo", - "size": "Grandeco", - "date": "Dato", - "open": "Malfermi", - "delete": "Forigi", - "download": "Elŝuti", - "resume": "Daŭrigi", - "pause": "Paŭzigi", - "cancel": "Nuligi", - "apply": "Apliki", - "download-directory-setting": "Elŝutujo:", - "browse": "Foliumi", - "about-kiwix-desktop-title": "Kiwix Labortablo", - "about-learn-more-about-kiwix": "Lerni plu pri Kiwix", - "about-version": "Versio : {{VERSION}}", - "about-libraries-title": "Bibliotekoj", - "local-files": "Lokaj dosieroj", - "hide": "Kaŝi", - "open-in-browser": "Malfermi per retumilo", - "all": "Ĉiuj", - "pictures": "Bildoj", - "videos": "Filmetoj", - "yes": "jes", - "no": "ne", - "no-filter": "neniu filtro", - "open-link-in-web-browser": "Malfermi ligilon en retumilo", - "invalid-port": "Nevalida pordo", - "zim-open-fail-title": "Nevalida dosiero" -} +{ + "@metadata": { + "authors": [ + "Jakub Fabijan", + "Robin van der Vliet" + ] + }, + "name": "Esperanto", + "error-title": "Eraro", + "error-launch-server-message": "Eraro okazis!", + "local-kiwix-server": "Loka Kiwix-servilo", + "home-page": "Ĉefpaĝo", + "main-menu": "Ĉefa menuo", + "print": "Presi", + "new-tab": "Nova langeto", + "close-tab": "Fermi langeton", + "close": "Fermi", + "reopen-closed-tab": "Remalfermi fermitan langeton", + "open-file": "Malfermi la dosieron", + "search-article": "Serĉi artikolon", + "search-in-library": "Serĉi en biblioteko", + "help": "Helpo", + "about-kiwix": "Pri Kiwix", + "exit": "Ĉesi", + "save-file-as-window-title": "Konservi Dosieron kiel", + "file": "Dosiero", + "edit": "Redakti", + "view": "Vido", + "tools": "Iloj", + "window-title": "Biblioteko - Kiwix", + "search": "Serĉi", + "settings": "Agordoj", + "back": "Reveni", + "search-files": "Serĉi dosierojn", + "title": "Titolo", + "size": "Grandeco", + "date": "Dato", + "open": "Malfermi", + "delete": "Forigi", + "download": "Elŝuti", + "resume": "Daŭrigi", + "pause": "Paŭzigi", + "cancel": "Nuligi", + "apply": "Apliki", + "download-directory-setting": "Elŝutujo:", + "browse": "Foliumi", + "about-kiwix-desktop-title": "Kiwix Labortablo", + "about-learn-more-about-kiwix": "Lerni plu pri Kiwix", + "about-version": "Versio : {{VERSION}}", + "about-libraries-title": "Bibliotekoj", + "local-files": "Lokaj dosieroj", + "hide": "Kaŝi", + "open-in-browser": "Malfermi per retumilo", + "all": "Ĉiuj", + "pictures": "Bildoj", + "videos": "Filmetoj", + "yes": "jes", + "no": "ne", + "no-filter": "neniu filtro", + "open-link-in-web-browser": "Malfermi ligilon en retumilo", + "invalid-port": "Nevalida pordo", + "zim-open-fail-title": "Nevalida dosiero" +} diff --git a/resources/i18n/es.json b/resources/i18n/es.json index 4f1c18e04..53f1f9ec2 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -1,197 +1,197 @@ -{ - "@metadata": { - "authors": [ - "AlexanderFF", - "Carlespera", - "DDPAT", - "Destinid10 2", - "Eulalio", - "Fitoschido", - "Geryescalier", - "Ice bulldog", - "Josuert", - "La Mantis", - "Ncr", - "Ovruni", - "Sinopsistrans", - "Vis4valentine" - ] - }, - "name": "español", - "error-title": "Error", - "error-downloader-window-title": "No se puede crear el descargador", - "error-downloader-launch-message": "No se puede iniciar el descargador. Kiwix-desktop se iniciará pero no funcionarán las prestaciones de descarga!", - "error-launch-server-message": "¡Ha ocurrido un error!", - "error-archive": "No se puede obtener el archivo", - "open-zim": "Abrir archivo ZIM", - "local-kiwix-server": "Servidor Kiwix local", - "random-article": "Artículo al azar", - "random-article-error": "Error al abrir un artículo aleatorio.", - "home-page": "Página principal", - "main-menu": "Menú principal", - "print": "Imprimir", - "print-page-error": "Ha ocurrido un error durante la impresión.", - "new-tab": "Pestaña nueva", - "close-tab": "Cerrar pestaña", - "close": "Cerrar", - "reopen-closed-tab": "Reabrir pestaña cerrada", - "browse-library": "Explorar biblioteca", - "open-file": "Abrir archivo", - "open-recent": "Abrir recientes", - "search-article": "Buscar en el artículo", - "search-in-library": "Buscar en la biblioteca", - "find-in-page": "Encontrar en la página", - "set-fullscreen": "Establecer pantalla completa", - "quit-fullscreen": "Salir de la pantalla completa", - "table-of-content": "Sumario", - "add-bookmark": "Añadir a lista de lectura", - "remove-bookmark": "Quitar de la lista de lectura", - "reading-list": "Lista de lectura", - "zoom-in": "Acercar", - "zoom-out": "Alejar", - "zoom-reset": "Restablecer escala", - "help": "Ayuda", - "feedback": "Comentarios", - "report-a-bug": "Informar de un defecto", - "request-a-feature": "Solicitar una función", - "about-kiwix": "Acerca de Kiwix", - "donate-to-support-kiwix": "Dona para apoyar a Kiwix", - "exit": "Salir", - "save-file-as-window-title": "Guardar archivo como", - "download-finished-message": "Se ha descargado el documento.", - "file": "Archivo", - "edit": "Editar", - "view": "Ver", - "tools": "Herramientas", - "window-title": "Biblioteca - Kiwix", - "search": "Buscar", - "browse-directory": "Examinar directorio", - "settings": "Configuración", - "back": "Atrás", - "forward": "Reenviar", - "search-files": "Buscar archivos", - "title": "Título", - "size": "Tamaño", - "date": "Fecha", - "content-type": "Tipo de contenido", - "reset-sort": "restablecer orden", - "open": "Abrir", - "delete": "Eliminar", - "download": "Descargar", - "resume": "Reanudar", - "pause": "Pausar", - "cancel": "Cancelar", - "apply": "Aplicar", - "port-for-local-kiwix-server-setting": "Puerto del servidor Kiwix local", - "zoom-level-setting": "Nivel de escala predeterminado", - "download-directory-setting": "Directorio de descargas", - "monitor-directory-setting": "Supervisar directorio", - "reset": "Restablecer", - "clear": "Limpiar", - "browse": "Explorar", - "about-kiwix-desktop-title": "Escritorio Kiwix", - "about-kiwix-desktop-description": "¡Kiwix te permite tener toda la Wikipedia a mano donde quiera que vayas! En un barco, en medio de la nada o en la cárcel, Kiwix te da acceso a todo el conocimiento humano. No necesitas Internet, todo está almacenado en tu computadora.", - "about-learn-more-about-kiwix": "Para conocer más sobre Kiwix", - "about-release-title": "Lanzamiento", - "about-source-code": "Este software se ha publicado en virtud de la Licencia Pública General de GNU, versión 3. El código fuente puede verse en GitHub.", - "about-version": "Versión: {{VERSION}}", - "about-reporting-problem-title": "Informar de un problema", - "about-report-issue": "Para abrir una incidencia, visite el rastreador de incidencias de Kiwix-desktop y abra una incidente que describa su problema.", - "about-report-issue-2": "Menciona la versión en el problema.", - "about-libraries-title": "Bibliotecas", - "kiwix-server-running-message": "El servidor Kiwix se está ejecutando y se puede acceder a él en la red local en:", - "kiwix-server-description": "Iniciar un servidor permite que otras computadoras en la red local accedan a su biblioteca Kiwix con un navegador web estándar.", - "fullscreen-notification": "Ahora estás en modo de pantalla completa. Presione ESC para salir!", - "online-files": "Archivos en línea", - "local-files": "Archivos locales", - "category": "Categoría", - "language": "Idioma", - "hide": "Ocultar", - "open-in-browser": "Abrir en el navegador", - "start-kiwix-server": "Iniciar servidor Kiwix", - "stop-kiwix-server": "Detener servidor Kiwix", - "all": "Todo", - "all_ips": "Todos - Modo doble pila", - "ipv4": "Solo IPv4", - "ipv6": "Solo IPv6", - "fulltext-search": "Búsqueda de texto completo", - "pictures": "Imágenes", - "videos": "Vídeos", - "ftindex": "Índice de texto completo", - "details": "Artículo completo", - "yes": "sí", - "no": "no", - "ok": "correcto", - "no-filter": "Sin filtro", - "open-link-in-web-browser": "Abrir enlace en navegador web", - "open-link-new-tab": "Abrir enlace en una nueva pestaña", - "download-dir-dialog-title": "¿Está seguro de que desea cambiar el directorio de descarga?", - "download-dir-dialog-msg": "La nueva ruta del directorio de descarga será:\n{{DIRECTORY}}", - "invalid-port": "Puerto no válido", - "zim-open-fail-title": "Archivo invalido", - "zim-open-fail-text": "El archivo ZIM {{ZIM}} no se puede abrir correctamente. Será eliminado de tu biblioteca.", - "monitor-dir-dialog-title": "¿Está seguro de que desea cambiar el directorio monitorio?", - "monitor-dir-dialog-msg": "La nueva ruta del directorio de monitoreo será:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "¿Está seguro de que desea borrar el directorio de monitoreo?", - "path-was-copied": "La ruta fue copiada", - "monitor-clear-dir-dialog-msg": "Esto dejará de buscar en el directorio de monitorio por nuevos archivos ZIM.", - "monitor-directory-tooltip": "Todos los archivos ZIM en este directorio se agregarán automáticamente a la biblioteca.", - "next-tab": "Mover a la siguiente pestaña", - "previous-tab": "Mover a la pestaña anterior", - "cancel-download": "Cancelar descarga", - "cancel-download-text": "¿Está seguro de que desea cancelar la descarga de {{ZIM}} ?", - "delete-book": "Eliminar libro", - "delete-book-text": "¿Está seguro de que desea eliminar {{ZIM}} ?", - "download-storage-error": "Erro de almacenamiento", - "download-storage-error-text": "El sistema no tiene suficiente almacenamiento disponible.", - "download-exceeds-max-file-size": "El tamaño de la descarga supera el tamaño máximo de archivo admitido por el sistema de archivos de destino.", - "download-dir-missing": "El directorio de descargas no existe.", - "download-dir-not-writable": "El directorio de descarga no se puede escribir.", - "download-unavailable": "Descarga no disponible", - "download-unavailable-text": "Esta descarga no está disponible.", - "open-book": "Abrir libro", - "download-book": "Descargar libro", - "pause-download": "Pausar descarga", - "resume-download": "Continuar descarga", - "open-folder": "Abrir carpeta", - "couldnt-open-location": "No se pudo abrir la ubicación", - "couldnt-open-location-text": "Kiwix no puede abrir la carpeta {{FOLDER}}", - "move-files-to-trash": "Mover archivos eliminados a la papelera", - "move-files-to-trash-text": "Esta acción moverá el archivo a la papelera.", - "perma-delete-files-text": "Esta acción eliminará permanentemente el archivo.", - "clear-filter": "Borrar los filtros establecidos actualmente", - "language-searcher-placeholder": "Filtrar por idioma", - "category-searcher-placeholder": "Filtrar por categoría", - "content-type-searcher-placeholder": "Filtrar por tipo de contenido", - "no-details": "Solo introducción", - "no-pictures": "Sin imágenes", - "no-videos": "Ningún video", - "open-previous-tabs-at-startup": "Abrir pestañas anteriores al inicio", - "preview-book-in-web-browser": "Vista previa del libro en el navegador web", - "file-not-found-title": "Archivo ZIM no encontrado", - "file-not-found-text": "El archivo ZIM no existe o no es legible", - "zim-id": "Identificación del archivo ZIM", - "zim-name": "Nombre del archivo ZIM", - "zim-path": "Ruta del archivo ZIM", - "bad-zim-file-error-page-title": "Archivo ZIM no válido", - "bad-zim-file-error-page-text": "El archivo ZIM no es válido o está corrompido.", - "zim-entry-path": "Vía de acceso ZIM", - "export-reading-list": "Exportar lista de lectura", - "export-reading-list-error": "Se ha producido un error durante la exportación de la lista de lectura.", - "import-reading-list": "Importar lista de lectura", - "import-reading-list-error": "Se ha producido un error durante la importación de la lista de lectura.", - "disable-sandbox": "Kiwix se ha iniciado desde una unidad de red. Se sabe que esto causa problemas de compatibilidad con el sandboxing de navegación. Como resultado, el sandbox será desactivado. ¿Desea continuar?", - "save-page-as": "Guardar como...", - "portable-disabled-tooltip": "Función deshabilitada en modo portátil", - "scroll-next-tab": "Desplazarse a la pestaña siguiente", - "scroll-previous-tab": "Desplazarse a la pestaña anterior", - "kiwix-search": "Búsqueda de Kiwix", - "search-options": "Opciones de búsqueda", - "read-article": "Leer artículo", - "read-text": "Leer el texto seleccionado", - "read-stop": "Dejar de leer", - "stop": "Detener", - "voice": "Voz", - "select-read-voice": "Seleccionar la voz de lectura", - "select-read-language": "Seleccione el idioma de lectura" -} +{ + "@metadata": { + "authors": [ + "AlexanderFF", + "Carlespera", + "DDPAT", + "Destinid10 2", + "Eulalio", + "Fitoschido", + "Geryescalier", + "Ice bulldog", + "Josuert", + "La Mantis", + "Ncr", + "Ovruni", + "Sinopsistrans", + "Vis4valentine" + ] + }, + "name": "español", + "error-title": "Error", + "error-downloader-window-title": "No se puede crear el descargador", + "error-downloader-launch-message": "No se puede iniciar el descargador. Kiwix-desktop se iniciará pero no funcionarán las prestaciones de descarga!", + "error-launch-server-message": "¡Ha ocurrido un error!", + "error-archive": "No se puede obtener el archivo", + "open-zim": "Abrir archivo ZIM", + "local-kiwix-server": "Servidor Kiwix local", + "random-article": "Artículo al azar", + "random-article-error": "Error al abrir un artículo aleatorio.", + "home-page": "Página principal", + "main-menu": "Menú principal", + "print": "Imprimir", + "print-page-error": "Ha ocurrido un error durante la impresión.", + "new-tab": "Pestaña nueva", + "close-tab": "Cerrar pestaña", + "close": "Cerrar", + "reopen-closed-tab": "Reabrir pestaña cerrada", + "browse-library": "Explorar biblioteca", + "open-file": "Abrir archivo", + "open-recent": "Abrir recientes", + "search-article": "Buscar en el artículo", + "search-in-library": "Buscar en la biblioteca", + "find-in-page": "Encontrar en la página", + "set-fullscreen": "Establecer pantalla completa", + "quit-fullscreen": "Salir de la pantalla completa", + "table-of-content": "Sumario", + "add-bookmark": "Añadir a lista de lectura", + "remove-bookmark": "Quitar de la lista de lectura", + "reading-list": "Lista de lectura", + "zoom-in": "Acercar", + "zoom-out": "Alejar", + "zoom-reset": "Restablecer escala", + "help": "Ayuda", + "feedback": "Comentarios", + "report-a-bug": "Informar de un defecto", + "request-a-feature": "Solicitar una función", + "about-kiwix": "Acerca de Kiwix", + "donate-to-support-kiwix": "Dona para apoyar a Kiwix", + "exit": "Salir", + "save-file-as-window-title": "Guardar archivo como", + "download-finished-message": "Se ha descargado el documento.", + "file": "Archivo", + "edit": "Editar", + "view": "Ver", + "tools": "Herramientas", + "window-title": "Biblioteca - Kiwix", + "search": "Buscar", + "browse-directory": "Examinar directorio", + "settings": "Configuración", + "back": "Atrás", + "forward": "Reenviar", + "search-files": "Buscar archivos", + "title": "Título", + "size": "Tamaño", + "date": "Fecha", + "content-type": "Tipo de contenido", + "reset-sort": "restablecer orden", + "open": "Abrir", + "delete": "Eliminar", + "download": "Descargar", + "resume": "Reanudar", + "pause": "Pausar", + "cancel": "Cancelar", + "apply": "Aplicar", + "port-for-local-kiwix-server-setting": "Puerto del servidor Kiwix local", + "zoom-level-setting": "Nivel de escala predeterminado", + "download-directory-setting": "Directorio de descargas", + "monitor-directory-setting": "Supervisar directorio", + "reset": "Restablecer", + "clear": "Limpiar", + "browse": "Explorar", + "about-kiwix-desktop-title": "Escritorio Kiwix", + "about-kiwix-desktop-description": "¡Kiwix te permite tener toda la Wikipedia a mano donde quiera que vayas! En un barco, en medio de la nada o en la cárcel, Kiwix te da acceso a todo el conocimiento humano. No necesitas Internet, todo está almacenado en tu computadora.", + "about-learn-more-about-kiwix": "Para conocer más sobre Kiwix", + "about-release-title": "Lanzamiento", + "about-source-code": "Este software se ha publicado en virtud de la Licencia Pública General de GNU, versión 3. El código fuente puede verse en GitHub.", + "about-version": "Versión: {{VERSION}}", + "about-reporting-problem-title": "Informar de un problema", + "about-report-issue": "Para abrir una incidencia, visite el rastreador de incidencias de Kiwix-desktop y abra una incidente que describa su problema.", + "about-report-issue-2": "Menciona la versión en el problema.", + "about-libraries-title": "Bibliotecas", + "kiwix-server-running-message": "El servidor Kiwix se está ejecutando y se puede acceder a él en la red local en:", + "kiwix-server-description": "Iniciar un servidor permite que otras computadoras en la red local accedan a su biblioteca Kiwix con un navegador web estándar.", + "fullscreen-notification": "Ahora estás en modo de pantalla completa. Presione ESC para salir!", + "online-files": "Archivos en línea", + "local-files": "Archivos locales", + "category": "Categoría", + "language": "Idioma", + "hide": "Ocultar", + "open-in-browser": "Abrir en el navegador", + "start-kiwix-server": "Iniciar servidor Kiwix", + "stop-kiwix-server": "Detener servidor Kiwix", + "all": "Todo", + "all_ips": "Todos - Modo doble pila", + "ipv4": "Solo IPv4", + "ipv6": "Solo IPv6", + "fulltext-search": "Búsqueda de texto completo", + "pictures": "Imágenes", + "videos": "Vídeos", + "ftindex": "Índice de texto completo", + "details": "Artículo completo", + "yes": "sí", + "no": "no", + "ok": "correcto", + "no-filter": "Sin filtro", + "open-link-in-web-browser": "Abrir enlace en navegador web", + "open-link-new-tab": "Abrir enlace en una nueva pestaña", + "download-dir-dialog-title": "¿Está seguro de que desea cambiar el directorio de descarga?", + "download-dir-dialog-msg": "La nueva ruta del directorio de descarga será:\n{{DIRECTORY}}", + "invalid-port": "Puerto no válido", + "zim-open-fail-title": "Archivo invalido", + "zim-open-fail-text": "El archivo ZIM {{ZIM}} no se puede abrir correctamente. Será eliminado de tu biblioteca.", + "monitor-dir-dialog-title": "¿Está seguro de que desea cambiar el directorio monitorio?", + "monitor-dir-dialog-msg": "La nueva ruta del directorio de monitoreo será:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "¿Está seguro de que desea borrar el directorio de monitoreo?", + "path-was-copied": "La ruta fue copiada", + "monitor-clear-dir-dialog-msg": "Esto dejará de buscar en el directorio de monitorio por nuevos archivos ZIM.", + "monitor-directory-tooltip": "Todos los archivos ZIM en este directorio se agregarán automáticamente a la biblioteca.", + "next-tab": "Mover a la siguiente pestaña", + "previous-tab": "Mover a la pestaña anterior", + "cancel-download": "Cancelar descarga", + "cancel-download-text": "¿Está seguro de que desea cancelar la descarga de {{ZIM}} ?", + "delete-book": "Eliminar libro", + "delete-book-text": "¿Está seguro de que desea eliminar {{ZIM}} ?", + "download-storage-error": "Erro de almacenamiento", + "download-storage-error-text": "El sistema no tiene suficiente almacenamiento disponible.", + "download-exceeds-max-file-size": "El tamaño de la descarga supera el tamaño máximo de archivo admitido por el sistema de archivos de destino.", + "download-dir-missing": "El directorio de descargas no existe.", + "download-dir-not-writable": "El directorio de descarga no se puede escribir.", + "download-unavailable": "Descarga no disponible", + "download-unavailable-text": "Esta descarga no está disponible.", + "open-book": "Abrir libro", + "download-book": "Descargar libro", + "pause-download": "Pausar descarga", + "resume-download": "Continuar descarga", + "open-folder": "Abrir carpeta", + "couldnt-open-location": "No se pudo abrir la ubicación", + "couldnt-open-location-text": "Kiwix no puede abrir la carpeta {{FOLDER}}", + "move-files-to-trash": "Mover archivos eliminados a la papelera", + "move-files-to-trash-text": "Esta acción moverá el archivo a la papelera.", + "perma-delete-files-text": "Esta acción eliminará permanentemente el archivo.", + "clear-filter": "Borrar los filtros establecidos actualmente", + "language-searcher-placeholder": "Filtrar por idioma", + "category-searcher-placeholder": "Filtrar por categoría", + "content-type-searcher-placeholder": "Filtrar por tipo de contenido", + "no-details": "Solo introducción", + "no-pictures": "Sin imágenes", + "no-videos": "Ningún video", + "open-previous-tabs-at-startup": "Abrir pestañas anteriores al inicio", + "preview-book-in-web-browser": "Vista previa del libro en el navegador web", + "file-not-found-title": "Archivo ZIM no encontrado", + "file-not-found-text": "El archivo ZIM no existe o no es legible", + "zim-id": "Identificación del archivo ZIM", + "zim-name": "Nombre del archivo ZIM", + "zim-path": "Ruta del archivo ZIM", + "bad-zim-file-error-page-title": "Archivo ZIM no válido", + "bad-zim-file-error-page-text": "El archivo ZIM no es válido o está corrompido.", + "zim-entry-path": "Vía de acceso ZIM", + "export-reading-list": "Exportar lista de lectura", + "export-reading-list-error": "Se ha producido un error durante la exportación de la lista de lectura.", + "import-reading-list": "Importar lista de lectura", + "import-reading-list-error": "Se ha producido un error durante la importación de la lista de lectura.", + "disable-sandbox": "Kiwix se ha iniciado desde una unidad de red. Se sabe que esto causa problemas de compatibilidad con el sandboxing de navegación. Como resultado, el sandbox será desactivado. ¿Desea continuar?", + "save-page-as": "Guardar como...", + "portable-disabled-tooltip": "Función deshabilitada en modo portátil", + "scroll-next-tab": "Desplazarse a la pestaña siguiente", + "scroll-previous-tab": "Desplazarse a la pestaña anterior", + "kiwix-search": "Búsqueda de Kiwix", + "search-options": "Opciones de búsqueda", + "read-article": "Leer artículo", + "read-text": "Leer el texto seleccionado", + "read-stop": "Dejar de leer", + "stop": "Detener", + "voice": "Voz", + "select-read-voice": "Seleccionar la voz de lectura", + "select-read-language": "Seleccione el idioma de lectura" +} diff --git a/resources/i18n/fa.json b/resources/i18n/fa.json index a0f8883ac..31b50cf41 100644 --- a/resources/i18n/fa.json +++ b/resources/i18n/fa.json @@ -1,64 +1,64 @@ -{ - "@metadata": { - "authors": [ - "FarsiNevis", - "Jeeputer", - "Rulebased" - ] - }, - "name": "انگلیسی", - "error-title": "خطا", - "random-article": "مقالهٔ تصادفی", - "random-article-error": "ناتوان در گشودن مقالهٔ تصادفی.", - "print": "چاپ", - "new-tab": "برگهٔ جدید", - "close-tab": "بستن برگه", - "close": "بستن", - "reopen-closed-tab": "برگهٔ بسته‌شده را دوباره باز کن", - "browse-library": "مرور کتابخانه", - "open-file": "باز کردن پرونده", - "search-in-library": "جستجو در کتابخانه", - "find-in-page": "جستجو در صفحه", - "quit-fullscreen": "خروج از تمام‌صفحه", - "reading-list": "فهرست مطالعه", - "zoom-in": "بزرگ‌نمایی", - "zoom-out": "کوچک‌نمایی", - "help": "راهنما", - "feedback": "بازخورد", - "report-a-bug": "گزارش یک اشکال", - "exit": "خروج", - "save-file-as-window-title": "ذخیرهٔ پرونده به‌صورت", - "download-finished-message": "مدرک مورد نظر بارگیری شد.", - "file": "پرونده", - "edit": "ویرایش", - "view": "مشاهده", - "tools": "ابزارها", - "search": "جستجو", - "settings": "تنظیمات", - "search-files": "جستجوی پرونده‌ها", - "title": "عنوان", - "size": "اندازه", - "date": "تاریخ", - "content-type": "نوع محتوا", - "open": "باز کن", - "delete": "حذف", - "download": "بارگیری", - "resume": "ادامه", - "pause": "مکث", - "cancel": "لغو", - "apply": "اِعمال کن", - "browse": "مرور", - "about-privacy-policy": "سیاست محرمانگی", - "about-reporting-problem-title": "گزارش مشکل", - "about-libraries-title": "کتابخانه‌ها", - "local-files": "پرونده‌های محلی", - "language": "زبان", - "hide": "پنهان کن", - "open-in-browser": "در مرورگر باز کن", - "all": "همه", - "open-link-new-tab": "گشودن پیوند در زبانه‌ای جدید", - "export-reading-list": "برون‌بری فهرست مطالعه", - "export-reading-list-error": "در طول برون‌بری فهرست مطالعه خطایی رخ داد.", - "import-reading-list": "درون‌ریزی فهرست مطالعه", - "import-reading-list-error": "در طول درون‌ریزی فهرست مطالعه خطایی رخ داد." -} +{ + "@metadata": { + "authors": [ + "FarsiNevis", + "Jeeputer", + "Rulebased" + ] + }, + "name": "انگلیسی", + "error-title": "خطا", + "random-article": "مقالهٔ تصادفی", + "random-article-error": "ناتوان در گشودن مقالهٔ تصادفی.", + "print": "چاپ", + "new-tab": "برگهٔ جدید", + "close-tab": "بستن برگه", + "close": "بستن", + "reopen-closed-tab": "برگهٔ بسته‌شده را دوباره باز کن", + "browse-library": "مرور کتابخانه", + "open-file": "باز کردن پرونده", + "search-in-library": "جستجو در کتابخانه", + "find-in-page": "جستجو در صفحه", + "quit-fullscreen": "خروج از تمام‌صفحه", + "reading-list": "فهرست مطالعه", + "zoom-in": "بزرگ‌نمایی", + "zoom-out": "کوچک‌نمایی", + "help": "راهنما", + "feedback": "بازخورد", + "report-a-bug": "گزارش یک اشکال", + "exit": "خروج", + "save-file-as-window-title": "ذخیرهٔ پرونده به‌صورت", + "download-finished-message": "مدرک مورد نظر بارگیری شد.", + "file": "پرونده", + "edit": "ویرایش", + "view": "مشاهده", + "tools": "ابزارها", + "search": "جستجو", + "settings": "تنظیمات", + "search-files": "جستجوی پرونده‌ها", + "title": "عنوان", + "size": "اندازه", + "date": "تاریخ", + "content-type": "نوع محتوا", + "open": "باز کن", + "delete": "حذف", + "download": "بارگیری", + "resume": "ادامه", + "pause": "مکث", + "cancel": "لغو", + "apply": "اِعمال کن", + "browse": "مرور", + "about-privacy-policy": "سیاست محرمانگی", + "about-reporting-problem-title": "گزارش مشکل", + "about-libraries-title": "کتابخانه‌ها", + "local-files": "پرونده‌های محلی", + "language": "زبان", + "hide": "پنهان کن", + "open-in-browser": "در مرورگر باز کن", + "all": "همه", + "open-link-new-tab": "گشودن پیوند در زبانه‌ای جدید", + "export-reading-list": "برون‌بری فهرست مطالعه", + "export-reading-list-error": "در طول برون‌بری فهرست مطالعه خطایی رخ داد.", + "import-reading-list": "درون‌ریزی فهرست مطالعه", + "import-reading-list-error": "در طول درون‌ریزی فهرست مطالعه خطایی رخ داد." +} diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index 98df60e40..6eea59c6b 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -1,126 +1,126 @@ -{ - "@metadata": { - "authors": [ - "MITO", - "Nike", - "Pyscowicz", - "Rönttönen" - ] - }, - "name": "suomi", - "error-title": "Virhe", - "error-downloader-window-title": "Latausohjelmaa ei voi luoda", - "error-launch-server-message": "Tapahtui virhe!", - "open-zim": "Avaa ZIM-tiedosto", - "local-kiwix-server": "Paikallinen Kiwix-palvelin", - "random-article": "Satunnainen artikkeli", - "random-article-error": "Virhe avattaessa satunnaista artikkelia.", - "home-page": "Kotisivu", - "main-menu": "Päävalikko", - "print": "Tulosta", - "print-page-error": "Tulostettaessa tapahtui virhe.", - "new-tab": "Uusi välilehti", - "close-tab": "Sulje välilehti", - "close": "Sulje", - "reopen-closed-tab": "Avaa suljettu välilehti uudelleen", - "browse-library": "Selaa kirjastoa", - "open-file": "Avaa tiedosto", - "open-recent": "Avaa viimeisin", - "search-article": "Hae artikkelia", - "search-in-library": "Hae kirjastossa", - "find-in-page": "Etsi sivulta", - "set-fullscreen": "Siirry koko näytön tilaan", - "quit-fullscreen": "Poistu koko näytön tilasta", - "table-of-content": "Sisällysluettelo", - "reading-list": "Lukulista", - "zoom-in": "Lähennä", - "zoom-out": "Loitonna", - "zoom-reset": "Zoomauksen nollaus", - "help": "Ohje", - "feedback": "Palaute", - "report-a-bug": "Ilmoita bugista", - "request-a-feature": "Ehdota ominaisuutta", - "about-kiwix": "Tietoja Kiwixistä", - "donate-to-support-kiwix": "Lahjoita tukeaksi Kiwixiä", - "exit": "Poistu", - "save-file-as-window-title": "Tallenna tiedosto nimellä", - "download-finished-message": "Asiakirja on ladattu", - "file": "Tiedosto", - "edit": "Muokkaa", - "view": "Näytä", - "tools": "Työkalut", - "window-title": "Kirjasto - Kiwix", - "search": "Hae", - "browse-directory": "Selaa hakemistoa", - "settings": "Asetukset", - "back": "Takaisin", - "forward": "Eteenpäin", - "search-files": "Etsi tiedostoja", - "title": "Otsikko", - "size": "Koko", - "date": "Päivämäärä", - "content-type": "Sisältötyyppi", - "reset-sort": "nollaa lajittelu", - "open": "Avaa", - "delete": "Poista", - "download": "Lataa", - "resume": "Jatka", - "pause": "Tauko", - "cancel": "Peruuta", - "apply": "Käytä", - "port-for-local-kiwix-server-setting": "Portti paikalliselle Kiwix-palvelimelle", - "zoom-level-setting": "Oletuszoomaustaso", - "download-directory-setting": "Lataushakemisto", - "reset": "Nollaa", - "clear": "Tyhjennä", - "browse": "Selaa", - "about-kiwix-desktop-title": "Kiwix-työpöytä", - "about-learn-more-about-kiwix": "Lisätietoja Kiwixistä", - "about-release-title": "Julkaisu", - "about-version": "Versio: {{VERSION}}", - "about-libraries-title": "Kirjastot", - "fullscreen-notification": "Olet nyt koko näytön tilassa. Paina ESC poistuaksesi!", - "online-files": "Online-tiedostot", - "local-files": "Paikalliset tiedostot", - "category": "Luokka", - "language": "Kieli", - "hide": "Piilota", - "open-in-browser": "Avaa selaimessa", - "start-kiwix-server": "Käynnistä Kiwix-palvelin", - "stop-kiwix-server": "Pysäytä Kiwix-palvelin", - "all": "Kaikki", - "pictures": "Kuvat", - "videos": "Videot", - "details": "Koko artikkeli", - "yes": "kyllä", - "no": "ei", - "open-link-in-web-browser": "Avaa linkki verkkoselaimessa", - "open-link-new-tab": "Avaa linkki uudessa välilehdessä", - "download-dir-dialog-title": "Haluatko varmasti vaihtaa lataushakemistoa?", - "download-dir-dialog-msg": "Uusi lataushakemiston polku on:\n{{DIRECTORY}}", - "invalid-port": "Virheellinen portti", - "zim-open-fail-title": "Virheellinen tiedosto", - "monitor-directory-tooltip": "Kaikki ZIM-tiedostot tässä hakemistossa lisätään automaattisesti kirjastoon.", - "next-tab": "Siirry seuraavaan välilehteen", - "previous-tab": "Siirry edelliseen välilehteen", - "cancel-download": "Peruuta lataus", - "cancel-download-text": "Haluatko varmasti peruuttaa tiedoston {{ZIM}} latauksen?", - "delete-book": "Poista kirja", - "delete-book-text": "Haluatko varmasti poistaa {{ZIM}}?", - "open-book": "Avaa kirja", - "download-book": "Lataa kirja", - "pause-download": "Keskeytä lataus", - "resume-download": "Jatka latausta", - "open-folder": "Avaa kansio", - "couldnt-open-location": "Sijaintia ei voitu avata", - "couldnt-open-location-text": "Kiwix ei pystynyt avaamaan kansiota {{FOLDER}}", - "move-files-to-trash": "Siirrä poistetut tiedostot roskakoriin", - "move-files-to-trash-text": "Tämä toiminto siirtää tiedoston roskakoriin.", - "perma-delete-files-text": "Tämä toiminto poistaa tiedoston pysyvästi.", - "language-searcher-placeholder": "Suodata kielen perusteella", - "category-searcher-placeholder": "Suodata luokan perusteella", - "content-type-searcher-placeholder": "Suodata sisältötyypin perusteella", - "no-pictures": "Ei kuvia", - "no-videos": "Ei videoita", - "open-previous-tabs-at-startup": "Avaa edelliset välilehdet käynnistyksen yhteydessä" -} +{ + "@metadata": { + "authors": [ + "MITO", + "Nike", + "Pyscowicz", + "Rönttönen" + ] + }, + "name": "suomi", + "error-title": "Virhe", + "error-downloader-window-title": "Latausohjelmaa ei voi luoda", + "error-launch-server-message": "Tapahtui virhe!", + "open-zim": "Avaa ZIM-tiedosto", + "local-kiwix-server": "Paikallinen Kiwix-palvelin", + "random-article": "Satunnainen artikkeli", + "random-article-error": "Virhe avattaessa satunnaista artikkelia.", + "home-page": "Kotisivu", + "main-menu": "Päävalikko", + "print": "Tulosta", + "print-page-error": "Tulostettaessa tapahtui virhe.", + "new-tab": "Uusi välilehti", + "close-tab": "Sulje välilehti", + "close": "Sulje", + "reopen-closed-tab": "Avaa suljettu välilehti uudelleen", + "browse-library": "Selaa kirjastoa", + "open-file": "Avaa tiedosto", + "open-recent": "Avaa viimeisin", + "search-article": "Hae artikkelia", + "search-in-library": "Hae kirjastossa", + "find-in-page": "Etsi sivulta", + "set-fullscreen": "Siirry koko näytön tilaan", + "quit-fullscreen": "Poistu koko näytön tilasta", + "table-of-content": "Sisällysluettelo", + "reading-list": "Lukulista", + "zoom-in": "Lähennä", + "zoom-out": "Loitonna", + "zoom-reset": "Zoomauksen nollaus", + "help": "Ohje", + "feedback": "Palaute", + "report-a-bug": "Ilmoita bugista", + "request-a-feature": "Ehdota ominaisuutta", + "about-kiwix": "Tietoja Kiwixistä", + "donate-to-support-kiwix": "Lahjoita tukeaksi Kiwixiä", + "exit": "Poistu", + "save-file-as-window-title": "Tallenna tiedosto nimellä", + "download-finished-message": "Asiakirja on ladattu", + "file": "Tiedosto", + "edit": "Muokkaa", + "view": "Näytä", + "tools": "Työkalut", + "window-title": "Kirjasto - Kiwix", + "search": "Hae", + "browse-directory": "Selaa hakemistoa", + "settings": "Asetukset", + "back": "Takaisin", + "forward": "Eteenpäin", + "search-files": "Etsi tiedostoja", + "title": "Otsikko", + "size": "Koko", + "date": "Päivämäärä", + "content-type": "Sisältötyyppi", + "reset-sort": "nollaa lajittelu", + "open": "Avaa", + "delete": "Poista", + "download": "Lataa", + "resume": "Jatka", + "pause": "Tauko", + "cancel": "Peruuta", + "apply": "Käytä", + "port-for-local-kiwix-server-setting": "Portti paikalliselle Kiwix-palvelimelle", + "zoom-level-setting": "Oletuszoomaustaso", + "download-directory-setting": "Lataushakemisto", + "reset": "Nollaa", + "clear": "Tyhjennä", + "browse": "Selaa", + "about-kiwix-desktop-title": "Kiwix-työpöytä", + "about-learn-more-about-kiwix": "Lisätietoja Kiwixistä", + "about-release-title": "Julkaisu", + "about-version": "Versio: {{VERSION}}", + "about-libraries-title": "Kirjastot", + "fullscreen-notification": "Olet nyt koko näytön tilassa. Paina ESC poistuaksesi!", + "online-files": "Online-tiedostot", + "local-files": "Paikalliset tiedostot", + "category": "Luokka", + "language": "Kieli", + "hide": "Piilota", + "open-in-browser": "Avaa selaimessa", + "start-kiwix-server": "Käynnistä Kiwix-palvelin", + "stop-kiwix-server": "Pysäytä Kiwix-palvelin", + "all": "Kaikki", + "pictures": "Kuvat", + "videos": "Videot", + "details": "Koko artikkeli", + "yes": "kyllä", + "no": "ei", + "open-link-in-web-browser": "Avaa linkki verkkoselaimessa", + "open-link-new-tab": "Avaa linkki uudessa välilehdessä", + "download-dir-dialog-title": "Haluatko varmasti vaihtaa lataushakemistoa?", + "download-dir-dialog-msg": "Uusi lataushakemiston polku on:\n{{DIRECTORY}}", + "invalid-port": "Virheellinen portti", + "zim-open-fail-title": "Virheellinen tiedosto", + "monitor-directory-tooltip": "Kaikki ZIM-tiedostot tässä hakemistossa lisätään automaattisesti kirjastoon.", + "next-tab": "Siirry seuraavaan välilehteen", + "previous-tab": "Siirry edelliseen välilehteen", + "cancel-download": "Peruuta lataus", + "cancel-download-text": "Haluatko varmasti peruuttaa tiedoston {{ZIM}} latauksen?", + "delete-book": "Poista kirja", + "delete-book-text": "Haluatko varmasti poistaa {{ZIM}}?", + "open-book": "Avaa kirja", + "download-book": "Lataa kirja", + "pause-download": "Keskeytä lataus", + "resume-download": "Jatka latausta", + "open-folder": "Avaa kansio", + "couldnt-open-location": "Sijaintia ei voitu avata", + "couldnt-open-location-text": "Kiwix ei pystynyt avaamaan kansiota {{FOLDER}}", + "move-files-to-trash": "Siirrä poistetut tiedostot roskakoriin", + "move-files-to-trash-text": "Tämä toiminto siirtää tiedoston roskakoriin.", + "perma-delete-files-text": "Tämä toiminto poistaa tiedoston pysyvästi.", + "language-searcher-placeholder": "Suodata kielen perusteella", + "category-searcher-placeholder": "Suodata luokan perusteella", + "content-type-searcher-placeholder": "Suodata sisältötyypin perusteella", + "no-pictures": "Ei kuvia", + "no-videos": "Ei videoita", + "open-previous-tabs-at-startup": "Avaa edelliset välilehdet käynnistyksen yhteydessä" +} diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index 08213cc93..34ef1ab8f 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -1,203 +1,203 @@ -{ - "@metadata": { - "authors": [ - "Benoit74", - "Constant", - "Florian COLLIN", - "Gomoko", - "Goombiis", - "Graineahumus", - "Hecatonchire", - "Mahabarata", - "McDutchie", - "Méthodes Bulebe Hangi", - "Stephane", - "Terio legale", - "Verdy p", - "Wladek92" - ] - }, - "name": "Français", - "error-title": "Erreur", - "error-downloader-window-title": "Impossible de créer le processus de téléchargement", - "error-downloader-launch-message": "Impossible de lancer le téléchargement, Kiwix-desktop démarrera mais toutes les fonctions de téléchargement ne fonctionneront pas !", - "error-launch-server-message": "Une erreur s’est produite !", - "error-archive": "Impossible d’obtenir l’archive", - "error-opening-file": "Une erreur s'est produite lors de l'ouverture du fichier.", - "open-zim": "Ouvrir un fichier ZIM", - "local-kiwix-server": "Serveur local Kiwix", - "random-article": "Article au hasard", - "random-article-error": "Échec à l’ouverture d’un article au hasard.", - "home-page": "Page principale", - "main-menu": "Menu principal", - "print": "Imprimer", - "print-page-error": "Une erreur s’est produite lors de l’impression.", - "new-tab": "Nouvel onglet", - "close-tab": "Fermer l’onglet", - "close": "Fermer", - "reopen-closed-tab": "Rouvrir l’onglet fermé", - "browse-library": "Parcourir la bibliothèque", - "open-file": "Ouvrir un fichier", - "save-file": "Enregistrer le fichier", - "open-recent": "Fichiers récemment ouverts", - "search-article": "Rechercher un article", - "search-in-library": "Chercher dans la bibliothèque", - "find-in-page": "Chercher dans la page", - "set-fullscreen": "Mode plein écran", - "quit-fullscreen": "Quitter le mode plein écran", - "table-of-content": "Table des matières", - "add-bookmark": "Ajouter à la liste de lecture", - "remove-bookmark": "Supprimer de la liste de lecture", - "reading-list": "Liste de lecture", - "zoom-in": "Zoom avant", - "zoom-out": "Zoom arrière", - "zoom-reset": "Réinitialisation du zoom", - "help": "Aide", - "feedback": "Retour d’information", - "report-a-bug": "Signaler une anomalie", - "request-a-feature": "Demander une fonctionnalité", - "about-kiwix": "À propos de kiwix", - "donate-to-support-kiwix": "Faire un don pour soutenir Kiwix", - "exit": "Quitter", - "save-file-as-window-title": "Enregistrer le fichier sous", - "download-finished": "Téléchargement terminé", - "download-finished-message": "Le document a été téléchargé.", - "file": "Fichier", - "edit": "Modifier", - "view": "Afficher", - "tools": "Outils", - "window-title": "Bibliothèque – Kiwix", - "search": "Recherche", - "browse-directory": "Parcourir un dossier", - "settings": "Paramètres", - "back": "Retour", - "forward": "Suivant", - "search-files": "Chercher des fichiers", - "title": "Titre", - "size": "Taille", - "date": "Date", - "content-type": "Type de contenu", - "reset-sort": "réinitialiser le tri", - "open": "Ouvrir", - "delete": "Supprimer", - "download": "Télécharger", - "resume": "Reprendre", - "pause": "Pause", - "cancel": "Annuler", - "apply": "Appliquer", - "port-for-local-kiwix-server-setting": "Port pour le serveur Kiwix local", - "zoom-level-setting": "Niveau de zoom par défaut", - "download-directory-setting": "Dossier de téléchargement", - "monitor-directory-setting": "Surveiller le répertoire", - "reset": "Réinitialiser", - "clear": "Effacer", - "browse": "Parcourir", - "about-kiwix-desktop-title": "Kiwix pour ordinateurs de bureau", - "about-kiwix-desktop-description": "Kiwix vous permet d’avoir tout Wikipédia à portée de main où que vous alliez ! Sur un bateau, au milieu de nulle part, ou en prison, Kiwix vous donne accès à tout le savoir humain. Vous n’avez pas besoin d’Internet, tout est conservé dans votre ordinateur.", - "about-learn-more-about-kiwix": "En savoir plus sur Kiwix", - "about-privacy-policy": "Politique de confidentialité", - "about-release-title": "Publication", - "about-source-code": "Ce logiciel est publié selon les termes de la licence GNU General Public License version 3. Vous pouvez consulter le code source sur Github.", - "about-version": "Version : {{VERSION}}", - "about-reporting-problem-title": "Signaler des problèmes", - "about-report-issue": "Pour signaler un problème, allez sur la page de signalement et ouvrez un ticket en décrivant votre problème.", - "about-report-issue-2": "Veuillez indiquer la version de Kiwix dans votre signalement.", - "about-libraries-title": "Bibliothèques", - "kiwix-server-running-message": "Le serveur Kiwix est lancé et peut être accédé sur le réseau local à :", - "kiwix-server-description": "Démarrer un serveur permet à d’autres ordinateurs du réseau local d’accéder à votre bibliothèque Kiwix avec un navigateur web classique.", - "fullscreen-notification": "Vous êtes maintenant en plein écran. Appuyez sur Échap pour quitter !", - "online-files": "Fichiers en ligne", - "local-files": "Fichiers locaux", - "category": "Catégorie", - "language": "Langue", - "hide": "Masquer", - "open-in-browser": "Ouvrir dans un navigateur", - "start-kiwix-server": "Démarrer le serveur Kiwix", - "stop-kiwix-server": "Arrêter le serveur Kiwix", - "all": "Tout", - "all_ips": "Tous - IPv4 et IPv6", - "ipv4": "Mode IPv4 uniquement", - "ipv6": "Mode IPv6 uniquement", - "fulltext-search": "Recherche plein texte", - "pictures": "Images", - "videos": "Vidéos", - "ftindex": "Index en plein texte", - "details": "Article complet", - "yes": "oui", - "no": "non", - "ok": "ok", - "no-filter": "aucun filtre", - "open-link-in-web-browser": "Ouvrir le lien dans votre navigateur web", - "open-link-new-tab": "Ouvrir le lien dans un nouvel onglet", - "download-dir-dialog-title": "Voulez-vous vraiment modifier le répertoire de téléchargement ?", - "download-dir-dialog-msg": "Le nouveau chemin du répertoire de téléchargement sera :\n{{DIRECTORY}}", - "invalid-port": "Port non valide", - "zim-open-fail-title": "Fichier non valide", - "zim-open-fail-text": "Le fichier ZIM {{ZIM}} ne peut être ouvert correctement. Il sera supprimé de votre bibliothèque.", - "monitor-dir-dialog-title": "Voulez-vous vraiment modifier le répertoire de surveillance ?", - "monitor-dir-dialog-msg": "Le nouveau chemin du répertoire du moniteur sera :\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Voulez-vous vraiment effacer le répertoire de surveillance ?", - "path-was-copied": "Le chemin a été copié", - "monitor-clear-dir-dialog-msg": "Ceci arrêtera la vérification du répertoire de surveillance des nouveaux fichiers ZIM.", - "monitor-directory-tooltip": "Tous les fichiers ZIM dans ce répertoire seront automatiquement ajoutés à la bibliothèque.", - "next-tab": "Passer à l’onglet suivant", - "previous-tab": "Passer à l’onglet précédent", - "cancel-download": "Annuler le téléchargement", - "cancel-download-text": "Êtes-vous sûr de vouloir annuler le téléchargement de {{ZIM}} ?", - "delete-book": "Supprimer le livre", - "delete-book-text": "Êtes-vous sûr de vouloir supprimer {{ZIM}} ?", - "download-storage-error": "Erreur de stockage", - "download-storage-error-text": "Le système n’a pas assez de place de stockage disponible.", - "download-exceeds-max-file-size": "La taille du téléchargement dépasse la taille de fichier maximale prise en charge par le système de fichiers cible.", - "download-dir-missing": "Le répertoire de téléchargement n’existe pas.", - "download-dir-not-writable": "Le répertoire de téléchargement n’est pas accessible en écriture.", - "download-unavailable": "Téléchargement indisponible", - "download-unavailable-text": "Ce téléchargement n’est pas disponible.", - "open-book": "Ouvrir le livre", - "download-book": "Télécharger le livre", - "pause-download": "Mettre en pause le téléchargement", - "resume-download": "Reprendre le téléchargement", - "open-folder": "Ouvrir le dossier", - "couldnt-open-location": "Impossible d’ouvrir l’emplacement", - "couldnt-open-location-text": "Kiwix ne peut pas ouvrir le dossier {{FOLDER}}", - "move-files-to-trash": "Déplacer les fichiers supprimés vers la corbeille", - "move-files-to-trash-text": "Cette action déplacera le fichier vers la corbeille.", - "perma-delete-files-text": "Cette action supprimera définitivement le fichier.", - "clear-filter": "Réinitialiser les filtres actuels", - "language-searcher-placeholder": "Filtrer par langue", - "category-searcher-placeholder": "Filtrer par catégorie", - "content-type-searcher-placeholder": "Filtrer par type de contenu", - "no-details": "Introduction seulement", - "no-pictures": "Aucune image", - "no-videos": "Aucune vidéo", - "open-previous-tabs-at-startup": "Ouvrir les onglets précédents au démarrage", - "preview-book-in-web-browser": "Aperçu du livre dans un navigateur web", - "file-not-found-title": "Fichier ZIM non trouvé", - "file-not-found-text": "Le fichier ZIM n'existe pas ou n'est pas lisible", - "zim-id": "ZIM Id", - "zim-name": "Nom Zim", - "zim-path": "Voie du fichier Zim", - "bad-zim-file-error-page-title": "Fichier ZIM non valide", - "bad-zim-file-error-page-text": "Le fichier ZIM est invalide ou corrompu.", - "zim-entry-path": "Chemin d'accès ZIM", - "export-reading-list": "Exporter la liste de lecture", - "export-reading-list-error": "Une erreur s'est produite lors de l'exportation de la liste de lecture.", - "import-reading-list": "Importer la liste de lecture", - "import-reading-list-error": "Une erreur s'est produite lors de l'importation de la liste de lecture.", - "disable-sandbox": "Kiwix a été lancé à partir d'un lecteur réseau. Ceci est connu pour causer des problèmes de compatibilité avec le sandbox (bac à sable) de navigation. Par conséquent, le sandbox (bac à sable) sera désactivé. Voulez-vous continuer ?", - "save-page-as": "Enregistrer sous...", - "portable-disabled-tooltip": "Fonction désactivée en mode portable", - "scroll-next-tab": "Faire défiler vers l' onglet suivant", - "scroll-previous-tab": "Faire défiler vers l' onglet précédent", - "kiwix-search": "Recherche Kiwix", - "search-options": "Options de recherche", - "read-article": "Lire l’article", - "read-text": "Lire le texte sélectionné", - "read-stop": "Arrêter la lecture", - "stop": "Arrêter", - "voice": "Voix", - "select-read-voice": "Sélectionner la voix de lecture", - "select-read-language": "Sélectionner la langue de lecture", - "save-or-open": "Enregistrer ou ouvrir le fichier", - "save-or-open-text": "Que doit faire Kiwix avec ce fichier ?" -} +{ + "@metadata": { + "authors": [ + "Benoit74", + "Constant", + "Florian COLLIN", + "Gomoko", + "Goombiis", + "Graineahumus", + "Hecatonchire", + "Mahabarata", + "McDutchie", + "Méthodes Bulebe Hangi", + "Stephane", + "Terio legale", + "Verdy p", + "Wladek92" + ] + }, + "name": "Français", + "error-title": "Erreur", + "error-downloader-window-title": "Impossible de créer le processus de téléchargement", + "error-downloader-launch-message": "Impossible de lancer le téléchargement, Kiwix-desktop démarrera mais toutes les fonctions de téléchargement ne fonctionneront pas !", + "error-launch-server-message": "Une erreur s’est produite !", + "error-archive": "Impossible d’obtenir l’archive", + "error-opening-file": "Une erreur s'est produite lors de l'ouverture du fichier.", + "open-zim": "Ouvrir un fichier ZIM", + "local-kiwix-server": "Serveur local Kiwix", + "random-article": "Article au hasard", + "random-article-error": "Échec à l’ouverture d’un article au hasard.", + "home-page": "Page principale", + "main-menu": "Menu principal", + "print": "Imprimer", + "print-page-error": "Une erreur s’est produite lors de l’impression.", + "new-tab": "Nouvel onglet", + "close-tab": "Fermer l’onglet", + "close": "Fermer", + "reopen-closed-tab": "Rouvrir l’onglet fermé", + "browse-library": "Parcourir la bibliothèque", + "open-file": "Ouvrir un fichier", + "save-file": "Enregistrer le fichier", + "open-recent": "Fichiers récemment ouverts", + "search-article": "Rechercher un article", + "search-in-library": "Chercher dans la bibliothèque", + "find-in-page": "Chercher dans la page", + "set-fullscreen": "Mode plein écran", + "quit-fullscreen": "Quitter le mode plein écran", + "table-of-content": "Table des matières", + "add-bookmark": "Ajouter à la liste de lecture", + "remove-bookmark": "Supprimer de la liste de lecture", + "reading-list": "Liste de lecture", + "zoom-in": "Zoom avant", + "zoom-out": "Zoom arrière", + "zoom-reset": "Réinitialisation du zoom", + "help": "Aide", + "feedback": "Retour d’information", + "report-a-bug": "Signaler une anomalie", + "request-a-feature": "Demander une fonctionnalité", + "about-kiwix": "À propos de kiwix", + "donate-to-support-kiwix": "Faire un don pour soutenir Kiwix", + "exit": "Quitter", + "save-file-as-window-title": "Enregistrer le fichier sous", + "download-finished": "Téléchargement terminé", + "download-finished-message": "Le document a été téléchargé.", + "file": "Fichier", + "edit": "Modifier", + "view": "Afficher", + "tools": "Outils", + "window-title": "Bibliothèque – Kiwix", + "search": "Recherche", + "browse-directory": "Parcourir un dossier", + "settings": "Paramètres", + "back": "Retour", + "forward": "Suivant", + "search-files": "Chercher des fichiers", + "title": "Titre", + "size": "Taille", + "date": "Date", + "content-type": "Type de contenu", + "reset-sort": "réinitialiser le tri", + "open": "Ouvrir", + "delete": "Supprimer", + "download": "Télécharger", + "resume": "Reprendre", + "pause": "Pause", + "cancel": "Annuler", + "apply": "Appliquer", + "port-for-local-kiwix-server-setting": "Port pour le serveur Kiwix local", + "zoom-level-setting": "Niveau de zoom par défaut", + "download-directory-setting": "Dossier de téléchargement", + "monitor-directory-setting": "Surveiller le répertoire", + "reset": "Réinitialiser", + "clear": "Effacer", + "browse": "Parcourir", + "about-kiwix-desktop-title": "Kiwix pour ordinateurs de bureau", + "about-kiwix-desktop-description": "Kiwix vous permet d’avoir tout Wikipédia à portée de main où que vous alliez ! Sur un bateau, au milieu de nulle part, ou en prison, Kiwix vous donne accès à tout le savoir humain. Vous n’avez pas besoin d’Internet, tout est conservé dans votre ordinateur.", + "about-learn-more-about-kiwix": "En savoir plus sur Kiwix", + "about-privacy-policy": "Politique de confidentialité", + "about-release-title": "Publication", + "about-source-code": "Ce logiciel est publié selon les termes de la licence GNU General Public License version 3. Vous pouvez consulter le code source sur Github.", + "about-version": "Version : {{VERSION}}", + "about-reporting-problem-title": "Signaler des problèmes", + "about-report-issue": "Pour signaler un problème, allez sur la page de signalement et ouvrez un ticket en décrivant votre problème.", + "about-report-issue-2": "Veuillez indiquer la version de Kiwix dans votre signalement.", + "about-libraries-title": "Bibliothèques", + "kiwix-server-running-message": "Le serveur Kiwix est lancé et peut être accédé sur le réseau local à :", + "kiwix-server-description": "Démarrer un serveur permet à d’autres ordinateurs du réseau local d’accéder à votre bibliothèque Kiwix avec un navigateur web classique.", + "fullscreen-notification": "Vous êtes maintenant en plein écran. Appuyez sur Échap pour quitter !", + "online-files": "Fichiers en ligne", + "local-files": "Fichiers locaux", + "category": "Catégorie", + "language": "Langue", + "hide": "Masquer", + "open-in-browser": "Ouvrir dans un navigateur", + "start-kiwix-server": "Démarrer le serveur Kiwix", + "stop-kiwix-server": "Arrêter le serveur Kiwix", + "all": "Tout", + "all_ips": "Tous - IPv4 et IPv6", + "ipv4": "Mode IPv4 uniquement", + "ipv6": "Mode IPv6 uniquement", + "fulltext-search": "Recherche plein texte", + "pictures": "Images", + "videos": "Vidéos", + "ftindex": "Index en plein texte", + "details": "Article complet", + "yes": "oui", + "no": "non", + "ok": "ok", + "no-filter": "aucun filtre", + "open-link-in-web-browser": "Ouvrir le lien dans votre navigateur web", + "open-link-new-tab": "Ouvrir le lien dans un nouvel onglet", + "download-dir-dialog-title": "Voulez-vous vraiment modifier le répertoire de téléchargement ?", + "download-dir-dialog-msg": "Le nouveau chemin du répertoire de téléchargement sera :\n{{DIRECTORY}}", + "invalid-port": "Port non valide", + "zim-open-fail-title": "Fichier non valide", + "zim-open-fail-text": "Le fichier ZIM {{ZIM}} ne peut être ouvert correctement. Il sera supprimé de votre bibliothèque.", + "monitor-dir-dialog-title": "Voulez-vous vraiment modifier le répertoire de surveillance ?", + "monitor-dir-dialog-msg": "Le nouveau chemin du répertoire du moniteur sera :\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Voulez-vous vraiment effacer le répertoire de surveillance ?", + "path-was-copied": "Le chemin a été copié", + "monitor-clear-dir-dialog-msg": "Ceci arrêtera la vérification du répertoire de surveillance des nouveaux fichiers ZIM.", + "monitor-directory-tooltip": "Tous les fichiers ZIM dans ce répertoire seront automatiquement ajoutés à la bibliothèque.", + "next-tab": "Passer à l’onglet suivant", + "previous-tab": "Passer à l’onglet précédent", + "cancel-download": "Annuler le téléchargement", + "cancel-download-text": "Êtes-vous sûr de vouloir annuler le téléchargement de {{ZIM}} ?", + "delete-book": "Supprimer le livre", + "delete-book-text": "Êtes-vous sûr de vouloir supprimer {{ZIM}} ?", + "download-storage-error": "Erreur de stockage", + "download-storage-error-text": "Le système n’a pas assez de place de stockage disponible.", + "download-exceeds-max-file-size": "La taille du téléchargement dépasse la taille de fichier maximale prise en charge par le système de fichiers cible.", + "download-dir-missing": "Le répertoire de téléchargement n’existe pas.", + "download-dir-not-writable": "Le répertoire de téléchargement n’est pas accessible en écriture.", + "download-unavailable": "Téléchargement indisponible", + "download-unavailable-text": "Ce téléchargement n’est pas disponible.", + "open-book": "Ouvrir le livre", + "download-book": "Télécharger le livre", + "pause-download": "Mettre en pause le téléchargement", + "resume-download": "Reprendre le téléchargement", + "open-folder": "Ouvrir le dossier", + "couldnt-open-location": "Impossible d’ouvrir l’emplacement", + "couldnt-open-location-text": "Kiwix ne peut pas ouvrir le dossier {{FOLDER}}", + "move-files-to-trash": "Déplacer les fichiers supprimés vers la corbeille", + "move-files-to-trash-text": "Cette action déplacera le fichier vers la corbeille.", + "perma-delete-files-text": "Cette action supprimera définitivement le fichier.", + "clear-filter": "Réinitialiser les filtres actuels", + "language-searcher-placeholder": "Filtrer par langue", + "category-searcher-placeholder": "Filtrer par catégorie", + "content-type-searcher-placeholder": "Filtrer par type de contenu", + "no-details": "Introduction seulement", + "no-pictures": "Aucune image", + "no-videos": "Aucune vidéo", + "open-previous-tabs-at-startup": "Ouvrir les onglets précédents au démarrage", + "preview-book-in-web-browser": "Aperçu du livre dans un navigateur web", + "file-not-found-title": "Fichier ZIM non trouvé", + "file-not-found-text": "Le fichier ZIM n'existe pas ou n'est pas lisible", + "zim-id": "ZIM Id", + "zim-name": "Nom Zim", + "zim-path": "Voie du fichier Zim", + "bad-zim-file-error-page-title": "Fichier ZIM non valide", + "bad-zim-file-error-page-text": "Le fichier ZIM est invalide ou corrompu.", + "zim-entry-path": "Chemin d'accès ZIM", + "export-reading-list": "Exporter la liste de lecture", + "export-reading-list-error": "Une erreur s'est produite lors de l'exportation de la liste de lecture.", + "import-reading-list": "Importer la liste de lecture", + "import-reading-list-error": "Une erreur s'est produite lors de l'importation de la liste de lecture.", + "disable-sandbox": "Kiwix a été lancé à partir d'un lecteur réseau. Ceci est connu pour causer des problèmes de compatibilité avec le sandbox (bac à sable) de navigation. Par conséquent, le sandbox (bac à sable) sera désactivé. Voulez-vous continuer ?", + "save-page-as": "Enregistrer sous...", + "portable-disabled-tooltip": "Fonction désactivée en mode portable", + "scroll-next-tab": "Faire défiler vers l' onglet suivant", + "scroll-previous-tab": "Faire défiler vers l' onglet précédent", + "kiwix-search": "Recherche Kiwix", + "search-options": "Options de recherche", + "read-article": "Lire l’article", + "read-text": "Lire le texte sélectionné", + "read-stop": "Arrêter la lecture", + "stop": "Arrêter", + "voice": "Voix", + "select-read-voice": "Sélectionner la voix de lecture", + "select-read-language": "Sélectionner la langue de lecture", + "save-or-open": "Enregistrer ou ouvrir le fichier", + "save-or-open-text": "Que doit faire Kiwix avec ce fichier ?" +} diff --git a/resources/i18n/gsw.json b/resources/i18n/gsw.json index bdf24bf27..96003c280 100644 --- a/resources/i18n/gsw.json +++ b/resources/i18n/gsw.json @@ -1,52 +1,52 @@ -{ - "@metadata": { - "authors": [ - "McDutchie", - "Rschlegel" - ] - }, - "name": "Schwiizerdütsch", - "open-zim": "Zim öffnä", - "random-article": "Zuefälligi Syte", - "print": "Drucke", - "browse-library": "Dur'd Bibliothek stöbere", - "open-file": "Datei uftuä", - "search-in-library": "Bibliothek dürsuechä", - "find-in-page": "I de Syte finde", - "feedback": "Rückmäldige", - "about-kiwix": "Über Kiwix", - "donate-to-support-kiwix": "Spände um Kiwix z'unterschtütze", - "exit": "Beände", - "save-file-as-window-title": "Datei speicherä als", - "download-finished-message": "S Document isch abegladä worde.", - "file": "Datei", - "edit": "Bearbeite", - "view": "Aaluege", - "tools": "Wärchzüüg", - "search": "Sueche", - "settings": "Yystellige", - "back": "Zrügg", - "forward": "Wyter", - "title": "Titel", - "size": "Grössi", - "date": "Datum", - "content-type": "Inhaltstyp", - "open": "Ufmache", - "delete": "Lösche", - "download": "Abelade", - "resume": "Wiitermache", - "pause": "Pousiere", - "cancel": "Abbräche", - "apply": "Aawände", - "reset": "Zrüggsetze", - "about-reporting-problem-title": "Es Problem mälde", - "about-libraries-title": "Bibliotheke", - "local-files": "Lokali Dateiä", - "hide": "Uusblände", - "all": "Alli", - "fulltext-search": "Volltexschtsuechi", - "pictures": "Bilder", - "videos": "Videos", - "ftindex": "Volltexschtindex", - "no-filter": "kei Filter" -} +{ + "@metadata": { + "authors": [ + "McDutchie", + "Rschlegel" + ] + }, + "name": "Schwiizerdütsch", + "open-zim": "Zim öffnä", + "random-article": "Zuefälligi Syte", + "print": "Drucke", + "browse-library": "Dur'd Bibliothek stöbere", + "open-file": "Datei uftuä", + "search-in-library": "Bibliothek dürsuechä", + "find-in-page": "I de Syte finde", + "feedback": "Rückmäldige", + "about-kiwix": "Über Kiwix", + "donate-to-support-kiwix": "Spände um Kiwix z'unterschtütze", + "exit": "Beände", + "save-file-as-window-title": "Datei speicherä als", + "download-finished-message": "S Document isch abegladä worde.", + "file": "Datei", + "edit": "Bearbeite", + "view": "Aaluege", + "tools": "Wärchzüüg", + "search": "Sueche", + "settings": "Yystellige", + "back": "Zrügg", + "forward": "Wyter", + "title": "Titel", + "size": "Grössi", + "date": "Datum", + "content-type": "Inhaltstyp", + "open": "Ufmache", + "delete": "Lösche", + "download": "Abelade", + "resume": "Wiitermache", + "pause": "Pousiere", + "cancel": "Abbräche", + "apply": "Aawände", + "reset": "Zrüggsetze", + "about-reporting-problem-title": "Es Problem mälde", + "about-libraries-title": "Bibliotheke", + "local-files": "Lokali Dateiä", + "hide": "Uusblände", + "all": "Alli", + "fulltext-search": "Volltexschtsuechi", + "pictures": "Bilder", + "videos": "Videos", + "ftindex": "Volltexschtindex", + "no-filter": "kei Filter" +} diff --git a/resources/i18n/ha.json b/resources/i18n/ha.json index 001f97dac..cd311b57b 100644 --- a/resources/i18n/ha.json +++ b/resources/i18n/ha.json @@ -1,150 +1,150 @@ -{ - "@metadata": { - "authors": [ - "787IYO", - "El-hussain14", - "Rofiatmustapha12", - "Yusuf Sa'adu" - ] - }, - "name": "turanci", - "error-title": "kuskure", - "error-downloader-window-title": "Ba za a iya ƙirƙirar mai saukewa ba", - "error-downloader-launch-message": "Ba zai yiwu a kaddamar da mai saukewa ba, Kiwix-desktop zai fara amma duk ayyukan saukewa ba za su yi aiki ba!", - "error-launch-server-message": "An yi kuskure!", - "error-archive": "Ba za a iya samun tarihin ba", - "open-zim": "Buɗe Fayil na ZIM", - "local-kiwix-server": "Sabar Kiwix ta gida", - "random-article": "Labari na bazuwar", - "random-article-error": "An kasa buɗe labarin bazuwar.", - "home-page": "Shafin gida", - "main-menu": "Babban menu", - "print": "Bugawa", - "print-page-error": "Kuskuren ya faru yayin bugawa.", - "new-tab": "Sabon shafin", - "close-tab": "Rufe shafin", - "close": "Rufe", - "reopen-closed-tab": "Sake bude shafin da aka rufe", - "browse-library": "Nemo ɗakin karatu", - "open-file": "Bude fayil", - "open-recent": "Bude kwanan nan", - "search-article": "Duba mukala", - "search-in-library": "Bincika a cikin dakin karatu", - "find-in-page": "Nema a shafi", - "set-fullscreen": "Saita cikakken allo", - "quit-fullscreen": "A daina cikakken allo", - "table-of-content": "Abubuwan dake ciki", - "reading-list": "Jerin karatu", - "zoom-in": "Zuƙo ciki", - "zoom-out": "Zuƙo waje", - "zoom-reset": "Sake saitin zuƙowa", - "help": "Taimako", - "feedback": "Bayani", - "report-a-bug": "ka bada labarin duk wata matsala", - "request-a-feature": "Nemi fasali", - "about-kiwix": "Game da Kiwix", - "donate-to-support-kiwix": "Ba da gudummawa don tallafawa Kiwix", - "exit": "Fita", - "save-file-as-window-title": "Ajiye fayil kamar yadda", - "download-finished-message": "An sauke takardar.", - "file": "Fayil", - "edit": "Gyara", - "view": "Duba", - "tools": "Akwatin kayan aiki", - "window-title": "Laburaren - Kiwix", - "search": "Bincike", - "browse-directory": "Littafin Browse", - "settings": "Saituttuka", - "back": "Baya", - "forward": "Gaba", - "search-files": "Bincika fayiloli", - "title": "Take", - "size": "Girma", - "date": "Kwanan wata", - "content-type": "Irin abubuwan da ke ciki", - "reset-sort": "sake saitawa", - "open": "Buɗe", - "delete": "Share", - "download": "Sauke", - "resume": "Ci gaba", - "pause": "Dakatar da shi", - "cancel": "Yanke", - "apply": "Nema", - "port-for-local-kiwix-server-setting": "Tashar jiragen ruwa don uwar garken Kiwix na gida", - "zoom-level-setting": "Matsayin zoom na kuskure", - "download-directory-setting": "Bayanan saukewa", - "monitor-directory-setting": "Lissafin mai kula", - "reset": "Sake saiti", - "clear": "Share", - "browse": "Browse", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix yana ba ka damar samun dukkan Wikipedia a hannu duk inda kuka tafi! A cikin jirgin ruwa, a tsakiyar babu inda, ko a kurkuku, Kiwix yana ba ku damar samun dukkan ilimin ɗan adam. Ba ka buƙatar Intanet, ana adana komai akan kwamfutarka.", - "about-learn-more-about-kiwix": "Ƙara koyo game da Kiwix", - "about-release-title": "Saki", - "about-source-code": "An fito da wannan software a ƙarƙashin sharuɗɗan GNU Janar na Lasisi na Jama'a 3. Duba lambar tushe akan Github .", - "about-version": "Siga: {{VERSION}}", - "about-reporting-problem-title": "Bada rahoton matsala", - "about-report-issue": "Don buɗe matsala, ziyarci Kiwix-tebur mai sa ido kuma buɗe batun da ke bayyana matsalar ku.", - "about-report-issue-2": "Da fatan za a ambaci sigar a cikin fitowar.", - "about-libraries-title": "Dakunan karatu", - "kiwix-server-running-message": "Sabar Kiwix tana gudana kuma ana iya samun dama ga cibiyar sadarwar gida a:", - "kiwix-server-description": "Fara uwar garken yana ba da damar wasu kwamfutoci a cikin hanyar sadarwar gida don samun damar ɗakin karatu na Kiwix tare da madaidaicin burauzar gidan yanar gizo.", - "fullscreen-notification": "Yanzu kuna cikin yanayin cikakken allo. Latsa ESC don barin!", - "online-files": "Fayilolin Kan layi", - "local-files": "Fayilolin gida", - "category": "Rukuni", - "language": "Harshe", - "hide": "Ɓoye", - "open-in-browser": "Bude a browser", - "start-kiwix-server": "Fara Sabar Kiwix", - "stop-kiwix-server": "Dakatar da Sabar Kiwix", - "all": "Duka", - "fulltext-search": "Binciken cikakken rubutu", - "pictures": "Hotuna", - "videos": "Bidiyo", - "ftindex": "Cikakken rubutun", - "details": "Cikakken labarin", - "yes": "eh", - "no": "a'a", - "ok": "ok", - "no-filter": "babu tacewa", - "open-link-in-web-browser": "Bude hanyar haɗi a cikin burauzar yanar gizo", - "open-link-new-tab": "Bude mahadar a sabon shafi?", - "download-dir-dialog-title": "Shin kun tabbata kuna so ku canza kundin saukewa?", - "download-dir-dialog-msg": "Sabuwar hanyar duba jagorar za ta kasance:\n{{DIRECTORY}}", - "invalid-port": "Rami marar inganci", - "zim-open-fail-title": "Fayil mara inganci", - "zim-open-fail-text": "Ba za a iya buɗe fayil ɗin ZIM {{ZIM}} yadda ya kamata ba. Za a cire shi daga ɗakin karatu.", - "monitor-dir-dialog-title": "Shin kun tabbata kuna son canza kundin adireshi?", - "monitor-dir-dialog-msg": "Sabuwar hanyar duba jagorar za ta kasance:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Shin kun tabbata kuna son share kundin adireshi?", - "monitor-clear-dir-dialog-msg": "Wannan zai dakatar da duba kundin adireshi don sabbin fayilolin ZIM.", - "monitor-directory-tooltip": "Duk fayilolin ZIM da ke cikin wannan kundin za a ƙara su ta atomatik zuwa ɗakin karatu.", - "next-tab": "Matsar zuwa shafi na gaba", - "previous-tab": "Ka matsa zuwa shafin da ya gabata", - "cancel-download": "Ka soke saukewa", - "cancel-download-text": "Shin kun tabbata kuna son soke zazzagewar {{ZIM}}?", - "delete-book": "Share littafin", - "delete-book-text": "Shin kun tabbata kuna son share {{ZIM}}?", - "download-storage-error": "Kuskuren Ma'aji", - "download-storage-error-text": "Tsarin ba shi da isasshen ma'aji.", - "download-unavailable": "Saukewa Ba a samu ba", - "download-unavailable-text": "Wannan saukewa ba ya samuwa.", - "open-book": "Bude littafi", - "download-book": "Sauke littafin", - "pause-download": "Dakatar da saukewa", - "resume-download": "Ci gaba da saukewa", - "open-folder": "Bude fayil", - "couldnt-open-location": "Ba za a iya bude wurin ba", - "couldnt-open-location-text": "Kiwix ba zai iya bude folder ba {{FOLDER}}", - "move-files-to-trash": "Matsar da fayilolin da aka goge zuwa sharar gida", - "move-files-to-trash-text": "Wannan aikin zai motsa fayil ɗin zuwa sharar gida.", - "perma-delete-files-text": "Wannan aikin zai share fayil ɗin har abada.", - "clear-filter": "Share abubuwan tacewa a halin yanzu", - "language-searcher-placeholder": "Harshen tacewa", - "category-searcher-placeholder": "Nau'in tacewa", - "content-type-searcher-placeholder": "Nau'in abun ciki na tacewa", - "no-details": "Gabatarwa kawai", - "no-pictures": "Babu Hotuna", - "no-videos": "Babu Bidiyo" -} +{ + "@metadata": { + "authors": [ + "787IYO", + "El-hussain14", + "Rofiatmustapha12", + "Yusuf Sa'adu" + ] + }, + "name": "turanci", + "error-title": "kuskure", + "error-downloader-window-title": "Ba za a iya ƙirƙirar mai saukewa ba", + "error-downloader-launch-message": "Ba zai yiwu a kaddamar da mai saukewa ba, Kiwix-desktop zai fara amma duk ayyukan saukewa ba za su yi aiki ba!", + "error-launch-server-message": "An yi kuskure!", + "error-archive": "Ba za a iya samun tarihin ba", + "open-zim": "Buɗe Fayil na ZIM", + "local-kiwix-server": "Sabar Kiwix ta gida", + "random-article": "Labari na bazuwar", + "random-article-error": "An kasa buɗe labarin bazuwar.", + "home-page": "Shafin gida", + "main-menu": "Babban menu", + "print": "Bugawa", + "print-page-error": "Kuskuren ya faru yayin bugawa.", + "new-tab": "Sabon shafin", + "close-tab": "Rufe shafin", + "close": "Rufe", + "reopen-closed-tab": "Sake bude shafin da aka rufe", + "browse-library": "Nemo ɗakin karatu", + "open-file": "Bude fayil", + "open-recent": "Bude kwanan nan", + "search-article": "Duba mukala", + "search-in-library": "Bincika a cikin dakin karatu", + "find-in-page": "Nema a shafi", + "set-fullscreen": "Saita cikakken allo", + "quit-fullscreen": "A daina cikakken allo", + "table-of-content": "Abubuwan dake ciki", + "reading-list": "Jerin karatu", + "zoom-in": "Zuƙo ciki", + "zoom-out": "Zuƙo waje", + "zoom-reset": "Sake saitin zuƙowa", + "help": "Taimako", + "feedback": "Bayani", + "report-a-bug": "ka bada labarin duk wata matsala", + "request-a-feature": "Nemi fasali", + "about-kiwix": "Game da Kiwix", + "donate-to-support-kiwix": "Ba da gudummawa don tallafawa Kiwix", + "exit": "Fita", + "save-file-as-window-title": "Ajiye fayil kamar yadda", + "download-finished-message": "An sauke takardar.", + "file": "Fayil", + "edit": "Gyara", + "view": "Duba", + "tools": "Akwatin kayan aiki", + "window-title": "Laburaren - Kiwix", + "search": "Bincike", + "browse-directory": "Littafin Browse", + "settings": "Saituttuka", + "back": "Baya", + "forward": "Gaba", + "search-files": "Bincika fayiloli", + "title": "Take", + "size": "Girma", + "date": "Kwanan wata", + "content-type": "Irin abubuwan da ke ciki", + "reset-sort": "sake saitawa", + "open": "Buɗe", + "delete": "Share", + "download": "Sauke", + "resume": "Ci gaba", + "pause": "Dakatar da shi", + "cancel": "Yanke", + "apply": "Nema", + "port-for-local-kiwix-server-setting": "Tashar jiragen ruwa don uwar garken Kiwix na gida", + "zoom-level-setting": "Matsayin zoom na kuskure", + "download-directory-setting": "Bayanan saukewa", + "monitor-directory-setting": "Lissafin mai kula", + "reset": "Sake saiti", + "clear": "Share", + "browse": "Browse", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix yana ba ka damar samun dukkan Wikipedia a hannu duk inda kuka tafi! A cikin jirgin ruwa, a tsakiyar babu inda, ko a kurkuku, Kiwix yana ba ku damar samun dukkan ilimin ɗan adam. Ba ka buƙatar Intanet, ana adana komai akan kwamfutarka.", + "about-learn-more-about-kiwix": "Ƙara koyo game da Kiwix", + "about-release-title": "Saki", + "about-source-code": "An fito da wannan software a ƙarƙashin sharuɗɗan GNU Janar na Lasisi na Jama'a 3. Duba lambar tushe akan Github .", + "about-version": "Siga: {{VERSION}}", + "about-reporting-problem-title": "Bada rahoton matsala", + "about-report-issue": "Don buɗe matsala, ziyarci Kiwix-tebur mai sa ido kuma buɗe batun da ke bayyana matsalar ku.", + "about-report-issue-2": "Da fatan za a ambaci sigar a cikin fitowar.", + "about-libraries-title": "Dakunan karatu", + "kiwix-server-running-message": "Sabar Kiwix tana gudana kuma ana iya samun dama ga cibiyar sadarwar gida a:", + "kiwix-server-description": "Fara uwar garken yana ba da damar wasu kwamfutoci a cikin hanyar sadarwar gida don samun damar ɗakin karatu na Kiwix tare da madaidaicin burauzar gidan yanar gizo.", + "fullscreen-notification": "Yanzu kuna cikin yanayin cikakken allo. Latsa ESC don barin!", + "online-files": "Fayilolin Kan layi", + "local-files": "Fayilolin gida", + "category": "Rukuni", + "language": "Harshe", + "hide": "Ɓoye", + "open-in-browser": "Bude a browser", + "start-kiwix-server": "Fara Sabar Kiwix", + "stop-kiwix-server": "Dakatar da Sabar Kiwix", + "all": "Duka", + "fulltext-search": "Binciken cikakken rubutu", + "pictures": "Hotuna", + "videos": "Bidiyo", + "ftindex": "Cikakken rubutun", + "details": "Cikakken labarin", + "yes": "eh", + "no": "a'a", + "ok": "ok", + "no-filter": "babu tacewa", + "open-link-in-web-browser": "Bude hanyar haɗi a cikin burauzar yanar gizo", + "open-link-new-tab": "Bude mahadar a sabon shafi?", + "download-dir-dialog-title": "Shin kun tabbata kuna so ku canza kundin saukewa?", + "download-dir-dialog-msg": "Sabuwar hanyar duba jagorar za ta kasance:\n{{DIRECTORY}}", + "invalid-port": "Rami marar inganci", + "zim-open-fail-title": "Fayil mara inganci", + "zim-open-fail-text": "Ba za a iya buɗe fayil ɗin ZIM {{ZIM}} yadda ya kamata ba. Za a cire shi daga ɗakin karatu.", + "monitor-dir-dialog-title": "Shin kun tabbata kuna son canza kundin adireshi?", + "monitor-dir-dialog-msg": "Sabuwar hanyar duba jagorar za ta kasance:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Shin kun tabbata kuna son share kundin adireshi?", + "monitor-clear-dir-dialog-msg": "Wannan zai dakatar da duba kundin adireshi don sabbin fayilolin ZIM.", + "monitor-directory-tooltip": "Duk fayilolin ZIM da ke cikin wannan kundin za a ƙara su ta atomatik zuwa ɗakin karatu.", + "next-tab": "Matsar zuwa shafi na gaba", + "previous-tab": "Ka matsa zuwa shafin da ya gabata", + "cancel-download": "Ka soke saukewa", + "cancel-download-text": "Shin kun tabbata kuna son soke zazzagewar {{ZIM}}?", + "delete-book": "Share littafin", + "delete-book-text": "Shin kun tabbata kuna son share {{ZIM}}?", + "download-storage-error": "Kuskuren Ma'aji", + "download-storage-error-text": "Tsarin ba shi da isasshen ma'aji.", + "download-unavailable": "Saukewa Ba a samu ba", + "download-unavailable-text": "Wannan saukewa ba ya samuwa.", + "open-book": "Bude littafi", + "download-book": "Sauke littafin", + "pause-download": "Dakatar da saukewa", + "resume-download": "Ci gaba da saukewa", + "open-folder": "Bude fayil", + "couldnt-open-location": "Ba za a iya bude wurin ba", + "couldnt-open-location-text": "Kiwix ba zai iya bude folder ba {{FOLDER}}", + "move-files-to-trash": "Matsar da fayilolin da aka goge zuwa sharar gida", + "move-files-to-trash-text": "Wannan aikin zai motsa fayil ɗin zuwa sharar gida.", + "perma-delete-files-text": "Wannan aikin zai share fayil ɗin har abada.", + "clear-filter": "Share abubuwan tacewa a halin yanzu", + "language-searcher-placeholder": "Harshen tacewa", + "category-searcher-placeholder": "Nau'in tacewa", + "content-type-searcher-placeholder": "Nau'in abun ciki na tacewa", + "no-details": "Gabatarwa kawai", + "no-pictures": "Babu Hotuna", + "no-videos": "Babu Bidiyo" +} diff --git a/resources/i18n/he.json b/resources/i18n/he.json index 5913696d5..19ff599c4 100644 --- a/resources/i18n/he.json +++ b/resources/i18n/he.json @@ -1,193 +1,193 @@ -{ - "@metadata": { - "authors": [ - "Amire80", - "McDutchie", - "Steeve815", - "YaronSh" - ] - }, - "name": "עברית", - "error-title": "שגיאה", - "error-downloader-window-title": "לא ניתן ליצור מנגנון הורדה", - "error-downloader-launch-message": "אי אפשר לטעון מנגנון הורדה, Kiwix לשולחן העבודה יופעל אך יכולות ההורדה לא תפעלנה!", - "error-launch-server-message": "אירעה שגיאה!", - "error-archive": "לא ניתן לקבל את הארכיון", - "error-opening-file": "אירעה שגיאה בפתיחת הקובץ.", - "open-zim": "פתיחת קובץ ZIM", - "local-kiwix-server": "שרת Kiwix מקומי", - "random-article": "ערך אקראי", - "random-article-error": "פתיחת ערך אקראי לא עבדה.", - "home-page": "דף הבית", - "main-menu": "תפריט ראשי", - "print": "הדפסה", - "print-page-error": "אירעה שגיאה בעת הדפסה.", - "new-tab": "כרטיסייה חדשה", - "close-tab": "סגירת לשונית", - "close": "סגירה", - "reopen-closed-tab": "פתיחת הלשונית שנסגרה מחדש", - "browse-library": "עיון בספרייה", - "open-file": "פתיחת קובץ", - "save-file": "שמירת קובץ", - "open-recent": "פתיחת אחרון", - "search-article": "חיפוש ערך", - "search-in-library": "חיפוש בספרייה", - "find-in-page": "איתור בעמוד", - "set-fullscreen": "הגדרת מסך מלא", - "quit-fullscreen": "יציאה ממסך מלא", - "table-of-content": "תוכן עניינים", - "add-bookmark": "הוספה לרשימת הקריאה", - "remove-bookmark": "הסרה מרשימת הקריאה", - "reading-list": "רשימת קריאה", - "zoom-in": "להתקרב", - "zoom-out": "להתרחק", - "zoom-reset": "איפוס תקריב", - "help": "עזרה", - "feedback": "משוב", - "report-a-bug": "דיווח על תקלה", - "request-a-feature": "לבקש יכולת", - "about-kiwix": "על אודות Kiwix", - "donate-to-support-kiwix": "ניתן לתרום כדי לתמוך ב־Kiwix", - "exit": "יציאה", - "save-file-as-window-title": "שמירת הקובץ בשם", - "download-finished": "ההורדה הסתיימה", - "download-finished-message": "המסמך התקבל.", - "file": "קובץ", - "edit": "עריכה", - "view": "תצוגה", - "tools": "כלים", - "window-title": "ספרייה - Kiwix", - "search": "חיפוש", - "browse-directory": "עיון בספרייה", - "settings": "הגדרות", - "back": "חזרה", - "forward": "קדימה", - "search-files": "חיפוש קבצים", - "title": "כותרת", - "size": "גודל", - "date": "תאריך", - "content-type": "סוג התוכן", - "reset-sort": "איפוס מיון", - "open": "פתיחה", - "delete": "מחיקה", - "download": "הורדה", - "resume": "המשך", - "pause": "השהיה", - "cancel": "ביטול", - "apply": "החלה", - "port-for-local-kiwix-server-setting": "פתחה לשרת Kiwix מקומי", - "zoom-level-setting": "רמת תקריב כברירת מחדל", - "download-directory-setting": "תיקיית הורדה", - "monitor-directory-setting": "תיקיית ניטור", - "reset": "איפוס", - "clear": "ניקוי", - "browse": "עיון", - "about-kiwix-desktop-title": "Kiwix לשולחן העבודה", - "about-kiwix-desktop-description": "קיוויקס מאפשר לך לקחת אתך את ויקיפדיה לכל מקום שהוא! בסירה, באמצע שום מקום או בכלא, קיוויקס מעניק לך גישה לכל הידע האנושי. אין צורך באינטרנט, הכול מאוחסן אצלך במחשב.", - "about-learn-more-about-kiwix": "מידע נוסף אודות Kiwix", - "about-privacy-policy": "מדיניות פרטיות", - "about-release-title": "מהדורה", - "about-source-code": "תכנה זו מופצת בכפוף לתנאי גרסה 3 של הרישיון הציבורי הכללי של גנו. ניתן לצפות בקוד המקור ב־Github.", - "about-version": "גרסה: {{VERSION}}", - "about-reporting-problem-title": "דיווח תקלות", - "about-report-issue": "כדי לדווח על תקלה, עליך בקר בעוקב התקלות של Kiwix לשולחן העבודה ולהוסיף דיווח שמתאר את הבעיה שלך.", - "about-report-issue-2": "נא לציין בדיווח את מספר הגרסה.", - "about-libraries-title": "ספריות", - "kiwix-server-running-message": "שרת ה־Kiwix פעיל וניתן לגשת אליו דרך הרשת המקומית בכתובת:", - "kiwix-server-description": "הפעלת שרת מאפשרת למחשבים אחרים ברשת המקומית לגשת לספריית ה־Kiwix שלך דרך דפדפן אינטרנט תקני.", - "fullscreen-notification": "הגדלת למסך מלא. ESC ליציאה!", - "online-files": "קבצים מקוונים", - "local-files": "קבצים מקומיים", - "category": "קטגוריה", - "language": "שפה", - "hide": "הסתרה", - "open-in-browser": "פתיחה בדפדפן", - "start-kiwix-server": "הפעלת שרת Kiwix", - "stop-kiwix-server": "עצירת שרת Kiwix", - "all": "הכול", - "all_ips": "הכול - מצב שני תקנים (Dual Stack)", - "ipv4": "מצב IPv4 בלבד", - "ipv6": "מצב IPv6 בלבד", - "fulltext-search": "חיפוש טקסט מלא", - "pictures": "תמונות", - "videos": "סרטונים", - "ftindex": "מפתח כל הטקסט", - "details": "ערך מלא", - "yes": "כן", - "no": "לא", - "ok": "אישור", - "no-filter": "ללא מסנן", - "open-link-in-web-browser": "פתיחת קישור בדפדפן", - "open-link-new-tab": "פתיחת קישור בלשונית חדשה", - "download-dir-dialog-title": "להחליף את תיקיית ההורדה?", - "download-dir-dialog-msg": "נתיב תיקיית ההורדה החדש הוא:\n{{DIRECTORY}}", - "invalid-port": "פתחה שגויה", - "zim-open-fail-title": "קובץ בלתי־תקין", - "zim-open-fail-text": "קובץ ה־ZIM בשם {{ZIM}} אינו יכול להיפתח באופן הולם. הוא יוסר מהספרייה שלך.", - "monitor-dir-dialog-title": "האם ברצונך באמת לשנות את תיקיית הניטור?", - "monitor-dir-dialog-msg": "הנתיב החדש לתיקיית הניתור יהיה:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "האם ברצונך באמת לנקות את תיקיית הניטור?", - "path-was-copied": "הנתיב הועתק", - "monitor-clear-dir-dialog-msg": "זה יפסיק לדגום את תיקיית הניטור ולחפש בה קובצי ZIM חדשים.", - "monitor-directory-tooltip": "כל קובצי ה־ZIM בתיקייה הזאת יוספו אוטומטית לספרייה.", - "next-tab": "לעבור ללשונית הבאה", - "previous-tab": "לעבור ללשונית הקודמת", - "cancel-download": "ביטול הורדה", - "cancel-download-text": "האם באמת לבטל את ההורדה של {{ZIM}}?", - "delete-book": "מחיקת ספר", - "delete-book-text": "האם באמת למחוק את {{ZIM}}?", - "download-storage-error": "שגיאת אחסון", - "download-storage-error-text": "במערכת אין מספיק אחסון זמין.", - "download-exceeds-max-file-size": "גודל ההורדה חורג מגודל הקובץ המרבי הנתמך על־ידי מערכת הקבצים ביעד.", - "download-dir-missing": "תיקיית ההורדות לא קיימת.", - "download-dir-not-writable": "תיקיית ההורדות נעולה לכתיבה.", - "download-unavailable": "הורדה אינה זמינה", - "download-unavailable-text": "ההורדה הזאת אינה זמינה.", - "open-book": "פתיחת ספר", - "download-book": "הורדת ספר", - "pause-download": "עצירת ההורדה", - "resume-download": "המשך ההורדה", - "open-folder": "פתיחת תיקייה", - "couldnt-open-location": "לא ניתן לפתוח את המיקום", - "couldnt-open-location-text": "קיוויקס לא הצליח לפתוח את התיקייה {{FOLDER}}", - "move-files-to-trash": "להעביר קבצים שנמחקים לאשפה", - "move-files-to-trash-text": "הפעולה הזאת תעביר את הקובץ לאשפה.", - "perma-delete-files-text": "הפעולה האת תמחק את הקובץ לתמיד.", - "clear-filter": "ניקוי המסננים שמוגדרים כעת", - "language-searcher-placeholder": "סינון לפי שפה", - "category-searcher-placeholder": "סינון לפי קטגוריה", - "content-type-searcher-placeholder": "סינון לפי סוג תוכן", - "no-details": "הקדמה בלבד", - "no-pictures": "ללא תמונות", - "no-videos": "ללא סרטים", - "open-previous-tabs-at-startup": "לפתוח את הלשוניות הקודמות בעת ההפעלה", - "preview-book-in-web-browser": "תצוגה מקדימה של ספר בדפדפן", - "file-not-found-title": "קובץ ה־ZIM לא נמצא", - "file-not-found-text": "קובץ ה־ZIM אינו קיים או אינו קריא", - "zim-id": "מזהה ה־Zim", - "zim-name": "שם ה־Zim", - "zim-path": "נתיב קובץ ה־Zim", - "bad-zim-file-error-page-title": "קובץ ZIM בלתי־תקין", - "bad-zim-file-error-page-text": "קובץ ה־ZIM בלתי־תקין או מקולקל.", - "zim-entry-path": "נתיב כניסה ל־ZIM", - "export-reading-list": "יצוא רשימת קריאה", - "export-reading-list-error": "אירעה שגיאה בעת יצוא רשימת קריאה.", - "import-reading-list": "יבוא רשימת קריאה", - "import-reading-list-error": "אירעה שגיאה בעת יבוא רשימת קריאה.", - "disable-sandbox": "קיוויקס הופעל מכונן רשת. זה ידוע כגורם לבעיות תאימות בשל ארגז החול של העיון (browsing sandboxing). כתוצאה מזה, ארגז החול יושבת. האם להמשיך?", - "save-page-as": "שמירה בשם...", - "portable-disabled-tooltip": "הפונקציה מושבתת במצב נייד", - "scroll-next-tab": "לגלול ללשונית הבאה", - "scroll-previous-tab": "לגלול ללשונית הקודמת", - "kiwix-search": "חיפוש קיוויקס", - "search-options": "אפשרויות חיפוש", - "read-article": "להקריא את הערך", - "read-text": "להקריא את הטקסט שנבחר", - "read-stop": "להפסיק להקריא", - "stop": "עצירה", - "voice": "קול", - "select-read-voice": "בחירת קול ההקראה", - "select-read-language": "בחירת שפת ההקראה", - "save-or-open": "שמירה או פתיחה של קובץ", - "save-or-open-text": "מה קיוויקס צריך לעשות עם הקובץ הזה?" -} +{ + "@metadata": { + "authors": [ + "Amire80", + "McDutchie", + "Steeve815", + "YaronSh" + ] + }, + "name": "עברית", + "error-title": "שגיאה", + "error-downloader-window-title": "לא ניתן ליצור מנגנון הורדה", + "error-downloader-launch-message": "אי אפשר לטעון מנגנון הורדה, Kiwix לשולחן העבודה יופעל אך יכולות ההורדה לא תפעלנה!", + "error-launch-server-message": "אירעה שגיאה!", + "error-archive": "לא ניתן לקבל את הארכיון", + "error-opening-file": "אירעה שגיאה בפתיחת הקובץ.", + "open-zim": "פתיחת קובץ ZIM", + "local-kiwix-server": "שרת Kiwix מקומי", + "random-article": "ערך אקראי", + "random-article-error": "פתיחת ערך אקראי לא עבדה.", + "home-page": "דף הבית", + "main-menu": "תפריט ראשי", + "print": "הדפסה", + "print-page-error": "אירעה שגיאה בעת הדפסה.", + "new-tab": "כרטיסייה חדשה", + "close-tab": "סגירת לשונית", + "close": "סגירה", + "reopen-closed-tab": "פתיחת הלשונית שנסגרה מחדש", + "browse-library": "עיון בספרייה", + "open-file": "פתיחת קובץ", + "save-file": "שמירת קובץ", + "open-recent": "פתיחת אחרון", + "search-article": "חיפוש ערך", + "search-in-library": "חיפוש בספרייה", + "find-in-page": "איתור בעמוד", + "set-fullscreen": "הגדרת מסך מלא", + "quit-fullscreen": "יציאה ממסך מלא", + "table-of-content": "תוכן עניינים", + "add-bookmark": "הוספה לרשימת הקריאה", + "remove-bookmark": "הסרה מרשימת הקריאה", + "reading-list": "רשימת קריאה", + "zoom-in": "להתקרב", + "zoom-out": "להתרחק", + "zoom-reset": "איפוס תקריב", + "help": "עזרה", + "feedback": "משוב", + "report-a-bug": "דיווח על תקלה", + "request-a-feature": "לבקש יכולת", + "about-kiwix": "על אודות Kiwix", + "donate-to-support-kiwix": "ניתן לתרום כדי לתמוך ב־Kiwix", + "exit": "יציאה", + "save-file-as-window-title": "שמירת הקובץ בשם", + "download-finished": "ההורדה הסתיימה", + "download-finished-message": "המסמך התקבל.", + "file": "קובץ", + "edit": "עריכה", + "view": "תצוגה", + "tools": "כלים", + "window-title": "ספרייה - Kiwix", + "search": "חיפוש", + "browse-directory": "עיון בספרייה", + "settings": "הגדרות", + "back": "חזרה", + "forward": "קדימה", + "search-files": "חיפוש קבצים", + "title": "כותרת", + "size": "גודל", + "date": "תאריך", + "content-type": "סוג התוכן", + "reset-sort": "איפוס מיון", + "open": "פתיחה", + "delete": "מחיקה", + "download": "הורדה", + "resume": "המשך", + "pause": "השהיה", + "cancel": "ביטול", + "apply": "החלה", + "port-for-local-kiwix-server-setting": "פתחה לשרת Kiwix מקומי", + "zoom-level-setting": "רמת תקריב כברירת מחדל", + "download-directory-setting": "תיקיית הורדה", + "monitor-directory-setting": "תיקיית ניטור", + "reset": "איפוס", + "clear": "ניקוי", + "browse": "עיון", + "about-kiwix-desktop-title": "Kiwix לשולחן העבודה", + "about-kiwix-desktop-description": "קיוויקס מאפשר לך לקחת אתך את ויקיפדיה לכל מקום שהוא! בסירה, באמצע שום מקום או בכלא, קיוויקס מעניק לך גישה לכל הידע האנושי. אין צורך באינטרנט, הכול מאוחסן אצלך במחשב.", + "about-learn-more-about-kiwix": "מידע נוסף אודות Kiwix", + "about-privacy-policy": "מדיניות פרטיות", + "about-release-title": "מהדורה", + "about-source-code": "תכנה זו מופצת בכפוף לתנאי גרסה 3 של הרישיון הציבורי הכללי של גנו. ניתן לצפות בקוד המקור ב־Github.", + "about-version": "גרסה: {{VERSION}}", + "about-reporting-problem-title": "דיווח תקלות", + "about-report-issue": "כדי לדווח על תקלה, עליך בקר בעוקב התקלות של Kiwix לשולחן העבודה ולהוסיף דיווח שמתאר את הבעיה שלך.", + "about-report-issue-2": "נא לציין בדיווח את מספר הגרסה.", + "about-libraries-title": "ספריות", + "kiwix-server-running-message": "שרת ה־Kiwix פעיל וניתן לגשת אליו דרך הרשת המקומית בכתובת:", + "kiwix-server-description": "הפעלת שרת מאפשרת למחשבים אחרים ברשת המקומית לגשת לספריית ה־Kiwix שלך דרך דפדפן אינטרנט תקני.", + "fullscreen-notification": "הגדלת למסך מלא. ESC ליציאה!", + "online-files": "קבצים מקוונים", + "local-files": "קבצים מקומיים", + "category": "קטגוריה", + "language": "שפה", + "hide": "הסתרה", + "open-in-browser": "פתיחה בדפדפן", + "start-kiwix-server": "הפעלת שרת Kiwix", + "stop-kiwix-server": "עצירת שרת Kiwix", + "all": "הכול", + "all_ips": "הכול - מצב שני תקנים (Dual Stack)", + "ipv4": "מצב IPv4 בלבד", + "ipv6": "מצב IPv6 בלבד", + "fulltext-search": "חיפוש טקסט מלא", + "pictures": "תמונות", + "videos": "סרטונים", + "ftindex": "מפתח כל הטקסט", + "details": "ערך מלא", + "yes": "כן", + "no": "לא", + "ok": "אישור", + "no-filter": "ללא מסנן", + "open-link-in-web-browser": "פתיחת קישור בדפדפן", + "open-link-new-tab": "פתיחת קישור בלשונית חדשה", + "download-dir-dialog-title": "להחליף את תיקיית ההורדה?", + "download-dir-dialog-msg": "נתיב תיקיית ההורדה החדש הוא:\n{{DIRECTORY}}", + "invalid-port": "פתחה שגויה", + "zim-open-fail-title": "קובץ בלתי־תקין", + "zim-open-fail-text": "קובץ ה־ZIM בשם {{ZIM}} אינו יכול להיפתח באופן הולם. הוא יוסר מהספרייה שלך.", + "monitor-dir-dialog-title": "האם ברצונך באמת לשנות את תיקיית הניטור?", + "monitor-dir-dialog-msg": "הנתיב החדש לתיקיית הניתור יהיה:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "האם ברצונך באמת לנקות את תיקיית הניטור?", + "path-was-copied": "הנתיב הועתק", + "monitor-clear-dir-dialog-msg": "זה יפסיק לדגום את תיקיית הניטור ולחפש בה קובצי ZIM חדשים.", + "monitor-directory-tooltip": "כל קובצי ה־ZIM בתיקייה הזאת יוספו אוטומטית לספרייה.", + "next-tab": "לעבור ללשונית הבאה", + "previous-tab": "לעבור ללשונית הקודמת", + "cancel-download": "ביטול הורדה", + "cancel-download-text": "האם באמת לבטל את ההורדה של {{ZIM}}?", + "delete-book": "מחיקת ספר", + "delete-book-text": "האם באמת למחוק את {{ZIM}}?", + "download-storage-error": "שגיאת אחסון", + "download-storage-error-text": "במערכת אין מספיק אחסון זמין.", + "download-exceeds-max-file-size": "גודל ההורדה חורג מגודל הקובץ המרבי הנתמך על־ידי מערכת הקבצים ביעד.", + "download-dir-missing": "תיקיית ההורדות לא קיימת.", + "download-dir-not-writable": "תיקיית ההורדות נעולה לכתיבה.", + "download-unavailable": "הורדה אינה זמינה", + "download-unavailable-text": "ההורדה הזאת אינה זמינה.", + "open-book": "פתיחת ספר", + "download-book": "הורדת ספר", + "pause-download": "עצירת ההורדה", + "resume-download": "המשך ההורדה", + "open-folder": "פתיחת תיקייה", + "couldnt-open-location": "לא ניתן לפתוח את המיקום", + "couldnt-open-location-text": "קיוויקס לא הצליח לפתוח את התיקייה {{FOLDER}}", + "move-files-to-trash": "להעביר קבצים שנמחקים לאשפה", + "move-files-to-trash-text": "הפעולה הזאת תעביר את הקובץ לאשפה.", + "perma-delete-files-text": "הפעולה האת תמחק את הקובץ לתמיד.", + "clear-filter": "ניקוי המסננים שמוגדרים כעת", + "language-searcher-placeholder": "סינון לפי שפה", + "category-searcher-placeholder": "סינון לפי קטגוריה", + "content-type-searcher-placeholder": "סינון לפי סוג תוכן", + "no-details": "הקדמה בלבד", + "no-pictures": "ללא תמונות", + "no-videos": "ללא סרטים", + "open-previous-tabs-at-startup": "לפתוח את הלשוניות הקודמות בעת ההפעלה", + "preview-book-in-web-browser": "תצוגה מקדימה של ספר בדפדפן", + "file-not-found-title": "קובץ ה־ZIM לא נמצא", + "file-not-found-text": "קובץ ה־ZIM אינו קיים או אינו קריא", + "zim-id": "מזהה ה־Zim", + "zim-name": "שם ה־Zim", + "zim-path": "נתיב קובץ ה־Zim", + "bad-zim-file-error-page-title": "קובץ ZIM בלתי־תקין", + "bad-zim-file-error-page-text": "קובץ ה־ZIM בלתי־תקין או מקולקל.", + "zim-entry-path": "נתיב כניסה ל־ZIM", + "export-reading-list": "יצוא רשימת קריאה", + "export-reading-list-error": "אירעה שגיאה בעת יצוא רשימת קריאה.", + "import-reading-list": "יבוא רשימת קריאה", + "import-reading-list-error": "אירעה שגיאה בעת יבוא רשימת קריאה.", + "disable-sandbox": "קיוויקס הופעל מכונן רשת. זה ידוע כגורם לבעיות תאימות בשל ארגז החול של העיון (browsing sandboxing). כתוצאה מזה, ארגז החול יושבת. האם להמשיך?", + "save-page-as": "שמירה בשם...", + "portable-disabled-tooltip": "הפונקציה מושבתת במצב נייד", + "scroll-next-tab": "לגלול ללשונית הבאה", + "scroll-previous-tab": "לגלול ללשונית הקודמת", + "kiwix-search": "חיפוש קיוויקס", + "search-options": "אפשרויות חיפוש", + "read-article": "להקריא את הערך", + "read-text": "להקריא את הטקסט שנבחר", + "read-stop": "להפסיק להקריא", + "stop": "עצירה", + "voice": "קול", + "select-read-voice": "בחירת קול ההקראה", + "select-read-language": "בחירת שפת ההקראה", + "save-or-open": "שמירה או פתיחה של קובץ", + "save-or-open-text": "מה קיוויקס צריך לעשות עם הקובץ הזה?" +} diff --git a/resources/i18n/hi.json b/resources/i18n/hi.json index 6199c4fe3..9e129a473 100644 --- a/resources/i18n/hi.json +++ b/resources/i18n/hi.json @@ -1,148 +1,148 @@ -{ - "@metadata": { - "authors": [ - "Abijeet Patro", - "Juuz0" - ] - }, - "name": "हिन्दी", - "error-title": "त्रुटि", - "error-downloader-window-title": "डाउनलोडर बनाने में असमर्थ", - "error-downloader-launch-message": "डाउनलोडर लॉन्च करना असंभव है, किविक्स-डेस्कटॉप शुरू हो जाएगा लेकिन सभी डाउनलोड फ़ंक्शन काम नहीं करेंगे!", - "error-launch-server-message": "एक त्रुटि आई।", - "error-archive": "पुरालेख नहीं मिल सका", - "open-zim": "ZIM फ़ाइल खोलें", - "local-kiwix-server": "स्थानीय किविक्स सर्वर", - "random-article": "यादृच्छिक लेख", - "random-article-error": "एक यादृच्छिक लेख खोलने में विफल.", - "home-page": "मुख पृष्ठ", - "main-menu": "मुख्य मेन्यू", - "print": "प्रिंट करें", - "print-page-error": "मुद्रण करते समय कोई त्रुटि उत्पन्न हुई है.", - "new-tab": "नया टैब", - "close-tab": "टैब बंद करें", - "close": "बंद करें", - "reopen-closed-tab": "बंद टैब पुनः खोलें", - "browse-library": "पुस्तकालय ब्राउज़ करें", - "open-file": "फ़ाइल खोलें", - "open-recent": "हाल ही वाला खोलें", - "search-article": "लेख खोजें", - "search-in-library": "पुस्तकालय में खोजें", - "find-in-page": "पृष्ठ में ढूंढें", - "set-fullscreen": "पूर्ण स्क्रीन सेट करें", - "quit-fullscreen": "पूर्ण स्क्रीन छोड़ें", - "table-of-content": "विषयसूची", - "reading-list": "पढ़ने की सूची", - "zoom-in": "आकार बढ़ाएँ", - "zoom-out": "आकार घटाएँ", - "zoom-reset": "ज़ूम रीसेट करें", - "help": "सहायता", - "feedback": "आपके सुझाव", - "report-a-bug": "समस्या को सूचित करें", - "request-a-feature": "एक सुविधा का अनुरोध करें", - "about-kiwix": "किवीक्स के बारे में", - "donate-to-support-kiwix": "किवीक्स का समर्थन करने के लिए दान करें", - "exit": "निकास", - "save-file-as-window-title": "फ़ाइल को इस रूप में सहेजें", - "download-finished-message": "दस्तावेज़ डाउनलोड कर लिया गया है।", - "file": "फ़ाइल", - "edit": "संपादित करें", - "view": "देखें", - "tools": "उपकरण", - "window-title": "पुस्तकालय - किवीक्स", - "search": "खोजें", - "browse-directory": "निर्देशिका ब्राउज़ करें", - "settings": "सेटिंग्स", - "back": "पीछे", - "forward": "आगे", - "search-files": "फ़ाइलें खोजें", - "title": "शीर्षक", - "size": "आकार", - "date": "दिनांक", - "content-type": "सामग्री पृष्ठ", - "reset-sort": "क्रम रीसेट करें", - "open": "खोलें", - "delete": "हटाएँ", - "download": "डाउनलोड करें", - "resume": "दुबारा शुरू करें", - "pause": "थामें", - "cancel": "रद्द करें", - "apply": "लागू करें", - "port-for-local-kiwix-server-setting": "स्थानीय किविक्स सर्वर के लिए पोर्ट", - "zoom-level-setting": "डिफ़ॉल्ट ज़ूम स्तर", - "download-directory-setting": "यहाँ डाउनलोड करे", - "monitor-directory-setting": "इसकी निगरानी करें", - "reset": "रीसेट करें", - "clear": "खाली करें", - "browse": "ब्राउज़", - "about-kiwix-desktop-title": "किविक्स डेस्कटॉप", - "about-kiwix-desktop-description": "किविक्स आपको जहाँ भी आप जाते हैं, संपूर्ण विकिपीडिया उपलब्ध रखने की अनुमति देता है! नाव पर, बीच रास्ते में, या जेल में, किविक्स आपको सभी मानवीय ज्ञान तक पहुंच प्रदान करता है। आपको इंटरनेट की आवश्यकता नहीं है, सब कुछ आपके कंप्यूटर पर संग्रहीत है।", - "about-learn-more-about-kiwix": "कीविक्स के बारे में और जानें", - "about-privacy-policy": "गोपनीयता नीति", - "about-release-title": "प्रकाशन", - "about-source-code": "यह सॉफ़्टवेयर GNU जनरल पब्लिक लाइसेंस संस्करण 3 की शर्तों के तहत जारी किया गया है। Github पर स्रोत कोड देखें।", - "about-version": "संस्करण : {{VERSION}}", - "about-reporting-problem-title": "समस्या की सूचना दें", - "about-report-issue": "किसी समस्या को खोलने के लिए, किविक्स-डेस्कटॉप समस्या ट्रैकर पर जाएँ और अपनी समस्या का वर्णन करने वाला एक मुद्दा खोलें।", - "about-report-issue-2": "कृपया अंक में संस्करण का उल्लेख करें।", - "about-libraries-title": "पुस्तकालय", - "kiwix-server-running-message": "कीविक्स सर्वर चल रहा है और स्थानीय नेटवर्क में पहुँचा जा सकता हैः", - "kiwix-server-description": "सर्वर शुरू करने से स्थानीय नेटवर्क में अन्य कंप्यूटरों को एक मानक वेब ब्राउज़र के साथ आपकी कीविक्स लाइब्रेरी तक पहुंचने की अनुमति मिलती है।", - "fullscreen-notification": "अब आप पूर्ण स्क्रीन मोड में हैं. छोड़ने के लिए ESC दबाएँ!", - "online-files": "ऑनलाइन फ़ाइलें", - "local-files": "स्थानीय फ़ाइलें", - "category": "श्रेणी", - "language": "भाषा", - "hide": "छुपाएँ", - "open-in-browser": "ब्राउज़र में खोलें", - "start-kiwix-server": "किविक्स सर्वर प्रारंभ करें", - "stop-kiwix-server": "कीविक्स सर्वर को रोकें", - "all": "सभी", - "fulltext-search": "पूरा पाठ खोजें", - "pictures": "चित्र", - "videos": "विडियो", - "ftindex": "पूर्णपाठ अनुक्रमणिका", - "details": "पूरा लेख", - "yes": "हाँ", - "no": "नहीं", - "ok": "ठीक है", - "no-filter": "कोई फिल्टर नहीं", - "open-link-in-web-browser": "वेब ब्राउज़र में लिंक खोलें", - "download-dir-dialog-title": "क्या आप निश्चित रूप से डाउनलोड निर्देशिका को बदलना चाहते हैं", - "download-dir-dialog-msg": "नई डाउनलोड निर्देशिका पथ होगा:\n{{DIRECTORY}}", - "invalid-port": "अमान्य पोर्ट", - "zim-open-fail-title": "अमान्य फ़ाइल", - "zim-open-fail-text": "ज़िम फ़ाइल {{ZIM}} ठीक से नहीं खोली जा सकती है। इसे आपके पुस्तकालय से हटा दिया जाएगा।", - "monitor-dir-dialog-title": "क्या आप निश्चित रूप से मॉनिटर निर्देशिका को बदलना चाहते हैं", - "monitor-dir-dialog-msg": "नया मॉनिटर निर्देशिका पथ होगा:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "क्या आप निश्चित रूप से मॉनिटर निर्देशिका को साफ़ करना चाहते हैं", - "monitor-clear-dir-dialog-msg": "यह नई ZIM फ़ाइलों के लिए मॉनिटर निर्देशिका की जाँच करना बंद कर देगा।", - "monitor-directory-tooltip": "इस निर्देशिका में सभी ज़िम फ़ाइलों को स्वचालित रूप से पुस्तकालय में जोड़ा जाएगा।", - "next-tab": "अगले टैब पर जाएँ", - "previous-tab": "पिछले टैब पर जाएँ", - "cancel-download": "डाउनलोड रद्द करें", - "cancel-download-text": "क्या आप निश्चित रूप से {{ZIM}} का डाउनलोड रद्द करना चाहते हैं?", - "delete-book": "पुस्तक हटाएँ", - "delete-book-text": "क्या आप वाकई {{ZIM}} को हटाना चाहते हैं?", - "download-storage-error": "भंडारण त्रुटि", - "download-storage-error-text": "सिस्टम में पर्याप्त भंडारण उपलब्ध नहीं है।", - "download-unavailable": "डाउनलोड अनुपलब्ध", - "download-unavailable-text": "यह डाउनलोड अनुपलब्ध है।", - "open-book": "किताब खोले", - "download-book": "पुस्तक डाउनलोड करें", - "pause-download": "डाउनलोड रोकें", - "resume-download": "डाउनलोड फिर से शुरू करें", - "open-folder": "फोल्डर खोलें", - "couldnt-open-location": "स्थान नहीं खुल सका", - "couldnt-open-location-text": "किविक्स फ़ोल्डर {{FOLDER}} खोलने में सक्षम नहीं है", - "move-files-to-trash": "हटाई गई फ़ाइलों को ट्रैश में ले जाएं", - "move-files-to-trash-text": "यह क्रिया फ़ाइल को ट्रैश में ले जाएगी.", - "perma-delete-files-text": "यह क्रिया फ़ाइल को स्थायी रूप से हटा देगी।", - "clear-filter": "वर्तमान में सेट किए गए फिल्टर को साफ़ करें", - "language-searcher-placeholder": "भाषा अनुसार छानें", - "category-searcher-placeholder": "श्रणी के अनुसार छानें", - "content-type-searcher-placeholder": "सामग्री प्रकार छानें", - "no-details": "केवल परिचय", - "no-pictures": "कोई चित्र नहीं", - "no-videos": "कोई चलचित्र नहीं" -} +{ + "@metadata": { + "authors": [ + "Abijeet Patro", + "Juuz0" + ] + }, + "name": "हिन्दी", + "error-title": "त्रुटि", + "error-downloader-window-title": "डाउनलोडर बनाने में असमर्थ", + "error-downloader-launch-message": "डाउनलोडर लॉन्च करना असंभव है, किविक्स-डेस्कटॉप शुरू हो जाएगा लेकिन सभी डाउनलोड फ़ंक्शन काम नहीं करेंगे!", + "error-launch-server-message": "एक त्रुटि आई।", + "error-archive": "पुरालेख नहीं मिल सका", + "open-zim": "ZIM फ़ाइल खोलें", + "local-kiwix-server": "स्थानीय किविक्स सर्वर", + "random-article": "यादृच्छिक लेख", + "random-article-error": "एक यादृच्छिक लेख खोलने में विफल.", + "home-page": "मुख पृष्ठ", + "main-menu": "मुख्य मेन्यू", + "print": "प्रिंट करें", + "print-page-error": "मुद्रण करते समय कोई त्रुटि उत्पन्न हुई है.", + "new-tab": "नया टैब", + "close-tab": "टैब बंद करें", + "close": "बंद करें", + "reopen-closed-tab": "बंद टैब पुनः खोलें", + "browse-library": "पुस्तकालय ब्राउज़ करें", + "open-file": "फ़ाइल खोलें", + "open-recent": "हाल ही वाला खोलें", + "search-article": "लेख खोजें", + "search-in-library": "पुस्तकालय में खोजें", + "find-in-page": "पृष्ठ में ढूंढें", + "set-fullscreen": "पूर्ण स्क्रीन सेट करें", + "quit-fullscreen": "पूर्ण स्क्रीन छोड़ें", + "table-of-content": "विषयसूची", + "reading-list": "पढ़ने की सूची", + "zoom-in": "आकार बढ़ाएँ", + "zoom-out": "आकार घटाएँ", + "zoom-reset": "ज़ूम रीसेट करें", + "help": "सहायता", + "feedback": "आपके सुझाव", + "report-a-bug": "समस्या को सूचित करें", + "request-a-feature": "एक सुविधा का अनुरोध करें", + "about-kiwix": "किवीक्स के बारे में", + "donate-to-support-kiwix": "किवीक्स का समर्थन करने के लिए दान करें", + "exit": "निकास", + "save-file-as-window-title": "फ़ाइल को इस रूप में सहेजें", + "download-finished-message": "दस्तावेज़ डाउनलोड कर लिया गया है।", + "file": "फ़ाइल", + "edit": "संपादित करें", + "view": "देखें", + "tools": "उपकरण", + "window-title": "पुस्तकालय - किवीक्स", + "search": "खोजें", + "browse-directory": "निर्देशिका ब्राउज़ करें", + "settings": "सेटिंग्स", + "back": "पीछे", + "forward": "आगे", + "search-files": "फ़ाइलें खोजें", + "title": "शीर्षक", + "size": "आकार", + "date": "दिनांक", + "content-type": "सामग्री पृष्ठ", + "reset-sort": "क्रम रीसेट करें", + "open": "खोलें", + "delete": "हटाएँ", + "download": "डाउनलोड करें", + "resume": "दुबारा शुरू करें", + "pause": "थामें", + "cancel": "रद्द करें", + "apply": "लागू करें", + "port-for-local-kiwix-server-setting": "स्थानीय किविक्स सर्वर के लिए पोर्ट", + "zoom-level-setting": "डिफ़ॉल्ट ज़ूम स्तर", + "download-directory-setting": "यहाँ डाउनलोड करे", + "monitor-directory-setting": "इसकी निगरानी करें", + "reset": "रीसेट करें", + "clear": "खाली करें", + "browse": "ब्राउज़", + "about-kiwix-desktop-title": "किविक्स डेस्कटॉप", + "about-kiwix-desktop-description": "किविक्स आपको जहाँ भी आप जाते हैं, संपूर्ण विकिपीडिया उपलब्ध रखने की अनुमति देता है! नाव पर, बीच रास्ते में, या जेल में, किविक्स आपको सभी मानवीय ज्ञान तक पहुंच प्रदान करता है। आपको इंटरनेट की आवश्यकता नहीं है, सब कुछ आपके कंप्यूटर पर संग्रहीत है।", + "about-learn-more-about-kiwix": "कीविक्स के बारे में और जानें", + "about-privacy-policy": "गोपनीयता नीति", + "about-release-title": "प्रकाशन", + "about-source-code": "यह सॉफ़्टवेयर GNU जनरल पब्लिक लाइसेंस संस्करण 3 की शर्तों के तहत जारी किया गया है। Github पर स्रोत कोड देखें।", + "about-version": "संस्करण : {{VERSION}}", + "about-reporting-problem-title": "समस्या की सूचना दें", + "about-report-issue": "किसी समस्या को खोलने के लिए, किविक्स-डेस्कटॉप समस्या ट्रैकर पर जाएँ और अपनी समस्या का वर्णन करने वाला एक मुद्दा खोलें।", + "about-report-issue-2": "कृपया अंक में संस्करण का उल्लेख करें।", + "about-libraries-title": "पुस्तकालय", + "kiwix-server-running-message": "कीविक्स सर्वर चल रहा है और स्थानीय नेटवर्क में पहुँचा जा सकता हैः", + "kiwix-server-description": "सर्वर शुरू करने से स्थानीय नेटवर्क में अन्य कंप्यूटरों को एक मानक वेब ब्राउज़र के साथ आपकी कीविक्स लाइब्रेरी तक पहुंचने की अनुमति मिलती है।", + "fullscreen-notification": "अब आप पूर्ण स्क्रीन मोड में हैं. छोड़ने के लिए ESC दबाएँ!", + "online-files": "ऑनलाइन फ़ाइलें", + "local-files": "स्थानीय फ़ाइलें", + "category": "श्रेणी", + "language": "भाषा", + "hide": "छुपाएँ", + "open-in-browser": "ब्राउज़र में खोलें", + "start-kiwix-server": "किविक्स सर्वर प्रारंभ करें", + "stop-kiwix-server": "कीविक्स सर्वर को रोकें", + "all": "सभी", + "fulltext-search": "पूरा पाठ खोजें", + "pictures": "चित्र", + "videos": "विडियो", + "ftindex": "पूर्णपाठ अनुक्रमणिका", + "details": "पूरा लेख", + "yes": "हाँ", + "no": "नहीं", + "ok": "ठीक है", + "no-filter": "कोई फिल्टर नहीं", + "open-link-in-web-browser": "वेब ब्राउज़र में लिंक खोलें", + "download-dir-dialog-title": "क्या आप निश्चित रूप से डाउनलोड निर्देशिका को बदलना चाहते हैं", + "download-dir-dialog-msg": "नई डाउनलोड निर्देशिका पथ होगा:\n{{DIRECTORY}}", + "invalid-port": "अमान्य पोर्ट", + "zim-open-fail-title": "अमान्य फ़ाइल", + "zim-open-fail-text": "ज़िम फ़ाइल {{ZIM}} ठीक से नहीं खोली जा सकती है। इसे आपके पुस्तकालय से हटा दिया जाएगा।", + "monitor-dir-dialog-title": "क्या आप निश्चित रूप से मॉनिटर निर्देशिका को बदलना चाहते हैं", + "monitor-dir-dialog-msg": "नया मॉनिटर निर्देशिका पथ होगा:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "क्या आप निश्चित रूप से मॉनिटर निर्देशिका को साफ़ करना चाहते हैं", + "monitor-clear-dir-dialog-msg": "यह नई ZIM फ़ाइलों के लिए मॉनिटर निर्देशिका की जाँच करना बंद कर देगा।", + "monitor-directory-tooltip": "इस निर्देशिका में सभी ज़िम फ़ाइलों को स्वचालित रूप से पुस्तकालय में जोड़ा जाएगा।", + "next-tab": "अगले टैब पर जाएँ", + "previous-tab": "पिछले टैब पर जाएँ", + "cancel-download": "डाउनलोड रद्द करें", + "cancel-download-text": "क्या आप निश्चित रूप से {{ZIM}} का डाउनलोड रद्द करना चाहते हैं?", + "delete-book": "पुस्तक हटाएँ", + "delete-book-text": "क्या आप वाकई {{ZIM}} को हटाना चाहते हैं?", + "download-storage-error": "भंडारण त्रुटि", + "download-storage-error-text": "सिस्टम में पर्याप्त भंडारण उपलब्ध नहीं है।", + "download-unavailable": "डाउनलोड अनुपलब्ध", + "download-unavailable-text": "यह डाउनलोड अनुपलब्ध है।", + "open-book": "किताब खोले", + "download-book": "पुस्तक डाउनलोड करें", + "pause-download": "डाउनलोड रोकें", + "resume-download": "डाउनलोड फिर से शुरू करें", + "open-folder": "फोल्डर खोलें", + "couldnt-open-location": "स्थान नहीं खुल सका", + "couldnt-open-location-text": "किविक्स फ़ोल्डर {{FOLDER}} खोलने में सक्षम नहीं है", + "move-files-to-trash": "हटाई गई फ़ाइलों को ट्रैश में ले जाएं", + "move-files-to-trash-text": "यह क्रिया फ़ाइल को ट्रैश में ले जाएगी.", + "perma-delete-files-text": "यह क्रिया फ़ाइल को स्थायी रूप से हटा देगी।", + "clear-filter": "वर्तमान में सेट किए गए फिल्टर को साफ़ करें", + "language-searcher-placeholder": "भाषा अनुसार छानें", + "category-searcher-placeholder": "श्रणी के अनुसार छानें", + "content-type-searcher-placeholder": "सामग्री प्रकार छानें", + "no-details": "केवल परिचय", + "no-pictures": "कोई चित्र नहीं", + "no-videos": "कोई चलचित्र नहीं" +} diff --git a/resources/i18n/hy.json b/resources/i18n/hy.json index 15aa71e20..c1ff9a3ae 100644 --- a/resources/i18n/hy.json +++ b/resources/i18n/hy.json @@ -1,48 +1,48 @@ -{ - "@metadata": { - "authors": [ - "Ajeje Brazorf", - "Kareyac", - "McDutchie", - "Սերգեյ Սաֆարյան" - ] - }, - "name": "Հայերեն", - "home-page": "Հիմնական էջ", - "main-menu": "Հիմնական ընտրացանկ", - "print": "Տպել", - "find-in-page": "Գտնել էջում", - "zoom-in": "Խոշորացնել", - "zoom-out": "Հեռվացնել", - "help": "Օգնություն", - "exit": "Դուրս գալ", - "file": "Նիշք", - "edit": "Խմբագրել", - "view": "Դիտել", - "tools": "Գործիքներ", - "search": "Որոնել", - "settings": "Կարգավորումներ", - "back": "Հետ", - "forward": "Առաջ", - "search-files": "Որոնել նիշքեր", - "title": "Վերնագիր", - "size": "Չափ", - "date": "Ամսաթիվ", - "open": "Բացել", - "delete": "Ջնջել", - "download": "Ներբեռնել", - "pause": "Դադար", - "cancel": "Չեղարկել", - "apply": "Կիրառել", - "about-reporting-problem-title": "Հաղորդել խնդիրների մասին", - "about-libraries-title": "Գրադարաններ", - "category": "Կատեգորիա", - "language": "Լեզու", - "hide": "Թաքցնել", - "all": "Բոլորը", - "yes": "այո", - "no": "ոչ", - "language-searcher-placeholder": "Զտել ըստ լեզվի", - "category-searcher-placeholder": "Զտել ըստ կատեգորիայի", - "no-videos": "Տեսանյութ չկա" -} +{ + "@metadata": { + "authors": [ + "Ajeje Brazorf", + "Kareyac", + "McDutchie", + "Սերգեյ Սաֆարյան" + ] + }, + "name": "Հայերեն", + "home-page": "Հիմնական էջ", + "main-menu": "Հիմնական ընտրացանկ", + "print": "Տպել", + "find-in-page": "Գտնել էջում", + "zoom-in": "Խոշորացնել", + "zoom-out": "Հեռվացնել", + "help": "Օգնություն", + "exit": "Դուրս գալ", + "file": "Նիշք", + "edit": "Խմբագրել", + "view": "Դիտել", + "tools": "Գործիքներ", + "search": "Որոնել", + "settings": "Կարգավորումներ", + "back": "Հետ", + "forward": "Առաջ", + "search-files": "Որոնել նիշքեր", + "title": "Վերնագիր", + "size": "Չափ", + "date": "Ամսաթիվ", + "open": "Բացել", + "delete": "Ջնջել", + "download": "Ներբեռնել", + "pause": "Դադար", + "cancel": "Չեղարկել", + "apply": "Կիրառել", + "about-reporting-problem-title": "Հաղորդել խնդիրների մասին", + "about-libraries-title": "Գրադարաններ", + "category": "Կատեգորիա", + "language": "Լեզու", + "hide": "Թաքցնել", + "all": "Բոլորը", + "yes": "այո", + "no": "ոչ", + "language-searcher-placeholder": "Զտել ըստ լեզվի", + "category-searcher-placeholder": "Զտել ըստ կատեգորիայի", + "no-videos": "Տեսանյութ չկա" +} diff --git a/resources/i18n/ia.json b/resources/i18n/ia.json index b3d347261..3488b62e8 100644 --- a/resources/i18n/ia.json +++ b/resources/i18n/ia.json @@ -1,172 +1,172 @@ -{ - "@metadata": { - "authors": [ - "McDutchie" - ] - }, - "name": "Interlingua", - "error-title": "Error", - "error-downloader-window-title": "Non pote crear discargator", - "error-downloader-launch-message": "Impossibile lancear le discargator. Kiwix-desktop se initiara ma tote le functiones de discargamento non functionara!", - "error-launch-server-message": "Un error ha occurrite!", - "error-archive": "Non poter obtener le archivo", - "open-zim": "Aperir file ZIM", - "local-kiwix-server": "Servitor Kiwix local", - "random-article": "Articulo aleatori", - "random-article-error": "Non poteva aperir un articulo aleatori.", - "home-page": "Pagina principal", - "main-menu": "Menu principal", - "print": "Imprimer", - "print-page-error": "Un error ha occurrite durante le impression.", - "new-tab": "Nove scheda", - "close-tab": "Clauder scheda", - "close": "Clauder", - "reopen-closed-tab": "Reaperir scheda claudite", - "browse-library": "Explorar bibliotheca", - "open-file": "Aperir file", - "open-recent": "Aperir recente", - "search-article": "Cercar articulo", - "search-in-library": "Cercar in bibliotheca", - "find-in-page": "Cercar in pagina", - "set-fullscreen": "Entrar in plen schermo", - "quit-fullscreen": "Exir del plen schermo", - "table-of-content": "Tabula de contento", - "add-bookmark": "Adder al lista de lectura", - "remove-bookmark": "Remover del lista de lectura", - "reading-list": "Lista de lectura", - "zoom-in": "Zoom avante", - "zoom-out": "Zoom retro", - "zoom-reset": "Reinitialisar zoom", - "help": "Adjuta", - "feedback": "Commentario", - "report-a-bug": "Reportar un defecto", - "request-a-feature": "Requestar un function", - "about-kiwix": "A proposito de Kiwix", - "donate-to-support-kiwix": "Donar pro sustener Kiwix", - "exit": "Exir", - "save-file-as-window-title": "Salveguardar file como", - "download-finished-message": "Le documento ha essite discargate.", - "file": "File", - "edit": "Modificar", - "view": "Vider", - "tools": "Instrumentos", - "window-title": "Bibliotheca – Kiwix", - "search": "Recerca", - "browse-directory": "Explorar directorio", - "settings": "Parametros", - "back": "Retro", - "forward": "Avante", - "search-files": "Cercar files", - "title": "Titulo", - "size": "Grandor", - "date": "Data", - "content-type": "Typo de contento", - "reset-sort": "reinitialisar ordinamento", - "open": "Aperir", - "delete": "Deler", - "download": "Discargar", - "resume": "Reprender", - "pause": "Pausar", - "cancel": "Cancellar", - "apply": "Applicar", - "port-for-local-kiwix-server-setting": "Porto pro le servitor Kiwix local", - "zoom-level-setting": "Nivello de zoom predefinite", - "download-directory-setting": "Directorio de discargamento", - "monitor-directory-setting": "Surveliar directorio", - "reset": "Reinitialisar", - "clear": "Rader", - "browse": "Explorar", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix te permitte haber tote Wikipedia a portata de mano ubique tu va! Sur un barca, in le medio de nulle parte o in prision, Kiwix te da accesso a tote le cognoscentia human. Tu non ha besonio de Internet, toto es immagazinate sur tu computator.", - "about-learn-more-about-kiwix": "Leger plus sur Kiwix", - "about-release-title": "Edition", - "about-source-code": "Iste software es publicate sub le terminos del Licentia Public General de GNU, version 3. Consulta le codice fonte sur GitHub.", - "about-version": "Version: {{VERSION}}", - "about-reporting-problem-title": "Signalar problemas", - "about-report-issue": "Pro signalar un problema, visita le traciator de problemas de Kiwix-desktop e aperi un incidente que describe tu problema.", - "about-report-issue-2": "Per favor mentiona le version in le incidente.", - "about-libraries-title": "Bibliothecas", - "kiwix-server-running-message": "Le servitor Kiwix es in execution e disponibile in le rete local a:", - "kiwix-server-description": "Initiar un servitor permitte a altere computatores in rete local acceder a tu bibliotheca Kiwix con un navigator web standard.", - "fullscreen-notification": "Tu es ora in modo de plen schermo. Preme ESC pro exir!", - "online-files": "Files in linea", - "local-files": "Files local", - "category": "Categoria", - "language": "Lingua", - "hide": "Celar", - "open-in-browser": "Aperir in navigator", - "start-kiwix-server": "Initiar servitor Kiwix", - "stop-kiwix-server": "Stoppar servitor Kiwix", - "all": "Toto", - "fulltext-search": "Recerca in texto complete", - "pictures": "Imagines", - "videos": "Videos", - "ftindex": "Indice de texto complete", - "details": "Articulo complete", - "yes": "si", - "no": "no", - "ok": "ok", - "no-filter": "necun filtro", - "open-link-in-web-browser": "Aperir ligamine in navigator web", - "open-link-new-tab": "Aperir ligamine in nove scheda", - "download-dir-dialog-title": "Es tu secur de voler cambiar le directorio de discargamento?", - "download-dir-dialog-msg": "Le nove directorio de discargamento essera:\n{{DIRECTORY}}", - "invalid-port": "Porto non valide", - "zim-open-fail-title": "File non valide", - "zim-open-fail-text": "Le file ZIM ({{ZIM}}) non pote esser aperite correctemente. Illo essera removite de tu bibliotheca.", - "monitor-dir-dialog-title": "Es tu secur de voler cambiar le directorio de surveliantia?", - "monitor-dir-dialog-msg": "Le nove directorio de surveliantia essera:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Es tu secur de voler rader le directorio de surveliantia?", - "path-was-copied": "Le percurso ha essite copiate", - "monitor-clear-dir-dialog-msg": "Isto cessara le verification del directorio de surveliantia pro nove files ZIM.", - "monitor-directory-tooltip": "Tote le files ZIM in iste directorio essera automaticamente addite al bibliotheca.", - "next-tab": "Passar al scheda sequente", - "previous-tab": "Passar al scheda precedente", - "cancel-download": "Cancellar discargamento", - "cancel-download-text": "Es tu secur de voler cancellar le discargamento de {{ZIM}}?", - "delete-book": "Deler libro", - "delete-book-text": "Es tu secur de voler deler {{ZIM}}?", - "download-storage-error": "Error de immagazinage", - "download-storage-error-text": "Le systema non ha bastante spatio de immagazinage disponibile.", - "download-exceeds-max-file-size": "Le grandor del discargamento excede le dimension maxime de files supportate per le systema de files del destination.", - "download-dir-missing": "Le directorio de discargamento non existe.", - "download-dir-not-writable": "Necun permission de scriptura pro le directorio de discargamento.", - "download-unavailable": "Discargamento indisponibile", - "download-unavailable-text": "Iste discargamento non es disponibile.", - "open-book": "Aperir libro", - "download-book": "Discargar libro", - "pause-download": "Pausar discargamento", - "resume-download": "Reprender discargamento", - "open-folder": "Aperir dossier", - "couldnt-open-location": "Non poteva aperir le loco", - "couldnt-open-location-text": "Kiwix non pote aperir le dossier {{FOLDER}}", - "move-files-to-trash": "Displaciar files delite al corbe a papiro", - "move-files-to-trash-text": "Iste action displaciara le file al corbe a papiro.", - "perma-delete-files-text": "Iste action delera permanentemente le file.", - "clear-filter": "Rader le filtros actualmente definite", - "language-searcher-placeholder": "Filtrar per lingua", - "category-searcher-placeholder": "Filtrar per categoria", - "content-type-searcher-placeholder": "Filtrar per typo de contento", - "no-details": "Introduction solmente", - "no-pictures": "Necun imagine", - "no-videos": "Necun video", - "open-previous-tabs-at-startup": "Aperir previe schedas al initio", - "preview-book-in-web-browser": "Previsualisar libro in navigator web", - "file-not-found-title": "File ZIM non trovate", - "file-not-found-text": "Le file ZIM non existe o non es legibile", - "zim-id": "ID del ZIM", - "zim-name": "Nomine del ZIM", - "zim-path": "Percurso al file ZIM", - "bad-zim-file-error-page-title": "File ZIM non valide", - "bad-zim-file-error-page-text": "Le file ZIM non es valide o ha essite corrumpite.", - "zim-entry-path": "Percurso de entrata ZIM", - "export-reading-list": "Exportar lista de lectura", - "export-reading-list-error": "Un error ha occurrite durante le exportation del lista de lectura.", - "import-reading-list": "Importar lista de lectura", - "import-reading-list-error": "Un error ha occurrite durante le importation del lista de lectura.", - "disable-sandbox": "Kiwix ha essite lanceate desde un disco de rete. Isto es cognoscite pro causar problemas de compatibilitate con le isolator (sandbox) del navigator del web. Como resultato, le isolator essera disactivate. Vole tu continuar?", - "save-page-as": "Salveguardar como…", - "portable-disabled-tooltip": "Function disactivate in modo portabile", - "scroll-next-tab": "Rolar al scheda sequente", - "scroll-previous-tab": "Rolar al scheda precedente" -} +{ + "@metadata": { + "authors": [ + "McDutchie" + ] + }, + "name": "Interlingua", + "error-title": "Error", + "error-downloader-window-title": "Non pote crear discargator", + "error-downloader-launch-message": "Impossibile lancear le discargator. Kiwix-desktop se initiara ma tote le functiones de discargamento non functionara!", + "error-launch-server-message": "Un error ha occurrite!", + "error-archive": "Non poter obtener le archivo", + "open-zim": "Aperir file ZIM", + "local-kiwix-server": "Servitor Kiwix local", + "random-article": "Articulo aleatori", + "random-article-error": "Non poteva aperir un articulo aleatori.", + "home-page": "Pagina principal", + "main-menu": "Menu principal", + "print": "Imprimer", + "print-page-error": "Un error ha occurrite durante le impression.", + "new-tab": "Nove scheda", + "close-tab": "Clauder scheda", + "close": "Clauder", + "reopen-closed-tab": "Reaperir scheda claudite", + "browse-library": "Explorar bibliotheca", + "open-file": "Aperir file", + "open-recent": "Aperir recente", + "search-article": "Cercar articulo", + "search-in-library": "Cercar in bibliotheca", + "find-in-page": "Cercar in pagina", + "set-fullscreen": "Entrar in plen schermo", + "quit-fullscreen": "Exir del plen schermo", + "table-of-content": "Tabula de contento", + "add-bookmark": "Adder al lista de lectura", + "remove-bookmark": "Remover del lista de lectura", + "reading-list": "Lista de lectura", + "zoom-in": "Zoom avante", + "zoom-out": "Zoom retro", + "zoom-reset": "Reinitialisar zoom", + "help": "Adjuta", + "feedback": "Commentario", + "report-a-bug": "Reportar un defecto", + "request-a-feature": "Requestar un function", + "about-kiwix": "A proposito de Kiwix", + "donate-to-support-kiwix": "Donar pro sustener Kiwix", + "exit": "Exir", + "save-file-as-window-title": "Salveguardar file como", + "download-finished-message": "Le documento ha essite discargate.", + "file": "File", + "edit": "Modificar", + "view": "Vider", + "tools": "Instrumentos", + "window-title": "Bibliotheca – Kiwix", + "search": "Recerca", + "browse-directory": "Explorar directorio", + "settings": "Parametros", + "back": "Retro", + "forward": "Avante", + "search-files": "Cercar files", + "title": "Titulo", + "size": "Grandor", + "date": "Data", + "content-type": "Typo de contento", + "reset-sort": "reinitialisar ordinamento", + "open": "Aperir", + "delete": "Deler", + "download": "Discargar", + "resume": "Reprender", + "pause": "Pausar", + "cancel": "Cancellar", + "apply": "Applicar", + "port-for-local-kiwix-server-setting": "Porto pro le servitor Kiwix local", + "zoom-level-setting": "Nivello de zoom predefinite", + "download-directory-setting": "Directorio de discargamento", + "monitor-directory-setting": "Surveliar directorio", + "reset": "Reinitialisar", + "clear": "Rader", + "browse": "Explorar", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix te permitte haber tote Wikipedia a portata de mano ubique tu va! Sur un barca, in le medio de nulle parte o in prision, Kiwix te da accesso a tote le cognoscentia human. Tu non ha besonio de Internet, toto es immagazinate sur tu computator.", + "about-learn-more-about-kiwix": "Leger plus sur Kiwix", + "about-release-title": "Edition", + "about-source-code": "Iste software es publicate sub le terminos del Licentia Public General de GNU, version 3. Consulta le codice fonte sur GitHub.", + "about-version": "Version: {{VERSION}}", + "about-reporting-problem-title": "Signalar problemas", + "about-report-issue": "Pro signalar un problema, visita le traciator de problemas de Kiwix-desktop e aperi un incidente que describe tu problema.", + "about-report-issue-2": "Per favor mentiona le version in le incidente.", + "about-libraries-title": "Bibliothecas", + "kiwix-server-running-message": "Le servitor Kiwix es in execution e disponibile in le rete local a:", + "kiwix-server-description": "Initiar un servitor permitte a altere computatores in rete local acceder a tu bibliotheca Kiwix con un navigator web standard.", + "fullscreen-notification": "Tu es ora in modo de plen schermo. Preme ESC pro exir!", + "online-files": "Files in linea", + "local-files": "Files local", + "category": "Categoria", + "language": "Lingua", + "hide": "Celar", + "open-in-browser": "Aperir in navigator", + "start-kiwix-server": "Initiar servitor Kiwix", + "stop-kiwix-server": "Stoppar servitor Kiwix", + "all": "Toto", + "fulltext-search": "Recerca in texto complete", + "pictures": "Imagines", + "videos": "Videos", + "ftindex": "Indice de texto complete", + "details": "Articulo complete", + "yes": "si", + "no": "no", + "ok": "ok", + "no-filter": "necun filtro", + "open-link-in-web-browser": "Aperir ligamine in navigator web", + "open-link-new-tab": "Aperir ligamine in nove scheda", + "download-dir-dialog-title": "Es tu secur de voler cambiar le directorio de discargamento?", + "download-dir-dialog-msg": "Le nove directorio de discargamento essera:\n{{DIRECTORY}}", + "invalid-port": "Porto non valide", + "zim-open-fail-title": "File non valide", + "zim-open-fail-text": "Le file ZIM ({{ZIM}}) non pote esser aperite correctemente. Illo essera removite de tu bibliotheca.", + "monitor-dir-dialog-title": "Es tu secur de voler cambiar le directorio de surveliantia?", + "monitor-dir-dialog-msg": "Le nove directorio de surveliantia essera:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Es tu secur de voler rader le directorio de surveliantia?", + "path-was-copied": "Le percurso ha essite copiate", + "monitor-clear-dir-dialog-msg": "Isto cessara le verification del directorio de surveliantia pro nove files ZIM.", + "monitor-directory-tooltip": "Tote le files ZIM in iste directorio essera automaticamente addite al bibliotheca.", + "next-tab": "Passar al scheda sequente", + "previous-tab": "Passar al scheda precedente", + "cancel-download": "Cancellar discargamento", + "cancel-download-text": "Es tu secur de voler cancellar le discargamento de {{ZIM}}?", + "delete-book": "Deler libro", + "delete-book-text": "Es tu secur de voler deler {{ZIM}}?", + "download-storage-error": "Error de immagazinage", + "download-storage-error-text": "Le systema non ha bastante spatio de immagazinage disponibile.", + "download-exceeds-max-file-size": "Le grandor del discargamento excede le dimension maxime de files supportate per le systema de files del destination.", + "download-dir-missing": "Le directorio de discargamento non existe.", + "download-dir-not-writable": "Necun permission de scriptura pro le directorio de discargamento.", + "download-unavailable": "Discargamento indisponibile", + "download-unavailable-text": "Iste discargamento non es disponibile.", + "open-book": "Aperir libro", + "download-book": "Discargar libro", + "pause-download": "Pausar discargamento", + "resume-download": "Reprender discargamento", + "open-folder": "Aperir dossier", + "couldnt-open-location": "Non poteva aperir le loco", + "couldnt-open-location-text": "Kiwix non pote aperir le dossier {{FOLDER}}", + "move-files-to-trash": "Displaciar files delite al corbe a papiro", + "move-files-to-trash-text": "Iste action displaciara le file al corbe a papiro.", + "perma-delete-files-text": "Iste action delera permanentemente le file.", + "clear-filter": "Rader le filtros actualmente definite", + "language-searcher-placeholder": "Filtrar per lingua", + "category-searcher-placeholder": "Filtrar per categoria", + "content-type-searcher-placeholder": "Filtrar per typo de contento", + "no-details": "Introduction solmente", + "no-pictures": "Necun imagine", + "no-videos": "Necun video", + "open-previous-tabs-at-startup": "Aperir previe schedas al initio", + "preview-book-in-web-browser": "Previsualisar libro in navigator web", + "file-not-found-title": "File ZIM non trovate", + "file-not-found-text": "Le file ZIM non existe o non es legibile", + "zim-id": "ID del ZIM", + "zim-name": "Nomine del ZIM", + "zim-path": "Percurso al file ZIM", + "bad-zim-file-error-page-title": "File ZIM non valide", + "bad-zim-file-error-page-text": "Le file ZIM non es valide o ha essite corrumpite.", + "zim-entry-path": "Percurso de entrata ZIM", + "export-reading-list": "Exportar lista de lectura", + "export-reading-list-error": "Un error ha occurrite durante le exportation del lista de lectura.", + "import-reading-list": "Importar lista de lectura", + "import-reading-list-error": "Un error ha occurrite durante le importation del lista de lectura.", + "disable-sandbox": "Kiwix ha essite lanceate desde un disco de rete. Isto es cognoscite pro causar problemas de compatibilitate con le isolator (sandbox) del navigator del web. Como resultato, le isolator essera disactivate. Vole tu continuar?", + "save-page-as": "Salveguardar como…", + "portable-disabled-tooltip": "Function disactivate in modo portabile", + "scroll-next-tab": "Rolar al scheda sequente", + "scroll-previous-tab": "Rolar al scheda precedente" +} diff --git a/resources/i18n/id.json b/resources/i18n/id.json index 51689515e..d1f5e5098 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -1,193 +1,193 @@ -{ - "@metadata": { - "authors": [ - "Akmaie Ajam", - "Esiahaan", - "Farras", - "McDutchie", - "Veracious" - ] - }, - "name": "Bahasa Indonesia", - "error-title": "Galat", - "error-downloader-window-title": "Tidak dapat membuat pengunduh", - "error-downloader-launch-message": "Tidak dapat meluncurkan pengunduh, Kiwix-desktop akan mulai tetapi semua fungsi pengunduhan tidak berfungsi!", - "error-launch-server-message": "Telah terjadi kesalahan!", - "error-archive": "Gagal memuat arsip", - "error-opening-file": "Ada kesalahan saat membuka berkas.", - "open-zim": "Buka Berkas ZIM", - "local-kiwix-server": "Server Kiwix Lokal", - "random-article": "Artikel Acak", - "random-article-error": "Gagal membuka artikel acak.", - "home-page": "Beranda", - "main-menu": "Menu utama", - "print": "Cetak", - "print-page-error": "Terjadi kesalahan saat mencetak.", - "new-tab": "Tab baru", - "close-tab": "Tutup tab", - "close": "Tutup", - "reopen-closed-tab": "Buka kembali tab yang telah tertutup", - "browse-library": "Lihat isi perpustakaan", - "open-file": "Buka file", - "save-file": "Simpan berkas", - "open-recent": "Buka yang terbaru", - "search-article": "Cari artikel", - "search-in-library": "Cari di perpustakaan", - "find-in-page": "Temukan di halaman", - "set-fullscreen": "Setel layar penuh", - "quit-fullscreen": "Keluar dari layar penuh", - "table-of-content": "Daftar isi", - "add-bookmark": "Tambah ke daftar bacaan", - "remove-bookmark": "Hapus dari daftar bacaan", - "reading-list": "Daftar bacaan", - "zoom-in": "Perbesar", - "zoom-out": "Perkecil", - "zoom-reset": "Atur ulang zoom", - "help": "Bantuan", - "feedback": "Umpan balik", - "report-a-bug": "Laporkan kesalahan", - "request-a-feature": "Minta fitur", - "about-kiwix": "Tentang Kiwix", - "donate-to-support-kiwix": "Berikan donasi untuk mendukung Kiwix", - "exit": "Keluar", - "save-file-as-window-title": "Simpan Berkas sebagai", - "download-finished": "Unduh Selesai", - "download-finished-message": "Dokumen telah diunduh.", - "file": "File", - "edit": "Sunting", - "view": "Lihat", - "tools": "Peralatan", - "window-title": "Perpustakaan - Kiwix", - "search": "Cari", - "browse-directory": "Telusuri Direktori", - "settings": "Pengaturan", - "back": "Kembali", - "forward": "Teruskan", - "search-files": "Cari berkas", - "title": "Judul", - "size": "Ukuran", - "date": "Tanggal", - "content-type": "Jenis konten", - "reset-sort": "atur ulang pengurutan", - "open": "Buka", - "delete": "Hapus", - "download": "Unduh", - "resume": "Lanjutkan", - "pause": "Jeda", - "cancel": "Batal", - "apply": "Terapkan", - "port-for-local-kiwix-server-setting": "Port untuk server Kiwix lokal", - "zoom-level-setting": "Tingkat zoom default", - "download-directory-setting": "Direktori unduhan", - "monitor-directory-setting": "Direktori pantauan", - "reset": "Atur ulang", - "clear": "Kosongkan", - "browse": "Jelajahi", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix memungkinkan Anda memiliki seluruh Wikipedia dimanapun Anda berada! Di atas kapal, di tengah antah berantah, atau di penjara, Kiwix memberi Anda akses ke semua pengetahuan manusia. Anda tidak memerlukan Internet, semuanya tersimpan di komputer Anda.", - "about-learn-more-about-kiwix": "Pelajari lebih lanjut tentang Kiwix", - "about-release-title": "Rilis", - "about-source-code": "Perangkat lunak ini dirilis berdasarkan ketentuan Lisensi Publik Umum GNU versi 3. Lihat kode sumber di Github .", - "about-version": "Versi: {{VERSION}}", - "about-reporting-problem-title": "Melaporkan masalah", - "about-report-issue": "Untuk membuka masalah, kunjungi pelacak masalah Kiwix-desktop dan buka masalah yang menjelaskan permasalahan Anda.", - "about-report-issue-2": "Harap sebutkan versi yang ada pada masalah tersebut.", - "about-libraries-title": "Perpustakaan", - "kiwix-server-running-message": "Server Kiwix sedang berjalan dan dapat diakses di jaringan lokal di:", - "kiwix-server-description": "Memulai server memungkinkan komputer lain di jaringan lokal mengakses perpustakaan Kiwix Anda dengan peramban web standar.", - "fullscreen-notification": "Anda sekarang berada dalam mode layar penuh. Tekan ESC untuk keluar!", - "online-files": "File Online", - "local-files": "Berkas Lokal", - "category": "Kategori", - "language": "Bahasa", - "hide": "Sembunyikan", - "open-in-browser": "Buka di browser", - "start-kiwix-server": "Mulai Server Kiwix", - "stop-kiwix-server": "Hentikan Server Kiwix", - "all": "Semua", - "all_ips": "Semua - Mode Tumpukan Ganda", - "ipv4": "Mode Hanya IPv4", - "ipv6": "Mode Hanya IPv6", - "fulltext-search": "Pencarian teks lengkap", - "pictures": "Gambar", - "videos": "Video", - "ftindex": "Indeks teks lengkap", - "details": "Artikel lengkap", - "yes": "ya", - "no": "tidak", - "ok": "ok", - "no-filter": "tanpa filter", - "open-link-in-web-browser": "Buka pranala di peramban web", - "open-link-new-tab": "Buka pranala di tab baru", - "download-dir-dialog-title": "Apakah Anda yakin ingin mengubah direktori unduhan?", - "download-dir-dialog-msg": "Jalur direktori unduhan baru akan menjadi: \n{{DIRECTORY}}", - "invalid-port": "Port tidak sah", - "zim-open-fail-title": "Berkas tidak sah", - "zim-open-fail-text": "Berkas ZIM {{ZIM}} tidak dapat dibuka dengan benar. Berkas ini akan dihapus dari pustaka Anda.", - "monitor-dir-dialog-title": "Apakah Anda yakin ingin mengubah direktori pantauan?", - "monitor-dir-dialog-msg": "Jalur direktori pantauan baru akan menjadi: \n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Apakah Anda yakin ingin menghapus direktori pantauan?", - "path-was-copied": "Jalur telah disalin", - "monitor-clear-dir-dialog-msg": "Ini akan menghentikan pemeriksaan direktori pantauan untuk berkas ZIM baru.", - "monitor-directory-tooltip": "Semua berkas ZIM dalam direktori ini akan ditambahkan ke perpustakaan secara otomatis.", - "next-tab": "Pindah ke tab berikutnya", - "previous-tab": "Pindah ke tab sebelumnya", - "cancel-download": "Batalkan unduhan", - "cancel-download-text": "Apakah Anda yakin ingin membatalkan pengunduhan {{ZIM}}?", - "delete-book": "Hapus buku", - "delete-book-text": "Apakah Anda yakin ingin menghapus {{ZIM}}?", - "download-storage-error": "Kesalahan Penyimpanan", - "download-storage-error-text": "Sistem tidak memiliki penyimpanan yang cukup.", - "download-exceeds-max-file-size": "Ukuran unduhan melebihi ukuran berkas maksimum yang didukung oleh sistem berkas target.", - "download-dir-missing": "Direktori unduhan tidak ada.", - "download-dir-not-writable": "Direktori unduhan tidak dapat ditulis.", - "download-unavailable": "Unduh Tidak Tersedia", - "download-unavailable-text": "Unduhan ini tidak tersedia.", - "open-book": "Buka buku", - "download-book": "Unduh buku", - "pause-download": "Jeda unduhan", - "resume-download": "Lanjutkan unduhan", - "open-folder": "Buka folder", - "couldnt-open-location": "Tidak dapat membuka lokasi", - "couldnt-open-location-text": "Kiwix tidak dapat membuka folder {{FOLDER}}", - "move-files-to-trash": "Pindahkan berkas yang dihapus ke tempat sampah", - "move-files-to-trash-text": "Tindakan ini akan memindahkan berkas ke tempat sampah.", - "perma-delete-files-text": "Tindakan ini akan menghapus berkas secara permanen.", - "clear-filter": "Hapus filter yang saat ini ditetapkan", - "language-searcher-placeholder": "Saring menurut bahasa", - "category-searcher-placeholder": "Saring menurut kategori", - "content-type-searcher-placeholder": "Saring menurut jenis konten", - "no-details": "Hanya pengantar", - "no-pictures": "Tanpa Gambar", - "no-videos": "Tanpa Video", - "open-previous-tabs-at-startup": "Buka tab sebelumnya saat memulai", - "preview-book-in-web-browser": "Pratinjau buku di peramban web", - "file-not-found-title": "Berkas ZIM Tidak Ditemukan", - "file-not-found-text": "Berkas ZIM tidak ada atau tidak dapat dibaca", - "zim-id": "Id ZIM", - "zim-name": "Nama ZIM", - "zim-path": "Jalur File ZIM", - "bad-zim-file-error-page-title": "Berkas ZIM Tidak Sah", - "bad-zim-file-error-page-text": "Berkas ZIM tidak sah atau rusak.", - "zim-entry-path": "Jalur Masuk ZIM", - "export-reading-list": "Ekspor daftar bacaan", - "export-reading-list-error": "Ada kesalahan saat mengekspor daftar bacaan.", - "import-reading-list": "Ekspor daftar bacaan", - "import-reading-list-error": "Ada kesalahan saat mengimpor daftar bacaan.", - "disable-sandbox": "Kiwix telah diluncurkan dari drive jaringan. Hal ini diketahui menyebabkan masalah kompatibilitas dengan isolasi keamanan (sandbox) peramban. Akibatnya, sandbox akan dinonaktifkan. Apakah Anda ingin melanjutkan?", - "save-page-as": "Simpan Sebagai...", - "portable-disabled-tooltip": "Fungsi dinonaktifkan dalam mode portabel", - "scroll-next-tab": "Gulir ke tab berikutnya", - "scroll-previous-tab": "Gulir ke tab sebelumnya", - "kiwix-search": "Pencarian Kiwix", - "search-options": "Opsi Pencarian", - "read-article": "Baca artikel", - "read-text": "Baca teks yang dipilih", - "read-stop": "Berhenti membaca", - "stop": "Berhenti", - "voice": "Suara", - "select-read-voice": "Pilih suara bacaan", - "select-read-language": "Pilih bahasa bacaan", - "save-or-open": "Simpan atau Buka berkas", - "save-or-open-text": "Apa yang harus dilakukan Kiwix dengan berkas ini?" -} +{ + "@metadata": { + "authors": [ + "Akmaie Ajam", + "Esiahaan", + "Farras", + "McDutchie", + "Veracious" + ] + }, + "name": "Bahasa Indonesia", + "error-title": "Galat", + "error-downloader-window-title": "Tidak dapat membuat pengunduh", + "error-downloader-launch-message": "Tidak dapat meluncurkan pengunduh, Kiwix-desktop akan mulai tetapi semua fungsi pengunduhan tidak berfungsi!", + "error-launch-server-message": "Telah terjadi kesalahan!", + "error-archive": "Gagal memuat arsip", + "error-opening-file": "Ada kesalahan saat membuka berkas.", + "open-zim": "Buka Berkas ZIM", + "local-kiwix-server": "Server Kiwix Lokal", + "random-article": "Artikel Acak", + "random-article-error": "Gagal membuka artikel acak.", + "home-page": "Beranda", + "main-menu": "Menu utama", + "print": "Cetak", + "print-page-error": "Terjadi kesalahan saat mencetak.", + "new-tab": "Tab baru", + "close-tab": "Tutup tab", + "close": "Tutup", + "reopen-closed-tab": "Buka kembali tab yang telah tertutup", + "browse-library": "Lihat isi perpustakaan", + "open-file": "Buka file", + "save-file": "Simpan berkas", + "open-recent": "Buka yang terbaru", + "search-article": "Cari artikel", + "search-in-library": "Cari di perpustakaan", + "find-in-page": "Temukan di halaman", + "set-fullscreen": "Setel layar penuh", + "quit-fullscreen": "Keluar dari layar penuh", + "table-of-content": "Daftar isi", + "add-bookmark": "Tambah ke daftar bacaan", + "remove-bookmark": "Hapus dari daftar bacaan", + "reading-list": "Daftar bacaan", + "zoom-in": "Perbesar", + "zoom-out": "Perkecil", + "zoom-reset": "Atur ulang zoom", + "help": "Bantuan", + "feedback": "Umpan balik", + "report-a-bug": "Laporkan kesalahan", + "request-a-feature": "Minta fitur", + "about-kiwix": "Tentang Kiwix", + "donate-to-support-kiwix": "Berikan donasi untuk mendukung Kiwix", + "exit": "Keluar", + "save-file-as-window-title": "Simpan Berkas sebagai", + "download-finished": "Unduh Selesai", + "download-finished-message": "Dokumen telah diunduh.", + "file": "File", + "edit": "Sunting", + "view": "Lihat", + "tools": "Peralatan", + "window-title": "Perpustakaan - Kiwix", + "search": "Cari", + "browse-directory": "Telusuri Direktori", + "settings": "Pengaturan", + "back": "Kembali", + "forward": "Teruskan", + "search-files": "Cari berkas", + "title": "Judul", + "size": "Ukuran", + "date": "Tanggal", + "content-type": "Jenis konten", + "reset-sort": "atur ulang pengurutan", + "open": "Buka", + "delete": "Hapus", + "download": "Unduh", + "resume": "Lanjutkan", + "pause": "Jeda", + "cancel": "Batal", + "apply": "Terapkan", + "port-for-local-kiwix-server-setting": "Port untuk server Kiwix lokal", + "zoom-level-setting": "Tingkat zoom default", + "download-directory-setting": "Direktori unduhan", + "monitor-directory-setting": "Direktori pantauan", + "reset": "Atur ulang", + "clear": "Kosongkan", + "browse": "Jelajahi", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix memungkinkan Anda memiliki seluruh Wikipedia dimanapun Anda berada! Di atas kapal, di tengah antah berantah, atau di penjara, Kiwix memberi Anda akses ke semua pengetahuan manusia. Anda tidak memerlukan Internet, semuanya tersimpan di komputer Anda.", + "about-learn-more-about-kiwix": "Pelajari lebih lanjut tentang Kiwix", + "about-release-title": "Rilis", + "about-source-code": "Perangkat lunak ini dirilis berdasarkan ketentuan Lisensi Publik Umum GNU versi 3. Lihat kode sumber di Github .", + "about-version": "Versi: {{VERSION}}", + "about-reporting-problem-title": "Melaporkan masalah", + "about-report-issue": "Untuk membuka masalah, kunjungi pelacak masalah Kiwix-desktop dan buka masalah yang menjelaskan permasalahan Anda.", + "about-report-issue-2": "Harap sebutkan versi yang ada pada masalah tersebut.", + "about-libraries-title": "Perpustakaan", + "kiwix-server-running-message": "Server Kiwix sedang berjalan dan dapat diakses di jaringan lokal di:", + "kiwix-server-description": "Memulai server memungkinkan komputer lain di jaringan lokal mengakses perpustakaan Kiwix Anda dengan peramban web standar.", + "fullscreen-notification": "Anda sekarang berada dalam mode layar penuh. Tekan ESC untuk keluar!", + "online-files": "File Online", + "local-files": "Berkas Lokal", + "category": "Kategori", + "language": "Bahasa", + "hide": "Sembunyikan", + "open-in-browser": "Buka di browser", + "start-kiwix-server": "Mulai Server Kiwix", + "stop-kiwix-server": "Hentikan Server Kiwix", + "all": "Semua", + "all_ips": "Semua - Mode Tumpukan Ganda", + "ipv4": "Mode Hanya IPv4", + "ipv6": "Mode Hanya IPv6", + "fulltext-search": "Pencarian teks lengkap", + "pictures": "Gambar", + "videos": "Video", + "ftindex": "Indeks teks lengkap", + "details": "Artikel lengkap", + "yes": "ya", + "no": "tidak", + "ok": "ok", + "no-filter": "tanpa filter", + "open-link-in-web-browser": "Buka pranala di peramban web", + "open-link-new-tab": "Buka pranala di tab baru", + "download-dir-dialog-title": "Apakah Anda yakin ingin mengubah direktori unduhan?", + "download-dir-dialog-msg": "Jalur direktori unduhan baru akan menjadi: \n{{DIRECTORY}}", + "invalid-port": "Port tidak sah", + "zim-open-fail-title": "Berkas tidak sah", + "zim-open-fail-text": "Berkas ZIM {{ZIM}} tidak dapat dibuka dengan benar. Berkas ini akan dihapus dari pustaka Anda.", + "monitor-dir-dialog-title": "Apakah Anda yakin ingin mengubah direktori pantauan?", + "monitor-dir-dialog-msg": "Jalur direktori pantauan baru akan menjadi: \n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Apakah Anda yakin ingin menghapus direktori pantauan?", + "path-was-copied": "Jalur telah disalin", + "monitor-clear-dir-dialog-msg": "Ini akan menghentikan pemeriksaan direktori pantauan untuk berkas ZIM baru.", + "monitor-directory-tooltip": "Semua berkas ZIM dalam direktori ini akan ditambahkan ke perpustakaan secara otomatis.", + "next-tab": "Pindah ke tab berikutnya", + "previous-tab": "Pindah ke tab sebelumnya", + "cancel-download": "Batalkan unduhan", + "cancel-download-text": "Apakah Anda yakin ingin membatalkan pengunduhan {{ZIM}}?", + "delete-book": "Hapus buku", + "delete-book-text": "Apakah Anda yakin ingin menghapus {{ZIM}}?", + "download-storage-error": "Kesalahan Penyimpanan", + "download-storage-error-text": "Sistem tidak memiliki penyimpanan yang cukup.", + "download-exceeds-max-file-size": "Ukuran unduhan melebihi ukuran berkas maksimum yang didukung oleh sistem berkas target.", + "download-dir-missing": "Direktori unduhan tidak ada.", + "download-dir-not-writable": "Direktori unduhan tidak dapat ditulis.", + "download-unavailable": "Unduh Tidak Tersedia", + "download-unavailable-text": "Unduhan ini tidak tersedia.", + "open-book": "Buka buku", + "download-book": "Unduh buku", + "pause-download": "Jeda unduhan", + "resume-download": "Lanjutkan unduhan", + "open-folder": "Buka folder", + "couldnt-open-location": "Tidak dapat membuka lokasi", + "couldnt-open-location-text": "Kiwix tidak dapat membuka folder {{FOLDER}}", + "move-files-to-trash": "Pindahkan berkas yang dihapus ke tempat sampah", + "move-files-to-trash-text": "Tindakan ini akan memindahkan berkas ke tempat sampah.", + "perma-delete-files-text": "Tindakan ini akan menghapus berkas secara permanen.", + "clear-filter": "Hapus filter yang saat ini ditetapkan", + "language-searcher-placeholder": "Saring menurut bahasa", + "category-searcher-placeholder": "Saring menurut kategori", + "content-type-searcher-placeholder": "Saring menurut jenis konten", + "no-details": "Hanya pengantar", + "no-pictures": "Tanpa Gambar", + "no-videos": "Tanpa Video", + "open-previous-tabs-at-startup": "Buka tab sebelumnya saat memulai", + "preview-book-in-web-browser": "Pratinjau buku di peramban web", + "file-not-found-title": "Berkas ZIM Tidak Ditemukan", + "file-not-found-text": "Berkas ZIM tidak ada atau tidak dapat dibaca", + "zim-id": "Id ZIM", + "zim-name": "Nama ZIM", + "zim-path": "Jalur File ZIM", + "bad-zim-file-error-page-title": "Berkas ZIM Tidak Sah", + "bad-zim-file-error-page-text": "Berkas ZIM tidak sah atau rusak.", + "zim-entry-path": "Jalur Masuk ZIM", + "export-reading-list": "Ekspor daftar bacaan", + "export-reading-list-error": "Ada kesalahan saat mengekspor daftar bacaan.", + "import-reading-list": "Ekspor daftar bacaan", + "import-reading-list-error": "Ada kesalahan saat mengimpor daftar bacaan.", + "disable-sandbox": "Kiwix telah diluncurkan dari drive jaringan. Hal ini diketahui menyebabkan masalah kompatibilitas dengan isolasi keamanan (sandbox) peramban. Akibatnya, sandbox akan dinonaktifkan. Apakah Anda ingin melanjutkan?", + "save-page-as": "Simpan Sebagai...", + "portable-disabled-tooltip": "Fungsi dinonaktifkan dalam mode portabel", + "scroll-next-tab": "Gulir ke tab berikutnya", + "scroll-previous-tab": "Gulir ke tab sebelumnya", + "kiwix-search": "Pencarian Kiwix", + "search-options": "Opsi Pencarian", + "read-article": "Baca artikel", + "read-text": "Baca teks yang dipilih", + "read-stop": "Berhenti membaca", + "stop": "Berhenti", + "voice": "Suara", + "select-read-voice": "Pilih suara bacaan", + "select-read-language": "Pilih bahasa bacaan", + "save-or-open": "Simpan atau Buka berkas", + "save-or-open-text": "Apa yang harus dilakukan Kiwix dengan berkas ini?" +} diff --git a/resources/i18n/ie.json b/resources/i18n/ie.json index f843a2c4c..dbd0971cd 100644 --- a/resources/i18n/ie.json +++ b/resources/i18n/ie.json @@ -1,52 +1,52 @@ -{ - "@metadata": { - "authors": [ - "OIS" - ] - }, - "name": "Interlingue", - "open-zim": "Aperter Zim", - "print": "Printar", - "new-tab": "Nov carte", - "close-tab": "Cluder li carte", - "browse-library": "Navigar li biblioteca", - "open-file": "Aperter un file", - "table-of-content": "Contenete", - "zoom-in": "Agrandar", - "zoom-out": "Diminuer", - "help": "Auxilie", - "report-a-bug": "Raportar un defecte", - "about-kiwix": "Pri Kiwix", - "exit": "Surtir", - "file": "File", - "edit": "Redacter", - "view": "Vise", - "tools": "Utensiles", - "window-title": "Biblioteca - Kiwix", - "search": "Serchar", - "settings": "Preferenties", - "title": "Titul", - "size": "Grandor", - "date": "Date", - "open": "Aperter", - "delete": "Deleter", - "resume": "Reprender", - "pause": "Pausar", - "cancel": "Anullar", - "apply": "Applicar", - "browse": "Navigar", - "about-version": "Version: {{VERSION}}", - "about-libraries-title": "Bibliotecas", - "local-files": "Local files", - "hide": "Ocultar", - "all": "Omni", - "fulltext-search": "Plen-textual sercha", - "pictures": "Images", - "videos": "Videos", - "ftindex": "Plen-textual index", - "details": "Complet articul", - "yes": "yes", - "no": "no", - "no-filter": "sin filtre", - "invalid-port": "Ínvalid portu" -} +{ + "@metadata": { + "authors": [ + "OIS" + ] + }, + "name": "Interlingue", + "open-zim": "Aperter Zim", + "print": "Printar", + "new-tab": "Nov carte", + "close-tab": "Cluder li carte", + "browse-library": "Navigar li biblioteca", + "open-file": "Aperter un file", + "table-of-content": "Contenete", + "zoom-in": "Agrandar", + "zoom-out": "Diminuer", + "help": "Auxilie", + "report-a-bug": "Raportar un defecte", + "about-kiwix": "Pri Kiwix", + "exit": "Surtir", + "file": "File", + "edit": "Redacter", + "view": "Vise", + "tools": "Utensiles", + "window-title": "Biblioteca - Kiwix", + "search": "Serchar", + "settings": "Preferenties", + "title": "Titul", + "size": "Grandor", + "date": "Date", + "open": "Aperter", + "delete": "Deleter", + "resume": "Reprender", + "pause": "Pausar", + "cancel": "Anullar", + "apply": "Applicar", + "browse": "Navigar", + "about-version": "Version: {{VERSION}}", + "about-libraries-title": "Bibliotecas", + "local-files": "Local files", + "hide": "Ocultar", + "all": "Omni", + "fulltext-search": "Plen-textual sercha", + "pictures": "Images", + "videos": "Videos", + "ftindex": "Plen-textual index", + "details": "Complet articul", + "yes": "yes", + "no": "no", + "no-filter": "sin filtre", + "invalid-port": "Ínvalid portu" +} diff --git a/resources/i18n/ig.json b/resources/i18n/ig.json index a50fd7324..287ef6d0b 100644 --- a/resources/i18n/ig.json +++ b/resources/i18n/ig.json @@ -1,148 +1,148 @@ -{ - "@metadata": { - "authors": [ - "Accuratecy051", - "Akwugo", - "Oby Ezeilo" - ] - }, - "name": "Bekee", - "error-title": "mmejọ", - "error-downloader-window-title": "Enweghị ike ikepụta danloder", - "error-downloader-launch-message": "Mgbe Enweghị ike ịnabata danloder, Kiwix-destop ga amalite mana ngwá ọrụ danlod agaghị arụ ọrụ.", - "error-launch-server-message": "Enwere mmejọ gosịpụtara", - "error-archive": "Enweghị ike inweta nke edebere", - "open-zim": "Mehee faịlụ Zim", - "local-kiwix-server": "Ihe nkesa Kiwix mpaghara", - "random-article": "Edemede enweghị usoro", - "random-article-error": "Emepeghị akụkọ enweghị usoro.", - "home-page": "Peeji mbu", - "main-menu": "Isi menu", - "print": "Bipụta", - "print-page-error": "Enwere mperi mgbe a na-ebi akwụkwọ.", - "new-tab": "Taabụ ọhụrụ", - "close-tab": "Mechie taabụ", - "close": "Mechie", - "reopen-closed-tab": "Mepee taabụ mechiri emechi", - "browse-library": "Chọgharịa n'ọbá akwụkwọ", - "open-file": "Mepee faịlụ", - "open-recent": "Mepee nso nso a", - "search-article": "Chọọ akụkọ", - "search-in-library": "Chọọ n'ọbá akwụkwọ", - "find-in-page": "chọta na ibe", - "set-fullscreen": "Tọọ ihuenyo zuru oke", - "quit-fullscreen": "Kwụsị ihuenyo zuru ezu", - "table-of-content": "Tebulu ọdịnaya", - "reading-list": "Ndetù ńwùzoro", - "zoom-in": "Bugharịa", - "zoom-out": "Wepụ ya", - "zoom-reset": "Ntọgharị mbugharị", - "help": "Enyemaka", - "feedback": "Nzaghachi", - "report-a-bug": "Kpesa ahụhụ", - "request-a-feature": "Rịọ atụmatụ", - "about-kiwix": "Banyere Kiwix", - "donate-to-support-kiwix": "Nye onyinye iji kwado Kiwix", - "exit": "Mèzi", - "save-file-as-window-title": "Chekwaa faịlụ ka", - "download-finished-message": "Ebudatala akwụkwọ ahụ.", - "file": "Àfabà", - "edit": "Mèzi", - "view": "Lee", - "tools": "Ngwaọrụ", - "window-title": "Ụlọ akwụkwọ - Kiwix", - "search": "Tùwe", - "browse-directory": "Chọgharịa ndekọ", - "settings": "Ntọala", - "back": "Nke–azụ", - "forward": "Gawa n'ihu", - "search-files": "Họrọ faịlụ", - "title": "Isiokwu", - "size": "Ívụ", - "date": "Ǹgụ́ụ̀bọ̀chị̀", - "content-type": "Ihü ihe dị", - "reset-sort": "tọgharịa ụdị", - "open": "Mepee", - "delete": "Kàcha", - "download": "Budata", - "resume": "maliteghachi", - "pause": "Ịkwụsịtụ", - "cancel": "Hapụ̀", - "apply": "Tinye", - "port-for-local-kiwix-server-setting": "Port maka sava Kiwix mpaghara", - "zoom-level-setting": "Ọkwa mbugharị mbụ", - "download-directory-setting": "Budata ndekọ", - "monitor-directory-setting": "Nyochaa ndekọ", - "reset": "Mẹ̀wárí n'ísí", - "clear": "Kpochapụ", - "browse": "Garia", - "about-kiwix-desktop-title": "Kiwix Desktọpụ", - "about-kiwix-desktop-description": "Kiwix na-enye gị ohere ịnweta Wikipedia niile n'aka ebe ọ bụla ị na-aga! N'elu ụgbọ mmiri, n'etiti ebe ọ bụla, ma ọ bụ n'ụlọ mkpọrọ, Kiwix na-enye gị ohere ịnweta ihe ọmụma mmadụ niile. Ịchọghị ịntanetị, a na-echekwa ihe niile na kọmputa gị.", - "about-learn-more-about-kiwix": "Mụtakwuo maka Kiwix", - "about-release-title": "Hapụ", - "about-source-code": "A tọhapụrụ ngwanrọ a n'okpuru usoro nke GNU General Public License version 3. Lelee koodu isi mmalite na Github .", - "about-version": "Ụdị: {{VERSION}}", - "about-reporting-problem-title": "Nsogbu mkpesa", - "about-report-issue": "Iji mepee esemokwu, gaa na Kiwix-desktop tracker ma mepee okwu na-akọwa nsogbu gị.", - "about-report-issue-2": "Biko kwuo ụdịdị na mbipụta a.", - "about-libraries-title": "Ụlọ akwụkwọ", - "kiwix-server-running-message": "Ihe nkesa Kiwix na-agba ọsọ ma enwere ike ịnweta ya na netwọk mpaghara na:", - "kiwix-server-description": "Ịmalite ihe nkesa na-enye ohere ka kọmputa ndị ọzọ dị na netwọk mpaghara nweta ọbá akwụkwọ Kiwix gị na ihe nchọgharị weebụ ọkọlọtọ.", - "fullscreen-notification": "Ị nọ na ọnọdụ ihuenyo zuru oke ugbu a. Pịa ESC ka ị kwụsị!", - "online-files": "Faịlụ dị n'ịntanetị", - "local-files": "Faịlụ mpaghara", - "category": "Ụdàkọ", - "language": "Asụsụ", - "hide": "Zònarị", - "open-in-browser": "Mepee na ihe nchọgharị", - "start-kiwix-server": "Malite sava Kiwix", - "stop-kiwix-server": "Kwụsị sava Kiwix", - "all": "Haníle", - "fulltext-search": "Ọchụchọ ederede zuru oke", - "pictures": "Foto", - "videos": "Vịdịo", - "ftindex": "Ndekọ ederede zuru oke", - "details": "Edemede zuru ezu", - "yes": "Ee", - "no": "Mba", - "ok": "Ọ dị mma", - "no-filter": "enweghị nzacha", - "open-link-in-web-browser": "Mepee njikọ na ihe nchọgharị weebụ", - "download-dir-dialog-title": "Ị ji n'aka na ịchọrọ ịgbanwe ndekọ ntuziaka nbudata?", - "download-dir-dialog-msg": "Ụzọ ndekọ nbudata ọhụrụ ga-abụ:\n{{DIRECTORY}}", - "invalid-port": "Portụ na-abaghị uru", - "zim-open-fail-title": "Faịlụ na-abaghị uru", - "zim-open-fail-text": "Enweghị ike imepe faịlụ ZIM {{ZIM}} nke ọma. A ga-ewepụ ya n'ọbá akwụkwọ gị.", - "monitor-dir-dialog-title": "Ị ji n'aka na ịchọrọ ịgbanwe ndekọ ntuziaka nbudata?", - "monitor-dir-dialog-msg": "Ụzọ ndekọ nbudata ọhụrụ ga-abụ:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Ị ji n'aka na ịchọrọ ịgbanwe ndekọ ntuziaka nbudata?", - "monitor-clear-dir-dialog-msg": "Nke a ga-akwụsị ịlele ndekọ ndekọ maka faịlụ ZIM ọhụrụ.", - "monitor-directory-tooltip": "A ga-atụkwasị faịlụ ZIM niile dị na ndekọ aha na-akpaghị aka n'ọbá akwụkwọ.", - "next-tab": "Gaa na taabụ na-esote", - "previous-tab": "Gaa na taabụ gara aga", - "cancel-download": "Kagbuo nbudata", - "cancel-download-text": "Ị ji n'aka na ịchọrọ ịkagbu nbudata nke {{ZIM}} ?", - "delete-book": "Hichapụ akwụkwọ", - "delete-book-text": "Ị ji n'aka na ịchọrọ ihichapụ {{ZIM}} ?", - "download-storage-error": "Mperi nchekwa", - "download-storage-error-text": "Sistemu enweghị nchekwa zuru oke dị.", - "download-unavailable": "Budata adịghị", - "download-unavailable-text": "nbudata a adịghị.", - "open-book": "Mepee akwụkwọ", - "download-book": "Budata akwụkwọ", - "pause-download": "Kwụsị nbudata", - "resume-download": "Malitegharịa nbudata", - "open-folder": "Mepee folda", - "couldnt-open-location": "Enweghị ike imepe ebe", - "couldnt-open-location-text": "Kiwix enweghị ike imepe folda {{FOLDER}}", - "move-files-to-trash": "Bugharịa faịlụ ehichapụ na ahịhịa", - "move-files-to-trash-text": "Ihe omume a ga-ebuga faịlụ ahụ ka ọ bụrụ ahịhịa.", - "perma-delete-files-text": "Omume a ga-ehichapụ faịlụ ahụ kpamkpam.", - "clear-filter": "Hichapụ ihe nzacha edobere ugbu a", - "language-searcher-placeholder": "Asụsụ nzacha", - "category-searcher-placeholder": "Otu nzacha", - "content-type-searcher-placeholder": "Ụdị ọdịnaya iyo", - "no-details": "Naanị mmalite", - "no-pictures": "Enweghị Foto", - "no-videos": "Enweghị vidiyo" -} +{ + "@metadata": { + "authors": [ + "Accuratecy051", + "Akwugo", + "Oby Ezeilo" + ] + }, + "name": "Bekee", + "error-title": "mmejọ", + "error-downloader-window-title": "Enweghị ike ikepụta danloder", + "error-downloader-launch-message": "Mgbe Enweghị ike ịnabata danloder, Kiwix-destop ga amalite mana ngwá ọrụ danlod agaghị arụ ọrụ.", + "error-launch-server-message": "Enwere mmejọ gosịpụtara", + "error-archive": "Enweghị ike inweta nke edebere", + "open-zim": "Mehee faịlụ Zim", + "local-kiwix-server": "Ihe nkesa Kiwix mpaghara", + "random-article": "Edemede enweghị usoro", + "random-article-error": "Emepeghị akụkọ enweghị usoro.", + "home-page": "Peeji mbu", + "main-menu": "Isi menu", + "print": "Bipụta", + "print-page-error": "Enwere mperi mgbe a na-ebi akwụkwọ.", + "new-tab": "Taabụ ọhụrụ", + "close-tab": "Mechie taabụ", + "close": "Mechie", + "reopen-closed-tab": "Mepee taabụ mechiri emechi", + "browse-library": "Chọgharịa n'ọbá akwụkwọ", + "open-file": "Mepee faịlụ", + "open-recent": "Mepee nso nso a", + "search-article": "Chọọ akụkọ", + "search-in-library": "Chọọ n'ọbá akwụkwọ", + "find-in-page": "chọta na ibe", + "set-fullscreen": "Tọọ ihuenyo zuru oke", + "quit-fullscreen": "Kwụsị ihuenyo zuru ezu", + "table-of-content": "Tebulu ọdịnaya", + "reading-list": "Ndetù ńwùzoro", + "zoom-in": "Bugharịa", + "zoom-out": "Wepụ ya", + "zoom-reset": "Ntọgharị mbugharị", + "help": "Enyemaka", + "feedback": "Nzaghachi", + "report-a-bug": "Kpesa ahụhụ", + "request-a-feature": "Rịọ atụmatụ", + "about-kiwix": "Banyere Kiwix", + "donate-to-support-kiwix": "Nye onyinye iji kwado Kiwix", + "exit": "Mèzi", + "save-file-as-window-title": "Chekwaa faịlụ ka", + "download-finished-message": "Ebudatala akwụkwọ ahụ.", + "file": "Àfabà", + "edit": "Mèzi", + "view": "Lee", + "tools": "Ngwaọrụ", + "window-title": "Ụlọ akwụkwọ - Kiwix", + "search": "Tùwe", + "browse-directory": "Chọgharịa ndekọ", + "settings": "Ntọala", + "back": "Nke–azụ", + "forward": "Gawa n'ihu", + "search-files": "Họrọ faịlụ", + "title": "Isiokwu", + "size": "Ívụ", + "date": "Ǹgụ́ụ̀bọ̀chị̀", + "content-type": "Ihü ihe dị", + "reset-sort": "tọgharịa ụdị", + "open": "Mepee", + "delete": "Kàcha", + "download": "Budata", + "resume": "maliteghachi", + "pause": "Ịkwụsịtụ", + "cancel": "Hapụ̀", + "apply": "Tinye", + "port-for-local-kiwix-server-setting": "Port maka sava Kiwix mpaghara", + "zoom-level-setting": "Ọkwa mbugharị mbụ", + "download-directory-setting": "Budata ndekọ", + "monitor-directory-setting": "Nyochaa ndekọ", + "reset": "Mẹ̀wárí n'ísí", + "clear": "Kpochapụ", + "browse": "Garia", + "about-kiwix-desktop-title": "Kiwix Desktọpụ", + "about-kiwix-desktop-description": "Kiwix na-enye gị ohere ịnweta Wikipedia niile n'aka ebe ọ bụla ị na-aga! N'elu ụgbọ mmiri, n'etiti ebe ọ bụla, ma ọ bụ n'ụlọ mkpọrọ, Kiwix na-enye gị ohere ịnweta ihe ọmụma mmadụ niile. Ịchọghị ịntanetị, a na-echekwa ihe niile na kọmputa gị.", + "about-learn-more-about-kiwix": "Mụtakwuo maka Kiwix", + "about-release-title": "Hapụ", + "about-source-code": "A tọhapụrụ ngwanrọ a n'okpuru usoro nke GNU General Public License version 3. Lelee koodu isi mmalite na Github .", + "about-version": "Ụdị: {{VERSION}}", + "about-reporting-problem-title": "Nsogbu mkpesa", + "about-report-issue": "Iji mepee esemokwu, gaa na Kiwix-desktop tracker ma mepee okwu na-akọwa nsogbu gị.", + "about-report-issue-2": "Biko kwuo ụdịdị na mbipụta a.", + "about-libraries-title": "Ụlọ akwụkwọ", + "kiwix-server-running-message": "Ihe nkesa Kiwix na-agba ọsọ ma enwere ike ịnweta ya na netwọk mpaghara na:", + "kiwix-server-description": "Ịmalite ihe nkesa na-enye ohere ka kọmputa ndị ọzọ dị na netwọk mpaghara nweta ọbá akwụkwọ Kiwix gị na ihe nchọgharị weebụ ọkọlọtọ.", + "fullscreen-notification": "Ị nọ na ọnọdụ ihuenyo zuru oke ugbu a. Pịa ESC ka ị kwụsị!", + "online-files": "Faịlụ dị n'ịntanetị", + "local-files": "Faịlụ mpaghara", + "category": "Ụdàkọ", + "language": "Asụsụ", + "hide": "Zònarị", + "open-in-browser": "Mepee na ihe nchọgharị", + "start-kiwix-server": "Malite sava Kiwix", + "stop-kiwix-server": "Kwụsị sava Kiwix", + "all": "Haníle", + "fulltext-search": "Ọchụchọ ederede zuru oke", + "pictures": "Foto", + "videos": "Vịdịo", + "ftindex": "Ndekọ ederede zuru oke", + "details": "Edemede zuru ezu", + "yes": "Ee", + "no": "Mba", + "ok": "Ọ dị mma", + "no-filter": "enweghị nzacha", + "open-link-in-web-browser": "Mepee njikọ na ihe nchọgharị weebụ", + "download-dir-dialog-title": "Ị ji n'aka na ịchọrọ ịgbanwe ndekọ ntuziaka nbudata?", + "download-dir-dialog-msg": "Ụzọ ndekọ nbudata ọhụrụ ga-abụ:\n{{DIRECTORY}}", + "invalid-port": "Portụ na-abaghị uru", + "zim-open-fail-title": "Faịlụ na-abaghị uru", + "zim-open-fail-text": "Enweghị ike imepe faịlụ ZIM {{ZIM}} nke ọma. A ga-ewepụ ya n'ọbá akwụkwọ gị.", + "monitor-dir-dialog-title": "Ị ji n'aka na ịchọrọ ịgbanwe ndekọ ntuziaka nbudata?", + "monitor-dir-dialog-msg": "Ụzọ ndekọ nbudata ọhụrụ ga-abụ:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Ị ji n'aka na ịchọrọ ịgbanwe ndekọ ntuziaka nbudata?", + "monitor-clear-dir-dialog-msg": "Nke a ga-akwụsị ịlele ndekọ ndekọ maka faịlụ ZIM ọhụrụ.", + "monitor-directory-tooltip": "A ga-atụkwasị faịlụ ZIM niile dị na ndekọ aha na-akpaghị aka n'ọbá akwụkwọ.", + "next-tab": "Gaa na taabụ na-esote", + "previous-tab": "Gaa na taabụ gara aga", + "cancel-download": "Kagbuo nbudata", + "cancel-download-text": "Ị ji n'aka na ịchọrọ ịkagbu nbudata nke {{ZIM}} ?", + "delete-book": "Hichapụ akwụkwọ", + "delete-book-text": "Ị ji n'aka na ịchọrọ ihichapụ {{ZIM}} ?", + "download-storage-error": "Mperi nchekwa", + "download-storage-error-text": "Sistemu enweghị nchekwa zuru oke dị.", + "download-unavailable": "Budata adịghị", + "download-unavailable-text": "nbudata a adịghị.", + "open-book": "Mepee akwụkwọ", + "download-book": "Budata akwụkwọ", + "pause-download": "Kwụsị nbudata", + "resume-download": "Malitegharịa nbudata", + "open-folder": "Mepee folda", + "couldnt-open-location": "Enweghị ike imepe ebe", + "couldnt-open-location-text": "Kiwix enweghị ike imepe folda {{FOLDER}}", + "move-files-to-trash": "Bugharịa faịlụ ehichapụ na ahịhịa", + "move-files-to-trash-text": "Ihe omume a ga-ebuga faịlụ ahụ ka ọ bụrụ ahịhịa.", + "perma-delete-files-text": "Omume a ga-ehichapụ faịlụ ahụ kpamkpam.", + "clear-filter": "Hichapụ ihe nzacha edobere ugbu a", + "language-searcher-placeholder": "Asụsụ nzacha", + "category-searcher-placeholder": "Otu nzacha", + "content-type-searcher-placeholder": "Ụdị ọdịnaya iyo", + "no-details": "Naanị mmalite", + "no-pictures": "Enweghị Foto", + "no-videos": "Enweghị vidiyo" +} diff --git a/resources/i18n/igl.json b/resources/i18n/igl.json index 36e1f4427..9a28161d5 100644 --- a/resources/i18n/igl.json +++ b/resources/i18n/igl.json @@ -1,78 +1,78 @@ -{ - "@metadata": { - "authors": [ - "AgnesAbah", - "Beracy", - "JosefAnthony", - "Ogalihillary" - ] - }, - "name": "Enéfu", - "error-title": "Inyoñ", - "error-downloader-window-title": "Ineke chen", - "open-zim": "Bí Zim filu", - "print": "Che dufu", - "print-page-error": "Ẹnwu ka chi alu ki ache dufu", - "close-tab": "Re apa", - "close": "Re", - "reopen-closed-tab": "Dabi bi APA kichẹ re", - "open-file": "Bí filu", - "open-recent": "Bi e titoi", - "search-article": "Tene ita", - "search-in-library": "Tene efu ugbo kami ẹwnu jọ", - "find-in-page": "Tene efu apa", - "zoom-in": "Che ki nana", - "zoom-out": "Che ki nya", - "zoom-reset": "Dabi Che tẹgwu", - "help": "Abune", - "feedback": "Ẹlọ", - "report-a-bug": "Rainọ akanya", - "about-kiwix": "Lugbo Kiwix", - "donate-to-support-kiwix": "Du kẹ chatẹko Kiwix", - "exit": "Kwefu", - "save-file-as-window-title": "Fí filu kojo dabí", - "file": "Filu", - "edit": "Ñọche", - "view": "Go", - "tools": "Amí ewñ ane", - "window-title": "Ugbo kuma kẹnwu jọ - Kiwix", - "search": "Téné", - "settings": "Eñoche", - "back": "Ubi", - "forward": "Ogba", - "search-files": "Téné amí filu", - "title": "Ojí", - "size": "Unana", - "date": "Iko", - "content-type": "Ogbá kí ná óla wéwé", - "reset-sort": "Dabi Che ẹre", - "open": "Bi", - "delete": "Kpalu", - "resume": "Edabi wa", - "pause": "Mọ́ ọ́wọ́ du no", - "cancel": "Kpalú", - "apply": "Tene", - "about-release-title": "Tónó", - "hide": "Du maja", - "all": "Ocha ka du", - "pictures": "Foto", - "videos": "Vidíyo", - "details": "Ola otakada Chakadu", - "yes": "Iii", - "no": "É e", - "ok": "Ok", - "next-tab": "Lo tí tap omune", - "previous-tab": "Lotí tap igbele", - "cancel-download": "Fí download kpalu", - "delete-book": "Kpo taka da lu", - "delete-book-text": "É ché sure ka kí ní é téné kpalu {{ZIM}}?", - "open-book": "Bió takada", - "pause-download": "Wei download", - "resume-download": "Shane download", - "open-folder": "Bí failu", - "couldnt-open-location": "Mà méké bí ugbo kí de ñ", - "couldnt-open-location-text": "Kiwix neké bí folder ñ{{FOLDER}}", - "move-files-to-trash": "Amí failu kù ma kpalu mu ma ti ugbo iwa", - "move-files-to-trash-text": "É ñwu ké ché í a jé nwí failu í lotí ugbo iwa", - "no-pictures": "I foto dó món" -} +{ + "@metadata": { + "authors": [ + "AgnesAbah", + "Beracy", + "JosefAnthony", + "Ogalihillary" + ] + }, + "name": "Enéfu", + "error-title": "Inyoñ", + "error-downloader-window-title": "Ineke chen", + "open-zim": "Bí Zim filu", + "print": "Che dufu", + "print-page-error": "Ẹnwu ka chi alu ki ache dufu", + "close-tab": "Re apa", + "close": "Re", + "reopen-closed-tab": "Dabi bi APA kichẹ re", + "open-file": "Bí filu", + "open-recent": "Bi e titoi", + "search-article": "Tene ita", + "search-in-library": "Tene efu ugbo kami ẹwnu jọ", + "find-in-page": "Tene efu apa", + "zoom-in": "Che ki nana", + "zoom-out": "Che ki nya", + "zoom-reset": "Dabi Che tẹgwu", + "help": "Abune", + "feedback": "Ẹlọ", + "report-a-bug": "Rainọ akanya", + "about-kiwix": "Lugbo Kiwix", + "donate-to-support-kiwix": "Du kẹ chatẹko Kiwix", + "exit": "Kwefu", + "save-file-as-window-title": "Fí filu kojo dabí", + "file": "Filu", + "edit": "Ñọche", + "view": "Go", + "tools": "Amí ewñ ane", + "window-title": "Ugbo kuma kẹnwu jọ - Kiwix", + "search": "Téné", + "settings": "Eñoche", + "back": "Ubi", + "forward": "Ogba", + "search-files": "Téné amí filu", + "title": "Ojí", + "size": "Unana", + "date": "Iko", + "content-type": "Ogbá kí ná óla wéwé", + "reset-sort": "Dabi Che ẹre", + "open": "Bi", + "delete": "Kpalu", + "resume": "Edabi wa", + "pause": "Mọ́ ọ́wọ́ du no", + "cancel": "Kpalú", + "apply": "Tene", + "about-release-title": "Tónó", + "hide": "Du maja", + "all": "Ocha ka du", + "pictures": "Foto", + "videos": "Vidíyo", + "details": "Ola otakada Chakadu", + "yes": "Iii", + "no": "É e", + "ok": "Ok", + "next-tab": "Lo tí tap omune", + "previous-tab": "Lotí tap igbele", + "cancel-download": "Fí download kpalu", + "delete-book": "Kpo taka da lu", + "delete-book-text": "É ché sure ka kí ní é téné kpalu {{ZIM}}?", + "open-book": "Bió takada", + "pause-download": "Wei download", + "resume-download": "Shane download", + "open-folder": "Bí failu", + "couldnt-open-location": "Mà méké bí ugbo kí de ñ", + "couldnt-open-location-text": "Kiwix neké bí folder ñ{{FOLDER}}", + "move-files-to-trash": "Amí failu kù ma kpalu mu ma ti ugbo iwa", + "move-files-to-trash-text": "É ñwu ké ché í a jé nwí failu í lotí ugbo iwa", + "no-pictures": "I foto dó món" +} diff --git a/resources/i18n/io.json b/resources/i18n/io.json index a0481bec8..a01300f24 100644 --- a/resources/i18n/io.json +++ b/resources/i18n/io.json @@ -1,39 +1,39 @@ -{ - "@metadata": { - "authors": [ - "Joao Xavier" - ] - }, - "open-zim": "Apertez Zim", - "home-page": "Hem-pagino", - "print": "Imprimar", - "open-file": "Apertar arkivo", - "open-recent": "Recente apertita", - "search-article": "Serchez artiklo", - "reading-list": "Lekto-listo", - "zoom-reset": "Riajustar \"zoom\"", - "help": "Helpo", - "feedback": "Retro-informo", - "report-a-bug": "Informez defekto", - "donate-to-support-kiwix": "Donez por suportar Kiwix", - "download-finished-message": "Ca dokumento descharjesis.", - "edit": "Redaktez", - "tools": "Utensili", - "search": "Serchez", - "back": "Addope", - "search-files": "Serchar arkivi", - "title": "Titulo", - "size": "Grandeso", - "content-type": "Tipo di kontenajo", - "download": "Descharjez", - "resume": "Durigar", - "cancel": "Anular", - "apply": "Aplikar", - "hide": "Celez", - "start-kiwix-server": "Startar servero Kiwix", - "fulltext-search": "Serchar kompleta texto", - "pictures": "Imaji", - "yes": "yes", - "no": "no", - "no-filter": "nula filtrilo" -} +{ + "@metadata": { + "authors": [ + "Joao Xavier" + ] + }, + "open-zim": "Apertez Zim", + "home-page": "Hem-pagino", + "print": "Imprimar", + "open-file": "Apertar arkivo", + "open-recent": "Recente apertita", + "search-article": "Serchez artiklo", + "reading-list": "Lekto-listo", + "zoom-reset": "Riajustar \"zoom\"", + "help": "Helpo", + "feedback": "Retro-informo", + "report-a-bug": "Informez defekto", + "donate-to-support-kiwix": "Donez por suportar Kiwix", + "download-finished-message": "Ca dokumento descharjesis.", + "edit": "Redaktez", + "tools": "Utensili", + "search": "Serchez", + "back": "Addope", + "search-files": "Serchar arkivi", + "title": "Titulo", + "size": "Grandeso", + "content-type": "Tipo di kontenajo", + "download": "Descharjez", + "resume": "Durigar", + "cancel": "Anular", + "apply": "Aplikar", + "hide": "Celez", + "start-kiwix-server": "Startar servero Kiwix", + "fulltext-search": "Serchar kompleta texto", + "pictures": "Imaji", + "yes": "yes", + "no": "no", + "no-filter": "nula filtrilo" +} diff --git a/resources/i18n/it.json b/resources/i18n/it.json index 81e277994..b5f42ab41 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -1,139 +1,139 @@ -{ - "@metadata": { - "authors": [ - "Ajeje Brazorf", - "Albano", - "Albe Albe460", - "Beta16", - "Gianfranco", - "IrsY", - "McDutchie", - "Scompo", - "Stefano" - ] - }, - "name": "italiano", - "error-title": "Errore", - "error-downloader-window-title": "Impossibile creare downloader", - "error-downloader-launch-message": "Impossibile avviare il downloader. Kiwix-desktop si avvierà, ma tutte le funzioni di download non funzioneranno.", - "error-launch-server-message": "Si è verificato un errore.", - "error-archive": "Impossibile ottenere l'archivio", - "error-opening-file": "Si è verificato un errore durante l'apertura del file.", - "open-zim": "Apri file ZIM", - "local-kiwix-server": "Server Kiwix locale", - "random-article": "Voce a caso", - "random-article-error": "Impossibile aprire un articolo casuale.", - "home-page": "Pagina principale", - "main-menu": "Menu principale", - "print": "Stampa", - "print-page-error": "Si è verificato un errore durante la stampa.", - "new-tab": "Nuova scheda", - "close-tab": "Chiudi scheda", - "close": "Chiudi", - "reopen-closed-tab": "Riapri scheda chiusa", - "browse-library": "Esplora libreria", - "open-file": "Apri file", - "save-file": "Salva file", - "open-recent": "Apri recenti", - "search-article": "Ricerca voce", - "search-in-library": "Cerca nella libreria", - "find-in-page": "Trova nella pagina", - "set-fullscreen": "Imposta a schermo intero", - "quit-fullscreen": "Esci da schermo intero", - "table-of-content": "Indice", - "add-bookmark": "Aggiungi all'elenco di lettura", - "remove-bookmark": "Rimuovi dall'elenco di lettura", - "reading-list": "Elenco di lettura", - "zoom-in": "Ingrandisci", - "zoom-out": "Rimpicciolisci", - "zoom-reset": "Reimposta ingrandimento", - "help": "Aiuto", - "feedback": "Commenti", - "report-a-bug": "Segnala un errore", - "request-a-feature": "Richiedi una funzionalità", - "about-kiwix": "Su Kiwix", - "donate-to-support-kiwix": "Dona per sostenere Kiwix", - "exit": "Esci", - "save-file-as-window-title": "Salva file come", - "download-finished-message": "Il documento è stato scaricato.", - "file": "File", - "edit": "Modifica", - "view": "Visualizza", - "tools": "Strumenti", - "window-title": "Libreria - Kiwix", - "search": "Ricerca", - "browse-directory": "Sfoglia la directory", - "settings": "Impostazioni", - "back": "Indietro", - "forward": "Inoltra", - "search-files": "Cerca nei file", - "title": "Titolo", - "size": "Dimensione", - "date": "Data", - "content-type": "Tipo di contenuto", - "reset-sort": "reimposta ordinamento", - "open": "Apri", - "delete": "Cancella", - "download": "Scarica", - "resume": "Riprendi", - "pause": "Pausa", - "cancel": "Annulla", - "apply": "Applica", - "port-for-local-kiwix-server-setting": "Porta per il server Kiwix locale", - "zoom-level-setting": "Livello di ingrandimento predefinito", - "download-directory-setting": "Cartella dati scaricati", - "monitor-directory-setting": "Monitora la directory", - "reset": "Ripristina", - "clear": "Pulisci", - "browse": "Esplora", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix ti consente di avere l'intera Wikipedia a portata di mano ovunque tu vada! Su una barca, in mezzo al nulla o in prigione, Kiwix ti dà accesso a tutta la conoscenza umana. Non hai bisogno di Internet, tutto è archiviato sul tuo computer.", - "about-learn-more-about-kiwix": "Ulteriori informazioni su Kiwix", - "about-privacy-policy": "Informativa sulla privacy", - "about-release-title": "Versione", - "about-source-code": "Questo software è rilasciato secondo i termini della GNU General Public License versione 3. Visualizza il codice sorgente su Github.", - "about-version": "Versione: {{VERSION}}", - "about-reporting-problem-title": "Segnala problemi", - "about-report-issue": "Per aprire un problema, visita Kiwix-desktop Issue Tracker e apri una segnalazione che descriva il tuo problema.", - "about-report-issue-2": "Menziona la versione nel problema.", - "about-libraries-title": "Biblioteche", - "kiwix-server-running-message": "Il server Kiwix è in esecuzione ed è possibile accedervi nella rete locale all'indirizzo:", - "kiwix-server-description": "L'avvio di un server consente ad altri computer nella rete locale di accedere alla tua libreria Kiwix con un browser web standard.", - "fullscreen-notification": "Ora sei in modalità a schermo intero. Premi ESC per uscire!", - "local-files": "File locali", - "hide": "Nascondi", - "open-in-browser": "Apri nel browser", - "start-kiwix-server": "Avvia server Kiwix", - "stop-kiwix-server": "Arresta il server Kiwix", - "all": "Tutto", - "ipv4": "Modalità solo IPv4", - "ipv6": "Modalità solo IPv6", - "fulltext-search": "Ricerca nell'intero testo", - "pictures": "Immagini", - "videos": "Video", - "ftindex": "Indice del testo completo", - "details": "Articolo completo", - "yes": "sì", - "no": "no", - "ok": "ok", - "no-filter": "Nessun filtro", - "open-link-in-web-browser": "Apri collegamento nel browser web", - "open-link-new-tab": "Apri collegamento in una nuova scheda", - "download-dir-dialog-title": "Sei sicuro di voler cambiare la directory di download?", - "download-dir-dialog-msg": "Il nuovo percorso della directory di download sarà:\n{{DIRECTORY}}", - "invalid-port": "Porta non valida", - "zim-open-fail-title": "File non valido", - "zim-open-fail-text": "Il file ZIM {{ZIM}} non può essere aperto correttamente. Verrà rimosso dalla tua libreria.", - "monitor-dir-dialog-title": "Sei sicuro di voler cambiare la directory di monitoraggio?", - "monitor-dir-dialog-msg": "Il nuovo percorso della directory del monitor sarà:\n{{DIRECTORY}}", - "monitor-directory-tooltip": "Tutti i file ZIM in questa directory verranno automaticamente aggiunti alla libreria.", - "next-tab": "Passa alla scheda successiva", - "previous-tab": "Passa alla scheda precedente", - "cancel-download": "Annulla download", - "cancel-download-text": "Sei sicuro di voler annullare il download di {{ZIM}} ?", - "delete-book": "Cancella libro", - "delete-book-text": "Sei sicuro di voler cancellare {{ZIM}} ?", - "download-storage-error-text": "Il sistema non dispone di memoria sufficiente.", - "download-unavailable-text": "Questo download non è disponibile.", - "save-or-open": "Salva o apri file" -} +{ + "@metadata": { + "authors": [ + "Ajeje Brazorf", + "Albano", + "Albe Albe460", + "Beta16", + "Gianfranco", + "IrsY", + "McDutchie", + "Scompo", + "Stefano" + ] + }, + "name": "italiano", + "error-title": "Errore", + "error-downloader-window-title": "Impossibile creare downloader", + "error-downloader-launch-message": "Impossibile avviare il downloader. Kiwix-desktop si avvierà, ma tutte le funzioni di download non funzioneranno.", + "error-launch-server-message": "Si è verificato un errore.", + "error-archive": "Impossibile ottenere l'archivio", + "error-opening-file": "Si è verificato un errore durante l'apertura del file.", + "open-zim": "Apri file ZIM", + "local-kiwix-server": "Server Kiwix locale", + "random-article": "Voce a caso", + "random-article-error": "Impossibile aprire un articolo casuale.", + "home-page": "Pagina principale", + "main-menu": "Menu principale", + "print": "Stampa", + "print-page-error": "Si è verificato un errore durante la stampa.", + "new-tab": "Nuova scheda", + "close-tab": "Chiudi scheda", + "close": "Chiudi", + "reopen-closed-tab": "Riapri scheda chiusa", + "browse-library": "Esplora libreria", + "open-file": "Apri file", + "save-file": "Salva file", + "open-recent": "Apri recenti", + "search-article": "Ricerca voce", + "search-in-library": "Cerca nella libreria", + "find-in-page": "Trova nella pagina", + "set-fullscreen": "Imposta a schermo intero", + "quit-fullscreen": "Esci da schermo intero", + "table-of-content": "Indice", + "add-bookmark": "Aggiungi all'elenco di lettura", + "remove-bookmark": "Rimuovi dall'elenco di lettura", + "reading-list": "Elenco di lettura", + "zoom-in": "Ingrandisci", + "zoom-out": "Rimpicciolisci", + "zoom-reset": "Reimposta ingrandimento", + "help": "Aiuto", + "feedback": "Commenti", + "report-a-bug": "Segnala un errore", + "request-a-feature": "Richiedi una funzionalità", + "about-kiwix": "Su Kiwix", + "donate-to-support-kiwix": "Dona per sostenere Kiwix", + "exit": "Esci", + "save-file-as-window-title": "Salva file come", + "download-finished-message": "Il documento è stato scaricato.", + "file": "File", + "edit": "Modifica", + "view": "Visualizza", + "tools": "Strumenti", + "window-title": "Libreria - Kiwix", + "search": "Ricerca", + "browse-directory": "Sfoglia la directory", + "settings": "Impostazioni", + "back": "Indietro", + "forward": "Inoltra", + "search-files": "Cerca nei file", + "title": "Titolo", + "size": "Dimensione", + "date": "Data", + "content-type": "Tipo di contenuto", + "reset-sort": "reimposta ordinamento", + "open": "Apri", + "delete": "Cancella", + "download": "Scarica", + "resume": "Riprendi", + "pause": "Pausa", + "cancel": "Annulla", + "apply": "Applica", + "port-for-local-kiwix-server-setting": "Porta per il server Kiwix locale", + "zoom-level-setting": "Livello di ingrandimento predefinito", + "download-directory-setting": "Cartella dati scaricati", + "monitor-directory-setting": "Monitora la directory", + "reset": "Ripristina", + "clear": "Pulisci", + "browse": "Esplora", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix ti consente di avere l'intera Wikipedia a portata di mano ovunque tu vada! Su una barca, in mezzo al nulla o in prigione, Kiwix ti dà accesso a tutta la conoscenza umana. Non hai bisogno di Internet, tutto è archiviato sul tuo computer.", + "about-learn-more-about-kiwix": "Ulteriori informazioni su Kiwix", + "about-privacy-policy": "Informativa sulla privacy", + "about-release-title": "Versione", + "about-source-code": "Questo software è rilasciato secondo i termini della GNU General Public License versione 3. Visualizza il codice sorgente su Github.", + "about-version": "Versione: {{VERSION}}", + "about-reporting-problem-title": "Segnala problemi", + "about-report-issue": "Per aprire un problema, visita Kiwix-desktop Issue Tracker e apri una segnalazione che descriva il tuo problema.", + "about-report-issue-2": "Menziona la versione nel problema.", + "about-libraries-title": "Biblioteche", + "kiwix-server-running-message": "Il server Kiwix è in esecuzione ed è possibile accedervi nella rete locale all'indirizzo:", + "kiwix-server-description": "L'avvio di un server consente ad altri computer nella rete locale di accedere alla tua libreria Kiwix con un browser web standard.", + "fullscreen-notification": "Ora sei in modalità a schermo intero. Premi ESC per uscire!", + "local-files": "File locali", + "hide": "Nascondi", + "open-in-browser": "Apri nel browser", + "start-kiwix-server": "Avvia server Kiwix", + "stop-kiwix-server": "Arresta il server Kiwix", + "all": "Tutto", + "ipv4": "Modalità solo IPv4", + "ipv6": "Modalità solo IPv6", + "fulltext-search": "Ricerca nell'intero testo", + "pictures": "Immagini", + "videos": "Video", + "ftindex": "Indice del testo completo", + "details": "Articolo completo", + "yes": "sì", + "no": "no", + "ok": "ok", + "no-filter": "Nessun filtro", + "open-link-in-web-browser": "Apri collegamento nel browser web", + "open-link-new-tab": "Apri collegamento in una nuova scheda", + "download-dir-dialog-title": "Sei sicuro di voler cambiare la directory di download?", + "download-dir-dialog-msg": "Il nuovo percorso della directory di download sarà:\n{{DIRECTORY}}", + "invalid-port": "Porta non valida", + "zim-open-fail-title": "File non valido", + "zim-open-fail-text": "Il file ZIM {{ZIM}} non può essere aperto correttamente. Verrà rimosso dalla tua libreria.", + "monitor-dir-dialog-title": "Sei sicuro di voler cambiare la directory di monitoraggio?", + "monitor-dir-dialog-msg": "Il nuovo percorso della directory del monitor sarà:\n{{DIRECTORY}}", + "monitor-directory-tooltip": "Tutti i file ZIM in questa directory verranno automaticamente aggiunti alla libreria.", + "next-tab": "Passa alla scheda successiva", + "previous-tab": "Passa alla scheda precedente", + "cancel-download": "Annulla download", + "cancel-download-text": "Sei sicuro di voler annullare il download di {{ZIM}} ?", + "delete-book": "Cancella libro", + "delete-book-text": "Sei sicuro di voler cancellare {{ZIM}} ?", + "download-storage-error-text": "Il sistema non dispone di memoria sufficiente.", + "download-unavailable-text": "Questo download non è disponibile.", + "save-or-open": "Salva o apri file" +} diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index c76600d38..5d7ace3ee 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -1,53 +1,53 @@ -{ - "@metadata": { - "authors": [ - "Chqaz", - "Funa-enpitu", - "LaMagiaaa", - "MathXplore", - "Mugenpman", - "Object*tofu", - "Omotecho", - "Shirayuki", - "Tmv", - "春春眠眠" - ] - }, - "name": "英語", - "error-title": "エラー", - "error-launch-server-message": "エラーが発生しました。", - "error-archive": "アーカイブを取得できない", - "open-zim": "ZIMファイルを開く", - "random-article": "おまかせ記事", - "print": "印刷", - "print-page-error": "印刷中にエラーが発生しました。", - "new-tab": "新しいタブ", - "close-tab": "タブを閉じる", - "search-article": "記事を検索", - "find-in-page": "ページ内検索", - "feedback": "フィードバック", - "file": "ファイル", - "edit": "編集", - "view": "閲覧", - "search": "検索", - "settings": "設定", - "back": "戻る", - "search-files": "ファイルを検索", - "title": "タイトル", - "size": "サイズ", - "date": "日付", - "open": "開く", - "delete": "削除", - "clear": "クリア", - "about-kiwix-desktop-description": "Kiwix を使えば、あなたがどこへ行くときでもウィキペディア全体を一緒に持ち歩くことができます!ボートの上でも、なんでもない場所のど真ん中でも、あるいは刑務所でも、Kiwix は全人類の知識へのアクセスを可能にします。インターネットは必要ありません、すべてはあなたのコンピューター上に保存されます。", - "about-reporting-problem-title": "問題を報告", - "hide": "非表示", - "open-in-browser": "ブラウザを開く", - "all": "すべて", - "open-link-in-web-browser": "Webブラウザでリンクを開く", - "invalid-port": "無効なポート", - "zim-open-fail-title": "無効なファイル", - "next-tab": "次のタブに移動", - "previous-tab": "前のタブに移動", - "cancel-download": "ダウンロードをキャンセル" -} +{ + "@metadata": { + "authors": [ + "Chqaz", + "Funa-enpitu", + "LaMagiaaa", + "MathXplore", + "Mugenpman", + "Object*tofu", + "Omotecho", + "Shirayuki", + "Tmv", + "春春眠眠" + ] + }, + "name": "英語", + "error-title": "エラー", + "error-launch-server-message": "エラーが発生しました。", + "error-archive": "アーカイブを取得できない", + "open-zim": "ZIMファイルを開く", + "random-article": "おまかせ記事", + "print": "印刷", + "print-page-error": "印刷中にエラーが発生しました。", + "new-tab": "新しいタブ", + "close-tab": "タブを閉じる", + "search-article": "記事を検索", + "find-in-page": "ページ内検索", + "feedback": "フィードバック", + "file": "ファイル", + "edit": "編集", + "view": "閲覧", + "search": "検索", + "settings": "設定", + "back": "戻る", + "search-files": "ファイルを検索", + "title": "タイトル", + "size": "サイズ", + "date": "日付", + "open": "開く", + "delete": "削除", + "clear": "クリア", + "about-kiwix-desktop-description": "Kiwix を使えば、あなたがどこへ行くときでもウィキペディア全体を一緒に持ち歩くことができます!ボートの上でも、なんでもない場所のど真ん中でも、あるいは刑務所でも、Kiwix は全人類の知識へのアクセスを可能にします。インターネットは必要ありません、すべてはあなたのコンピューター上に保存されます。", + "about-reporting-problem-title": "問題を報告", + "hide": "非表示", + "open-in-browser": "ブラウザを開く", + "all": "すべて", + "open-link-in-web-browser": "Webブラウザでリンクを開く", + "invalid-port": "無効なポート", + "zim-open-fail-title": "無効なファイル", + "next-tab": "次のタブに移動", + "previous-tab": "前のタブに移動", + "cancel-download": "ダウンロードをキャンセル" +} diff --git a/resources/i18n/ko.json b/resources/i18n/ko.json index 17cee1eb9..64d4bdb1e 100644 --- a/resources/i18n/ko.json +++ b/resources/i18n/ko.json @@ -1,198 +1,198 @@ -{ - "@metadata": { - "authors": [ - "Apzp79", - "Codenstory", - "Fitoschido", - "McDutchie", - "Nuevo Paso", - "Suleiman the Magnificent Television", - "YeBoy371", - "Ykhwong", - "그냥기여자" - ] - }, - "name": "한국어", - "error-title": "오류", - "error-downloader-window-title": "다운로더를 만들 수 없습니다", - "error-downloader-launch-message": "다운로더를 시작할 수 없습니다. Kiwix-desktop이 시작되더라도 모든 다운로드 기능이 작동하지 않을 것입니다!", - "error-launch-server-message": "오류가 발생했습니다!", - "error-archive": "아카이브를 가져올 수 없습니다", - "error-opening-file": "파일을 여는 중 오류가 발생했습니다.", - "open-zim": "ZIM 파일 열기", - "local-kiwix-server": "로컬 Kiwix 서버", - "random-article": "임의의 문서", - "random-article-error": "임의의 문서 열기를 실패했습니다.", - "home-page": "홈페이지", - "main-menu": "주 메뉴", - "print": "인쇄", - "print-page-error": "인쇄 중에 오류가 발생했습니다.", - "new-tab": "새 탭", - "close-tab": "탭 닫기", - "close": "닫기", - "reopen-closed-tab": "닫힌 탭 다시 열기", - "browse-library": "라이브러리 찾아보기", - "open-file": "파일 열기", - "save-file": "파일 저장", - "open-recent": "최근 항목 열기", - "search-article": "문서 검색", - "search-in-library": "라이브러리에서 검색", - "find-in-page": "문서에서 찾기", - "set-fullscreen": "전체 화면 설정", - "quit-fullscreen": "전체 화면에서 나가기", - "table-of-content": "목차", - "add-bookmark": "읽기 목록에 추가", - "remove-bookmark": "읽기 목록에서 제거", - "reading-list": "읽기 목록", - "zoom-in": "확대", - "zoom-out": "축소", - "zoom-reset": "확대/축소 재설정", - "help": "도움말", - "feedback": "피드백", - "report-a-bug": "버그 보고", - "request-a-feature": "기능 요청", - "about-kiwix": "Kiwix 정보", - "donate-to-support-kiwix": "Kiwix 후원하기", - "exit": "나가기", - "save-file-as-window-title": "다른 이름으로 파일 저장", - "download-finished": "다운로드 완료", - "download-finished-message": "문서가 다운로드되었습니다.", - "file": "파일", - "edit": "편집", - "view": "보기", - "tools": "도구", - "window-title": "라이브러리 - Kiwix", - "search": "검색", - "browse-directory": "디렉터리 찾아보기", - "settings": "설정", - "back": "뒤로", - "forward": "앞으로", - "search-files": "파일 검색", - "title": "제목", - "size": "크기", - "date": "날짜", - "content-type": "콘텐츠 유형", - "reset-sort": "정렬 초기화", - "open": "열기", - "delete": "삭제", - "download": "다운로드", - "resume": "계속하기", - "pause": "일시 정지", - "cancel": "취소", - "apply": "적용", - "port-for-local-kiwix-server-setting": "로컬 Kiwix 서버 포트", - "zoom-level-setting": "기본 확대/축소 수준", - "download-directory-setting": "다운로드 디렉터리", - "monitor-directory-setting": "모니터 디렉터리", - "reset": "재설정", - "clear": "비우기", - "browse": "찾아보기", - "about-kiwix-desktop-title": "Kiwix 데스크톱", - "about-kiwix-desktop-description": "Kiwix를 사용하면 어느 곳에 있든지 위키백과를 사용할 수 있습니다! 보트 위에서도, 인적이 드문 곳, 심지어 감옥에서도 Kiwix는 인류의 모든 지식에 접근하게 해 줍니다. 인터넷이 필요 없고 모든 것이 컴퓨터에 저장됩니다.", - "about-learn-more-about-kiwix": "Kiwix에 관해 더 알아보기", - "about-privacy-policy": "개인정보보호정책", - "about-release-title": "릴리스", - "about-source-code": "이 소프트웨어는 GNU GPLv3의 조항에 따라 출시됩니다. 소스 코드는 깃허브에서 볼 수 있습니다.", - "about-version": "버전: {{VERSION}}", - "about-reporting-problem-title": "문제점 보고", - "about-report-issue": "이슈를 열려면 Kiwix-desktop 이슈 트래커를 방문하고 이슈를 열어서 문제를 기술하십시오.", - "about-report-issue-2": "이슈에 버전을 언급해 주십시오.", - "about-libraries-title": "라이브러리", - "kiwix-server-running-message": "Kiwix 서버가 실행 중이며 다음의 로컬 네트워크에 접근이 가능합니다:", - "kiwix-server-description": "서버를 시작하면 로컬 네트워크의 다른 컴퓨터가 표준 웹 브라우저를 사용하여 Kiwix 라이브러리에 접근할 수 있습니다.", - "fullscreen-notification": "현재 전체 화면 모드입니다. 빠져나가려면 ESC를 누르십시오!", - "online-files": "온라인 파일", - "local-files": "로컬 파일", - "category": "분류", - "language": "언어", - "hide": "숨기기", - "open-in-browser": "브라우저에서 열기", - "start-kiwix-server": "Kiwix 서버 시작", - "stop-kiwix-server": "Kiwix 서버 중지", - "all": "모두", - "all_ips": "모두 - 듀얼 스택 모드", - "ipv4": "IPv4 전용 모드", - "ipv6": "IPv6 전용 모드", - "fulltext-search": "본문 검색", - "pictures": "사진", - "videos": "동영상", - "ftindex": "전문 색인", - "details": "모든 문서", - "yes": "예", - "no": "아니오", - "ok": "확인", - "no-filter": "필터 없음", - "open-link-in-web-browser": "웹 브라우저로 링크 열기", - "open-link-new-tab": "새 탭에서 링크 열기", - "download-dir-dialog-title": "다운로드 디렉터리를 변경하시겠습니까?", - "download-dir-dialog-msg": "새 다운로드 디렉터리 경로는 다음과 같습니다:\n{{DIRECTORY}}", - "invalid-port": "유효하지 않은 포트", - "zim-open-fail-title": "잘못된 파일", - "zim-open-fail-text": "{{ZIM}} ZIM 파일을 적절하게 삭제할 수 없습니다. 내 라이브러리에서 제거됩니다.", - "monitor-dir-dialog-title": "모니터 디렉터리를 변경하시겠습니까?", - "monitor-dir-dialog-msg": "새 모니터 디렉터리는 다음과 같습니다: {{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "모니터 디렉터리를 비우시겠습니까?", - "path-was-copied": "경로가 복사되었습니다", - "monitor-clear-dir-dialog-msg": "새 ZIM 파일의 모니터 디렉터리의 확인을 중단합니다.", - "monitor-directory-tooltip": "이 디렉터리의 모든 ZIM 파일은 자동으로 라이브러리에 추가됩니다.", - "next-tab": "다음 탭으로 이동", - "previous-tab": "이전 탭으로 이동", - "cancel-download": "다운로드 취소", - "cancel-download-text": "{{ZIM}}의 다운로드를 취소하시겠습니까?", - "delete-book": "책 삭제", - "delete-book-text": "{{ZIM}} 파일을 삭제하시겠습니까?", - "download-storage-error": "저장 오류", - "download-storage-error-text": "시스템에 사용 가능한 저장공간이 부족합니다.", - "download-exceeds-max-file-size": "다운로드 크기가 대상 파일 시스템에서 지원하는 최대 파일 크기를 초과했습니다.", - "download-dir-missing": "다운로드 디렉터리가 존재하지 않습니다.", - "download-dir-not-writable": "다운로드 디렉터리에 쓸 수 없습니다.", - "download-unavailable": "다운로드 불가", - "download-unavailable-text": "이 다운로드는 이용이 불가능합니다.", - "open-book": "책 열기", - "download-book": "책 다운로드", - "pause-download": "다운로드 일시 중지", - "resume-download": "이어서 다운로드", - "open-folder": "폴더 열기", - "couldnt-open-location": "위치를 열 수 없습니다", - "couldnt-open-location-text": "Kiwix는 {{FOLDER}} 폴더를 열 수 없습니다", - "move-files-to-trash": "삭제된 파일을 휴지통으로 이동", - "move-files-to-trash-text": "이 작업을 수행하면 파일이 휴지통으로 이동됩니다.", - "perma-delete-files-text": "이 작업을 수행하면 파일이 영구적으로 삭제됩니다.", - "clear-filter": "현재 설정된 필터 지우기", - "language-searcher-placeholder": "언어별 필터", - "category-searcher-placeholder": "분류별 필터", - "content-type-searcher-placeholder": "콘텐츠 유형별 필터", - "no-details": "소개만", - "no-pictures": "사진 없음", - "no-videos": "동영상 없음", - "open-previous-tabs-at-startup": "시작 시 이전 탭 열기", - "preview-book-in-web-browser": "웹 브라우저에서 책 미리 보기", - "file-not-found-title": "ZIM 파일이 없습니다", - "file-not-found-text": "ZIM 파일이 존재하지 않거나 읽을 수 없습니다", - "zim-id": "ZIM Id", - "zim-name": "ZIM 이름", - "zim-path": "ZIM 파일 경로", - "bad-zim-file-error-page-title": "유효하지 않은 ZIM 파일", - "bad-zim-file-error-page-text": "ZIM 파일은 유효하지 않거나 손상되었습니다.", - "zim-entry-path": "ZIM 엔트리 경로", - "export-reading-list": "읽기 목록 내보내기", - "export-reading-list-error": "읽기 목록을 내보내는 동안 오류가 발생했습니다.", - "import-reading-list": "읽기 목록 가져오기", - "import-reading-list-error": "읽기 목록을 가져오는 동안 오류가 발생했습니다.", - "disable-sandbox": "Kiwix가 파일서버에서 시작. 이는 검색의 샌드박스에 호환문제를 일으키는 것으로 알려짐. 따라서 샌드박스는 비활성화될 것임. 계속하기?", - "save-page-as": "다른 이름으로 저장...", - "portable-disabled-tooltip": "포터블 모드에서 기능이 비활성화됩니다", - "scroll-next-tab": "다음 탭으로 스크롤", - "scroll-previous-tab": "이전 탭으로 스크롤", - "kiwix-search": "Kiwix 검색", - "search-options": "검색 옵션", - "read-article": "문서 읽기", - "read-text": "선택한 텍스트 읽기", - "read-stop": "읽기 중지", - "stop": "중지", - "voice": "음성", - "select-read-voice": "읽기 음성 선택", - "select-read-language": "읽기 언어 선택", - "save-or-open": "파일 저장 또는 열기", - "save-or-open-text": "Kiwix는 이 파일을 어떻게 처리해야 하나요?" -} +{ + "@metadata": { + "authors": [ + "Apzp79", + "Codenstory", + "Fitoschido", + "McDutchie", + "Nuevo Paso", + "Suleiman the Magnificent Television", + "YeBoy371", + "Ykhwong", + "그냥기여자" + ] + }, + "name": "한국어", + "error-title": "오류", + "error-downloader-window-title": "다운로더를 만들 수 없습니다", + "error-downloader-launch-message": "다운로더를 시작할 수 없습니다. Kiwix-desktop이 시작되더라도 모든 다운로드 기능이 작동하지 않을 것입니다!", + "error-launch-server-message": "오류가 발생했습니다!", + "error-archive": "아카이브를 가져올 수 없습니다", + "error-opening-file": "파일을 여는 중 오류가 발생했습니다.", + "open-zim": "ZIM 파일 열기", + "local-kiwix-server": "로컬 Kiwix 서버", + "random-article": "임의의 문서", + "random-article-error": "임의의 문서 열기를 실패했습니다.", + "home-page": "홈페이지", + "main-menu": "주 메뉴", + "print": "인쇄", + "print-page-error": "인쇄 중에 오류가 발생했습니다.", + "new-tab": "새 탭", + "close-tab": "탭 닫기", + "close": "닫기", + "reopen-closed-tab": "닫힌 탭 다시 열기", + "browse-library": "라이브러리 찾아보기", + "open-file": "파일 열기", + "save-file": "파일 저장", + "open-recent": "최근 항목 열기", + "search-article": "문서 검색", + "search-in-library": "라이브러리에서 검색", + "find-in-page": "문서에서 찾기", + "set-fullscreen": "전체 화면 설정", + "quit-fullscreen": "전체 화면에서 나가기", + "table-of-content": "목차", + "add-bookmark": "읽기 목록에 추가", + "remove-bookmark": "읽기 목록에서 제거", + "reading-list": "읽기 목록", + "zoom-in": "확대", + "zoom-out": "축소", + "zoom-reset": "확대/축소 재설정", + "help": "도움말", + "feedback": "피드백", + "report-a-bug": "버그 보고", + "request-a-feature": "기능 요청", + "about-kiwix": "Kiwix 정보", + "donate-to-support-kiwix": "Kiwix 후원하기", + "exit": "나가기", + "save-file-as-window-title": "다른 이름으로 파일 저장", + "download-finished": "다운로드 완료", + "download-finished-message": "문서가 다운로드되었습니다.", + "file": "파일", + "edit": "편집", + "view": "보기", + "tools": "도구", + "window-title": "라이브러리 - Kiwix", + "search": "검색", + "browse-directory": "디렉터리 찾아보기", + "settings": "설정", + "back": "뒤로", + "forward": "앞으로", + "search-files": "파일 검색", + "title": "제목", + "size": "크기", + "date": "날짜", + "content-type": "콘텐츠 유형", + "reset-sort": "정렬 초기화", + "open": "열기", + "delete": "삭제", + "download": "다운로드", + "resume": "계속하기", + "pause": "일시 정지", + "cancel": "취소", + "apply": "적용", + "port-for-local-kiwix-server-setting": "로컬 Kiwix 서버 포트", + "zoom-level-setting": "기본 확대/축소 수준", + "download-directory-setting": "다운로드 디렉터리", + "monitor-directory-setting": "모니터 디렉터리", + "reset": "재설정", + "clear": "비우기", + "browse": "찾아보기", + "about-kiwix-desktop-title": "Kiwix 데스크톱", + "about-kiwix-desktop-description": "Kiwix를 사용하면 어느 곳에 있든지 위키백과를 사용할 수 있습니다! 보트 위에서도, 인적이 드문 곳, 심지어 감옥에서도 Kiwix는 인류의 모든 지식에 접근하게 해 줍니다. 인터넷이 필요 없고 모든 것이 컴퓨터에 저장됩니다.", + "about-learn-more-about-kiwix": "Kiwix에 관해 더 알아보기", + "about-privacy-policy": "개인정보보호정책", + "about-release-title": "릴리스", + "about-source-code": "이 소프트웨어는 GNU GPLv3의 조항에 따라 출시됩니다. 소스 코드는 깃허브에서 볼 수 있습니다.", + "about-version": "버전: {{VERSION}}", + "about-reporting-problem-title": "문제점 보고", + "about-report-issue": "이슈를 열려면 Kiwix-desktop 이슈 트래커를 방문하고 이슈를 열어서 문제를 기술하십시오.", + "about-report-issue-2": "이슈에 버전을 언급해 주십시오.", + "about-libraries-title": "라이브러리", + "kiwix-server-running-message": "Kiwix 서버가 실행 중이며 다음의 로컬 네트워크에 접근이 가능합니다:", + "kiwix-server-description": "서버를 시작하면 로컬 네트워크의 다른 컴퓨터가 표준 웹 브라우저를 사용하여 Kiwix 라이브러리에 접근할 수 있습니다.", + "fullscreen-notification": "현재 전체 화면 모드입니다. 빠져나가려면 ESC를 누르십시오!", + "online-files": "온라인 파일", + "local-files": "로컬 파일", + "category": "분류", + "language": "언어", + "hide": "숨기기", + "open-in-browser": "브라우저에서 열기", + "start-kiwix-server": "Kiwix 서버 시작", + "stop-kiwix-server": "Kiwix 서버 중지", + "all": "모두", + "all_ips": "모두 - 듀얼 스택 모드", + "ipv4": "IPv4 전용 모드", + "ipv6": "IPv6 전용 모드", + "fulltext-search": "본문 검색", + "pictures": "사진", + "videos": "동영상", + "ftindex": "전문 색인", + "details": "모든 문서", + "yes": "예", + "no": "아니오", + "ok": "확인", + "no-filter": "필터 없음", + "open-link-in-web-browser": "웹 브라우저로 링크 열기", + "open-link-new-tab": "새 탭에서 링크 열기", + "download-dir-dialog-title": "다운로드 디렉터리를 변경하시겠습니까?", + "download-dir-dialog-msg": "새 다운로드 디렉터리 경로는 다음과 같습니다:\n{{DIRECTORY}}", + "invalid-port": "유효하지 않은 포트", + "zim-open-fail-title": "잘못된 파일", + "zim-open-fail-text": "{{ZIM}} ZIM 파일을 적절하게 삭제할 수 없습니다. 내 라이브러리에서 제거됩니다.", + "monitor-dir-dialog-title": "모니터 디렉터리를 변경하시겠습니까?", + "monitor-dir-dialog-msg": "새 모니터 디렉터리는 다음과 같습니다: {{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "모니터 디렉터리를 비우시겠습니까?", + "path-was-copied": "경로가 복사되었습니다", + "monitor-clear-dir-dialog-msg": "새 ZIM 파일의 모니터 디렉터리의 확인을 중단합니다.", + "monitor-directory-tooltip": "이 디렉터리의 모든 ZIM 파일은 자동으로 라이브러리에 추가됩니다.", + "next-tab": "다음 탭으로 이동", + "previous-tab": "이전 탭으로 이동", + "cancel-download": "다운로드 취소", + "cancel-download-text": "{{ZIM}}의 다운로드를 취소하시겠습니까?", + "delete-book": "책 삭제", + "delete-book-text": "{{ZIM}} 파일을 삭제하시겠습니까?", + "download-storage-error": "저장 오류", + "download-storage-error-text": "시스템에 사용 가능한 저장공간이 부족합니다.", + "download-exceeds-max-file-size": "다운로드 크기가 대상 파일 시스템에서 지원하는 최대 파일 크기를 초과했습니다.", + "download-dir-missing": "다운로드 디렉터리가 존재하지 않습니다.", + "download-dir-not-writable": "다운로드 디렉터리에 쓸 수 없습니다.", + "download-unavailable": "다운로드 불가", + "download-unavailable-text": "이 다운로드는 이용이 불가능합니다.", + "open-book": "책 열기", + "download-book": "책 다운로드", + "pause-download": "다운로드 일시 중지", + "resume-download": "이어서 다운로드", + "open-folder": "폴더 열기", + "couldnt-open-location": "위치를 열 수 없습니다", + "couldnt-open-location-text": "Kiwix는 {{FOLDER}} 폴더를 열 수 없습니다", + "move-files-to-trash": "삭제된 파일을 휴지통으로 이동", + "move-files-to-trash-text": "이 작업을 수행하면 파일이 휴지통으로 이동됩니다.", + "perma-delete-files-text": "이 작업을 수행하면 파일이 영구적으로 삭제됩니다.", + "clear-filter": "현재 설정된 필터 지우기", + "language-searcher-placeholder": "언어별 필터", + "category-searcher-placeholder": "분류별 필터", + "content-type-searcher-placeholder": "콘텐츠 유형별 필터", + "no-details": "소개만", + "no-pictures": "사진 없음", + "no-videos": "동영상 없음", + "open-previous-tabs-at-startup": "시작 시 이전 탭 열기", + "preview-book-in-web-browser": "웹 브라우저에서 책 미리 보기", + "file-not-found-title": "ZIM 파일이 없습니다", + "file-not-found-text": "ZIM 파일이 존재하지 않거나 읽을 수 없습니다", + "zim-id": "ZIM Id", + "zim-name": "ZIM 이름", + "zim-path": "ZIM 파일 경로", + "bad-zim-file-error-page-title": "유효하지 않은 ZIM 파일", + "bad-zim-file-error-page-text": "ZIM 파일은 유효하지 않거나 손상되었습니다.", + "zim-entry-path": "ZIM 엔트리 경로", + "export-reading-list": "읽기 목록 내보내기", + "export-reading-list-error": "읽기 목록을 내보내는 동안 오류가 발생했습니다.", + "import-reading-list": "읽기 목록 가져오기", + "import-reading-list-error": "읽기 목록을 가져오는 동안 오류가 발생했습니다.", + "disable-sandbox": "Kiwix가 파일서버에서 시작. 이는 검색의 샌드박스에 호환문제를 일으키는 것으로 알려짐. 따라서 샌드박스는 비활성화될 것임. 계속하기?", + "save-page-as": "다른 이름으로 저장...", + "portable-disabled-tooltip": "포터블 모드에서 기능이 비활성화됩니다", + "scroll-next-tab": "다음 탭으로 스크롤", + "scroll-previous-tab": "이전 탭으로 스크롤", + "kiwix-search": "Kiwix 검색", + "search-options": "검색 옵션", + "read-article": "문서 읽기", + "read-text": "선택한 텍스트 읽기", + "read-stop": "읽기 중지", + "stop": "중지", + "voice": "음성", + "select-read-voice": "읽기 음성 선택", + "select-read-language": "읽기 언어 선택", + "save-or-open": "파일 저장 또는 열기", + "save-or-open-text": "Kiwix는 이 파일을 어떻게 처리해야 하나요?" +} diff --git a/resources/i18n/ku-latn.json b/resources/i18n/ku-latn.json index 099e8d4c6..67e9e45f2 100644 --- a/resources/i18n/ku-latn.json +++ b/resources/i18n/ku-latn.json @@ -1,118 +1,118 @@ -{ - "@metadata": { - "authors": [ - "Balyozxane", - "Bikarhêner" - ] - }, - "name": "Kurdî", - "error-title": "Çewtî", - "error-downloader-window-title": "Nikare daxînerê çêke", - "error-downloader-launch-message": "Nemimkun e ku daxînerê veke, wê Kîwîks-sermase dest pê bike lê wê hemû fonksiyonên daxistinê nexebitin!", - "error-launch-server-message": "Çewtiyekê rû da!", - "error-archive": "Arşîvê nikare bîne", - "open-zim": "Dosyeya ZIMê Veke", - "local-kiwix-server": "Servera Kiwixê ya Herêmî", - "random-article": "Gotara Ketober", - "random-article-error": "Bi ser neket ku gotareke ketober veke.", - "home-page": "Serûpel", - "main-menu": "Menuya esasî", - "print": "Çap bike", - "print-page-error": "Çewtiyeke qewimî gava çapkirinê.", - "new-tab": "Hilpekîna nû", - "close-tab": "Hilpekînê bigire", - "close": "Bigire", - "reopen-closed-tab": "Hilpekîna girtî ji nû ve veke", - "browse-library": "Çavê xwe li kitêbxaneyê bigerîne", - "open-file": "Dosyeyê veke", - "open-recent": "Dosyeyên dawîn veke", - "search-article": "Li gotarê bigere", - "search-in-library": "Li kitêbxaneyê bigere", - "find-in-page": "Di nav rûpelê de bibîne", - "set-fullscreen": "Ekrana dagirtî mîheng bike", - "quit-fullscreen": "Ji ekrana dagirtî derkeve", - "table-of-content": "Tabloya naverokê", - "reading-list": "Lîsteya xwendinê", - "zoom-in": "Nêzîk bike", - "zoom-out": "Dûr bike", - "zoom-reset": "Nêzîkkirinê reset bike", - "help": "Alîkarî", - "feedback": "Paşragihan", - "report-a-bug": "Çewtiyeke bişîne", - "request-a-feature": "Taybetmendiyekê bixwaze", - "about-kiwix": "Derbarê Kiwixê de", - "donate-to-support-kiwix": "Ji bo piştgirîkirinê bexş bike", - "exit": "Derkeve", - "save-file-as-window-title": "Dosyeyê qeyd bike wek", - "download-finished-message": "Dokûment hate daxistin.", - "file": "Dosye", - "edit": "Biguherîne", - "view": "Bibîne", - "tools": "Amûr", - "window-title": "Kitêbxane - Kiwix", - "search": "Lê bigere", - "browse-directory": "Çav bi pelrêçê bixe", - "settings": "Mîheng", - "back": "Vegere", - "forward": "Bişîne", - "search-files": "Li dosyeyan bigere", - "title": "Sernav", - "size": "Mezinahî", - "date": "Dîrok", - "content-type": "Cureyê naverokê", - "reset-sort": "rêzkirinê sifir bike", - "open": "Veke", - "delete": "Jê bibe", - "download": "Daxîne", - "resume": "Dewam bike", - "pause": "Rawestîne", - "cancel": "Betal bike", - "apply": "Bisepîne", - "port-for-local-kiwix-server-setting": "Nuqteya girêdanê ji bo servera Kiwixê yê lokal", - "zoom-level-setting": "Dereceya nêzîkkirinê", - "download-directory-setting": "Pelrêçê daxîne", - "monitor-directory-setting": "Pelrêça monîtorê", - "reset": "Sifir bike", - "clear": "Paqij bike", - "browse": "Çav pê bixe", - "about-kiwix-desktop-title": "Kiwix Sermase", - "about-kiwix-desktop-description": "Kiwix îmkan dide mirov ku mirov here ku jî temamê Wîkîpediyayê di destê mirov de be! Li ser qayikekê, di nîvê cihekî de an di hepsê de, Kiwix rê dide ku xwe bigihînî temamiya zanînên însanan. Ihtîyaciya te bi înternetê tine ye, her tişt li ser kombersa te tê depokirin.", - "about-learn-more-about-kiwix": "Zêdetir hîn bibe derbarê Kiwixê de", - "about-release-title": "Versiyon", - "about-source-code": "Nivîsbarî di bin banê şertên Lîsansa Giştî ya GNUyê versiyon 3 de hate belavkirin. Koda çavkanî bibîne li ser Githubê.", - "about-version": "Versiyon: {{VERSION}}", - "about-reporting-problem-title": "Raporkirina probleman", - "about-report-issue": "Ji bo vekirina pirsgirêkekî, serdana Kiwix-sermase şopgera probleman bike û pirsgirêka ku problema te îzah dike veke.", - "about-report-issue-2": "Ji kerema xwe di pirsgirêkê de behsa versiyonê jî bike", - "about-libraries-title": "Kitêbxane", - "kiwix-server-running-message": "Servera Kiwixê dixebite û dikare were gihîn di tora lokal de li:", - "kiwix-server-description": "Destpêkirina serverekê rê dide kombersên din yên di tora lokal de ku bi gerokeke webê ya standard re xwe bigihînin kitêbxaneya te ya Kiwixê.", - "fullscreen-notification": "Tu vêga di moda ekrana dagirtî de yî. Ji bo derketinê pêlî ESC-ê bike!", - "local-files": "Dosyeyên Lokal", - "hide": "Veşêre", - "open-in-browser": "Di gerokê de veke", - "start-kiwix-server": "Servera Kiwixê Bide Destpêkirin", - "stop-kiwix-server": "Servera Kiwixê Bide Sekinandin", - "all": "Hemû", - "fulltext-search": "Lêgerîna tam-nivîsê", - "pictures": "Wêne", - "videos": "Vîdeo", - "ftindex": "Îndeksa tam-nivîsî", - "details": "Gotara temamî", - "yes": "erê", - "no": "na", - "no-filter": "bêparzûn", - "open-link-in-web-browser": "Lînkê di geroka webê de veke", - "download-dir-dialog-title": "Tu jê piştrast î ku tu dixwazî pelrêça daxistinê biguherînî?", - "download-dir-dialog-msg": "Pelrêça daxistinê yê nû wê bibe:\n{{DIRECTORY}}", - "invalid-port": "Nuqteya girêdanê yê nederbasdar", - "zim-open-fail-title": "Dosyeya nederbasdar", - "zim-open-fail-text": "Dosyeya ZIMê ya {{ZIM}} nikare bi awayekî rast were vekirin. Ew wê ji kitêbxaneya te were rakirin.", - "monitor-dir-dialog-title": "Tu emîn î ku tu dixwazî pelrêça monîtorê biguherînî?", - "monitor-dir-dialog-msg": "Pelrêça monîtorê yê nû wê bibe:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Tu jê piştrast î ku tu dixwazî pelrêça monîtorê paqij bikî?", - "monitor-clear-dir-dialog-msg": "Ev wê kontrolkirina pelrêça monîtorê ji bo dosyeyên nû yên ZIMê bisekinîne.", - "monitor-directory-tooltip": "Temamê dosyeyên ZIMê yên di vê pelrêçê de wê bi otomatîkî tevlî kitêbxaneyê bên kirin.", - "next-tab": "Derbasî hilpekîna piştî bibe", - "previous-tab": "Derbasî hilpekîna berî bibe" -} +{ + "@metadata": { + "authors": [ + "Balyozxane", + "Bikarhêner" + ] + }, + "name": "Kurdî", + "error-title": "Çewtî", + "error-downloader-window-title": "Nikare daxînerê çêke", + "error-downloader-launch-message": "Nemimkun e ku daxînerê veke, wê Kîwîks-sermase dest pê bike lê wê hemû fonksiyonên daxistinê nexebitin!", + "error-launch-server-message": "Çewtiyekê rû da!", + "error-archive": "Arşîvê nikare bîne", + "open-zim": "Dosyeya ZIMê Veke", + "local-kiwix-server": "Servera Kiwixê ya Herêmî", + "random-article": "Gotara Ketober", + "random-article-error": "Bi ser neket ku gotareke ketober veke.", + "home-page": "Serûpel", + "main-menu": "Menuya esasî", + "print": "Çap bike", + "print-page-error": "Çewtiyeke qewimî gava çapkirinê.", + "new-tab": "Hilpekîna nû", + "close-tab": "Hilpekînê bigire", + "close": "Bigire", + "reopen-closed-tab": "Hilpekîna girtî ji nû ve veke", + "browse-library": "Çavê xwe li kitêbxaneyê bigerîne", + "open-file": "Dosyeyê veke", + "open-recent": "Dosyeyên dawîn veke", + "search-article": "Li gotarê bigere", + "search-in-library": "Li kitêbxaneyê bigere", + "find-in-page": "Di nav rûpelê de bibîne", + "set-fullscreen": "Ekrana dagirtî mîheng bike", + "quit-fullscreen": "Ji ekrana dagirtî derkeve", + "table-of-content": "Tabloya naverokê", + "reading-list": "Lîsteya xwendinê", + "zoom-in": "Nêzîk bike", + "zoom-out": "Dûr bike", + "zoom-reset": "Nêzîkkirinê reset bike", + "help": "Alîkarî", + "feedback": "Paşragihan", + "report-a-bug": "Çewtiyeke bişîne", + "request-a-feature": "Taybetmendiyekê bixwaze", + "about-kiwix": "Derbarê Kiwixê de", + "donate-to-support-kiwix": "Ji bo piştgirîkirinê bexş bike", + "exit": "Derkeve", + "save-file-as-window-title": "Dosyeyê qeyd bike wek", + "download-finished-message": "Dokûment hate daxistin.", + "file": "Dosye", + "edit": "Biguherîne", + "view": "Bibîne", + "tools": "Amûr", + "window-title": "Kitêbxane - Kiwix", + "search": "Lê bigere", + "browse-directory": "Çav bi pelrêçê bixe", + "settings": "Mîheng", + "back": "Vegere", + "forward": "Bişîne", + "search-files": "Li dosyeyan bigere", + "title": "Sernav", + "size": "Mezinahî", + "date": "Dîrok", + "content-type": "Cureyê naverokê", + "reset-sort": "rêzkirinê sifir bike", + "open": "Veke", + "delete": "Jê bibe", + "download": "Daxîne", + "resume": "Dewam bike", + "pause": "Rawestîne", + "cancel": "Betal bike", + "apply": "Bisepîne", + "port-for-local-kiwix-server-setting": "Nuqteya girêdanê ji bo servera Kiwixê yê lokal", + "zoom-level-setting": "Dereceya nêzîkkirinê", + "download-directory-setting": "Pelrêçê daxîne", + "monitor-directory-setting": "Pelrêça monîtorê", + "reset": "Sifir bike", + "clear": "Paqij bike", + "browse": "Çav pê bixe", + "about-kiwix-desktop-title": "Kiwix Sermase", + "about-kiwix-desktop-description": "Kiwix îmkan dide mirov ku mirov here ku jî temamê Wîkîpediyayê di destê mirov de be! Li ser qayikekê, di nîvê cihekî de an di hepsê de, Kiwix rê dide ku xwe bigihînî temamiya zanînên însanan. Ihtîyaciya te bi înternetê tine ye, her tişt li ser kombersa te tê depokirin.", + "about-learn-more-about-kiwix": "Zêdetir hîn bibe derbarê Kiwixê de", + "about-release-title": "Versiyon", + "about-source-code": "Nivîsbarî di bin banê şertên Lîsansa Giştî ya GNUyê versiyon 3 de hate belavkirin. Koda çavkanî bibîne li ser Githubê.", + "about-version": "Versiyon: {{VERSION}}", + "about-reporting-problem-title": "Raporkirina probleman", + "about-report-issue": "Ji bo vekirina pirsgirêkekî, serdana Kiwix-sermase şopgera probleman bike û pirsgirêka ku problema te îzah dike veke.", + "about-report-issue-2": "Ji kerema xwe di pirsgirêkê de behsa versiyonê jî bike", + "about-libraries-title": "Kitêbxane", + "kiwix-server-running-message": "Servera Kiwixê dixebite û dikare were gihîn di tora lokal de li:", + "kiwix-server-description": "Destpêkirina serverekê rê dide kombersên din yên di tora lokal de ku bi gerokeke webê ya standard re xwe bigihînin kitêbxaneya te ya Kiwixê.", + "fullscreen-notification": "Tu vêga di moda ekrana dagirtî de yî. Ji bo derketinê pêlî ESC-ê bike!", + "local-files": "Dosyeyên Lokal", + "hide": "Veşêre", + "open-in-browser": "Di gerokê de veke", + "start-kiwix-server": "Servera Kiwixê Bide Destpêkirin", + "stop-kiwix-server": "Servera Kiwixê Bide Sekinandin", + "all": "Hemû", + "fulltext-search": "Lêgerîna tam-nivîsê", + "pictures": "Wêne", + "videos": "Vîdeo", + "ftindex": "Îndeksa tam-nivîsî", + "details": "Gotara temamî", + "yes": "erê", + "no": "na", + "no-filter": "bêparzûn", + "open-link-in-web-browser": "Lînkê di geroka webê de veke", + "download-dir-dialog-title": "Tu jê piştrast î ku tu dixwazî pelrêça daxistinê biguherînî?", + "download-dir-dialog-msg": "Pelrêça daxistinê yê nû wê bibe:\n{{DIRECTORY}}", + "invalid-port": "Nuqteya girêdanê yê nederbasdar", + "zim-open-fail-title": "Dosyeya nederbasdar", + "zim-open-fail-text": "Dosyeya ZIMê ya {{ZIM}} nikare bi awayekî rast were vekirin. Ew wê ji kitêbxaneya te were rakirin.", + "monitor-dir-dialog-title": "Tu emîn î ku tu dixwazî pelrêça monîtorê biguherînî?", + "monitor-dir-dialog-msg": "Pelrêça monîtorê yê nû wê bibe:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Tu jê piştrast î ku tu dixwazî pelrêça monîtorê paqij bikî?", + "monitor-clear-dir-dialog-msg": "Ev wê kontrolkirina pelrêça monîtorê ji bo dosyeyên nû yên ZIMê bisekinîne.", + "monitor-directory-tooltip": "Temamê dosyeyên ZIMê yên di vê pelrêçê de wê bi otomatîkî tevlî kitêbxaneyê bên kirin.", + "next-tab": "Derbasî hilpekîna piştî bibe", + "previous-tab": "Derbasî hilpekîna berî bibe" +} diff --git a/resources/i18n/lb.json b/resources/i18n/lb.json index 7f36cbef8..ca03061b7 100644 --- a/resources/i18n/lb.json +++ b/resources/i18n/lb.json @@ -1,137 +1,137 @@ -{ - "@metadata": { - "authors": [ - "GilPe", - "Les Meloures", - "Robby", - "Volvox" - ] - }, - "name": "Lëtzebuergesch", - "error-title": "Feeler", - "error-downloader-launch-message": "Onméiglech fir den Downloader ze starten. De Kiwix-Desktop fänkt un, awer d'Downloadfunktioune funktionéieren net all.", - "error-launch-server-message": "Et ass e Feeler geschitt!", - "error-opening-file": "Et gouf e Feeler beim Opmaache vum Fichier.", - "open-zim": "ZIM Fichier opmaachen", - "local-kiwix-server": "Lokale Kiwix-Server", - "random-article": "Zoufällegen Artikel", - "random-article-error": "En zoufällegen Artikel konnt net opgemaach ginn.", - "home-page": "Haaptsäit", - "main-menu": "Haaptmenü", - "print": "Drécken", - "print-page-error": "Beim Drécken ass ee Feeler geschitt.", - "new-tab": "Neien Tab", - "close-tab": "Tab zoumaachen", - "close": "Zoumaachen", - "open-file": "Fichier opmaachen", - "save-file": "Fichier späicheren", - "search-article": "Artikel sichen", - "search-in-library": "An der Bibliothéik sichen", - "find-in-page": "Op der Säit sichen", - "table-of-content": "Inhaltsverzeechnes", - "add-bookmark": "Op d'Lieslëscht derbäisetzen", - "remove-bookmark": "Vun der Lieslëscht erofhuelen", - "reading-list": "Lieslëscht", - "zoom-in": "Vergréisseren", - "zoom-out": "Verklengeren", - "zoom-reset": "Zoom zrécksetzen", - "help": "Hëllef", - "feedback": "Feedback", - "report-a-bug": "E Feeler mellen", - "request-a-feature": "Eng Funktioun ufroen", - "about-kiwix": "Iwwer Kiwix", - "donate-to-support-kiwix": "Maacht en Don fir Kiwix z'ënnerstëtzen", - "exit": "Eraus goen", - "save-file-as-window-title": "Fichier späicheren als", - "download-finished-message": "D'Dokument gouf erofgelueden.", - "file": "Fichier", - "edit": "Änneren", - "view": "Weisen", - "tools": "Geschierkëscht", - "window-title": "Bibliothéik - Kiwix", - "search": "Sichen", - "settings": "Astellungen", - "back": "Zeréck", - "forward": "Virun", - "search-files": "Fichiere sichen", - "title": "Titel", - "size": "Gréisst", - "date": "Datum", - "content-type": "Typ vum Inhalt", - "reset-sort": "Zortéierung zrécksetzen", - "open": "Opmaachen", - "delete": "Läschen", - "download": "Eroflueden", - "resume": "Virufueren", - "pause": "Paus", - "cancel": "Ofbriechen", - "apply": "Uwenden", - "download-directory-setting": "Repertoire eroflueden", - "reset": "Zrécksetzen", - "browse": "Duerchsichen", - "about-kiwix-desktop-description": "Kiwix gëtt Iech d'Méiglechkeet déi ganz Wikipedia dobäi ze hunn, egal wou Dir sidd! Ob op engem Boot, matzen am Néierewou oder am Prisong: Kiwix gëtt Iech Zougang zur Gesamtheet vum mënschleche Wëssen. Dir braucht keen Internetzougang, alles ass lokal op Ärem Computer gespäichert.", - "about-learn-more-about-kiwix": "Méi iwwer Kiwix gewuer ginn", - "about-privacy-policy": "Dateschutzrichtlinn", - "about-version": "Versioun: {{VERSION}}", - "about-reporting-problem-title": "Problemer mellen", - "about-libraries-title": "Bibliothéiken", - "online-files": "Online-Fichieren", - "local-files": "Lokal Fichieren", - "category": "Kategorie", - "language": "Sprooch", - "hide": "Verstoppen", - "open-in-browser": "Am Browser opmaachen", - "start-kiwix-server": "Kiwix-Server starten", - "stop-kiwix-server": "Kiwix-Server stoppen", - "all": "All", - "fulltext-search": "Volltext-Sich", - "pictures": "Biller", - "videos": "Videoen", - "ftindex": "Volltext-Index", - "details": "Kompletten Artikel", - "yes": "jo", - "no": "neen", - "ok": "ok", - "no-filter": "kee Filter", - "open-link-in-web-browser": "Link am Browser opmaachen", - "open-link-new-tab": "Link an engem neien Tab opmaachen", - "invalid-port": "Ongültege Port", - "zim-open-fail-title": "Ongültege Fichier", - "cancel-download": "Download ofbriechen", - "cancel-download-text": "Sidd Dir sécher, datt Dir den Download vun {{ZIM}} ofbrieche wëllt?", - "delete-book": "Buch läschen", - "delete-book-text": "Sidd Dir sécher, datt Dir {{ZIM}} läsche wëllt?", - "download-storage-error": "Späicherfeeler", - "download-storage-error-text": "De System huet net genuch verfügbar Späicherplaz.", - "open-book": "Buch opmaachen", - "download-book": "Buch eroflueden", - "pause-download": "Erofluede pauséieren", - "resume-download": "Mam Erofluede virufueren", - "open-folder": "Dossier opmaachen", - "couldnt-open-location-text": "Kiwix kann den Dossier {{FOLDER}} net opmaachen", - "perma-delete-files-text": "No dëser Aktioun ass de Fichier permanent geläscht.", - "language-searcher-placeholder": "No Sprooch filteren", - "category-searcher-placeholder": "No Kategorie filteren", - "no-pictures": "Keng Biller", - "no-videos": "Keng Videoen", - "file-not-found-title": "ZIM-Fichier net fonnt", - "file-not-found-text": "De ZIM-Fichier gëtt et net oder e kann net gelies ginn", - "zim-id": "ZIM-ID", - "zim-name": "ZIM-Numm", - "bad-zim-file-error-page-title": "Ongültege ZIM-Fichier", - "export-reading-list": "Lieslëscht exportéieren", - "export-reading-list-error": "Beim Exportéiere vun der Lieslëscht ass ee Feeler geschitt.", - "import-reading-list": "Lieslëscht importéieren", - "import-reading-list-error": "Beim Importéiere vun der Lieslëscht ass ee Feeler geschitt.", - "kiwix-search": "Kiwix-Sich", - "search-options": "Sichoptiounen", - "read-article": "Artikel virliesen", - "read-text": "Ausgewielten Text virliesen", - "read-stop": "Ophale mat virliesen", - "stop": "Ophalen", - "voice": "Stëmm", - "select-read-voice": "Stëmm fir virzeliesen eraussichen", - "select-read-language": "Sprooch fir virzeliesen eraussichen", - "save-or-open": "Fichier späicheren oder opmaachen", - "save-or-open-text": "Wat soll Kiwix mam Fichier maachen?" -} +{ + "@metadata": { + "authors": [ + "GilPe", + "Les Meloures", + "Robby", + "Volvox" + ] + }, + "name": "Lëtzebuergesch", + "error-title": "Feeler", + "error-downloader-launch-message": "Onméiglech fir den Downloader ze starten. De Kiwix-Desktop fänkt un, awer d'Downloadfunktioune funktionéieren net all.", + "error-launch-server-message": "Et ass e Feeler geschitt!", + "error-opening-file": "Et gouf e Feeler beim Opmaache vum Fichier.", + "open-zim": "ZIM Fichier opmaachen", + "local-kiwix-server": "Lokale Kiwix-Server", + "random-article": "Zoufällegen Artikel", + "random-article-error": "En zoufällegen Artikel konnt net opgemaach ginn.", + "home-page": "Haaptsäit", + "main-menu": "Haaptmenü", + "print": "Drécken", + "print-page-error": "Beim Drécken ass ee Feeler geschitt.", + "new-tab": "Neien Tab", + "close-tab": "Tab zoumaachen", + "close": "Zoumaachen", + "open-file": "Fichier opmaachen", + "save-file": "Fichier späicheren", + "search-article": "Artikel sichen", + "search-in-library": "An der Bibliothéik sichen", + "find-in-page": "Op der Säit sichen", + "table-of-content": "Inhaltsverzeechnes", + "add-bookmark": "Op d'Lieslëscht derbäisetzen", + "remove-bookmark": "Vun der Lieslëscht erofhuelen", + "reading-list": "Lieslëscht", + "zoom-in": "Vergréisseren", + "zoom-out": "Verklengeren", + "zoom-reset": "Zoom zrécksetzen", + "help": "Hëllef", + "feedback": "Feedback", + "report-a-bug": "E Feeler mellen", + "request-a-feature": "Eng Funktioun ufroen", + "about-kiwix": "Iwwer Kiwix", + "donate-to-support-kiwix": "Maacht en Don fir Kiwix z'ënnerstëtzen", + "exit": "Eraus goen", + "save-file-as-window-title": "Fichier späicheren als", + "download-finished-message": "D'Dokument gouf erofgelueden.", + "file": "Fichier", + "edit": "Änneren", + "view": "Weisen", + "tools": "Geschierkëscht", + "window-title": "Bibliothéik - Kiwix", + "search": "Sichen", + "settings": "Astellungen", + "back": "Zeréck", + "forward": "Virun", + "search-files": "Fichiere sichen", + "title": "Titel", + "size": "Gréisst", + "date": "Datum", + "content-type": "Typ vum Inhalt", + "reset-sort": "Zortéierung zrécksetzen", + "open": "Opmaachen", + "delete": "Läschen", + "download": "Eroflueden", + "resume": "Virufueren", + "pause": "Paus", + "cancel": "Ofbriechen", + "apply": "Uwenden", + "download-directory-setting": "Repertoire eroflueden", + "reset": "Zrécksetzen", + "browse": "Duerchsichen", + "about-kiwix-desktop-description": "Kiwix gëtt Iech d'Méiglechkeet déi ganz Wikipedia dobäi ze hunn, egal wou Dir sidd! Ob op engem Boot, matzen am Néierewou oder am Prisong: Kiwix gëtt Iech Zougang zur Gesamtheet vum mënschleche Wëssen. Dir braucht keen Internetzougang, alles ass lokal op Ärem Computer gespäichert.", + "about-learn-more-about-kiwix": "Méi iwwer Kiwix gewuer ginn", + "about-privacy-policy": "Dateschutzrichtlinn", + "about-version": "Versioun: {{VERSION}}", + "about-reporting-problem-title": "Problemer mellen", + "about-libraries-title": "Bibliothéiken", + "online-files": "Online-Fichieren", + "local-files": "Lokal Fichieren", + "category": "Kategorie", + "language": "Sprooch", + "hide": "Verstoppen", + "open-in-browser": "Am Browser opmaachen", + "start-kiwix-server": "Kiwix-Server starten", + "stop-kiwix-server": "Kiwix-Server stoppen", + "all": "All", + "fulltext-search": "Volltext-Sich", + "pictures": "Biller", + "videos": "Videoen", + "ftindex": "Volltext-Index", + "details": "Kompletten Artikel", + "yes": "jo", + "no": "neen", + "ok": "ok", + "no-filter": "kee Filter", + "open-link-in-web-browser": "Link am Browser opmaachen", + "open-link-new-tab": "Link an engem neien Tab opmaachen", + "invalid-port": "Ongültege Port", + "zim-open-fail-title": "Ongültege Fichier", + "cancel-download": "Download ofbriechen", + "cancel-download-text": "Sidd Dir sécher, datt Dir den Download vun {{ZIM}} ofbrieche wëllt?", + "delete-book": "Buch läschen", + "delete-book-text": "Sidd Dir sécher, datt Dir {{ZIM}} läsche wëllt?", + "download-storage-error": "Späicherfeeler", + "download-storage-error-text": "De System huet net genuch verfügbar Späicherplaz.", + "open-book": "Buch opmaachen", + "download-book": "Buch eroflueden", + "pause-download": "Erofluede pauséieren", + "resume-download": "Mam Erofluede virufueren", + "open-folder": "Dossier opmaachen", + "couldnt-open-location-text": "Kiwix kann den Dossier {{FOLDER}} net opmaachen", + "perma-delete-files-text": "No dëser Aktioun ass de Fichier permanent geläscht.", + "language-searcher-placeholder": "No Sprooch filteren", + "category-searcher-placeholder": "No Kategorie filteren", + "no-pictures": "Keng Biller", + "no-videos": "Keng Videoen", + "file-not-found-title": "ZIM-Fichier net fonnt", + "file-not-found-text": "De ZIM-Fichier gëtt et net oder e kann net gelies ginn", + "zim-id": "ZIM-ID", + "zim-name": "ZIM-Numm", + "bad-zim-file-error-page-title": "Ongültege ZIM-Fichier", + "export-reading-list": "Lieslëscht exportéieren", + "export-reading-list-error": "Beim Exportéiere vun der Lieslëscht ass ee Feeler geschitt.", + "import-reading-list": "Lieslëscht importéieren", + "import-reading-list-error": "Beim Importéiere vun der Lieslëscht ass ee Feeler geschitt.", + "kiwix-search": "Kiwix-Sich", + "search-options": "Sichoptiounen", + "read-article": "Artikel virliesen", + "read-text": "Ausgewielten Text virliesen", + "read-stop": "Ophale mat virliesen", + "stop": "Ophalen", + "voice": "Stëmm", + "select-read-voice": "Stëmm fir virzeliesen eraussichen", + "select-read-language": "Sprooch fir virzeliesen eraussichen", + "save-or-open": "Fichier späicheren oder opmaachen", + "save-or-open-text": "Wat soll Kiwix mam Fichier maachen?" +} diff --git a/resources/i18n/mk.json b/resources/i18n/mk.json index a07ff42fa..adb87fbe6 100644 --- a/resources/i18n/mk.json +++ b/resources/i18n/mk.json @@ -1,190 +1,190 @@ -{ - "@metadata": { - "authors": [ - "Bjankuloski06" - ] - }, - "name": "македонски", - "error-title": "Грешка", - "error-downloader-window-title": "Не можам да го создадам преземачот", - "error-downloader-launch-message": "Не можам да го пуштам преземачот. Кивикс за сметач ќе се пушти, нема да работи ниедна од можностите за преземање!", - "error-launch-server-message": "Се појави грешка!", - "error-archive": "Не можам да го добијам архивот", - "error-opening-file": "Се јави грешка при отворањето на податотеката.", - "open-zim": "Отвори ZIM-податотека", - "local-kiwix-server": "Месен опслужувач на Кивикс", - "random-article": "Случајна статија", - "random-article-error": "Не успеав да отворам случајна статија.", - "home-page": "Почетна страница", - "main-menu": "Главно мени", - "print": "Печати", - "print-page-error": "Се појави грешка при печатењето.", - "new-tab": "Ново јазиче", - "close-tab": "Затвори јазиче", - "close": "Затвори", - "reopen-closed-tab": "Преотвори затворено јазиче", - "browse-library": "Прелистај библиотека", - "open-file": "Отвори податотека", - "save-file": "Зачувај податотека", - "open-recent": "Отвори скорешни", - "search-article": "Пребарај статија", - "search-in-library": "Пребарај во библиотеката", - "find-in-page": "Најди на страницата", - "set-fullscreen": "Дај цел екран", - "quit-fullscreen": "Излези од цел екран", - "table-of-content": "Содржина", - "add-bookmark": "Стави во список за читање", - "remove-bookmark": "Отстрани од список за читање", - "reading-list": "Список за читање", - "zoom-in": "Приближи", - "zoom-out": "Оддалечи", - "zoom-reset": "Врати размер", - "help": "Помош", - "feedback": "Мислења", - "report-a-bug": "Пријави грешка", - "request-a-feature": "Побарајте можност", - "about-kiwix": "За Кивикс", - "donate-to-support-kiwix": "Дарувајте на Кивикс", - "exit": "Излези", - "save-file-as-window-title": "Зачувај како", - "download-finished": "Преземањето заврши", - "download-finished-message": "Документот е преземен.", - "file": "Податотека", - "edit": "Уреди", - "view": "Преглед", - "tools": "Алатки", - "window-title": "Библиотека — Кивикс", - "search": "Пребарај", - "browse-directory": "Пребарај папки", - "settings": "Нагодувања", - "back": "Назад", - "forward": "Напред", - "search-files": "Пребарај податотеки", - "title": "Наслов", - "size": "Големина", - "date": "Датум", - "content-type": "Вид содржина", - "reset-sort": "врати подреденост", - "open": "Отворени", - "delete": "Избриши", - "download": "Преземи", - "resume": "Продолжи", - "pause": "Запри", - "cancel": "Откажи", - "apply": "Примени", - "port-for-local-kiwix-server-setting": "Порта на месниот опслужувач на Кивикс", - "zoom-level-setting": "Приближеност", - "download-directory-setting": "Папка за преземање", - "monitor-directory-setting": "Надзорна папка", - "reset": "Одново", - "clear": "Исчисти", - "browse": "Прелистај", - "about-kiwix-desktop-title": "Кивикс за сметач", - "about-kiwix-desktop-description": "Кивикс ви овозможува да ја имате цела Википедија во рака, каде и да сте! Било да сте на чамец, некаде во пустелија или во затвор, Кивикс ви дава пристап до сето знаење на човештвото. Не ви треба врска со семрежјето, бидејќи сето е складирано на вашиот сметач.", - "about-learn-more-about-kiwix": "Дознајте повеќе за Кивикс", - "about-privacy-policy": "Заштита на личните податоци", - "about-release-title": "Издание", - "about-source-code": "Оваа програма е објавена под условите на ГНУ-овата општа јавна лиценца, верзија 3. Изворниот код ќе го најдете на Github.", - "about-version": "Верзија: {{VERSION}}", - "about-reporting-problem-title": "Пријавување на проблеми", - "about-report-issue": "За да направите пријава на проблем, појдете на Следачот на проблеми за Кивикс за сметач и направете страница во која ќе го опишете проблемот.", - "about-report-issue-2": "Спомнете ја верзијата во проблемот.", - "about-libraries-title": "Библиотеки", - "kiwix-server-running-message": "Опслужувачот на Кивикс работи и може да му се пристапи во месната мрежа на:", - "kiwix-server-description": "Отпочнувањето на опслужувачот им овозможува на другите сметачи во месната мрежа да имаат пристап кон библиотеката на Кивикс со стандарден семрежен прелистувач.", - "fullscreen-notification": "Сега сте во целоекрански режим. Стиснете на Esc за да излезете!", - "online-files": "Семрежни податотеки", - "local-files": "Месни податотеки", - "category": "Категорија", - "language": "Јазик", - "hide": "Скриј", - "open-in-browser": "Отвори во прелистувач", - "start-kiwix-server": "Пушти го опслужувачот на Кивикс", - "stop-kiwix-server": "Запри го опслужувачот на Кивикс", - "all": "Сè", - "all_ips": "Сите — Режим на двоен пласт", - "ipv4": "Режим на само IPv4", - "ipv6": "Режим на само IPv6", - "fulltext-search": "Пребарување по цел текст", - "pictures": "Слики", - "videos": "Видеа", - "ftindex": "Целотекстен индекс", - "details": "Целосна статија", - "yes": "да", - "no": "не", - "ok": "ок", - "no-filter": "без филтер", - "open-link-in-web-browser": "Отвори ја врската во прелистувач", - "open-link-new-tab": "Отвори ја врската во ново јазиче", - "download-dir-dialog-title": "Дали сигурно сакате да ја смените папката за преземање?", - "download-dir-dialog-msg": "Новата папка за преземање ќе биде:\n{{DIRECTORY}}", - "invalid-port": "Неважечка порта", - "zim-open-fail-title": "Неважечка податотека", - "zim-open-fail-text": "ZIM-податотеката {{ZIM}} не се отвора како што треба. Ќе ја остранам од вашата библиотека.", - "monitor-dir-dialog-title": "Дали сигурно сакате да ја смените надзорната папка?", - "monitor-dir-dialog-msg": "Патеката на новата надзорна папка ќе биде:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Дали сигурно сакате да ја чистистите надзорната папка?", - "path-was-copied": "Патеката е ископирана", - "monitor-clear-dir-dialog-msg": "Со ова повеќе нема да се проеврува дали во надзорната папка има нови ZIM-податотеки.", - "monitor-directory-tooltip": "Сите ZIM-податотеки во оваа папка автоматски ќе се додаваат во библиотеката.", - "next-tab": "Прејди на следното јазиче", - "previous-tab": "Прејди на претходното јазиче", - "cancel-download": "Откажи преземање", - "cancel-download-text": "Дали сигурно сакате да го откажете преземањето на {{ZIM}}?", - "delete-book": "Избриши книга", - "delete-book-text": "Дали сигурно сакате да го избришете {{ZIM}}?", - "download-storage-error": "Грешка во складирањето", - "download-storage-error-text": "Системот нема доволно складиштен простор.", - "download-exceeds-max-file-size": "Големината на преземеното ја надминува границата поддржана од целниот податочен систем.", - "download-dir-missing": "Папката за преземања не постои.", - "download-dir-not-writable": "Папката за преземања е незапислива.", - "download-unavailable": "Преземањето е недостапно", - "download-unavailable-text": "Ова преземање е недостапно.", - "open-book": "Отвори книга", - "download-book": "Преземи книга", - "pause-download": "Запри го преземањето", - "resume-download": "Продолжи преземање", - "open-folder": "Отвори папка", - "couldnt-open-location": "Не можам да го отворам местото", - "couldnt-open-location-text": "Кивикс не може да ја отвори папката {{FOLDER}}", - "move-files-to-trash": "Премести ги избришаните податотеки во корпа", - "move-files-to-trash-text": "Ова дејство ќе ја премести податотеката во корпа.", - "perma-delete-files-text": "Ова дејство трајно ќе ја избрише податотеката.", - "clear-filter": "Исчистете ги тековно поставените филтри", - "language-searcher-placeholder": "Филтрирај по јазик", - "category-searcher-placeholder": "Филтрирај по категорија", - "content-type-searcher-placeholder": "Филтрирај по вид на содржина", - "no-details": "Само вовед", - "no-pictures": "Нема слики", - "no-videos": "Нема видеа", - "open-previous-tabs-at-startup": "Отвори го претходното јазиче при пуштање", - "preview-book-in-web-browser": "Преглед на книгата во прелистувач", - "file-not-found-title": "ZIM-податотеката не е пронајдена", - "file-not-found-text": "ZIM-подадотеката не постои или е нечитлива", - "zim-id": "ZIM-назнака", - "zim-name": "ZIM-име", - "zim-path": "Патека на ZIM-податотеката", - "bad-zim-file-error-page-title": "Неважечка ZIM-податотека", - "bad-zim-file-error-page-text": "ZIM-податотеката е неважечка или оштетена.", - "zim-entry-path": "Патека на ZIM-влезот", - "export-reading-list": "Извези список за читање", - "export-reading-list-error": "Се јави грешка при извозот на списокот за читање.", - "import-reading-list": "Увези список за читање", - "import-reading-list-error": "Се јави грешка при увозот на списокот за читање.", - "disable-sandbox": "Кивикс беше пуштен од мрежен двигател. Ова знае да прави проблеми со складноста при прелистување. Поради тоа, песочникот ќе биде оневозможен. Дали сакате да продолжите?", - "save-page-as": "Зачувај како...", - "portable-disabled-tooltip": "Функцијата е оневозможена во пренослив режим", - "scroll-next-tab": "Пролизгајте кон следното јазиче", - "scroll-previous-tab": "Пролизгајте кон претходното јазиче", - "kiwix-search": "Пребарување по Кивикс", - "search-options": "Нагодувања за пребарување", - "read-article": "Прочитај ја статијата", - "read-text": "Прочитај го избраниот текст", - "read-stop": "Престани со читање", - "stop": "Престани", - "voice": "Глас", - "select-read-voice": "Изберете глас на читање", - "select-read-language": "Изберете јазик на читање", - "save-or-open": "Зачувај или Отвори податотека", - "save-or-open-text": "Што да прави Кивикс со податотекава?" -} +{ + "@metadata": { + "authors": [ + "Bjankuloski06" + ] + }, + "name": "македонски", + "error-title": "Грешка", + "error-downloader-window-title": "Не можам да го создадам преземачот", + "error-downloader-launch-message": "Не можам да го пуштам преземачот. Кивикс за сметач ќе се пушти, нема да работи ниедна од можностите за преземање!", + "error-launch-server-message": "Се појави грешка!", + "error-archive": "Не можам да го добијам архивот", + "error-opening-file": "Се јави грешка при отворањето на податотеката.", + "open-zim": "Отвори ZIM-податотека", + "local-kiwix-server": "Месен опслужувач на Кивикс", + "random-article": "Случајна статија", + "random-article-error": "Не успеав да отворам случајна статија.", + "home-page": "Почетна страница", + "main-menu": "Главно мени", + "print": "Печати", + "print-page-error": "Се појави грешка при печатењето.", + "new-tab": "Ново јазиче", + "close-tab": "Затвори јазиче", + "close": "Затвори", + "reopen-closed-tab": "Преотвори затворено јазиче", + "browse-library": "Прелистај библиотека", + "open-file": "Отвори податотека", + "save-file": "Зачувај податотека", + "open-recent": "Отвори скорешни", + "search-article": "Пребарај статија", + "search-in-library": "Пребарај во библиотеката", + "find-in-page": "Најди на страницата", + "set-fullscreen": "Дај цел екран", + "quit-fullscreen": "Излези од цел екран", + "table-of-content": "Содржина", + "add-bookmark": "Стави во список за читање", + "remove-bookmark": "Отстрани од список за читање", + "reading-list": "Список за читање", + "zoom-in": "Приближи", + "zoom-out": "Оддалечи", + "zoom-reset": "Врати размер", + "help": "Помош", + "feedback": "Мислења", + "report-a-bug": "Пријави грешка", + "request-a-feature": "Побарајте можност", + "about-kiwix": "За Кивикс", + "donate-to-support-kiwix": "Дарувајте на Кивикс", + "exit": "Излези", + "save-file-as-window-title": "Зачувај како", + "download-finished": "Преземањето заврши", + "download-finished-message": "Документот е преземен.", + "file": "Податотека", + "edit": "Уреди", + "view": "Преглед", + "tools": "Алатки", + "window-title": "Библиотека — Кивикс", + "search": "Пребарај", + "browse-directory": "Пребарај папки", + "settings": "Нагодувања", + "back": "Назад", + "forward": "Напред", + "search-files": "Пребарај податотеки", + "title": "Наслов", + "size": "Големина", + "date": "Датум", + "content-type": "Вид содржина", + "reset-sort": "врати подреденост", + "open": "Отворени", + "delete": "Избриши", + "download": "Преземи", + "resume": "Продолжи", + "pause": "Запри", + "cancel": "Откажи", + "apply": "Примени", + "port-for-local-kiwix-server-setting": "Порта на месниот опслужувач на Кивикс", + "zoom-level-setting": "Приближеност", + "download-directory-setting": "Папка за преземање", + "monitor-directory-setting": "Надзорна папка", + "reset": "Одново", + "clear": "Исчисти", + "browse": "Прелистај", + "about-kiwix-desktop-title": "Кивикс за сметач", + "about-kiwix-desktop-description": "Кивикс ви овозможува да ја имате цела Википедија во рака, каде и да сте! Било да сте на чамец, некаде во пустелија или во затвор, Кивикс ви дава пристап до сето знаење на човештвото. Не ви треба врска со семрежјето, бидејќи сето е складирано на вашиот сметач.", + "about-learn-more-about-kiwix": "Дознајте повеќе за Кивикс", + "about-privacy-policy": "Заштита на личните податоци", + "about-release-title": "Издание", + "about-source-code": "Оваа програма е објавена под условите на ГНУ-овата општа јавна лиценца, верзија 3. Изворниот код ќе го најдете на Github.", + "about-version": "Верзија: {{VERSION}}", + "about-reporting-problem-title": "Пријавување на проблеми", + "about-report-issue": "За да направите пријава на проблем, појдете на Следачот на проблеми за Кивикс за сметач и направете страница во која ќе го опишете проблемот.", + "about-report-issue-2": "Спомнете ја верзијата во проблемот.", + "about-libraries-title": "Библиотеки", + "kiwix-server-running-message": "Опслужувачот на Кивикс работи и може да му се пристапи во месната мрежа на:", + "kiwix-server-description": "Отпочнувањето на опслужувачот им овозможува на другите сметачи во месната мрежа да имаат пристап кон библиотеката на Кивикс со стандарден семрежен прелистувач.", + "fullscreen-notification": "Сега сте во целоекрански режим. Стиснете на Esc за да излезете!", + "online-files": "Семрежни податотеки", + "local-files": "Месни податотеки", + "category": "Категорија", + "language": "Јазик", + "hide": "Скриј", + "open-in-browser": "Отвори во прелистувач", + "start-kiwix-server": "Пушти го опслужувачот на Кивикс", + "stop-kiwix-server": "Запри го опслужувачот на Кивикс", + "all": "Сè", + "all_ips": "Сите — Режим на двоен пласт", + "ipv4": "Режим на само IPv4", + "ipv6": "Режим на само IPv6", + "fulltext-search": "Пребарување по цел текст", + "pictures": "Слики", + "videos": "Видеа", + "ftindex": "Целотекстен индекс", + "details": "Целосна статија", + "yes": "да", + "no": "не", + "ok": "ок", + "no-filter": "без филтер", + "open-link-in-web-browser": "Отвори ја врската во прелистувач", + "open-link-new-tab": "Отвори ја врската во ново јазиче", + "download-dir-dialog-title": "Дали сигурно сакате да ја смените папката за преземање?", + "download-dir-dialog-msg": "Новата папка за преземање ќе биде:\n{{DIRECTORY}}", + "invalid-port": "Неважечка порта", + "zim-open-fail-title": "Неважечка податотека", + "zim-open-fail-text": "ZIM-податотеката {{ZIM}} не се отвора како што треба. Ќе ја остранам од вашата библиотека.", + "monitor-dir-dialog-title": "Дали сигурно сакате да ја смените надзорната папка?", + "monitor-dir-dialog-msg": "Патеката на новата надзорна папка ќе биде:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Дали сигурно сакате да ја чистистите надзорната папка?", + "path-was-copied": "Патеката е ископирана", + "monitor-clear-dir-dialog-msg": "Со ова повеќе нема да се проеврува дали во надзорната папка има нови ZIM-податотеки.", + "monitor-directory-tooltip": "Сите ZIM-податотеки во оваа папка автоматски ќе се додаваат во библиотеката.", + "next-tab": "Прејди на следното јазиче", + "previous-tab": "Прејди на претходното јазиче", + "cancel-download": "Откажи преземање", + "cancel-download-text": "Дали сигурно сакате да го откажете преземањето на {{ZIM}}?", + "delete-book": "Избриши книга", + "delete-book-text": "Дали сигурно сакате да го избришете {{ZIM}}?", + "download-storage-error": "Грешка во складирањето", + "download-storage-error-text": "Системот нема доволно складиштен простор.", + "download-exceeds-max-file-size": "Големината на преземеното ја надминува границата поддржана од целниот податочен систем.", + "download-dir-missing": "Папката за преземања не постои.", + "download-dir-not-writable": "Папката за преземања е незапислива.", + "download-unavailable": "Преземањето е недостапно", + "download-unavailable-text": "Ова преземање е недостапно.", + "open-book": "Отвори книга", + "download-book": "Преземи книга", + "pause-download": "Запри го преземањето", + "resume-download": "Продолжи преземање", + "open-folder": "Отвори папка", + "couldnt-open-location": "Не можам да го отворам местото", + "couldnt-open-location-text": "Кивикс не може да ја отвори папката {{FOLDER}}", + "move-files-to-trash": "Премести ги избришаните податотеки во корпа", + "move-files-to-trash-text": "Ова дејство ќе ја премести податотеката во корпа.", + "perma-delete-files-text": "Ова дејство трајно ќе ја избрише податотеката.", + "clear-filter": "Исчистете ги тековно поставените филтри", + "language-searcher-placeholder": "Филтрирај по јазик", + "category-searcher-placeholder": "Филтрирај по категорија", + "content-type-searcher-placeholder": "Филтрирај по вид на содржина", + "no-details": "Само вовед", + "no-pictures": "Нема слики", + "no-videos": "Нема видеа", + "open-previous-tabs-at-startup": "Отвори го претходното јазиче при пуштање", + "preview-book-in-web-browser": "Преглед на книгата во прелистувач", + "file-not-found-title": "ZIM-податотеката не е пронајдена", + "file-not-found-text": "ZIM-подадотеката не постои или е нечитлива", + "zim-id": "ZIM-назнака", + "zim-name": "ZIM-име", + "zim-path": "Патека на ZIM-податотеката", + "bad-zim-file-error-page-title": "Неважечка ZIM-податотека", + "bad-zim-file-error-page-text": "ZIM-податотеката е неважечка или оштетена.", + "zim-entry-path": "Патека на ZIM-влезот", + "export-reading-list": "Извези список за читање", + "export-reading-list-error": "Се јави грешка при извозот на списокот за читање.", + "import-reading-list": "Увези список за читање", + "import-reading-list-error": "Се јави грешка при увозот на списокот за читање.", + "disable-sandbox": "Кивикс беше пуштен од мрежен двигател. Ова знае да прави проблеми со складноста при прелистување. Поради тоа, песочникот ќе биде оневозможен. Дали сакате да продолжите?", + "save-page-as": "Зачувај како...", + "portable-disabled-tooltip": "Функцијата е оневозможена во пренослив режим", + "scroll-next-tab": "Пролизгајте кон следното јазиче", + "scroll-previous-tab": "Пролизгајте кон претходното јазиче", + "kiwix-search": "Пребарување по Кивикс", + "search-options": "Нагодувања за пребарување", + "read-article": "Прочитај ја статијата", + "read-text": "Прочитај го избраниот текст", + "read-stop": "Престани со читање", + "stop": "Престани", + "voice": "Глас", + "select-read-voice": "Изберете глас на читање", + "select-read-language": "Изберете јазик на читање", + "save-or-open": "Зачувај или Отвори податотека", + "save-or-open-text": "Што да прави Кивикс со податотекава?" +} diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index 2507f30aa..253c0e841 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -1,53 +1,53 @@ -{ - "@metadata": { - "authors": [ - "Hakimi97", - "Tofeiku", - "Ultron90" - ] - }, - "name": "Bahasa Melayu", - "error-title": "Ralat", - "error-downloader-window-title": "Tidak dapat mencipta pemuat turun", - "random-article": "Rencana Rawak", - "main-menu": "Menu utama", - "print": "Cetak", - "close": "Tutup", - "open-file": "Buka fail", - "search-article": "Cari rencana", - "table-of-content": "Jadual isi kandungan", - "reading-list": "Senarai bacaan", - "help": "Bantuan", - "feedback": "Maklum balas", - "report-a-bug": "Laporkan pepijat", - "about-kiwix": "Tentang Kiwix", - "donate-to-support-kiwix": "Derma untuk menyokong Kiwix", - "exit": "Keluar", - "save-file-as-window-title": "Simpan Fail sebagai", - "download-finished-message": "Dokumen telah dimuat turun.", - "file": "Fail", - "edit": "Sunting", - "view": "Lihat", - "tools": "Peralatan", - "search": "Cari", - "settings": "Tetapan", - "back": "Kembali", - "title": "Tajuk", - "size": "Saiz", - "date": "Tarikh", - "content-type": "Jenis kandungan", - "open": "Buka", - "delete": "Hapuskan", - "download": "Muat turun", - "cancel": "Batalkan", - "about-libraries-title": "Perpustakaan", - "local-files": "Fail Tempatan", - "hide": "Sorokkan", - "all": "Semua", - "pictures": "Gambar", - "videos": "Video", - "details": "Rencana penuh", - "yes": "ya", - "no": "tidak", - "no-filter": "tiada penapis" -} +{ + "@metadata": { + "authors": [ + "Hakimi97", + "Tofeiku", + "Ultron90" + ] + }, + "name": "Bahasa Melayu", + "error-title": "Ralat", + "error-downloader-window-title": "Tidak dapat mencipta pemuat turun", + "random-article": "Rencana Rawak", + "main-menu": "Menu utama", + "print": "Cetak", + "close": "Tutup", + "open-file": "Buka fail", + "search-article": "Cari rencana", + "table-of-content": "Jadual isi kandungan", + "reading-list": "Senarai bacaan", + "help": "Bantuan", + "feedback": "Maklum balas", + "report-a-bug": "Laporkan pepijat", + "about-kiwix": "Tentang Kiwix", + "donate-to-support-kiwix": "Derma untuk menyokong Kiwix", + "exit": "Keluar", + "save-file-as-window-title": "Simpan Fail sebagai", + "download-finished-message": "Dokumen telah dimuat turun.", + "file": "Fail", + "edit": "Sunting", + "view": "Lihat", + "tools": "Peralatan", + "search": "Cari", + "settings": "Tetapan", + "back": "Kembali", + "title": "Tajuk", + "size": "Saiz", + "date": "Tarikh", + "content-type": "Jenis kandungan", + "open": "Buka", + "delete": "Hapuskan", + "download": "Muat turun", + "cancel": "Batalkan", + "about-libraries-title": "Perpustakaan", + "local-files": "Fail Tempatan", + "hide": "Sorokkan", + "all": "Semua", + "pictures": "Gambar", + "videos": "Video", + "details": "Rencana penuh", + "yes": "ya", + "no": "tidak", + "no-filter": "tiada penapis" +} diff --git a/resources/i18n/my.json b/resources/i18n/my.json index e36954e98..8a9cec29b 100644 --- a/resources/i18n/my.json +++ b/resources/i18n/my.json @@ -1,67 +1,67 @@ -{ - "@metadata": { - "authors": [ - "Dr Lotus Black", - "McDutchie" - ] - }, - "name": "မြန်မာဘာသာ", - "error-title": "အမှား", - "error-launch-server-message": "အမှားတစ်ခု ဖြစ်ပွားခဲ့ပါသည်", - "random-article": "ကျပန်း ဆောင်းပါး", - "home-page": "ပင်မစာမျက်နှာ", - "main-menu": "ပင်မ မီနူး", - "print": "ပရင့်ထုတ်", - "new-tab": "tab အသစ်", - "close-tab": "tab ပိတ်ရန်", - "close": "ပိတ်ရန်", - "open-file": "ဖိုင်ဖွင့်", - "search-article": "ဆောင်းပါးရှာဖွေရန်", - "search-in-library": "စာကြည့်တိုက်တွင်ရှာဖွေရန်", - "find-in-page": "စာမျက်နှာတွင် ရှာဖွေရန်", - "set-fullscreen": "ဆခရင်အပြည့်ထားရန်", - "quit-fullscreen": "ဆခရင်အပြည့်မှထွက်ရန်", - "table-of-content": "မာတိကာ", - "reading-list": "ဖတ်ရှုစာရင်း", - "zoom-in": "အကျယ်ချဲ့ရန်", - "zoom-out": "မြင်ကွင်း ချုံ့ရန်", - "help": "အကူအညီ", - "feedback": "အကြံပေးရန်", - "report-a-bug": "အခက်အခဲများ သတင်းပို့ရန်", - "exit": "ထွက်ရန်", - "file": "ဖိုင်", - "edit": "ပြင်ဆင်ရန်", - "view": "ကြည့်ရန်", - "tools": "ကိရိယာများ", - "search": "ရှာဖွေရန်", - "settings": "အပြင်အဆင်များ", - "back": "နောက်သို့", - "forward": "ရှေ့သို့", - "search-files": "ဖိုင်များကို ရှာရန်", - "title": "ခေါင်းစဉ်", - "size": "အရွယ်အစား", - "date": "နေ့စွဲ", - "open": "ဖွင့်ရန်", - "delete": "ဖျက်ရန်", - "download": "ဒေါင်းလုဒ်", - "resume": "ခေတ္တရပ်နားရာမှ ပြန်လုပ်ရန်", - "pause": "ခေတ္တရပ်", - "cancel": "မလုပ်တော့ပါ", - "apply": "သက်ရောက်စေရန်", - "reset": "ပြန်စရန်", - "browse": "ရှာဖွေလှန်လှောရန်", - "about-release-title": "ထုတ်ပြန်", - "about-version": "ဗားရှင်း : {{VERSION}}", - "about-reporting-problem-title": "အခက်အခဲ တင်ပြခြင်း", - "about-libraries-title": "စာကြည့်တိုက်များ", - "local-files": "ဒေသတွင်းဖိုင်များ", - "hide": "ဝှက်ရန်", - "open-in-browser": "ဘရောက်ဆာတွင် ဖွင့်ရန်", - "all": "အားလုံး", - "pictures": "ရုပ်ပုံများ", - "videos": "ဗီဒီယိုများ", - "details": "ဆောင်းပါးအပြည့်စုံ", - "yes": "ဟုတ်ပါသည်", - "no": "မလုပ်ပါ", - "no-filter": "စစ်ထုတ်မှုမရှိ" -} +{ + "@metadata": { + "authors": [ + "Dr Lotus Black", + "McDutchie" + ] + }, + "name": "မြန်မာဘာသာ", + "error-title": "အမှား", + "error-launch-server-message": "အမှားတစ်ခု ဖြစ်ပွားခဲ့ပါသည်", + "random-article": "ကျပန်း ဆောင်းပါး", + "home-page": "ပင်မစာမျက်နှာ", + "main-menu": "ပင်မ မီနူး", + "print": "ပရင့်ထုတ်", + "new-tab": "tab အသစ်", + "close-tab": "tab ပိတ်ရန်", + "close": "ပိတ်ရန်", + "open-file": "ဖိုင်ဖွင့်", + "search-article": "ဆောင်းပါးရှာဖွေရန်", + "search-in-library": "စာကြည့်တိုက်တွင်ရှာဖွေရန်", + "find-in-page": "စာမျက်နှာတွင် ရှာဖွေရန်", + "set-fullscreen": "ဆခရင်အပြည့်ထားရန်", + "quit-fullscreen": "ဆခရင်အပြည့်မှထွက်ရန်", + "table-of-content": "မာတိကာ", + "reading-list": "ဖတ်ရှုစာရင်း", + "zoom-in": "အကျယ်ချဲ့ရန်", + "zoom-out": "မြင်ကွင်း ချုံ့ရန်", + "help": "အကူအညီ", + "feedback": "အကြံပေးရန်", + "report-a-bug": "အခက်အခဲများ သတင်းပို့ရန်", + "exit": "ထွက်ရန်", + "file": "ဖိုင်", + "edit": "ပြင်ဆင်ရန်", + "view": "ကြည့်ရန်", + "tools": "ကိရိယာများ", + "search": "ရှာဖွေရန်", + "settings": "အပြင်အဆင်များ", + "back": "နောက်သို့", + "forward": "ရှေ့သို့", + "search-files": "ဖိုင်များကို ရှာရန်", + "title": "ခေါင်းစဉ်", + "size": "အရွယ်အစား", + "date": "နေ့စွဲ", + "open": "ဖွင့်ရန်", + "delete": "ဖျက်ရန်", + "download": "ဒေါင်းလုဒ်", + "resume": "ခေတ္တရပ်နားရာမှ ပြန်လုပ်ရန်", + "pause": "ခေတ္တရပ်", + "cancel": "မလုပ်တော့ပါ", + "apply": "သက်ရောက်စေရန်", + "reset": "ပြန်စရန်", + "browse": "ရှာဖွေလှန်လှောရန်", + "about-release-title": "ထုတ်ပြန်", + "about-version": "ဗားရှင်း : {{VERSION}}", + "about-reporting-problem-title": "အခက်အခဲ တင်ပြခြင်း", + "about-libraries-title": "စာကြည့်တိုက်များ", + "local-files": "ဒေသတွင်းဖိုင်များ", + "hide": "ဝှက်ရန်", + "open-in-browser": "ဘရောက်ဆာတွင် ဖွင့်ရန်", + "all": "အားလုံး", + "pictures": "ရုပ်ပုံများ", + "videos": "ဗီဒီယိုများ", + "details": "ဆောင်းပါးအပြည့်စုံ", + "yes": "ဟုတ်ပါသည်", + "no": "မလုပ်ပါ", + "no-filter": "စစ်ထုတ်မှုမရှိ" +} diff --git a/resources/i18n/nb.json b/resources/i18n/nb.json index 9d17a32e3..a9b17ea76 100644 --- a/resources/i18n/nb.json +++ b/resources/i18n/nb.json @@ -1,92 +1,92 @@ -{ - "@metadata": { - "authors": [ - "Event", - "Johshh" - ] - }, - "error-title": "Feil", - "new-tab": "Ny fane", - "close-tab": "Lukk fane", - "close": "Lukk", - "reopen-closed-tab": "Åpne lukket fane igjen", - "browse-library": "Bla gjennom biblioteket", - "open-file": "Åpne fil", - "open-recent": "Åpne nylig", - "search-article": "Søk i artikkelen", - "search-in-library": "Søk i biblioteket", - "find-in-page": "Finn på siden", - "set-fullscreen": "Aktiver fullskjerm", - "quit-fullscreen": "Avslutt fullskjerm", - "table-of-content": "Innholdsfortegnelse", - "reading-list": "Leseliste", - "zoom-in": "Zoom inn", - "zoom-out": "Zoom ut", - "help": "Hjelp", - "feedback": "Tilbakemelding", - "report-a-bug": "Rapporter en feil", - "request-a-feature": "Be om en funksjon", - "about-kiwix": "Om Kiwix", - "donate-to-support-kiwix": "Doner for å støtte Kiwix", - "exit": "Avslutt", - "save-file-as-window-title": "Lagre fil som", - "download-finished-message": "Dokumentet er lastet ned.", - "file": "Fil", - "edit": "Rediger", - "view": "Vis", - "tools": "Verktøy", - "window-title": "Bibliotek - Kiwix", - "search": "Søk", - "browse-directory": "Bla gjennom katalogen", - "settings": "Innstillinger", - "back": "Tilbake", - "forward": "Fremover", - "search-files": "Søk etter filer", - "title": "Tittel", - "size": "Størrelse", - "date": "Dato", - "content-type": "Innholdstype", - "reset-sort": "tilbakestill sortering", - "open": "Åpen", - "delete": "Slett", - "pause": "Pause", - "cancel": "Avbryt", - "apply": "Bruk", - "port-for-local-kiwix-server-setting": "Port for lokal Kiwix-tjener", - "zoom-level-setting": "Standard zoomnivå", - "download-directory-setting": "Last ned mappe", - "monitor-directory-setting": "Overvåk mappe", - "reset": "Nullstill", - "clear": "Tøm", - "browse": "Bla gjennom", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-learn-more-about-kiwix": "Lær mer om Kiwix", - "about-release-title": "Utgave", - "about-source-code": "Denne programvaren er utgitt under vilkårene i GNU General Public License versjon 3. Se kildekoden på Github.", - "about-version": "Versjon: {{VERSION}}", - "about-report-issue": "For å åpne et problem, gå til Kiwix-desktop issue tracker og åpne et problem som beskriver problemet ditt.", - "about-libraries-title": "Biblioteker", - "kiwix-server-running-message": "Kiwix-tjeneren kjører og kan nås i det lokale nettverket på:", - "kiwix-server-description": "Å starte en tjener lar andre datamaskiner i det lokale nettverket få tilgang til Kiwix-biblioteket med en standard nettleser.", - "fullscreen-notification": "Du er nå i fullskjermmodus. Trykk ESC for å avslutte!", - "online-files": "Online-filer", - "local-files": "Lokale filer", - "hide": "Skjul", - "open-in-browser": "Åpne i nettleser", - "start-kiwix-server": "Start Kiwix-tjener", - "stop-kiwix-server": "Stopp Kiwix-tjener", - "all": "Alle", - "fulltext-search": "Fulltekstsøk", - "pictures": "Bilder", - "videos": "Videoer", - "ftindex": "Fulltekstindeks", - "yes": "ja", - "no": "nei", - "open-link-in-web-browser": "Åpne lenken i nettleseren", - "download-dir-dialog-title": "Er du sikker på at du vil endre nedlastingsmappen?", - "invalid-port": "Ugyldig port", - "zim-open-fail-title": "Ugyldig fil", - "move-files-to-trash": "Flytt slettede filer til papirkurven", - "move-files-to-trash-text": "Denne handlingen vil flytte filen til papirkurven.", - "perma-delete-files-text": "Denne handlingen vil slette filen uopprettelig." -} +{ + "@metadata": { + "authors": [ + "Event", + "Johshh" + ] + }, + "error-title": "Feil", + "new-tab": "Ny fane", + "close-tab": "Lukk fane", + "close": "Lukk", + "reopen-closed-tab": "Åpne lukket fane igjen", + "browse-library": "Bla gjennom biblioteket", + "open-file": "Åpne fil", + "open-recent": "Åpne nylig", + "search-article": "Søk i artikkelen", + "search-in-library": "Søk i biblioteket", + "find-in-page": "Finn på siden", + "set-fullscreen": "Aktiver fullskjerm", + "quit-fullscreen": "Avslutt fullskjerm", + "table-of-content": "Innholdsfortegnelse", + "reading-list": "Leseliste", + "zoom-in": "Zoom inn", + "zoom-out": "Zoom ut", + "help": "Hjelp", + "feedback": "Tilbakemelding", + "report-a-bug": "Rapporter en feil", + "request-a-feature": "Be om en funksjon", + "about-kiwix": "Om Kiwix", + "donate-to-support-kiwix": "Doner for å støtte Kiwix", + "exit": "Avslutt", + "save-file-as-window-title": "Lagre fil som", + "download-finished-message": "Dokumentet er lastet ned.", + "file": "Fil", + "edit": "Rediger", + "view": "Vis", + "tools": "Verktøy", + "window-title": "Bibliotek - Kiwix", + "search": "Søk", + "browse-directory": "Bla gjennom katalogen", + "settings": "Innstillinger", + "back": "Tilbake", + "forward": "Fremover", + "search-files": "Søk etter filer", + "title": "Tittel", + "size": "Størrelse", + "date": "Dato", + "content-type": "Innholdstype", + "reset-sort": "tilbakestill sortering", + "open": "Åpen", + "delete": "Slett", + "pause": "Pause", + "cancel": "Avbryt", + "apply": "Bruk", + "port-for-local-kiwix-server-setting": "Port for lokal Kiwix-tjener", + "zoom-level-setting": "Standard zoomnivå", + "download-directory-setting": "Last ned mappe", + "monitor-directory-setting": "Overvåk mappe", + "reset": "Nullstill", + "clear": "Tøm", + "browse": "Bla gjennom", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-learn-more-about-kiwix": "Lær mer om Kiwix", + "about-release-title": "Utgave", + "about-source-code": "Denne programvaren er utgitt under vilkårene i GNU General Public License versjon 3. Se kildekoden på Github.", + "about-version": "Versjon: {{VERSION}}", + "about-report-issue": "For å åpne et problem, gå til Kiwix-desktop issue tracker og åpne et problem som beskriver problemet ditt.", + "about-libraries-title": "Biblioteker", + "kiwix-server-running-message": "Kiwix-tjeneren kjører og kan nås i det lokale nettverket på:", + "kiwix-server-description": "Å starte en tjener lar andre datamaskiner i det lokale nettverket få tilgang til Kiwix-biblioteket med en standard nettleser.", + "fullscreen-notification": "Du er nå i fullskjermmodus. Trykk ESC for å avslutte!", + "online-files": "Online-filer", + "local-files": "Lokale filer", + "hide": "Skjul", + "open-in-browser": "Åpne i nettleser", + "start-kiwix-server": "Start Kiwix-tjener", + "stop-kiwix-server": "Stopp Kiwix-tjener", + "all": "Alle", + "fulltext-search": "Fulltekstsøk", + "pictures": "Bilder", + "videos": "Videoer", + "ftindex": "Fulltekstindeks", + "yes": "ja", + "no": "nei", + "open-link-in-web-browser": "Åpne lenken i nettleseren", + "download-dir-dialog-title": "Er du sikker på at du vil endre nedlastingsmappen?", + "invalid-port": "Ugyldig port", + "zim-open-fail-title": "Ugyldig fil", + "move-files-to-trash": "Flytt slettede filer til papirkurven", + "move-files-to-trash-text": "Denne handlingen vil flytte filen til papirkurven.", + "perma-delete-files-text": "Denne handlingen vil slette filen uopprettelig." +} diff --git a/resources/i18n/nl.json b/resources/i18n/nl.json index b66c9ab5f..818e019b5 100644 --- a/resources/i18n/nl.json +++ b/resources/i18n/nl.json @@ -1,188 +1,188 @@ -{ - "@metadata": { - "authors": [ - "Dutchy45", - "HanV", - "Mainframe98", - "McDutchie", - "SPQRobin" - ] - }, - "name": "Nederlands", - "error-title": "Fout", - "error-downloader-window-title": "Kan downloader niet aanmaken", - "error-downloader-launch-message": "Het is onmogelijk om de downloader te starten. Kiwix-desktop start wel, maar de downloadfuncties werken niet!", - "error-launch-server-message": "Er is een fout opgetreden!", - "error-archive": "Kan het archief niet ophalen", - "open-zim": "ZIM-bestand openen", - "local-kiwix-server": "Lokale Kiwix-server", - "random-article": "Willekeurig artikel", - "random-article-error": "Fout bij openen willekeurig artikel.", - "home-page": "Startpagina", - "main-menu": "Hoofdmenu", - "print": "Afdrukken", - "print-page-error": "Er is een fout opgetreden tijdens het afdrukken.", - "new-tab": "Nieuw tabblad", - "close-tab": "Tabblad sluiten", - "close": "Sluiten", - "reopen-closed-tab": "Gesloten tabblad heropenen", - "browse-library": "Bibliotheek verkennen", - "open-file": "Bestand openen", - "open-recent": "Recentelijk geopend", - "search-article": "Artikel zoeken", - "search-in-library": "In bibliotheek zoeken", - "find-in-page": "In pagina zoeken", - "set-fullscreen": "Volledig scherm activeren", - "quit-fullscreen": "Volledig scherm verlaten", - "table-of-content": "Inhoudsopgave", - "add-bookmark": "Aan de leeslijst toevoegen", - "remove-bookmark": "Uit de leeslijst verwijderen", - "reading-list": "Leeslijst", - "zoom-in": "Inzoomen", - "zoom-out": "Uitzoomen", - "zoom-reset": "Tekstgrootte terugzetten", - "help": "Hulp", - "feedback": "Terugkoppeling", - "report-a-bug": "Probleem melden", - "request-a-feature": "Een functie aanvragen", - "about-kiwix": "Over Kiwix", - "donate-to-support-kiwix": "Doneer om Kiwix te steunen", - "exit": "Afsluiten", - "save-file-as-window-title": "Bestand opslaan als", - "download-finished-message": "Het document is gedownload.", - "file": "Bestand", - "edit": "Bewerken", - "view": "Beeld", - "tools": "Hulpmiddelen", - "window-title": "Bibliotheek - Kiwix", - "search": "Zoeken", - "browse-directory": "Door map bladeren", - "settings": "Instellingen", - "back": "Terug", - "forward": "Volgende", - "search-files": "Bestanden zoeken", - "title": "Titel", - "size": "Grootte", - "date": "Datum", - "content-type": "Inhoudstype", - "reset-sort": "sortering opnieuw instellen", - "open": "Openen", - "delete": "Verwijderen", - "download": "Downloaden", - "resume": "Hervatten", - "pause": "Pauzeren", - "cancel": "Annuleren", - "apply": "Toepassen", - "port-for-local-kiwix-server-setting": "Poort voor lokale Kiwix-server", - "zoom-level-setting": "Standaard zoomniveau", - "download-directory-setting": "Downloaddirectory", - "monitor-directory-setting": "Te volgen directory", - "reset": "Opnieuw instellen", - "clear": "Wissen", - "browse": "Bladeren", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Met Kiwix hebt u de hele Wikipedia bij de hand, waar u ook gaat! Op een boot, in niemandsland, of in de gevangenis: Kiwix geeft u toegang tot alle menselijke kennis. U hebt geen internet nodig, want alles wordt op uw computer opgeslagen.", - "about-learn-more-about-kiwix": "Meer informatie over Kiwix", - "about-release-title": "Uitgave", - "about-source-code": "Deze software is uitgebracht onder de voorwaarden van de GNU General Public License versie 3. Bekijk de broncode op Github .", - "about-version": "Versie: {{VERSION}}", - "about-reporting-problem-title": "Problemen melden", - "about-report-issue": "Om een probleem te melden, gaat u naar Kiwix-desktop issue tracker en opent u een melding met een beschrijving uw probleem.", - "about-report-issue-2": "Vermeld ook de versie in de melding.", - "about-libraries-title": "Bibliotheken", - "kiwix-server-running-message": "De Kiwix-server is actief en kan op het lokale netwerk worden benaderd via:", - "kiwix-server-description": "Door een server te starten, kunnen andere computers in het lokale netwerk met een standaard webbrowser toegang krijgen tot uw Kiwix-bibliotheek.", - "fullscreen-notification": "U bevindt zich nu in de modus Volledig scherm. Druk op ESC om te stoppen!", - "online-files": "Online-bestanden", - "local-files": "Lokale bestanden", - "category": "Categorie", - "language": "Taal", - "hide": "Verbergen", - "open-in-browser": "In browser openen", - "start-kiwix-server": "Kiwix-server starten", - "stop-kiwix-server": "Kiwix-server stoppen", - "all": "Alle", - "all_ips": "Beide (Dual Stack-modus)", - "ipv4": "Alleen IPv4", - "ipv6": "Alleen IPv6", - "fulltext-search": "In volledige tekst zoeken", - "pictures": "Afbeeldingen", - "videos": "Video’s", - "ftindex": "Index volledige tekst", - "details": "Volledig artikel", - "yes": "ja", - "no": "nee", - "ok": "OK", - "no-filter": "geen filter", - "open-link-in-web-browser": "Koppeling openen in webbrowser", - "open-link-new-tab": "Koppeling openen in nieuwe tab", - "download-dir-dialog-title": "Weet u zeker dat u de downloaddirectory wilt wijzigen?", - "download-dir-dialog-msg": "Het pad naar de nieuwe downloaddirectory wordt:\n{{DIRECTORY}}", - "invalid-port": "Ongeldige poort", - "zim-open-fail-title": "Ongeldig bestand", - "zim-open-fail-text": "Het ZIM-bestand {{ZIM}} kan niet correct worden geopend. Het wordt uit uw bibliotheek verwijderd.", - "monitor-dir-dialog-title": "Weet u zeker dat u de te volgen directory wilt wijzigen?", - "monitor-dir-dialog-msg": "Het pad naar de nieuwe te volgen directory wordt:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Weet u zeker dat u de te volgen directory wilt wissen?", - "path-was-copied": "Pad is gekopieerd", - "monitor-clear-dir-dialog-msg": "Hierdoor stopt het controleren van de te volgen directory op nieuwe ZIM-bestanden.", - "monitor-directory-tooltip": "Alle ZIM-bestanden in deze directory worden automatisch aan de bibliotheek toegevoegd.", - "next-tab": "Naar het volgende tabblad", - "previous-tab": "Naar het vorige tabblad", - "cancel-download": "Downloaden annuleren", - "cancel-download-text": "Weet u zeker dat u het downloaden van {{ZIM}} wilt annuleren?", - "delete-book": "Boek verwijderen", - "delete-book-text": "Weet u zeker dat u {{ZIM}} wilt verwijderen?", - "download-storage-error": "Opslagfout", - "download-storage-error-text": "Het systeem heeft niet voldoende opslagruimte beschikbaar.", - "download-exceeds-max-file-size": "De downloadgrootte overschrijdt de maximale bestandsgrootte die het doelbestandssysteem aankan.", - "download-dir-missing": "De downloaddirectory bestaat niet.", - "download-dir-not-writable": "De downloaddirectory is niet beschrijfbaar.", - "download-unavailable": "Download niet beschikbaar", - "download-unavailable-text": "Deze download is niet beschikbaar.", - "open-book": "Boek openen", - "download-book": "Boek downloaden", - "pause-download": "Downloaden pauzeren", - "resume-download": "Downloaden hervatten", - "open-folder": "Map openen", - "couldnt-open-location": "Kan locatie niet openen", - "couldnt-open-location-text": "Kiwix kan map {{FOLDER}} niet openen", - "move-files-to-trash": "Verwijderde bestanden naar de prullenbak verplaatsen", - "move-files-to-trash-text": "Met deze handeling wordt het bestand naar de prullenbak verplaatst.", - "perma-delete-files-text": "Met deze handeling wordt het bestand definitief verwijderd.", - "clear-filter": "De momenteel ingestelde filters wissen", - "language-searcher-placeholder": "Filteren op taal", - "category-searcher-placeholder": "Filteren op categorie", - "content-type-searcher-placeholder": "Filteren op inhoudstype", - "no-details": "Alleen introductie", - "no-pictures": "Geen afbeeldingen", - "no-videos": "Geen video’s", - "open-previous-tabs-at-startup": "Vorige tabbladen openen bij het opstarten", - "preview-book-in-web-browser": "Voorvertoning boek in webbrowser", - "file-not-found-title": "ZIM-bestand niet gevonden", - "file-not-found-text": "Het ZIM-bestand bestaat niet of is niet leesbaar", - "zim-id": "ZIM-ID", - "zim-name": "ZIM-naam", - "zim-path": "ZIM-bestandspad", - "bad-zim-file-error-page-title": "Onjuist ZIM-bestand", - "bad-zim-file-error-page-text": "Het ZIM-bestand is foutief of beschadigd.", - "zim-entry-path": "ZIM-toegangspad", - "export-reading-list": "Leeslijst exporteren", - "export-reading-list-error": "Er is een fout opgetreden tijdens het exporteren van de leeslijst.", - "import-reading-list": "Leeslijst importeren", - "import-reading-list-error": "Er is een fout opgetreden tijdens het importeren van de leeslijst.", - "disable-sandbox": "Kiwix is gestart vanaf een netwerkstation. Dit gaat niet samen met de veiligheidsafzondering (sandbox) van de browser. Daarom wordt de sandbox uitgeschakeld. Wilt u doorgaan?", - "save-page-as": "Opslaan als…", - "portable-disabled-tooltip": "Functie uitgeschakeld in draagbare modus", - "scroll-next-tab": "Naar het volgende tabblad scrollen", - "scroll-previous-tab": "Naar het vorige tabblad scrollen", - "kiwix-search": "Kiwix-zoekopdracht", - "search-options": "Zoekopties", - "read-article": "Artikel lezen", - "read-text": "Geselecteerde tekst lezen", - "read-stop": "Stoppen met lezen", - "stop": "Stoppen", - "voice": "Stem", - "select-read-voice": "Kies leesstem", - "select-read-language": "Kies leestaal" -} +{ + "@metadata": { + "authors": [ + "Dutchy45", + "HanV", + "Mainframe98", + "McDutchie", + "SPQRobin" + ] + }, + "name": "Nederlands", + "error-title": "Fout", + "error-downloader-window-title": "Kan downloader niet aanmaken", + "error-downloader-launch-message": "Het is onmogelijk om de downloader te starten. Kiwix-desktop start wel, maar de downloadfuncties werken niet!", + "error-launch-server-message": "Er is een fout opgetreden!", + "error-archive": "Kan het archief niet ophalen", + "open-zim": "ZIM-bestand openen", + "local-kiwix-server": "Lokale Kiwix-server", + "random-article": "Willekeurig artikel", + "random-article-error": "Fout bij openen willekeurig artikel.", + "home-page": "Startpagina", + "main-menu": "Hoofdmenu", + "print": "Afdrukken", + "print-page-error": "Er is een fout opgetreden tijdens het afdrukken.", + "new-tab": "Nieuw tabblad", + "close-tab": "Tabblad sluiten", + "close": "Sluiten", + "reopen-closed-tab": "Gesloten tabblad heropenen", + "browse-library": "Bibliotheek verkennen", + "open-file": "Bestand openen", + "open-recent": "Recentelijk geopend", + "search-article": "Artikel zoeken", + "search-in-library": "In bibliotheek zoeken", + "find-in-page": "In pagina zoeken", + "set-fullscreen": "Volledig scherm activeren", + "quit-fullscreen": "Volledig scherm verlaten", + "table-of-content": "Inhoudsopgave", + "add-bookmark": "Aan de leeslijst toevoegen", + "remove-bookmark": "Uit de leeslijst verwijderen", + "reading-list": "Leeslijst", + "zoom-in": "Inzoomen", + "zoom-out": "Uitzoomen", + "zoom-reset": "Tekstgrootte terugzetten", + "help": "Hulp", + "feedback": "Terugkoppeling", + "report-a-bug": "Probleem melden", + "request-a-feature": "Een functie aanvragen", + "about-kiwix": "Over Kiwix", + "donate-to-support-kiwix": "Doneer om Kiwix te steunen", + "exit": "Afsluiten", + "save-file-as-window-title": "Bestand opslaan als", + "download-finished-message": "Het document is gedownload.", + "file": "Bestand", + "edit": "Bewerken", + "view": "Beeld", + "tools": "Hulpmiddelen", + "window-title": "Bibliotheek - Kiwix", + "search": "Zoeken", + "browse-directory": "Door map bladeren", + "settings": "Instellingen", + "back": "Terug", + "forward": "Volgende", + "search-files": "Bestanden zoeken", + "title": "Titel", + "size": "Grootte", + "date": "Datum", + "content-type": "Inhoudstype", + "reset-sort": "sortering opnieuw instellen", + "open": "Openen", + "delete": "Verwijderen", + "download": "Downloaden", + "resume": "Hervatten", + "pause": "Pauzeren", + "cancel": "Annuleren", + "apply": "Toepassen", + "port-for-local-kiwix-server-setting": "Poort voor lokale Kiwix-server", + "zoom-level-setting": "Standaard zoomniveau", + "download-directory-setting": "Downloaddirectory", + "monitor-directory-setting": "Te volgen directory", + "reset": "Opnieuw instellen", + "clear": "Wissen", + "browse": "Bladeren", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Met Kiwix hebt u de hele Wikipedia bij de hand, waar u ook gaat! Op een boot, in niemandsland, of in de gevangenis: Kiwix geeft u toegang tot alle menselijke kennis. U hebt geen internet nodig, want alles wordt op uw computer opgeslagen.", + "about-learn-more-about-kiwix": "Meer informatie over Kiwix", + "about-release-title": "Uitgave", + "about-source-code": "Deze software is uitgebracht onder de voorwaarden van de GNU General Public License versie 3. Bekijk de broncode op Github .", + "about-version": "Versie: {{VERSION}}", + "about-reporting-problem-title": "Problemen melden", + "about-report-issue": "Om een probleem te melden, gaat u naar Kiwix-desktop issue tracker en opent u een melding met een beschrijving uw probleem.", + "about-report-issue-2": "Vermeld ook de versie in de melding.", + "about-libraries-title": "Bibliotheken", + "kiwix-server-running-message": "De Kiwix-server is actief en kan op het lokale netwerk worden benaderd via:", + "kiwix-server-description": "Door een server te starten, kunnen andere computers in het lokale netwerk met een standaard webbrowser toegang krijgen tot uw Kiwix-bibliotheek.", + "fullscreen-notification": "U bevindt zich nu in de modus Volledig scherm. Druk op ESC om te stoppen!", + "online-files": "Online-bestanden", + "local-files": "Lokale bestanden", + "category": "Categorie", + "language": "Taal", + "hide": "Verbergen", + "open-in-browser": "In browser openen", + "start-kiwix-server": "Kiwix-server starten", + "stop-kiwix-server": "Kiwix-server stoppen", + "all": "Alle", + "all_ips": "Beide (Dual Stack-modus)", + "ipv4": "Alleen IPv4", + "ipv6": "Alleen IPv6", + "fulltext-search": "In volledige tekst zoeken", + "pictures": "Afbeeldingen", + "videos": "Video’s", + "ftindex": "Index volledige tekst", + "details": "Volledig artikel", + "yes": "ja", + "no": "nee", + "ok": "OK", + "no-filter": "geen filter", + "open-link-in-web-browser": "Koppeling openen in webbrowser", + "open-link-new-tab": "Koppeling openen in nieuwe tab", + "download-dir-dialog-title": "Weet u zeker dat u de downloaddirectory wilt wijzigen?", + "download-dir-dialog-msg": "Het pad naar de nieuwe downloaddirectory wordt:\n{{DIRECTORY}}", + "invalid-port": "Ongeldige poort", + "zim-open-fail-title": "Ongeldig bestand", + "zim-open-fail-text": "Het ZIM-bestand {{ZIM}} kan niet correct worden geopend. Het wordt uit uw bibliotheek verwijderd.", + "monitor-dir-dialog-title": "Weet u zeker dat u de te volgen directory wilt wijzigen?", + "monitor-dir-dialog-msg": "Het pad naar de nieuwe te volgen directory wordt:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Weet u zeker dat u de te volgen directory wilt wissen?", + "path-was-copied": "Pad is gekopieerd", + "monitor-clear-dir-dialog-msg": "Hierdoor stopt het controleren van de te volgen directory op nieuwe ZIM-bestanden.", + "monitor-directory-tooltip": "Alle ZIM-bestanden in deze directory worden automatisch aan de bibliotheek toegevoegd.", + "next-tab": "Naar het volgende tabblad", + "previous-tab": "Naar het vorige tabblad", + "cancel-download": "Downloaden annuleren", + "cancel-download-text": "Weet u zeker dat u het downloaden van {{ZIM}} wilt annuleren?", + "delete-book": "Boek verwijderen", + "delete-book-text": "Weet u zeker dat u {{ZIM}} wilt verwijderen?", + "download-storage-error": "Opslagfout", + "download-storage-error-text": "Het systeem heeft niet voldoende opslagruimte beschikbaar.", + "download-exceeds-max-file-size": "De downloadgrootte overschrijdt de maximale bestandsgrootte die het doelbestandssysteem aankan.", + "download-dir-missing": "De downloaddirectory bestaat niet.", + "download-dir-not-writable": "De downloaddirectory is niet beschrijfbaar.", + "download-unavailable": "Download niet beschikbaar", + "download-unavailable-text": "Deze download is niet beschikbaar.", + "open-book": "Boek openen", + "download-book": "Boek downloaden", + "pause-download": "Downloaden pauzeren", + "resume-download": "Downloaden hervatten", + "open-folder": "Map openen", + "couldnt-open-location": "Kan locatie niet openen", + "couldnt-open-location-text": "Kiwix kan map {{FOLDER}} niet openen", + "move-files-to-trash": "Verwijderde bestanden naar de prullenbak verplaatsen", + "move-files-to-trash-text": "Met deze handeling wordt het bestand naar de prullenbak verplaatst.", + "perma-delete-files-text": "Met deze handeling wordt het bestand definitief verwijderd.", + "clear-filter": "De momenteel ingestelde filters wissen", + "language-searcher-placeholder": "Filteren op taal", + "category-searcher-placeholder": "Filteren op categorie", + "content-type-searcher-placeholder": "Filteren op inhoudstype", + "no-details": "Alleen introductie", + "no-pictures": "Geen afbeeldingen", + "no-videos": "Geen video’s", + "open-previous-tabs-at-startup": "Vorige tabbladen openen bij het opstarten", + "preview-book-in-web-browser": "Voorvertoning boek in webbrowser", + "file-not-found-title": "ZIM-bestand niet gevonden", + "file-not-found-text": "Het ZIM-bestand bestaat niet of is niet leesbaar", + "zim-id": "ZIM-ID", + "zim-name": "ZIM-naam", + "zim-path": "ZIM-bestandspad", + "bad-zim-file-error-page-title": "Onjuist ZIM-bestand", + "bad-zim-file-error-page-text": "Het ZIM-bestand is foutief of beschadigd.", + "zim-entry-path": "ZIM-toegangspad", + "export-reading-list": "Leeslijst exporteren", + "export-reading-list-error": "Er is een fout opgetreden tijdens het exporteren van de leeslijst.", + "import-reading-list": "Leeslijst importeren", + "import-reading-list-error": "Er is een fout opgetreden tijdens het importeren van de leeslijst.", + "disable-sandbox": "Kiwix is gestart vanaf een netwerkstation. Dit gaat niet samen met de veiligheidsafzondering (sandbox) van de browser. Daarom wordt de sandbox uitgeschakeld. Wilt u doorgaan?", + "save-page-as": "Opslaan als…", + "portable-disabled-tooltip": "Functie uitgeschakeld in draagbare modus", + "scroll-next-tab": "Naar het volgende tabblad scrollen", + "scroll-previous-tab": "Naar het vorige tabblad scrollen", + "kiwix-search": "Kiwix-zoekopdracht", + "search-options": "Zoekopties", + "read-article": "Artikel lezen", + "read-text": "Geselecteerde tekst lezen", + "read-stop": "Stoppen met lezen", + "stop": "Stoppen", + "voice": "Stem", + "select-read-voice": "Kies leesstem", + "select-read-language": "Kies leestaal" +} diff --git a/resources/i18n/nqo.json b/resources/i18n/nqo.json index ee8b5a8a3..3620f5020 100644 --- a/resources/i18n/nqo.json +++ b/resources/i18n/nqo.json @@ -1,119 +1,119 @@ -{ - "@metadata": { - "authors": [ - "Aboubacar.banamoridou.toure", - "Lancine.kounfantoh.fofana", - "ߒߓߋ߫ ߝߏߝߣߊ߫" - ] - }, - "name": "ߒߞߏ", - "error-title": "ߝߎ߬ߕߎ߲߬ߕߌ", - "error-downloader-window-title": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߕߍ߫ ߛߐ߲߬ ߛߌ߲ߘߌ߫ ߟߊ߫", - "error-downloader-launch-message": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߡߊ߫ ߛߐ߲߬ ߟߊߥߟߌ߬ ߟߊ߫߸ ߕߟߋ߬ߓߊ߮-ߞߥߌߞߛ ߘߌ߫ ߣߊ߬ ߘߊߡߌ߬ߣߊ߬ ߞߏ߬ߣߌ߲߫ ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߗߋߘߊ ߟߋ߬ ߡߎ߰ߡߍ ߕߍߣߊ߬ ߓߊ߯ߙߊ߫ ߟߊ߫߹", - "error-launch-server-message": "ߝߎ߬ߕߎ߲߬ߕߌ ߘߏ߫ ߓߘߊ߫ ߓߌ߬ߟߵߊ߬ ߘߐ߫", - "error-archive": "ߛߓߍߞߘߐ ߕߍ߫ ߛߐ߲߬ ߡߊߛߐ߬ߘߐ߲߫ ߠߊ߫", - "open-zim": "ߖ߭ߌߡ ߞߐߕߐ߮ ߘߊߦߟߍ߬", - "local-kiwix-server": "ߘߌ߲߬ߞߌߙߊ ߞߥߌߞߛ ߡߊߛߐߟߊ߲", - "random-article": "ߓߍ߲߬ߛߋ߲߬ߡߊ߫ ߞߎߡߘߊ", - "random-article-error": "ߓߍ߲߬ߛߋ߲߬ߡߊ߬ ߞߎߡߘߊ ߘߊߦߟߍߟߌ ߓߘߊ߫ ߗߌߙߏ߲߫.", - "home-page": "ߓߏ߬ߟߏ߲߬ߘߊ ߞߐߜߍ", - "main-menu": "ߓߏ߬ߟߏ߲߬ߘߊ ߛߙߍߘߍ", - "print": "ߜߌ߬ߙߌ߲߬ߘߌ߬ߟߌ", - "print-page-error": "ߝߎ߬ߕߎ߲߬ߕߌ ߘߏ߫ ߓߘߊ߫ ߓߌ߬ߟߴߊ߬ ߘߐ߫ ߞߊ߬ ߜߌ߬ߙߌ߲߬ߘߌ߬ߟߌ ߕߴߌ ߘߐ߫.", - "new-tab": "ߝߢߐߘߊ߫ ߞߎߘߊ", - "close-tab": "ߝߢߐߘߊ ߘߊߕߎ߲߯", - "close": "ߊ߬ ߘߊߕߎ߲߯", - "reopen-closed-tab": "ߘߊߞߎ߲߫ ߘߊߕߎ߲߯ߣߍ߲ ߘߊߦߟߍ߬ ߞߎߘߊ߫ ߘߌ߫", - "browse-library": "ߛߓߍߘߊ ߛߏ߲߯ߓߊ߫", - "open-file": "ߞߐߕߐ߮ ߘߊߦߟߍ߬", - "open-recent": "ߞߐ߯ߟߊ߫ ߘߊߦߟߍߣߍ߲", - "search-article": "ߞߎߡߘߊ ߢߌߣߌ߲߫", - "search-in-library": "ߕߌߙߌ߲ߠߌ߲ ߞߍ߫ ߛߓߍߘߊ ߞߣߐ߫", - "find-in-page": "ߊ߬ ߢߌߣߌ߲߫ ߞߐߜߍ ߘߐ߫", - "set-fullscreen": "ߘߊ߬ߣߊ߲߬ߥߟߊ ߘߝߊߣߍ߲ ߞߍߗߏ߮", - "quit-fullscreen": "ߓߐ߫ ߘߊ߬ߣߊ߲߬ߥߟߊ ߘߝߊߣߍ߲ ߘߐ߫", - "table-of-content": "ߞߣߐߘߐ ߟߎ߬ ߥߟߊ߬ߟߋ߲", - "reading-list": "ߘߐ߬ߞߊ߬ߙߊ߲߬ߠߌ߲߬ ߛߙߍߘߍ", - "zoom-in": "ߊ߬ ߡߊߛߊ߬ߡߊ߲߫", - "zoom-out": "ߊ߬ ߡߊߓߐ߫", - "zoom-reset": "ߖ߭ߎߡ ߢߡߊߛߊ߬ߦߌ", - "help": "ߘߍ߬ߡߍ߲߬ߠߌ߲", - "feedback": "ߞߙߐ߬ߛߌ߬ߕߊ", - "report-a-bug": "ߝߍ߭ ߘߏ߫ ߟߊߞߏߝߐ߫", - "request-a-feature": "ߗߋߘߊ ߘߏ߫ ߡߊߢߌ߬ߣߌ߲߬ߞߊ߫", - "about-kiwix": "ߞߊ߬ ߓߍ߲߬ ߞߥߌߞߛ ߡߊ߬", - "donate-to-support-kiwix": "ߣߌ߬ߟߌ ߞߍ߫ ߞߊ߬ ߞߥߌߞߛ ߞߐߡߊߓߌ߲ߓߌ߲߫", - "exit": "ߓߐ߫", - "save-file-as-window-title": "ߞߐߕߐ߮ ߟߊߞߎ߲߬ߘߎ߬ ߦߏ߫", - "download-finished-message": "ߘߐ߬ߛߙߋ ߓߘߊ߫ ߓߊ߲߫ ߟߊߖߌ߰ ߟߴߌ ߞߎ߲߬.", - "file": "ߞߐߕߐ߮", - "edit": "ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߫", - "view": "ߊ߬ ߦߋ߫", - "tools": "ߖߐ߯ߙߊ߲ ߠߎ߬", - "window-title": "ߞߥߌߞߛ – ߛߓߍߘߊ", - "search": "ߢߌߣߌ߲ߠߌ߲", - "browse-directory": "ߘߐ߬ߛߙߋ ߘߏ߫ ߓߍ߬ߙߍ߲߬ߓߍߙߍ߲߫", - "settings": "ߟߊ߬ߓߍ߲߬ߢߐ߲߰ߡߦߊ߬ߘߊ ߟߎ߬", - "back": "ߌ ߞߐߛߊ߬ߦߌ߬", - "forward": "ߢߍߝߍ߬", - "search-files": "ߞߐߕߐ߮ ߟߎ߬ ߢߌߣߌ߲߫", - "title": "ߞߎ߲߬ߕߐ߮", - "size": "ߢߊ߲ߞߊ߲", - "date": "ߕߎ߬ߡߊ߬ߘߊ", - "content-type": "ߞߣߐߘߐ߫ ߛߎ߯ߦߊ", - "reset-sort": "ߢߣߊߕߐߡߐ߲ߠߌ߲ ߘߊߡߌ߬ߣߊ߬ ߞߎߘߊ߫ ߘߌ߫", - "open": "ߊ߬ ߟߊߞߊ߬", - "delete": "ߊ߬ ߖߏ߬ߛߌ߬", - "download": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲", - "resume": "ߊ߬ ߘߊߡߌ߬ߣߊ߬", - "pause": "ߊ߬ ߟߊߟߐ߬", - "cancel": "ߊ߬ ߘߐߛߊ߬", - "apply": "ߊ߬ ߟߥߊߟߌ߫", - "port-for-local-kiwix-server-setting": "ߘߌ߲߬ߞߌߙߊ ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߘߏ߲߬ߘߊ", - "zoom-level-setting": "ߓߐߛߎ߲ ߡߛߊ߬ߡߊ߲߬ߠߌ߲ ߞߊߓߋ", - "download-directory-setting": "ߟߊߖߌ߰ߒ߬ߞߎ߲߬ߣߍ߲ ߠߎ߫ ߘߐ߬ߛߙߋ", - "monitor-directory-setting": "ߘߏ߲߬ߖߟߎ߬ߡߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ", - "reset": "ߊ߬ ߡߊߛߊ߬ߦߌ߬", - "clear": "ߊ߬ ߖߏ߰ߛߌ߬", - "browse": "ߊ߬ ߓߍ߬ߙߍ߲߬ߓߙߍ߲߫", - "about-kiwix-desktop-title": "ߕߟߋ߬ߓߊ߮ ߞߥߌߞߛ", - "about-kiwix-desktop-description": "ߞߥߌߞߛ ߦߴߊ߬ ߕߏ߫ ߟߊ߫ ߌ ߦߋ߫ ߛߋ߫ ߥߞߌߔߋߘߌߦߊ ߡߎ߰ߡߍ ߛߐ߬ߘߐ߲߬ ߠߊ߫ ߌ ߕߍ߮ ߘߐ߫߸ ߌ ߞߍ߫ ߘߊ߫ ߝߊ߲߬ ߛߌ߫߹ ߞߎߟߎ߲ ߞߣߐ߫߸ ߦߙߐ߫ ߟߊߞߏߟߏ߲ ߠߎ߬ ߕߍߡߊ߬ ߥߟߊ߫ ߥߛߎ (ߞߊ߬ߛߏ) ߟߊ߫߸ ߞߥߌߞߛ ߦߋ߫ ߝߙߍ߬ ߘߌ߫ ߟߴߌ ߡߊ߬ ߞߊ߬ ߡߐ߰ߣߍ߲߬ߝߌ߲ ߟߐ߲ߕߊ ߡߎ߰ߡߍ ߟߊߛߐ߬ߘߐ߲߬. ߌ ߡߊ߬ߞߏ߬ ߕߍ߫ ߓߟߐߟߐ߫ ߟߊ߫߸ ߊ߬ ߓߍ߯ ߟߊߡߙߊ߬ߣߍ߲߫ ߦߴߌ ߟߊ߫ ߕߟߋ߬ߓߊ߮ ߟߋ߬ ߞߣߐ߫.", - "about-learn-more-about-kiwix": "ߛߌߦߊߡߊ߲߫ ߘߐߞߊ߬ߙߊ߲߫ ߞߊ߬ ߓߍ߲߬ ߞߥߌߞߛ ߡߊ߬", - "about-release-title": "ߟߊ߬ߥߊ߲߬ߞߊ߬ߟߌ", - "about-source-code": "ߛߎ߲ߝߘߍ ߣߌ߲߬ ߠߊߥߊ߲߬ߞߊ߲߬ߣߍ߲߫ ߦߋ߫ GNU ߝߘߏ߬ߓߊ߬ ߕߦߊ ߟߊߞߊ߬ߝߏ߬ߣߍ߲ ߦߌߟߡߊ߫ ߃. (General Public Licence version 3.) ߟߋ߬ ߓߟߏ߫ ߞߘߐ߫. ߊ߬ ߓߐߖߎ ߘߏߝߙߍߕߍ ߦߋ߫ {{GITHUB_URL}} ߘߐ߫.", - "about-version": "ߦߌߟߡߊ {{VERSION}}", - "about-reporting-problem-title": "ߝߙߋߞߋ ߟߊߞߏߝߐ߫", - "about-report-issue": "ߖߐ߲߬ߛߊ߫ ߞߊ߬ ߝߙߋߞߋ ߘߏ߫ ߢߊߦߌ߬ߘߊ߬߸ ߥߊ߫ ߓߐ߫ {{TRACKER_URL}}ߞߥߌߞߛ-ߕߟߋ߬ߓߊ߮ ߝߙߋߞߋ ߢߊߦߌߘߊߟߊ߲ ߏ߬ ߞߐ߫ ߞߊ߬ ߓߐ߲ߘߊ ߘߏ߫ ߘߊߦߟߍ߬ ߞߵߌ ߟߊ߫ ߝߙߋߞߋ ߛߓߍ߫.", - "about-report-issue-2": "ߞߥߌߞߛ ߦߌߟߡߊ ߞߏߝߐ߫ ߌ ߟߊ߫ ߞߐ߯ߡߌ߲ߠߌ߲ ߘߐ߫ ߖߊ߰ߣߌ߲߫.", - "about-libraries-title": "ߛߓߍߘߊ", - "kiwix-server-running-message": "ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߦߋ߫ ߥߊ ߞߊ߲߬ ߊ߬ ߣߴߊ߬ ߘߌ߫ ߛߋ߫ ߟߊߛߐ߬ߘߐ߲߬ ߠߊ߫ ߘߌ߲߬ߞߌߙߊ ߓߟߐߟߐ ߟߊ߫:", - "kiwix-server-description": "ߡߊ߬ߛߐ߬ߟߊ߲ ߘߊߡߌߣߊߟߌ ߦߋ߫ ߕߟߋ߬ߓߊ߮ ߕߐ߭ ߟߎ߬ ߟߊߘߌ߬ߢߍ߬ ߟߊ߫ ߘߌ߲߬ߞߌߙߊ ߞߙߏ߬ߝߏ ߟߊ߫ ߞߵߌ ߟߊ߫ ߞߥߌߞߛ ߛߓߍߘߊ ߟߊߛߐ߬ߘߐ߲߬ ߛߎߡߊ߲ߘߊ߲ߕߊ ߓߟߐߟߐ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫.", - "fullscreen-notification": "ߌ ߦߋ߫ ߘߊ߬ߣߊ߲߬ߥߟߊ߬ ߘߝߊߣߍ߲ ߞߍߗߏ߮ ߟߋ߬ ߘߐ߫ ߡߎ߬ߕߎ߲߫. ߊ߬ ߞߘߐߞߊ߬ ߛߐ߲߬ߞߌ߲߫ ߞߊ߬ ߓߐ߫߹", - "local-files": "ߘߌ߲߬ߞߌߙߊ ߞߐߕߐ߮ ߟߎ߬", - "hide": "ߊ߬ ߢߡߊߘߏ߲߰", - "open-in-browser": "ߊ߬ ߟߊߞߊ߬ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫", - "start-kiwix-server": "ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߘߊߡߌ߬ߣߊ߬", - "stop-kiwix-server": "ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߟߊߟߐ߬", - "all": "ߊ߬ ߓߍ߯", - "fulltext-search": "ߞߟߏߜߍ߫ ߘߝߊߣߍ߲ ߢߌߣߌ߲ߠߌ߲", - "pictures": "ߖߌ߬ߦߊ߬ߓߍ ߟߎ߬", - "videos": "ߖߌ߬ߦߊ߬ߖߟߎ ߟߎ߬", - "ftindex": "ߛߓߍߟߌ߫ ߘߝߊߣߍ߲ ߡߊ߬ߛߙߋ", - "details": "ߞߎߡߘߊ߫ ߘߝߊߣߍ߲", - "yes": "ߐ߲߬ߐ߲ߐ߲߫", - "no": "ߊ߬ߦߌ߫", - "no-filter": "ߛߍ߲ߛߍ߲ߟߊ߲ ߕߴߦߋ߲߬", - "open-link-in-web-browser": "ߛߘߌ߬ߜߋ߲ ߘߊߦߟߍ߬ ߓߟߐߟߐ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫", - "download-dir-dialog-title": "ߊ߬ ߝߐ߫ ߌ ߘߍ߲߬ߞߣߍ߬ߦߊ߬ߣߍ߲߫ ߦߋ߫ ߞߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߘߏ߲߬ߖߟߎ߬ߡߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ ߡߊߝߊ߬ߟߋ߲߫؟", - "download-dir-dialog-msg": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߦߌ߬ߘߊ߬ߥߟߊ ߛߌߟߊ ߞߎߘߊ ߘߌߣߊ߬ ߞߍ߫: {{DIRECTORY}}", - "invalid-port": "ߘߏ߲߬ߘߊ߫ ߓߍ߲߬ߓߊߟߌ", - "zim-open-fail-title": "ߞߎ߲߬ߕߐ߮ ߓߍ߲߬ߓߊߟߌ", - "zim-open-fail-text": "ߖ߭ߌߡ ߞߐߕߐ߮ {{ZIM}} ߕߍߣߊ߬ ߛߐ߲߬ ߠߊ߫ ߟߊߞߊ߬ ߟߴߊ߬ ߢߊߓߘߍ ߡߊ߬. ߊ߬ ߘߌߣߊ߬ ߖߏ߰ߛߌ߫ ߌ ߟߊ߫ ߛߓߍߘߊ ߘߐ߫.", - "monitor-dir-dialog-title": "ߊ߬ ߝߐ߫ ߌ ߘߍ߲߬ߞߣߍ߬ߦߊ߬ߣߍ߲߫ ߦߋ߫ ߞߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߘߏ߲߬ߖߟߎ߬ߡߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ ߡߊߝߊ߬ߟߋ߲߫؟", - "monitor-dir-dialog-msg": "ߦߌ߬ߘߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ ߛߌߟߊ߫ ߞߎߘߊ ߘߌߣߊ߬ ߞߍ߫: {{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "ߊ߬ ߝߐ߫ ߌ ߘߍ߲߬ߞߣߍ߬ߦߊ߬ߣߍ߲߫ ߦߋ߫ ߞߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߦߌ߬ߘߊ߬ߟߌ߬ߟߊ߲ ߦߌ߬ߘߊ߬ߥߟߊ ߡߊߝߊ߬ߟߋ߲߫؟", - "monitor-clear-dir-dialog-msg": "ߏ߬ ߘߌߣߊ߬ ߦߌ߬ߘߊ߬ߟߌ߬ߟߊ߲ ߦߌ߬ߘߊ߬ߥߟߊ ߜߋ߬ߟߎ߲߬ߠߌ߲ ߠߊߟߐ߬ ߖ߭ߌߡ ߞߐߕߐ߯ ߞߎߘߊ ߟߎ߬ ߢߍ߫.", - "monitor-directory-tooltip": "ߖ߭ߌߡ ߞߐߕߐ߮ ߛߎ߯-ߎ-ߛߎ߫ ߦߋ߫ ߦߌ߬ߘߊ߬ߥߟߊ ߣߌ߲߬ ߘߐ߫߸ ߏ߬ ߟߎ߬ ߘߌߣߊ߬ ߓߌ߬ߟߊ߬ ߛߓߍߘߊ ߞߣߐ߫ ߞߍߒߖߘߍߦߋߓߟߏߡߊ߬.", - "next-tab": "ߕߊ߯ ߘߊߞߎ߲ ߢߍߕߍ ߘߐ߫", - "previous-tab": "ߕߊ߯ ߘߊߞߎ߲ ߕߊ߬ߡߌ߲߬ߣߍ߲ ߘߐ߫" -} +{ + "@metadata": { + "authors": [ + "Aboubacar.banamoridou.toure", + "Lancine.kounfantoh.fofana", + "ߒߓߋ߫ ߝߏߝߣߊ߫" + ] + }, + "name": "ߒߞߏ", + "error-title": "ߝߎ߬ߕߎ߲߬ߕߌ", + "error-downloader-window-title": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߕߍ߫ ߛߐ߲߬ ߛߌ߲ߘߌ߫ ߟߊ߫", + "error-downloader-launch-message": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߡߊ߫ ߛߐ߲߬ ߟߊߥߟߌ߬ ߟߊ߫߸ ߕߟߋ߬ߓߊ߮-ߞߥߌߞߛ ߘߌ߫ ߣߊ߬ ߘߊߡߌ߬ߣߊ߬ ߞߏ߬ߣߌ߲߫ ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߗߋߘߊ ߟߋ߬ ߡߎ߰ߡߍ ߕߍߣߊ߬ ߓߊ߯ߙߊ߫ ߟߊ߫߹", + "error-launch-server-message": "ߝߎ߬ߕߎ߲߬ߕߌ ߘߏ߫ ߓߘߊ߫ ߓߌ߬ߟߵߊ߬ ߘߐ߫", + "error-archive": "ߛߓߍߞߘߐ ߕߍ߫ ߛߐ߲߬ ߡߊߛߐ߬ߘߐ߲߫ ߠߊ߫", + "open-zim": "ߖ߭ߌߡ ߞߐߕߐ߮ ߘߊߦߟߍ߬", + "local-kiwix-server": "ߘߌ߲߬ߞߌߙߊ ߞߥߌߞߛ ߡߊߛߐߟߊ߲", + "random-article": "ߓߍ߲߬ߛߋ߲߬ߡߊ߫ ߞߎߡߘߊ", + "random-article-error": "ߓߍ߲߬ߛߋ߲߬ߡߊ߬ ߞߎߡߘߊ ߘߊߦߟߍߟߌ ߓߘߊ߫ ߗߌߙߏ߲߫.", + "home-page": "ߓߏ߬ߟߏ߲߬ߘߊ ߞߐߜߍ", + "main-menu": "ߓߏ߬ߟߏ߲߬ߘߊ ߛߙߍߘߍ", + "print": "ߜߌ߬ߙߌ߲߬ߘߌ߬ߟߌ", + "print-page-error": "ߝߎ߬ߕߎ߲߬ߕߌ ߘߏ߫ ߓߘߊ߫ ߓߌ߬ߟߴߊ߬ ߘߐ߫ ߞߊ߬ ߜߌ߬ߙߌ߲߬ߘߌ߬ߟߌ ߕߴߌ ߘߐ߫.", + "new-tab": "ߝߢߐߘߊ߫ ߞߎߘߊ", + "close-tab": "ߝߢߐߘߊ ߘߊߕߎ߲߯", + "close": "ߊ߬ ߘߊߕߎ߲߯", + "reopen-closed-tab": "ߘߊߞߎ߲߫ ߘߊߕߎ߲߯ߣߍ߲ ߘߊߦߟߍ߬ ߞߎߘߊ߫ ߘߌ߫", + "browse-library": "ߛߓߍߘߊ ߛߏ߲߯ߓߊ߫", + "open-file": "ߞߐߕߐ߮ ߘߊߦߟߍ߬", + "open-recent": "ߞߐ߯ߟߊ߫ ߘߊߦߟߍߣߍ߲", + "search-article": "ߞߎߡߘߊ ߢߌߣߌ߲߫", + "search-in-library": "ߕߌߙߌ߲ߠߌ߲ ߞߍ߫ ߛߓߍߘߊ ߞߣߐ߫", + "find-in-page": "ߊ߬ ߢߌߣߌ߲߫ ߞߐߜߍ ߘߐ߫", + "set-fullscreen": "ߘߊ߬ߣߊ߲߬ߥߟߊ ߘߝߊߣߍ߲ ߞߍߗߏ߮", + "quit-fullscreen": "ߓߐ߫ ߘߊ߬ߣߊ߲߬ߥߟߊ ߘߝߊߣߍ߲ ߘߐ߫", + "table-of-content": "ߞߣߐߘߐ ߟߎ߬ ߥߟߊ߬ߟߋ߲", + "reading-list": "ߘߐ߬ߞߊ߬ߙߊ߲߬ߠߌ߲߬ ߛߙߍߘߍ", + "zoom-in": "ߊ߬ ߡߊߛߊ߬ߡߊ߲߫", + "zoom-out": "ߊ߬ ߡߊߓߐ߫", + "zoom-reset": "ߖ߭ߎߡ ߢߡߊߛߊ߬ߦߌ", + "help": "ߘߍ߬ߡߍ߲߬ߠߌ߲", + "feedback": "ߞߙߐ߬ߛߌ߬ߕߊ", + "report-a-bug": "ߝߍ߭ ߘߏ߫ ߟߊߞߏߝߐ߫", + "request-a-feature": "ߗߋߘߊ ߘߏ߫ ߡߊߢߌ߬ߣߌ߲߬ߞߊ߫", + "about-kiwix": "ߞߊ߬ ߓߍ߲߬ ߞߥߌߞߛ ߡߊ߬", + "donate-to-support-kiwix": "ߣߌ߬ߟߌ ߞߍ߫ ߞߊ߬ ߞߥߌߞߛ ߞߐߡߊߓߌ߲ߓߌ߲߫", + "exit": "ߓߐ߫", + "save-file-as-window-title": "ߞߐߕߐ߮ ߟߊߞߎ߲߬ߘߎ߬ ߦߏ߫", + "download-finished-message": "ߘߐ߬ߛߙߋ ߓߘߊ߫ ߓߊ߲߫ ߟߊߖߌ߰ ߟߴߌ ߞߎ߲߬.", + "file": "ߞߐߕߐ߮", + "edit": "ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߫", + "view": "ߊ߬ ߦߋ߫", + "tools": "ߖߐ߯ߙߊ߲ ߠߎ߬", + "window-title": "ߞߥߌߞߛ – ߛߓߍߘߊ", + "search": "ߢߌߣߌ߲ߠߌ߲", + "browse-directory": "ߘߐ߬ߛߙߋ ߘߏ߫ ߓߍ߬ߙߍ߲߬ߓߍߙߍ߲߫", + "settings": "ߟߊ߬ߓߍ߲߬ߢߐ߲߰ߡߦߊ߬ߘߊ ߟߎ߬", + "back": "ߌ ߞߐߛߊ߬ߦߌ߬", + "forward": "ߢߍߝߍ߬", + "search-files": "ߞߐߕߐ߮ ߟߎ߬ ߢߌߣߌ߲߫", + "title": "ߞߎ߲߬ߕߐ߮", + "size": "ߢߊ߲ߞߊ߲", + "date": "ߕߎ߬ߡߊ߬ߘߊ", + "content-type": "ߞߣߐߘߐ߫ ߛߎ߯ߦߊ", + "reset-sort": "ߢߣߊߕߐߡߐ߲ߠߌ߲ ߘߊߡߌ߬ߣߊ߬ ߞߎߘߊ߫ ߘߌ߫", + "open": "ߊ߬ ߟߊߞߊ߬", + "delete": "ߊ߬ ߖߏ߬ߛߌ߬", + "download": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲", + "resume": "ߊ߬ ߘߊߡߌ߬ߣߊ߬", + "pause": "ߊ߬ ߟߊߟߐ߬", + "cancel": "ߊ߬ ߘߐߛߊ߬", + "apply": "ߊ߬ ߟߥߊߟߌ߫", + "port-for-local-kiwix-server-setting": "ߘߌ߲߬ߞߌߙߊ ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߘߏ߲߬ߘߊ", + "zoom-level-setting": "ߓߐߛߎ߲ ߡߛߊ߬ߡߊ߲߬ߠߌ߲ ߞߊߓߋ", + "download-directory-setting": "ߟߊߖߌ߰ߒ߬ߞߎ߲߬ߣߍ߲ ߠߎ߫ ߘߐ߬ߛߙߋ", + "monitor-directory-setting": "ߘߏ߲߬ߖߟߎ߬ߡߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ", + "reset": "ߊ߬ ߡߊߛߊ߬ߦߌ߬", + "clear": "ߊ߬ ߖߏ߰ߛߌ߬", + "browse": "ߊ߬ ߓߍ߬ߙߍ߲߬ߓߙߍ߲߫", + "about-kiwix-desktop-title": "ߕߟߋ߬ߓߊ߮ ߞߥߌߞߛ", + "about-kiwix-desktop-description": "ߞߥߌߞߛ ߦߴߊ߬ ߕߏ߫ ߟߊ߫ ߌ ߦߋ߫ ߛߋ߫ ߥߞߌߔߋߘߌߦߊ ߡߎ߰ߡߍ ߛߐ߬ߘߐ߲߬ ߠߊ߫ ߌ ߕߍ߮ ߘߐ߫߸ ߌ ߞߍ߫ ߘߊ߫ ߝߊ߲߬ ߛߌ߫߹ ߞߎߟߎ߲ ߞߣߐ߫߸ ߦߙߐ߫ ߟߊߞߏߟߏ߲ ߠߎ߬ ߕߍߡߊ߬ ߥߟߊ߫ ߥߛߎ (ߞߊ߬ߛߏ) ߟߊ߫߸ ߞߥߌߞߛ ߦߋ߫ ߝߙߍ߬ ߘߌ߫ ߟߴߌ ߡߊ߬ ߞߊ߬ ߡߐ߰ߣߍ߲߬ߝߌ߲ ߟߐ߲ߕߊ ߡߎ߰ߡߍ ߟߊߛߐ߬ߘߐ߲߬. ߌ ߡߊ߬ߞߏ߬ ߕߍ߫ ߓߟߐߟߐ߫ ߟߊ߫߸ ߊ߬ ߓߍ߯ ߟߊߡߙߊ߬ߣߍ߲߫ ߦߴߌ ߟߊ߫ ߕߟߋ߬ߓߊ߮ ߟߋ߬ ߞߣߐ߫.", + "about-learn-more-about-kiwix": "ߛߌߦߊߡߊ߲߫ ߘߐߞߊ߬ߙߊ߲߫ ߞߊ߬ ߓߍ߲߬ ߞߥߌߞߛ ߡߊ߬", + "about-release-title": "ߟߊ߬ߥߊ߲߬ߞߊ߬ߟߌ", + "about-source-code": "ߛߎ߲ߝߘߍ ߣߌ߲߬ ߠߊߥߊ߲߬ߞߊ߲߬ߣߍ߲߫ ߦߋ߫ GNU ߝߘߏ߬ߓߊ߬ ߕߦߊ ߟߊߞߊ߬ߝߏ߬ߣߍ߲ ߦߌߟߡߊ߫ ߃. (General Public Licence version 3.) ߟߋ߬ ߓߟߏ߫ ߞߘߐ߫. ߊ߬ ߓߐߖߎ ߘߏߝߙߍߕߍ ߦߋ߫ {{GITHUB_URL}} ߘߐ߫.", + "about-version": "ߦߌߟߡߊ {{VERSION}}", + "about-reporting-problem-title": "ߝߙߋߞߋ ߟߊߞߏߝߐ߫", + "about-report-issue": "ߖߐ߲߬ߛߊ߫ ߞߊ߬ ߝߙߋߞߋ ߘߏ߫ ߢߊߦߌ߬ߘߊ߬߸ ߥߊ߫ ߓߐ߫ {{TRACKER_URL}}ߞߥߌߞߛ-ߕߟߋ߬ߓߊ߮ ߝߙߋߞߋ ߢߊߦߌߘߊߟߊ߲ ߏ߬ ߞߐ߫ ߞߊ߬ ߓߐ߲ߘߊ ߘߏ߫ ߘߊߦߟߍ߬ ߞߵߌ ߟߊ߫ ߝߙߋߞߋ ߛߓߍ߫.", + "about-report-issue-2": "ߞߥߌߞߛ ߦߌߟߡߊ ߞߏߝߐ߫ ߌ ߟߊ߫ ߞߐ߯ߡߌ߲ߠߌ߲ ߘߐ߫ ߖߊ߰ߣߌ߲߫.", + "about-libraries-title": "ߛߓߍߘߊ", + "kiwix-server-running-message": "ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߦߋ߫ ߥߊ ߞߊ߲߬ ߊ߬ ߣߴߊ߬ ߘߌ߫ ߛߋ߫ ߟߊߛߐ߬ߘߐ߲߬ ߠߊ߫ ߘߌ߲߬ߞߌߙߊ ߓߟߐߟߐ ߟߊ߫:", + "kiwix-server-description": "ߡߊ߬ߛߐ߬ߟߊ߲ ߘߊߡߌߣߊߟߌ ߦߋ߫ ߕߟߋ߬ߓߊ߮ ߕߐ߭ ߟߎ߬ ߟߊߘߌ߬ߢߍ߬ ߟߊ߫ ߘߌ߲߬ߞߌߙߊ ߞߙߏ߬ߝߏ ߟߊ߫ ߞߵߌ ߟߊ߫ ߞߥߌߞߛ ߛߓߍߘߊ ߟߊߛߐ߬ߘߐ߲߬ ߛߎߡߊ߲ߘߊ߲ߕߊ ߓߟߐߟߐ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫.", + "fullscreen-notification": "ߌ ߦߋ߫ ߘߊ߬ߣߊ߲߬ߥߟߊ߬ ߘߝߊߣߍ߲ ߞߍߗߏ߮ ߟߋ߬ ߘߐ߫ ߡߎ߬ߕߎ߲߫. ߊ߬ ߞߘߐߞߊ߬ ߛߐ߲߬ߞߌ߲߫ ߞߊ߬ ߓߐ߫߹", + "local-files": "ߘߌ߲߬ߞߌߙߊ ߞߐߕߐ߮ ߟߎ߬", + "hide": "ߊ߬ ߢߡߊߘߏ߲߰", + "open-in-browser": "ߊ߬ ߟߊߞߊ߬ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫", + "start-kiwix-server": "ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߘߊߡߌ߬ߣߊ߬", + "stop-kiwix-server": "ߞߥߌߞߛ ߡߊߛߐߟߊ߲ ߟߊߟߐ߬", + "all": "ߊ߬ ߓߍ߯", + "fulltext-search": "ߞߟߏߜߍ߫ ߘߝߊߣߍ߲ ߢߌߣߌ߲ߠߌ߲", + "pictures": "ߖߌ߬ߦߊ߬ߓߍ ߟߎ߬", + "videos": "ߖߌ߬ߦߊ߬ߖߟߎ ߟߎ߬", + "ftindex": "ߛߓߍߟߌ߫ ߘߝߊߣߍ߲ ߡߊ߬ߛߙߋ", + "details": "ߞߎߡߘߊ߫ ߘߝߊߣߍ߲", + "yes": "ߐ߲߬ߐ߲ߐ߲߫", + "no": "ߊ߬ߦߌ߫", + "no-filter": "ߛߍ߲ߛߍ߲ߟߊ߲ ߕߴߦߋ߲߬", + "open-link-in-web-browser": "ߛߘߌ߬ߜߋ߲ ߘߊߦߟߍ߬ ߓߟߐߟߐ ߛߏ߲߯ߓߊߟߊ߲ ߠߊ߫", + "download-dir-dialog-title": "ߊ߬ ߝߐ߫ ߌ ߘߍ߲߬ߞߣߍ߬ߦߊ߬ߣߍ߲߫ ߦߋ߫ ߞߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߘߏ߲߬ߖߟߎ߬ߡߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ ߡߊߝߊ߬ߟߋ߲߫؟", + "download-dir-dialog-msg": "ߟߊ߬ߖߌ߰ߒ߬ߞߎ߲߬ߠߌ߲ ߦߌ߬ߘߊ߬ߥߟߊ ߛߌߟߊ ߞߎߘߊ ߘߌߣߊ߬ ߞߍ߫: {{DIRECTORY}}", + "invalid-port": "ߘߏ߲߬ߘߊ߫ ߓߍ߲߬ߓߊߟߌ", + "zim-open-fail-title": "ߞߎ߲߬ߕߐ߮ ߓߍ߲߬ߓߊߟߌ", + "zim-open-fail-text": "ߖ߭ߌߡ ߞߐߕߐ߮ {{ZIM}} ߕߍߣߊ߬ ߛߐ߲߬ ߠߊ߫ ߟߊߞߊ߬ ߟߴߊ߬ ߢߊߓߘߍ ߡߊ߬. ߊ߬ ߘߌߣߊ߬ ߖߏ߰ߛߌ߫ ߌ ߟߊ߫ ߛߓߍߘߊ ߘߐ߫.", + "monitor-dir-dialog-title": "ߊ߬ ߝߐ߫ ߌ ߘߍ߲߬ߞߣߍ߬ߦߊ߬ߣߍ߲߫ ߦߋ߫ ߞߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߘߏ߲߬ߖߟߎ߬ߡߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ ߡߊߝߊ߬ߟߋ߲߫؟", + "monitor-dir-dialog-msg": "ߦߌ߬ߘߊ߬ߟߌ ߦߌ߬ߘߊ߬ߥߟߊ ߛߌߟߊ߫ ߞߎߘߊ ߘߌߣߊ߬ ߞߍ߫: {{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "ߊ߬ ߝߐ߫ ߌ ߘߍ߲߬ߞߣߍ߬ߦߊ߬ߣߍ߲߫ ߦߋ߫ ߞߴߌ ߦߴߊ߬ ߝߍ߬ ߞߊ߬ ߦߌ߬ߘߊ߬ߟߌ߬ߟߊ߲ ߦߌ߬ߘߊ߬ߥߟߊ ߡߊߝߊ߬ߟߋ߲߫؟", + "monitor-clear-dir-dialog-msg": "ߏ߬ ߘߌߣߊ߬ ߦߌ߬ߘߊ߬ߟߌ߬ߟߊ߲ ߦߌ߬ߘߊ߬ߥߟߊ ߜߋ߬ߟߎ߲߬ߠߌ߲ ߠߊߟߐ߬ ߖ߭ߌߡ ߞߐߕߐ߯ ߞߎߘߊ ߟߎ߬ ߢߍ߫.", + "monitor-directory-tooltip": "ߖ߭ߌߡ ߞߐߕߐ߮ ߛߎ߯-ߎ-ߛߎ߫ ߦߋ߫ ߦߌ߬ߘߊ߬ߥߟߊ ߣߌ߲߬ ߘߐ߫߸ ߏ߬ ߟߎ߬ ߘߌߣߊ߬ ߓߌ߬ߟߊ߬ ߛߓߍߘߊ ߞߣߐ߫ ߞߍߒߖߘߍߦߋߓߟߏߡߊ߬.", + "next-tab": "ߕߊ߯ ߘߊߞߎ߲ ߢߍߕߍ ߘߐ߫", + "previous-tab": "ߕߊ߯ ߘߊߞߎ߲ ߕߊ߬ߡߌ߲߬ߣߍ߲ ߘߐ߫" +} diff --git a/resources/i18n/or.json b/resources/i18n/or.json index c8c25dc36..127426257 100644 --- a/resources/i18n/or.json +++ b/resources/i18n/or.json @@ -1,146 +1,146 @@ -{ - "@metadata": { - "authors": [ - "Gouri" - ] - }, - "name": "ଓଡି଼ଆ", - "error-title": "ତ୍ରୁଟି", - "error-downloader-window-title": "ଡାଉନଲୋଡର ସୃଷ୍ଟି କରିପାରିବେ ନାହିଁ", - "error-downloader-launch-message": "ଡାଉନଲୋଡର ଆରମ୍ଭ କରିବା ଅସମ୍ଭବ - କିୱିକ୍ସ - ଡେସ୍କଟପ ଆରମ୍ଭ ହେବ କିନ୍ତୁ ସମସ୍ତ ଡାଉନଲୋଡ୍ କାର୍ଯ୍ଯ଼ କାମ କରିବ ନାହିଁ", - "error-launch-server-message": "ଗୋଟିଏ ତ୍ରୁଟି ଘଟିଛି", - "error-archive": "ଅଭିଲେଖକୁ ପାଇପାରିଲା ନାହିଁ", - "open-zim": "ZIM ଫାଇଲକୁ ଖୋଲନ୍ତୁ", - "local-kiwix-server": "ସ୍ଥାନୀୟ କିୱିକ୍ସ ସର୍ଭର", - "random-article": "ଅନିୟମିତ ପ୍ରବନ୍ଧ", - "random-article-error": "ଏକ ଅନିୟମିତ ପ୍ରବନ୍ଧ ଖୋଲିବାରେ ବିଫଳ", - "home-page": "ମୂଳପୃଷ୍ଠା", - "main-menu": "ମୁଖ୍ୟ ମେନୁ", - "print": "ମୁଦ୍ରଣ କରନ୍ତୁ", - "print-page-error": "ମୁଦ୍ରଣ କରିବା ସମୟରେ ଏକ ତ୍ରୁଟି ଘଟିଛି", - "new-tab": "ନୂଆ ଟ୍ୟାବ୍", - "close-tab": "ଟ୍ୟାବ୍ ବନ୍ଦ କରନ୍ତୁ |", - "close": "ବନ୍ଦ କରନ୍ତୁ", - "reopen-closed-tab": "ବନ୍ଦ ଟ୍ୟାବ୍ ଖୋଲନ୍ତୁ", - "browse-library": "ଲାଇବ୍ରେରୀ ବ୍ରାଉଜ୍ କରନ୍ତୁ", - "open-file": "ଫାଇଲ୍ ଖୋଲନ୍ତୁ", - "open-recent": "ସମ୍ପ୍ରତି ଖୋଲନ୍ତୁ", - "search-article": "ପ୍ରବନ୍ଧ ଖୋଜ", - "search-in-library": "ଲାଇବ୍ରେରୀରେ ଖୋଜ", - "find-in-page": "ପୃଷ୍ଠାରେ ଖୋଜନ୍ତୁ", - "set-fullscreen": "ଫୁଲ୍ ସ୍କ୍ରିନ୍ ସେଟ୍ କରନ୍ତୁ", - "quit-fullscreen": "ଫୁଲ୍ ସ୍କ୍ରିନ୍ ଛାଡନ୍ତୁ", - "table-of-content": "ବିଷଯ଼ବସ୍ତୁ ସାରଣୀ", - "reading-list": "ପଠନ ତାଲିକା", - "zoom-in": "ବଡ଼ କରନ୍ତୁ", - "zoom-out": "ଛୋଟ କରନ୍ତୁ", - "zoom-reset": "ଜୁମ୍ ପୁନଃସ୍ଥାପନ", - "help": "ସାହାଯ୍ୟ", - "feedback": "ମତାମତ", - "report-a-bug": "ଏକ ତ୍ରୁଟି ରିପୋର୍ଟ କରନ୍ତୁ", - "request-a-feature": "ଏକ ବିଶେଷତା ପାଇଁ ଅନୁରୋଧ କରନ୍ତୁ", - "about-kiwix": "କିୱିକ୍ସ ବିଷଯ଼ରେ", - "donate-to-support-kiwix": "କିୱିକ୍ସକୁ ସମର୍ଥନ କରିବା ପାଇଁ ଦାନ କରନ୍ତୁ", - "exit": "ପ୍ରସ୍ଥାନ", - "save-file-as-window-title": "ଫାଇଲକୁ ଏହିପରି ସଂରକ୍ଷଣ କରନ୍ତୁ", - "download-finished-message": "ଡକ୍ଯ଼ୁମେଣ୍ଟଟି ଡାଉନଲୋଡ୍ ହୋଇଛି ।", - "file": "ଫାଇଲ", - "edit": "ସମ୍ପାଦନ କରନ୍ତୁ", - "view": "ଦେଖନ୍ତୁ", - "tools": "ଉପକରଣ", - "window-title": "ଲାଇବ୍ରେରୀ - କିୱିକ୍ସ", - "search": "ଖୋଜନ୍ତୁ", - "browse-directory": "ଡିରେକ୍ଟୋରୀ ବ୍ରାଉଜ୍ କରନ୍ତୁ", - "settings": "ସେଟିଂସମୂହ", - "back": "ପଛକୁ", - "forward": "ଆଗକୁ ବଢ଼ନ୍ତୁ", - "search-files": "ଫାଇଲଗୁଡିକ ସନ୍ଧାନ କରନ୍ତୁ", - "title": "ଶୀର୍ଷକ", - "size": "ଆକାର", - "date": "ତାରିଖ", - "content-type": "ସୂଚୀପତ୍ର", - "reset-sort": "ପୁନଃନିର୍ଦ୍ଧାରଣ କ୍ରମ", - "open": "ଖୋଲନ୍ତ", - "delete": "ଲିଭାନ୍ତୁ", - "download": "ଡାଉନଲୋଡ଼", - "resume": "ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ", - "pause": "ବିରାମ", - "cancel": "ବାତିଲ", - "apply": "ଆବେଦନ କରନ୍ତୁ", - "port-for-local-kiwix-server-setting": "ସ୍ଥାନୀଯ଼ କିୱିକ୍ସ ସର୍ଭର ପାଇଁ ପୋର୍ଟ", - "zoom-level-setting": "ଡିଫଲ୍ଟ ଜୁମ୍ ସ୍ତର", - "download-directory-setting": "ଡିରେକ୍ଟୋରୀ ଡାଉନଲୋଡ୍ କରନ୍ତୁ", - "monitor-directory-setting": "ଡିରେକ୍ଟୋରୀକୁ ମନିଟର୍ କରନ୍ତୁ", - "reset": "ପୁନଃସ୍ଥାପନ କରନ୍ତୁ", - "clear": "ଲିଭାନ୍ତୁ", - "browse": "ଖୋଜିବା", - "about-kiwix-desktop-title": "କିୱିକ୍ସ ଡେସ୍କଟପ", - "about-kiwix-desktop-description": "ଆପଣ ଯେଉଁଠିକି ଯାଆନ୍ତୁ , କିୱିକ୍ସ ଆପଣଙ୍କୁ ସମ୍ପୂର୍ଣ୍ଣ ୱିକିପିଡିଆ ହାତରେ ରଖିବାକୁ ଅନୁମତି ଦେଇଥାଏ । କୌଣସି ସ୍ଥାନର ମଝିରେ କିମ୍ବା ଜେଲ୍ରେ ଥିବା ଏକ ଡଙ୍ଗାରେ , କିୱିକ୍ସ ଆପଣଙ୍କୁ ସମସ୍ତ ମାନବ ଜ୍ଞାନକୁ ପ୍ରବେଶ ଦେଇଥାଏ । ଆପଣଙ୍କୁ ଇଣ୍ଟରନେଟ୍ର ଆବଶ୍ଯ଼କତା ନାହିଁ , କାରଣ ସବୁକିଛି ଆପଣଙ୍କ କମ୍ପ୍ଯ଼ୁଟରରେ ଗଚ୍ଛିତ ଅଛି ।", - "about-learn-more-about-kiwix": "କିୱିକ୍ସ ବିଷୟରେ ଅଧିକ ଜାଣନ୍ତୁ", - "about-release-title": "ରିଲିଜ୍", - "about-source-code": "ଏହି ସଫ୍ଟୱେର୍ GNU ଜେନେରାଲ୍ ପବ୍ଲିକ୍ ଲାଇସେନ୍ସ ସଂସ୍କରଣ 3 ଅନୁଯାୟୀ ପ୍ରକାଶିତ ହୋଇଛି | Github ରେ ଉତ୍ସ କୋଡ୍ ଦେଖନ୍ତୁ |", - "about-version": "ସଂସ୍କରଣ: {{VERSION}}", - "about-reporting-problem-title": "ରିପୋର୍ଟ ସମସ୍ୟା", - "about-report-issue": "ଏକ ସମସ୍ୟା ଖୋଲିବାକୁ, କିୱିକ୍ସ-ଡେସ୍କଟପ୍ ଇସ୍ୟୁ ଟ୍ରାକର୍ ପରିଦର୍ଶନ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର ସମସ୍ୟା ବର୍ଣ୍ଣନା କରୁଥିବା ଏକ ସମସ୍ୟା ଖୋଲନ୍ତୁ |", - "about-report-issue-2": "ଦଯ଼ାକରି ପ୍ରସଙ୍ଗଟିର ସଂସ୍କରଣ ବିଷଯ଼ରେ ଉଲ୍ଲେଖ କରନ୍ତୁ ।", - "about-libraries-title": "ପାଠାଗାର", - "kiwix-server-running-message": "କିୱିକ୍ସ ସର୍ଭର ଚାଲୁଛି ଏବଂ ସ୍ଥାନୀଯ଼ ନେଟୱର୍କରେ ଏଠାରେ ପ୍ରବେଶ କରାଯାଇପାରିବ:", - "kiwix-server-description": "ଏକ ସର୍ଭର ଆରମ୍ଭ କରିବା ସ୍ଥାନୀୟ ନେଟୱାର୍କରେ ଥିବା ଅନ୍ୟ କମ୍ପ୍ୟୁଟରଗୁଡ଼ିକୁ ଏକ ଷ୍ଟାଣ୍ଡାର୍ଡ ୱେବ୍ ବ୍ରାଉଜର୍ ସହିତ ଆପଣଙ୍କର କିୱିକ୍ସ ଲାଇବ୍ରେରୀକୁ ପ୍ରବେଶ କରିବାକୁ ଅନୁମତି ଦିଏ |", - "fullscreen-notification": "ଆପଣ ବର୍ତ୍ତମାନ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ ମୋଡ୍ ରେ ଅଛନ୍ତି | ଛାଡିବାକୁ ESC ଦବାନ୍ତୁ!", - "online-files": "ଅନଲାଇନ୍ ଫାଇଲ୍ |", - "local-files": "ସ୍ଥାନୀୟ ଫାଇଲ୍ |", - "category": "ବର୍ଗ", - "language": "ଭାଷା", - "hide": "ଲୁଚାନ୍ତୁ |", - "open-in-browser": "ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ |", - "start-kiwix-server": "କିୱିକ୍ସ ସର୍ଭର ଆରମ୍ଭ କରନ୍ତୁ", - "stop-kiwix-server": "କିୱିକ୍ସ ସର୍ଭରକୁ ବନ୍ଦ କରନ୍ତୁ", - "all": "ସମସ୍ତ", - "fulltext-search": "ପୂର୍ଣ୍ଣ ପାଠ ସନ୍ଧାନ", - "pictures": "ଛବିଗୁଡିକ", - "videos": "ଭିଡିଓଗୁଡିକ", - "ftindex": "ଫୁଲ୍ ଟେକ୍ସଟ୍ ଇଣ୍ଡେକ୍ସ", - "details": "ପୂର୍ଣ୍ଣ ପ୍ରବନ୍ଧ", - "yes": "ହଁ", - "no": "ନାହିଁ", - "ok": "ଠିକ୍ ଅଛି", - "no-filter": "ଅଛଣା", - "open-link-in-web-browser": "ୱେବ୍ ବ୍ରାଉଜରରେ ଲିଙ୍କ୍ ଖୋଲନ୍ତୁ", - "download-dir-dialog-title": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ଡାଉନଲୋଡ୍ ଡିରେକ୍ଟୋରୀ ପରିବର୍ତ୍ତନ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", - "download-dir-dialog-msg": "ନୂତନ ଡାଉନଲୋଡ୍ ଡିରେକ୍ଟୋରୀ ପଥ ହେବ: ${{DIRECTORY}}", - "invalid-port": "ଅବୈଧ ପୋର୍ଟ", - "zim-open-fail-title": "ଅବୈଧ ଫାଇଲ", - "zim-open-fail-text": "ZIM ଫାଇଲ୍ ${{ZIM}} properly ସଠିକ୍ ଭାବରେ ଖୋଲାଯାଇପାରିବ ନାହିଁ | ଏହା ଆପଣଙ୍କ ଲାଇବ୍ରେରୀରୁ ଅପସାରିତ ହେବ |", - "monitor-dir-dialog-title": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ମନିଟର ଡିରେକ୍ଟୋରୀକୁ ପରିବର୍ତ୍ତନ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", - "monitor-dir-dialog-msg": "ନୂତନ ମନିଟର ଡିରେକ୍ଟୋରୀ ପଥ ହେb: ${{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ମନିଟର ଡିରେକ୍ଟୋରୀକୁ ସଫା କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", - "monitor-clear-dir-dialog-msg": "ଏହା ନୂତନ ZIM ଫାଇଲଗୁଡ଼ିକ ପାଇଁ ମନିଟର ଡିରେକ୍ଟୋରୀ ଯାଞ୍ଚ କରିବା ବନ୍ଦ କରିବ |", - "monitor-directory-tooltip": "ଏହି ଡିରେକ୍ଟୋରୀରେ ଥିବା ସମସ୍ତ ZIM ଫାଇଲଗୁଡ଼ିକ ସ୍ୱୟଂଚାଳିତ ଭାବରେ ଲାଇବ୍ରେରୀରେ ଯୋଡାଯିବ |", - "next-tab": "ପରବର୍ତ୍ତୀ ଟ୍ୟାବକୁ ଯାଆନ୍ତୁ", - "previous-tab": "ପୂର୍ବ ଟ୍ୟାବକୁ ଯାଆନ୍ତୁ |", - "cancel-download": "ଡାଉନଲୋଡ୍ ବାତିଲ୍ କରନ୍ତୁ |", - "cancel-download-text": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ${{ZIM}} of ର ଡାଉନଲୋଡ୍ ବାତିଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", - "delete-book": "ବହି ବିଲୋପ କରନ୍ତୁ |", - "delete-book-text": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ {{ZIM}} ବିଲୋପ କରିବାକୁ ଚାହୁଁଛନ୍ତି?", - "download-storage-error": "ସଂରକ୍ଷଣ ତ୍ରୁଟି", - "download-storage-error-text": "ସିଷ୍ଟମରେ ପର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍ ଉପଲବ୍ଧ ନାହିଁ |", - "download-unavailable": "ଡାଉନଲୋଡ୍ ଉପଲବ୍ଧ ନାହିଁ |", - "download-unavailable-text": "ଏହି ଡାଉନଲୋଡ୍ ଉପଲବ୍ଧ ନାହିଁ |", - "open-book": "ଖୋଲା ପୁସ୍ତକ |", - "download-book": "ବହି ଡାଉନଲୋଡ୍ କରନ୍ତୁ |", - "pause-download": "ଡାଉନଲୋଡ୍ ବିରତି କରନ୍ତୁ |", - "resume-download": "ଡାଉନଲୋଡ୍ ଆରମ୍ଭ କରନ୍ତୁ |", - "open-folder": "ଫୋଲ୍ଡର ଖୋଲନ୍ତୁ", - "couldnt-open-location": "ଅବସ୍ଥାନ ଖୋଲିପାରିଲା ନାହିଁ |", - "couldnt-open-location-text": "କିୱିକ୍ସ ଫୋଲଡରକୁ ଖୋଲିବାରେ ସକ୍ଷମ ନୁହେଁ {{FOLDER}}", - "move-files-to-trash": "ଅପସାରିତ ଫାଇଲଗୁଡ଼ିକୁ ଆବର୍ଜନା ଭଣ୍ଡାରକୁ ସ୍ଥାନାନ୍ତର କରନ୍ତୁ", - "move-files-to-trash-text": "ଏହି କାର୍ଯ୍ଯ଼ ଫାଇଲଟିକୁ ଆବର୍ଜନା ପାତ୍ରକୁ ସ୍ଥାନାନ୍ତର କରିବ ।", - "perma-delete-files-text": "ଏହି କ୍ରିୟା ଫାଇଲ୍ କୁ ସବୁଦିନ ପାଇଁ ଡିଲିଟ୍ କରିବ", - "clear-filter": "ବର୍ତ୍ତମାନ ସେଟ ହୋଇଥିବା ଫିଲ୍ଟର୍ଗୁଡ଼ିକୁ ସଫା କରନ୍ତୁ", - "language-searcher-placeholder": "ଭାଷା ଫିଲ୍ଟର୍ କରନ୍ତୁ", - "category-searcher-placeholder": "ଫିଲ୍ଟର୍ ବର୍ଗ", - "content-type-searcher-placeholder": "ବିଷୟବସ୍ତୁ ପ୍ରକାର ଫିଲ୍ଟର୍ କରନ୍ତୁ |", - "no-details": "କେବଳ ପରିଚୟ", - "no-pictures": "କୌଣସି ଚିତ୍ର ନାହିଁ", - "no-videos": "କୌଣସି ଭିଡିଓ ନାହିଁ" -} +{ + "@metadata": { + "authors": [ + "Gouri" + ] + }, + "name": "ଓଡି଼ଆ", + "error-title": "ତ୍ରୁଟି", + "error-downloader-window-title": "ଡାଉନଲୋଡର ସୃଷ୍ଟି କରିପାରିବେ ନାହିଁ", + "error-downloader-launch-message": "ଡାଉନଲୋଡର ଆରମ୍ଭ କରିବା ଅସମ୍ଭବ - କିୱିକ୍ସ - ଡେସ୍କଟପ ଆରମ୍ଭ ହେବ କିନ୍ତୁ ସମସ୍ତ ଡାଉନଲୋଡ୍ କାର୍ଯ୍ଯ଼ କାମ କରିବ ନାହିଁ", + "error-launch-server-message": "ଗୋଟିଏ ତ୍ରୁଟି ଘଟିଛି", + "error-archive": "ଅଭିଲେଖକୁ ପାଇପାରିଲା ନାହିଁ", + "open-zim": "ZIM ଫାଇଲକୁ ଖୋଲନ୍ତୁ", + "local-kiwix-server": "ସ୍ଥାନୀୟ କିୱିକ୍ସ ସର୍ଭର", + "random-article": "ଅନିୟମିତ ପ୍ରବନ୍ଧ", + "random-article-error": "ଏକ ଅନିୟମିତ ପ୍ରବନ୍ଧ ଖୋଲିବାରେ ବିଫଳ", + "home-page": "ମୂଳପୃଷ୍ଠା", + "main-menu": "ମୁଖ୍ୟ ମେନୁ", + "print": "ମୁଦ୍ରଣ କରନ୍ତୁ", + "print-page-error": "ମୁଦ୍ରଣ କରିବା ସମୟରେ ଏକ ତ୍ରୁଟି ଘଟିଛି", + "new-tab": "ନୂଆ ଟ୍ୟାବ୍", + "close-tab": "ଟ୍ୟାବ୍ ବନ୍ଦ କରନ୍ତୁ |", + "close": "ବନ୍ଦ କରନ୍ତୁ", + "reopen-closed-tab": "ବନ୍ଦ ଟ୍ୟାବ୍ ଖୋଲନ୍ତୁ", + "browse-library": "ଲାଇବ୍ରେରୀ ବ୍ରାଉଜ୍ କରନ୍ତୁ", + "open-file": "ଫାଇଲ୍ ଖୋଲନ୍ତୁ", + "open-recent": "ସମ୍ପ୍ରତି ଖୋଲନ୍ତୁ", + "search-article": "ପ୍ରବନ୍ଧ ଖୋଜ", + "search-in-library": "ଲାଇବ୍ରେରୀରେ ଖୋଜ", + "find-in-page": "ପୃଷ୍ଠାରେ ଖୋଜନ୍ତୁ", + "set-fullscreen": "ଫୁଲ୍ ସ୍କ୍ରିନ୍ ସେଟ୍ କରନ୍ତୁ", + "quit-fullscreen": "ଫୁଲ୍ ସ୍କ୍ରିନ୍ ଛାଡନ୍ତୁ", + "table-of-content": "ବିଷଯ଼ବସ୍ତୁ ସାରଣୀ", + "reading-list": "ପଠନ ତାଲିକା", + "zoom-in": "ବଡ଼ କରନ୍ତୁ", + "zoom-out": "ଛୋଟ କରନ୍ତୁ", + "zoom-reset": "ଜୁମ୍ ପୁନଃସ୍ଥାପନ", + "help": "ସାହାଯ୍ୟ", + "feedback": "ମତାମତ", + "report-a-bug": "ଏକ ତ୍ରୁଟି ରିପୋର୍ଟ କରନ୍ତୁ", + "request-a-feature": "ଏକ ବିଶେଷତା ପାଇଁ ଅନୁରୋଧ କରନ୍ତୁ", + "about-kiwix": "କିୱିକ୍ସ ବିଷଯ଼ରେ", + "donate-to-support-kiwix": "କିୱିକ୍ସକୁ ସମର୍ଥନ କରିବା ପାଇଁ ଦାନ କରନ୍ତୁ", + "exit": "ପ୍ରସ୍ଥାନ", + "save-file-as-window-title": "ଫାଇଲକୁ ଏହିପରି ସଂରକ୍ଷଣ କରନ୍ତୁ", + "download-finished-message": "ଡକ୍ଯ଼ୁମେଣ୍ଟଟି ଡାଉନଲୋଡ୍ ହୋଇଛି ।", + "file": "ଫାଇଲ", + "edit": "ସମ୍ପାଦନ କରନ୍ତୁ", + "view": "ଦେଖନ୍ତୁ", + "tools": "ଉପକରଣ", + "window-title": "ଲାଇବ୍ରେରୀ - କିୱିକ୍ସ", + "search": "ଖୋଜନ୍ତୁ", + "browse-directory": "ଡିରେକ୍ଟୋରୀ ବ୍ରାଉଜ୍ କରନ୍ତୁ", + "settings": "ସେଟିଂସମୂହ", + "back": "ପଛକୁ", + "forward": "ଆଗକୁ ବଢ଼ନ୍ତୁ", + "search-files": "ଫାଇଲଗୁଡିକ ସନ୍ଧାନ କରନ୍ତୁ", + "title": "ଶୀର୍ଷକ", + "size": "ଆକାର", + "date": "ତାରିଖ", + "content-type": "ସୂଚୀପତ୍ର", + "reset-sort": "ପୁନଃନିର୍ଦ୍ଧାରଣ କ୍ରମ", + "open": "ଖୋଲନ୍ତ", + "delete": "ଲିଭାନ୍ତୁ", + "download": "ଡାଉନଲୋଡ଼", + "resume": "ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ", + "pause": "ବିରାମ", + "cancel": "ବାତିଲ", + "apply": "ଆବେଦନ କରନ୍ତୁ", + "port-for-local-kiwix-server-setting": "ସ୍ଥାନୀଯ଼ କିୱିକ୍ସ ସର୍ଭର ପାଇଁ ପୋର୍ଟ", + "zoom-level-setting": "ଡିଫଲ୍ଟ ଜୁମ୍ ସ୍ତର", + "download-directory-setting": "ଡିରେକ୍ଟୋରୀ ଡାଉନଲୋଡ୍ କରନ୍ତୁ", + "monitor-directory-setting": "ଡିରେକ୍ଟୋରୀକୁ ମନିଟର୍ କରନ୍ତୁ", + "reset": "ପୁନଃସ୍ଥାପନ କରନ୍ତୁ", + "clear": "ଲିଭାନ୍ତୁ", + "browse": "ଖୋଜିବା", + "about-kiwix-desktop-title": "କିୱିକ୍ସ ଡେସ୍କଟପ", + "about-kiwix-desktop-description": "ଆପଣ ଯେଉଁଠିକି ଯାଆନ୍ତୁ , କିୱିକ୍ସ ଆପଣଙ୍କୁ ସମ୍ପୂର୍ଣ୍ଣ ୱିକିପିଡିଆ ହାତରେ ରଖିବାକୁ ଅନୁମତି ଦେଇଥାଏ । କୌଣସି ସ୍ଥାନର ମଝିରେ କିମ୍ବା ଜେଲ୍ରେ ଥିବା ଏକ ଡଙ୍ଗାରେ , କିୱିକ୍ସ ଆପଣଙ୍କୁ ସମସ୍ତ ମାନବ ଜ୍ଞାନକୁ ପ୍ରବେଶ ଦେଇଥାଏ । ଆପଣଙ୍କୁ ଇଣ୍ଟରନେଟ୍ର ଆବଶ୍ଯ଼କତା ନାହିଁ , କାରଣ ସବୁକିଛି ଆପଣଙ୍କ କମ୍ପ୍ଯ଼ୁଟରରେ ଗଚ୍ଛିତ ଅଛି ।", + "about-learn-more-about-kiwix": "କିୱିକ୍ସ ବିଷୟରେ ଅଧିକ ଜାଣନ୍ତୁ", + "about-release-title": "ରିଲିଜ୍", + "about-source-code": "ଏହି ସଫ୍ଟୱେର୍ GNU ଜେନେରାଲ୍ ପବ୍ଲିକ୍ ଲାଇସେନ୍ସ ସଂସ୍କରଣ 3 ଅନୁଯାୟୀ ପ୍ରକାଶିତ ହୋଇଛି | Github ରେ ଉତ୍ସ କୋଡ୍ ଦେଖନ୍ତୁ |", + "about-version": "ସଂସ୍କରଣ: {{VERSION}}", + "about-reporting-problem-title": "ରିପୋର୍ଟ ସମସ୍ୟା", + "about-report-issue": "ଏକ ସମସ୍ୟା ଖୋଲିବାକୁ, କିୱିକ୍ସ-ଡେସ୍କଟପ୍ ଇସ୍ୟୁ ଟ୍ରାକର୍ ପରିଦର୍ଶନ କରନ୍ତୁ ଏବଂ ଆପଣଙ୍କର ସମସ୍ୟା ବର୍ଣ୍ଣନା କରୁଥିବା ଏକ ସମସ୍ୟା ଖୋଲନ୍ତୁ |", + "about-report-issue-2": "ଦଯ଼ାକରି ପ୍ରସଙ୍ଗଟିର ସଂସ୍କରଣ ବିଷଯ଼ରେ ଉଲ୍ଲେଖ କରନ୍ତୁ ।", + "about-libraries-title": "ପାଠାଗାର", + "kiwix-server-running-message": "କିୱିକ୍ସ ସର୍ଭର ଚାଲୁଛି ଏବଂ ସ୍ଥାନୀଯ଼ ନେଟୱର୍କରେ ଏଠାରେ ପ୍ରବେଶ କରାଯାଇପାରିବ:", + "kiwix-server-description": "ଏକ ସର୍ଭର ଆରମ୍ଭ କରିବା ସ୍ଥାନୀୟ ନେଟୱାର୍କରେ ଥିବା ଅନ୍ୟ କମ୍ପ୍ୟୁଟରଗୁଡ଼ିକୁ ଏକ ଷ୍ଟାଣ୍ଡାର୍ଡ ୱେବ୍ ବ୍ରାଉଜର୍ ସହିତ ଆପଣଙ୍କର କିୱିକ୍ସ ଲାଇବ୍ରେରୀକୁ ପ୍ରବେଶ କରିବାକୁ ଅନୁମତି ଦିଏ |", + "fullscreen-notification": "ଆପଣ ବର୍ତ୍ତମାନ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ ମୋଡ୍ ରେ ଅଛନ୍ତି | ଛାଡିବାକୁ ESC ଦବାନ୍ତୁ!", + "online-files": "ଅନଲାଇନ୍ ଫାଇଲ୍ |", + "local-files": "ସ୍ଥାନୀୟ ଫାଇଲ୍ |", + "category": "ବର୍ଗ", + "language": "ଭାଷା", + "hide": "ଲୁଚାନ୍ତୁ |", + "open-in-browser": "ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ |", + "start-kiwix-server": "କିୱିକ୍ସ ସର୍ଭର ଆରମ୍ଭ କରନ୍ତୁ", + "stop-kiwix-server": "କିୱିକ୍ସ ସର୍ଭରକୁ ବନ୍ଦ କରନ୍ତୁ", + "all": "ସମସ୍ତ", + "fulltext-search": "ପୂର୍ଣ୍ଣ ପାଠ ସନ୍ଧାନ", + "pictures": "ଛବିଗୁଡିକ", + "videos": "ଭିଡିଓଗୁଡିକ", + "ftindex": "ଫୁଲ୍ ଟେକ୍ସଟ୍ ଇଣ୍ଡେକ୍ସ", + "details": "ପୂର୍ଣ୍ଣ ପ୍ରବନ୍ଧ", + "yes": "ହଁ", + "no": "ନାହିଁ", + "ok": "ଠିକ୍ ଅଛି", + "no-filter": "ଅଛଣା", + "open-link-in-web-browser": "ୱେବ୍ ବ୍ରାଉଜରରେ ଲିଙ୍କ୍ ଖୋଲନ୍ତୁ", + "download-dir-dialog-title": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ଡାଉନଲୋଡ୍ ଡିରେକ୍ଟୋରୀ ପରିବର୍ତ୍ତନ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", + "download-dir-dialog-msg": "ନୂତନ ଡାଉନଲୋଡ୍ ଡିରେକ୍ଟୋରୀ ପଥ ହେବ: ${{DIRECTORY}}", + "invalid-port": "ଅବୈଧ ପୋର୍ଟ", + "zim-open-fail-title": "ଅବୈଧ ଫାଇଲ", + "zim-open-fail-text": "ZIM ଫାଇଲ୍ ${{ZIM}} properly ସଠିକ୍ ଭାବରେ ଖୋଲାଯାଇପାରିବ ନାହିଁ | ଏହା ଆପଣଙ୍କ ଲାଇବ୍ରେରୀରୁ ଅପସାରିତ ହେବ |", + "monitor-dir-dialog-title": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ମନିଟର ଡିରେକ୍ଟୋରୀକୁ ପରିବର୍ତ୍ତନ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", + "monitor-dir-dialog-msg": "ନୂତନ ମନିଟର ଡିରେକ୍ଟୋରୀ ପଥ ହେb: ${{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ମନିଟର ଡିରେକ୍ଟୋରୀକୁ ସଫା କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", + "monitor-clear-dir-dialog-msg": "ଏହା ନୂତନ ZIM ଫାଇଲଗୁଡ଼ିକ ପାଇଁ ମନିଟର ଡିରେକ୍ଟୋରୀ ଯାଞ୍ଚ କରିବା ବନ୍ଦ କରିବ |", + "monitor-directory-tooltip": "ଏହି ଡିରେକ୍ଟୋରୀରେ ଥିବା ସମସ୍ତ ZIM ଫାଇଲଗୁଡ଼ିକ ସ୍ୱୟଂଚାଳିତ ଭାବରେ ଲାଇବ୍ରେରୀରେ ଯୋଡାଯିବ |", + "next-tab": "ପରବର୍ତ୍ତୀ ଟ୍ୟାବକୁ ଯାଆନ୍ତୁ", + "previous-tab": "ପୂର୍ବ ଟ୍ୟାବକୁ ଯାଆନ୍ତୁ |", + "cancel-download": "ଡାଉନଲୋଡ୍ ବାତିଲ୍ କରନ୍ତୁ |", + "cancel-download-text": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ ${{ZIM}} of ର ଡାଉନଲୋଡ୍ ବାତିଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି?", + "delete-book": "ବହି ବିଲୋପ କରନ୍ତୁ |", + "delete-book-text": "ଆପଣ ନିଶ୍ଚିତ କି ଆପଣ {{ZIM}} ବିଲୋପ କରିବାକୁ ଚାହୁଁଛନ୍ତି?", + "download-storage-error": "ସଂରକ୍ଷଣ ତ୍ରୁଟି", + "download-storage-error-text": "ସିଷ୍ଟମରେ ପର୍ଯ୍ୟାପ୍ତ ଷ୍ଟୋରେଜ୍ ଉପଲବ୍ଧ ନାହିଁ |", + "download-unavailable": "ଡାଉନଲୋଡ୍ ଉପଲବ୍ଧ ନାହିଁ |", + "download-unavailable-text": "ଏହି ଡାଉନଲୋଡ୍ ଉପଲବ୍ଧ ନାହିଁ |", + "open-book": "ଖୋଲା ପୁସ୍ତକ |", + "download-book": "ବହି ଡାଉନଲୋଡ୍ କରନ୍ତୁ |", + "pause-download": "ଡାଉନଲୋଡ୍ ବିରତି କରନ୍ତୁ |", + "resume-download": "ଡାଉନଲୋଡ୍ ଆରମ୍ଭ କରନ୍ତୁ |", + "open-folder": "ଫୋଲ୍ଡର ଖୋଲନ୍ତୁ", + "couldnt-open-location": "ଅବସ୍ଥାନ ଖୋଲିପାରିଲା ନାହିଁ |", + "couldnt-open-location-text": "କିୱିକ୍ସ ଫୋଲଡରକୁ ଖୋଲିବାରେ ସକ୍ଷମ ନୁହେଁ {{FOLDER}}", + "move-files-to-trash": "ଅପସାରିତ ଫାଇଲଗୁଡ଼ିକୁ ଆବର୍ଜନା ଭଣ୍ଡାରକୁ ସ୍ଥାନାନ୍ତର କରନ୍ତୁ", + "move-files-to-trash-text": "ଏହି କାର୍ଯ୍ଯ଼ ଫାଇଲଟିକୁ ଆବର୍ଜନା ପାତ୍ରକୁ ସ୍ଥାନାନ୍ତର କରିବ ।", + "perma-delete-files-text": "ଏହି କ୍ରିୟା ଫାଇଲ୍ କୁ ସବୁଦିନ ପାଇଁ ଡିଲିଟ୍ କରିବ", + "clear-filter": "ବର୍ତ୍ତମାନ ସେଟ ହୋଇଥିବା ଫିଲ୍ଟର୍ଗୁଡ଼ିକୁ ସଫା କରନ୍ତୁ", + "language-searcher-placeholder": "ଭାଷା ଫିଲ୍ଟର୍ କରନ୍ତୁ", + "category-searcher-placeholder": "ଫିଲ୍ଟର୍ ବର୍ଗ", + "content-type-searcher-placeholder": "ବିଷୟବସ୍ତୁ ପ୍ରକାର ଫିଲ୍ଟର୍ କରନ୍ତୁ |", + "no-details": "କେବଳ ପରିଚୟ", + "no-pictures": "କୌଣସି ଚିତ୍ର ନାହିଁ", + "no-videos": "କୌଣସି ଭିଡିଓ ନାହିଁ" +} diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index 6a3556758..4b384c37c 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -1,140 +1,140 @@ -{ - "@metadata": { - "authors": [ - "McDutchie", - "Pietrasagh", - "Rail", - "Rainbow P", - "SRakestraw", - "Strebski", - "WaldiSt" - ] - }, - "name": "Polski", - "error-title": "Błąd", - "error-downloader-window-title": "Nie można utworzyć pobierania", - "error-downloader-launch-message": "Nie można uruchomić pobierania, uruchomi się Kiwix-desktop, ale wszystkie funkcje pobierania nie będą działać!", - "error-launch-server-message": "Wystąpił błąd!", - "open-zim": "Otwórz plik ZIM", - "local-kiwix-server": "Lokalny serwer Kiwix", - "random-article": "Losowy artykuł", - "random-article-error": "Nie udało się otworzyć losowego artykułu.", - "home-page": "Strona główna", - "main-menu": "Menu główne", - "print": "Drukuj", - "print-page-error": "Wystąpił błąd podczas drukowania.", - "new-tab": "Nowa zakładka", - "close-tab": "Zamknij kartę", - "close": "Zamknij", - "reopen-closed-tab": "Otwórz zamknięta kartę ponownie", - "browse-library": "Przeglądaj bibliotekę", - "open-file": "Otwórz plik", - "open-recent": "Otwórz ostatni", - "search-article": "Wyszukaj artykuł", - "search-in-library": "Szukaj w bibliotece", - "find-in-page": "Znajdź na stronie", - "set-fullscreen": "Ustaw pełny ekran", - "quit-fullscreen": "Wyjdź z trybu pełnoekranowego", - "table-of-content": "Spis treści", - "reading-list": "Lista do przeczytania", - "zoom-in": "Powiększ", - "zoom-out": "Pomniejsz", - "zoom-reset": "Resetowanie powiększenia", - "help": "Pomoc", - "feedback": "Opinie", - "report-a-bug": "Zgłoś błąd", - "request-a-feature": "Zaproponuj funkcję", - "about-kiwix": "O Kiwix", - "donate-to-support-kiwix": "Wspomóż rozwój Kiwix", - "exit": "Wyjdź", - "save-file-as-window-title": "Zapisz plik jako", - "download-finished-message": "Dokument został pobrany", - "file": "Plik", - "edit": "Edytuj", - "view": "Podgląd", - "tools": "Narzędzia", - "window-title": "Biblioteka - Kiwix", - "search": "Szukaj", - "browse-directory": "Przeglądaj katalog", - "settings": "Ustawienia", - "back": "Wstecz", - "forward": "Dalej", - "search-files": "Szukaj plików", - "title": "Tytuł", - "size": "Rozmiar", - "date": "Data", - "content-type": "Typ treści", - "reset-sort": "resetowanie sortowania", - "open": "Otwórz", - "delete": "Usuń", - "download": "Pobierz", - "resume": "Ponów", - "pause": "Pauza", - "cancel": "Anuluj", - "apply": "Zastosuj", - "port-for-local-kiwix-server-setting": "Port dla lokalnego serwera Kiwix", - "zoom-level-setting": "Domyślny poziom powiększenia", - "download-directory-setting": "Pobierz katalog", - "monitor-directory-setting": "Katalog monitorowany", - "reset": "Resetuj", - "clear": "Wyczyść", - "browse": "Przeglądaj", - "about-kiwix-desktop-title": "Pulpit Kiwix", - "about-kiwix-desktop-description": "Kiwix pozwala mieć pod ręką całą Wikipedię, gdziekolwiek jesteś! Na łodzi, w szczerym polu lub w więzieniu, Kiwix daje ci dostęp do całej ludzkiej wiedzy. Nie potrzebujesz Internetu, wszystko jest przechowywane na Twoim komputerze.", - "about-learn-more-about-kiwix": "Dowiedz się więcej o Kiwix", - "about-privacy-policy": "Polityka prywatności", - "about-release-title": "Wydanie", - "about-source-code": "To oprogramowanie jest wydawane na warunkach Powszechnej Licencji Publicznej GNU w wersji 3. Zobacz kod źródłowy na Github.", - "about-version": "Wersja: {{VERSION}}", - "about-reporting-problem-title": "Zgłaszanie błędów", - "about-report-issue": "Aby zgłosić problem odwiedź system śledzenia błędów w aplikacji Kiwix na komputery i stwórz nowy wpis opisujący Twój problem.", - "about-report-issue-2": "Wspomnij proszę używaną przez Ciebie wersję we wpisie.", - "about-libraries-title": "Biblioteki", - "kiwix-server-running-message": "Serwer Kiwix jest uruchomiony i można uzyskać do niego dostęp w sieci lokalnej pod adresem:", - "kiwix-server-description": "Uruchomienie serwera umożliwia innym komputerom w sieci lokalnej dostęp do biblioteki Kiwix za pomocą standardowej przeglądarki internetowej.", - "fullscreen-notification": "Znajdujesz się w trybie pełnoekranowym. Naciśnij ESC aby z niego wyjść!", - "local-files": "Pliki lokalne", - "category": "Kategoria", - "language": "Język", - "hide": "Ukryj", - "open-in-browser": "Otwórz w przeglądarce", - "start-kiwix-server": "Uruchom serwer Kiwix", - "stop-kiwix-server": "Zatrzymaj serwer Kiwix", - "all": "Wszystkie", - "fulltext-search": "Wyszukiwanie pełnotekstowe", - "pictures": "Zdjęcia", - "videos": "Filmy", - "ftindex": "Indeks pełnotekstowy", - "details": "Cały artykuł", - "yes": "tak", - "no": "nie", - "ok": "ok", - "no-filter": "brak filtra", - "open-link-in-web-browser": "Otwórz link w przeglądarce", - "open-link-new-tab": "Otwórz link w nowej karcie", - "download-dir-dialog-title": "Czy na pewno chcesz zmienić katalog pobierania?", - "download-dir-dialog-msg": "Nową ścieżką katalogu pobieranych plików będzie:\n{{DIRECTORY}}", - "invalid-port": "Nieprawidłowy port", - "zim-open-fail-title": "Niepoprawny plik", - "zim-open-fail-text": "Nie można poprawnie otworzyć pliku ZIM {{ZIM}}. Zostanie usunięty z Twojej biblioteki.", - "monitor-dir-dialog-title": "Czy na pewno chcesz zmienić katalog monitorowany?", - "monitor-dir-dialog-msg": "Nową ścieżką katalogu monitorowanego będzie:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Czy na pewno chcesz wyczyścić katalog monitorowany?", - "monitor-clear-dir-dialog-msg": "Spowoduje to zatrzymanie sprawdzania katalogu monitora w poszukiwaniu nowych plików ZIM.", - "monitor-directory-tooltip": "Wszystkie pliki ZIM w tym katalogu zostaną automatycznie dodane do biblioteki.", - "next-tab": "Przejdź do następnej karty", - "previous-tab": "Przejdź do poprzedniej karty", - "cancel-download": "Anuluj pobieranie", - "cancel-download-text": "Czy na pewno chcesz anulować pobieranie {{ZIM}}?", - "delete-book": "Usuń książkę", - "delete-book-text": "Czy na pewno chcesz usunąć {{ZIM}}?", - "download-book": "Pobierz książkę", - "pause-download": "Wstrzymaj pobieranie", - "resume-download": "Wznów pobieranie", - "open-folder": "Otwórz folder", - "couldnt-open-location": "Nie udało się otworzyć lokalizacji", - "couldnt-open-location-text": "Kiwix nie może otworzyć folderu {{FOLDER}}", - "move-files-to-trash": "Przenieś usunięte pliki do kosza", - "move-files-to-trash-text": "Ta czynność spowoduje przeniesienie pliku do kosza.", - "no-videos": "Brak filmów" -} +{ + "@metadata": { + "authors": [ + "McDutchie", + "Pietrasagh", + "Rail", + "Rainbow P", + "SRakestraw", + "Strebski", + "WaldiSt" + ] + }, + "name": "Polski", + "error-title": "Błąd", + "error-downloader-window-title": "Nie można utworzyć pobierania", + "error-downloader-launch-message": "Nie można uruchomić pobierania, uruchomi się Kiwix-desktop, ale wszystkie funkcje pobierania nie będą działać!", + "error-launch-server-message": "Wystąpił błąd!", + "open-zim": "Otwórz plik ZIM", + "local-kiwix-server": "Lokalny serwer Kiwix", + "random-article": "Losowy artykuł", + "random-article-error": "Nie udało się otworzyć losowego artykułu.", + "home-page": "Strona główna", + "main-menu": "Menu główne", + "print": "Drukuj", + "print-page-error": "Wystąpił błąd podczas drukowania.", + "new-tab": "Nowa zakładka", + "close-tab": "Zamknij kartę", + "close": "Zamknij", + "reopen-closed-tab": "Otwórz zamknięta kartę ponownie", + "browse-library": "Przeglądaj bibliotekę", + "open-file": "Otwórz plik", + "open-recent": "Otwórz ostatni", + "search-article": "Wyszukaj artykuł", + "search-in-library": "Szukaj w bibliotece", + "find-in-page": "Znajdź na stronie", + "set-fullscreen": "Ustaw pełny ekran", + "quit-fullscreen": "Wyjdź z trybu pełnoekranowego", + "table-of-content": "Spis treści", + "reading-list": "Lista do przeczytania", + "zoom-in": "Powiększ", + "zoom-out": "Pomniejsz", + "zoom-reset": "Resetowanie powiększenia", + "help": "Pomoc", + "feedback": "Opinie", + "report-a-bug": "Zgłoś błąd", + "request-a-feature": "Zaproponuj funkcję", + "about-kiwix": "O Kiwix", + "donate-to-support-kiwix": "Wspomóż rozwój Kiwix", + "exit": "Wyjdź", + "save-file-as-window-title": "Zapisz plik jako", + "download-finished-message": "Dokument został pobrany", + "file": "Plik", + "edit": "Edytuj", + "view": "Podgląd", + "tools": "Narzędzia", + "window-title": "Biblioteka - Kiwix", + "search": "Szukaj", + "browse-directory": "Przeglądaj katalog", + "settings": "Ustawienia", + "back": "Wstecz", + "forward": "Dalej", + "search-files": "Szukaj plików", + "title": "Tytuł", + "size": "Rozmiar", + "date": "Data", + "content-type": "Typ treści", + "reset-sort": "resetowanie sortowania", + "open": "Otwórz", + "delete": "Usuń", + "download": "Pobierz", + "resume": "Ponów", + "pause": "Pauza", + "cancel": "Anuluj", + "apply": "Zastosuj", + "port-for-local-kiwix-server-setting": "Port dla lokalnego serwera Kiwix", + "zoom-level-setting": "Domyślny poziom powiększenia", + "download-directory-setting": "Pobierz katalog", + "monitor-directory-setting": "Katalog monitorowany", + "reset": "Resetuj", + "clear": "Wyczyść", + "browse": "Przeglądaj", + "about-kiwix-desktop-title": "Pulpit Kiwix", + "about-kiwix-desktop-description": "Kiwix pozwala mieć pod ręką całą Wikipedię, gdziekolwiek jesteś! Na łodzi, w szczerym polu lub w więzieniu, Kiwix daje ci dostęp do całej ludzkiej wiedzy. Nie potrzebujesz Internetu, wszystko jest przechowywane na Twoim komputerze.", + "about-learn-more-about-kiwix": "Dowiedz się więcej o Kiwix", + "about-privacy-policy": "Polityka prywatności", + "about-release-title": "Wydanie", + "about-source-code": "To oprogramowanie jest wydawane na warunkach Powszechnej Licencji Publicznej GNU w wersji 3. Zobacz kod źródłowy na Github.", + "about-version": "Wersja: {{VERSION}}", + "about-reporting-problem-title": "Zgłaszanie błędów", + "about-report-issue": "Aby zgłosić problem odwiedź system śledzenia błędów w aplikacji Kiwix na komputery i stwórz nowy wpis opisujący Twój problem.", + "about-report-issue-2": "Wspomnij proszę używaną przez Ciebie wersję we wpisie.", + "about-libraries-title": "Biblioteki", + "kiwix-server-running-message": "Serwer Kiwix jest uruchomiony i można uzyskać do niego dostęp w sieci lokalnej pod adresem:", + "kiwix-server-description": "Uruchomienie serwera umożliwia innym komputerom w sieci lokalnej dostęp do biblioteki Kiwix za pomocą standardowej przeglądarki internetowej.", + "fullscreen-notification": "Znajdujesz się w trybie pełnoekranowym. Naciśnij ESC aby z niego wyjść!", + "local-files": "Pliki lokalne", + "category": "Kategoria", + "language": "Język", + "hide": "Ukryj", + "open-in-browser": "Otwórz w przeglądarce", + "start-kiwix-server": "Uruchom serwer Kiwix", + "stop-kiwix-server": "Zatrzymaj serwer Kiwix", + "all": "Wszystkie", + "fulltext-search": "Wyszukiwanie pełnotekstowe", + "pictures": "Zdjęcia", + "videos": "Filmy", + "ftindex": "Indeks pełnotekstowy", + "details": "Cały artykuł", + "yes": "tak", + "no": "nie", + "ok": "ok", + "no-filter": "brak filtra", + "open-link-in-web-browser": "Otwórz link w przeglądarce", + "open-link-new-tab": "Otwórz link w nowej karcie", + "download-dir-dialog-title": "Czy na pewno chcesz zmienić katalog pobierania?", + "download-dir-dialog-msg": "Nową ścieżką katalogu pobieranych plików będzie:\n{{DIRECTORY}}", + "invalid-port": "Nieprawidłowy port", + "zim-open-fail-title": "Niepoprawny plik", + "zim-open-fail-text": "Nie można poprawnie otworzyć pliku ZIM {{ZIM}}. Zostanie usunięty z Twojej biblioteki.", + "monitor-dir-dialog-title": "Czy na pewno chcesz zmienić katalog monitorowany?", + "monitor-dir-dialog-msg": "Nową ścieżką katalogu monitorowanego będzie:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Czy na pewno chcesz wyczyścić katalog monitorowany?", + "monitor-clear-dir-dialog-msg": "Spowoduje to zatrzymanie sprawdzania katalogu monitora w poszukiwaniu nowych plików ZIM.", + "monitor-directory-tooltip": "Wszystkie pliki ZIM w tym katalogu zostaną automatycznie dodane do biblioteki.", + "next-tab": "Przejdź do następnej karty", + "previous-tab": "Przejdź do poprzedniej karty", + "cancel-download": "Anuluj pobieranie", + "cancel-download-text": "Czy na pewno chcesz anulować pobieranie {{ZIM}}?", + "delete-book": "Usuń książkę", + "delete-book-text": "Czy na pewno chcesz usunąć {{ZIM}}?", + "download-book": "Pobierz książkę", + "pause-download": "Wstrzymaj pobieranie", + "resume-download": "Wznów pobieranie", + "open-folder": "Otwórz folder", + "couldnt-open-location": "Nie udało się otworzyć lokalizacji", + "couldnt-open-location-text": "Kiwix nie może otworzyć folderu {{FOLDER}}", + "move-files-to-trash": "Przenieś usunięte pliki do kosza", + "move-files-to-trash-text": "Ta czynność spowoduje przeniesienie pliku do kosza.", + "no-videos": "Brak filmów" +} diff --git a/resources/i18n/pt-br.json b/resources/i18n/pt-br.json index 9836c5854..32ce43862 100644 --- a/resources/i18n/pt-br.json +++ b/resources/i18n/pt-br.json @@ -1,178 +1,178 @@ -{ - "@metadata": { - "authors": [ - "Eduardo Addad de Oliveira", - "Eduardoaddad", - "Klgor1803", - "McDutchie", - "Obiru", - "Saturnow", - "YoReaper" - ] - }, - "name": "Português do Brasil", - "error-title": "Erro", - "error-downloader-window-title": "Não é possível criar o downloader", - "error-downloader-launch-message": "Impossível iniciar o downloader, o Kiwix-desktop será iniciado, mas todas as funções de download não funcionarão!", - "error-launch-server-message": "Ocorreu um erro!", - "error-archive": "Falha ao obter o arquivo", - "open-zim": "Abrir Arquivo ZIM", - "local-kiwix-server": "Servidor Kiwix local", - "random-article": "Artigo aleatório", - "random-article-error": "Falha ao abrir um artigo aleatório.", - "home-page": "Página principal", - "main-menu": "Menu principal", - "print": "Imprimir", - "print-page-error": "Ocorreu um erro durante a impressão.", - "new-tab": "Nova guia", - "close-tab": "Fechar guia", - "close": "Fechar", - "reopen-closed-tab": "Reabrir guia fechada", - "browse-library": "Navegar na biblioteca", - "open-file": "Abrir arquivo", - "open-recent": "Aberto recentemente", - "search-article": "Pesquisar artigo", - "search-in-library": "Pesquisar na biblioteca", - "find-in-page": "Encontrar na página", - "set-fullscreen": "Definir tela cheia", - "quit-fullscreen": "Sair da tela cheia", - "table-of-content": "Tabela de conteúdo", - "add-bookmark": "Adicionar na lista de leitura", - "remove-bookmark": "Remover da lista lista de leitura", - "reading-list": "Lista de leitura", - "zoom-in": "Aumentar o zoom", - "zoom-out": "Diminuir o zoom", - "zoom-reset": "Redefinição de zoom", - "help": "Ajuda", - "feedback": "Comentário", - "report-a-bug": "Relatar um erro", - "request-a-feature": "Solicitar um recurso", - "about-kiwix": "Sobre Kiwix", - "donate-to-support-kiwix": "Doar para apoiar o Kiwix", - "exit": "Sair", - "save-file-as-window-title": "Salvar arquivo como", - "download-finished-message": "O documento foi baixado.", - "file": "Arquivo", - "edit": "Editar", - "view": "Visualizar", - "tools": "Ferramentas", - "window-title": "Biblioteca - Kiwix", - "search": "Pesquisar", - "browse-directory": "Procurar diretório", - "settings": "Configurações", - "back": "Voltar", - "forward": "Encaminhar", - "search-files": "Pesquisar arquivos", - "title": "Título", - "size": "Tamanho", - "date": "Data", - "content-type": "Tipo de conteúdo", - "reset-sort": "redefinir classificação", - "open": "Abrir", - "delete": "Excluir", - "download": "Baixar", - "resume": "Retomar", - "pause": "Pausar", - "cancel": "Cancelar", - "apply": "Aplicar", - "port-for-local-kiwix-server-setting": "Porta para o servidor local Kiwix", - "zoom-level-setting": "Nível padrão de zoom", - "download-directory-setting": "Diretório de download", - "monitor-directory-setting": "Diretório de monitoramento", - "reset": "Redefinir", - "clear": "Limpar", - "browse": "Explorar", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "O Kiwix permite que você tenha toda a Wikipédia em mãos, onde quer que você vá! Em um barco, no meio do nada ou na cadeia, o Kiwix fornece acesso a todo o conhecimento humano. Você não precisa da Internet, tudo está armazenado no seu computador.", - "about-learn-more-about-kiwix": "Saiba mais sobre o Kiwix", - "about-release-title": "Lançamento", - "about-source-code": "Este software é lançado sob os termos da GNU General Public License version 3. Veja o código fonte em Github.", - "about-version": "Versão : {{VERSION}}", - "about-reporting-problem-title": "Reportando problema(s)", - "about-report-issue": "Para abrir um problema, visite Kiwix-desktop issue tracker e abra um problema descrevendo o seu problema.", - "about-report-issue-2": "Por favor mencione a versão na edição.", - "about-libraries-title": "Bibliotecas", - "kiwix-server-running-message": "O servidor Kiwix está em execução e pode ser acessado na rede local em:", - "kiwix-server-description": "Iniciar um servidor permite que outros computadores na rede local acessem sua biblioteca Kiwix com um navegador da Web padrão.", - "fullscreen-notification": "Agora você está no modo de tela cheia. Pressione ESC para sair!", - "online-files": "Arquivos Online", - "local-files": "Arquivos locais", - "category": "Categoria", - "language": "Idioma", - "hide": "Ocultar", - "open-in-browser": "Abrir no navegador", - "start-kiwix-server": "Inicie o servidor Kiwix", - "stop-kiwix-server": "Parar o servidor Kiwix", - "all": "Todos", - "fulltext-search": "Pesquisa de texto completo", - "pictures": "Fotografias", - "videos": "Vídeos", - "ftindex": "Índice de texto completo", - "details": "Artigo completo", - "yes": "sim", - "no": "não", - "ok": "ok", - "no-filter": "sem filtro", - "open-link-in-web-browser": "Abrir link no navegador da web", - "open-link-new-tab": "Abrir o link em uma nova guia", - "download-dir-dialog-title": "Tem certeza de que deseja alterar o diretório de download?", - "download-dir-dialog-msg": "O novo caminho do diretório de download será:\n{{DIRECTORY}}", - "invalid-port": "Porta inválida", - "zim-open-fail-title": "Arquivo inválido", - "zim-open-fail-text": "O arquivo ZIM {{ZIM}} não pôde ser aberto corretamente. Ele será removido da sua biblioteca.", - "monitor-dir-dialog-title": "Tem certeza de que deseja alterar o diretório de monitoramento?", - "monitor-dir-dialog-msg": "O novo caminho do diretório de monitoramento será:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Tem certeza de que deseja apagar o diretório de monitoramento?", - "path-was-copied": "Caminho foi copiado", - "monitor-clear-dir-dialog-msg": "Os arquivos pararão de serem checados no diretório de monitoramento para novos arquivos ZIM.", - "monitor-directory-tooltip": "Todos os arquivos ZIM nesse diretório serão adicionados na biblioteca automaticamente.", - "next-tab": "Mover para próxima guia", - "previous-tab": "Mover para guia anterior", - "cancel-download": "Cancelar download", - "cancel-download-text": "Tem certeza de que deseja cancelar o download de {{ZIM}}?", - "delete-book": "Apagar livro", - "delete-book-text": "Tem certeza de que deseja apagar {{ZIM}}?", - "download-storage-error": "Erro de Armazenamento", - "download-storage-error-text": "O sistema não tem armazenamento suficiente.", - "download-exceeds-max-file-size": "O tamanho do download excede o tamanho máximo do arquivo suportado pelo sistema.", - "download-dir-missing": "O diretório de download não existe.", - "download-dir-not-writable": "O diretório Download não pode ser escrito.", - "download-unavailable": "Download Indisponível", - "download-unavailable-text": "Esse download está indisponível", - "open-book": "Abrir livro", - "download-book": "Baixar livro", - "pause-download": "Pausar download", - "resume-download": "Continuar download", - "open-folder": "Abrir pasta", - "couldnt-open-location": "Não foi possível abrir o destino", - "couldnt-open-location-text": "Kiwix não possui permissão para abrir a pasta {{FOLDER}}", - "move-files-to-trash": "Mover arquivos apagados para lixeira", - "move-files-to-trash-text": "Essa ação irá mover o arquivo para lixeira.", - "perma-delete-files-text": "Essa ação irá apagar o arquivo permanentemente.", - "clear-filter": "Limpar as configurações de filtros atuais", - "language-searcher-placeholder": "Filtrar por idioma", - "category-searcher-placeholder": "Filtrar por categoria", - "content-type-searcher-placeholder": "Filtrar por tipo de conteúdo", - "no-details": "Apenas introdução", - "no-pictures": "Sem Imagens", - "no-videos": "Sem Vídeos", - "open-previous-tabs-at-startup": "Abrir guias anteriores ao iniciar", - "preview-book-in-web-browser": "Pré-visualizar livro no navegador", - "file-not-found-title": "Arquivo ZIM não encontrado", - "file-not-found-text": "O arquivo ZIM não existe ou não pode ser lido", - "zim-id": "Id do ZIM", - "zim-name": "Nome do ZIM", - "zim-path": "Caminho do arquivo ZIM", - "bad-zim-file-error-page-title": "Arquivo ZIM inválido", - "bad-zim-file-error-page-text": "O arquivo ZIM é inválido ou está corrompido.", - "zim-entry-path": "Caminho de entrada do ZIM", - "export-reading-list": "Exportar a lista de leitura", - "export-reading-list-error": "Ocorreu um erro ao exportar a lista de leitura.", - "import-reading-list": "Importar a lista de leitura", - "import-reading-list-error": "Ocorreu um erro ao importar a lista de leitura.", - "disable-sandbox": "O Kiwix foi aberto por um drive na rede. Isso é conhecido por causar problemas de compatibilidade ao navegar na sandbox. Como resultado, a sandbox vai ser desativada. Você quer prosseguir?", - "save-page-as": "Salvar como...", - "portable-disabled-tooltip": "Função desativada no modo portátil", - "scroll-next-tab": "Rolar para a próxima aba", - "scroll-previous-tab": "Mover para a aba anterior" -} +{ + "@metadata": { + "authors": [ + "Eduardo Addad de Oliveira", + "Eduardoaddad", + "Klgor1803", + "McDutchie", + "Obiru", + "Saturnow", + "YoReaper" + ] + }, + "name": "Português do Brasil", + "error-title": "Erro", + "error-downloader-window-title": "Não é possível criar o downloader", + "error-downloader-launch-message": "Impossível iniciar o downloader, o Kiwix-desktop será iniciado, mas todas as funções de download não funcionarão!", + "error-launch-server-message": "Ocorreu um erro!", + "error-archive": "Falha ao obter o arquivo", + "open-zim": "Abrir Arquivo ZIM", + "local-kiwix-server": "Servidor Kiwix local", + "random-article": "Artigo aleatório", + "random-article-error": "Falha ao abrir um artigo aleatório.", + "home-page": "Página principal", + "main-menu": "Menu principal", + "print": "Imprimir", + "print-page-error": "Ocorreu um erro durante a impressão.", + "new-tab": "Nova guia", + "close-tab": "Fechar guia", + "close": "Fechar", + "reopen-closed-tab": "Reabrir guia fechada", + "browse-library": "Navegar na biblioteca", + "open-file": "Abrir arquivo", + "open-recent": "Aberto recentemente", + "search-article": "Pesquisar artigo", + "search-in-library": "Pesquisar na biblioteca", + "find-in-page": "Encontrar na página", + "set-fullscreen": "Definir tela cheia", + "quit-fullscreen": "Sair da tela cheia", + "table-of-content": "Tabela de conteúdo", + "add-bookmark": "Adicionar na lista de leitura", + "remove-bookmark": "Remover da lista lista de leitura", + "reading-list": "Lista de leitura", + "zoom-in": "Aumentar o zoom", + "zoom-out": "Diminuir o zoom", + "zoom-reset": "Redefinição de zoom", + "help": "Ajuda", + "feedback": "Comentário", + "report-a-bug": "Relatar um erro", + "request-a-feature": "Solicitar um recurso", + "about-kiwix": "Sobre Kiwix", + "donate-to-support-kiwix": "Doar para apoiar o Kiwix", + "exit": "Sair", + "save-file-as-window-title": "Salvar arquivo como", + "download-finished-message": "O documento foi baixado.", + "file": "Arquivo", + "edit": "Editar", + "view": "Visualizar", + "tools": "Ferramentas", + "window-title": "Biblioteca - Kiwix", + "search": "Pesquisar", + "browse-directory": "Procurar diretório", + "settings": "Configurações", + "back": "Voltar", + "forward": "Encaminhar", + "search-files": "Pesquisar arquivos", + "title": "Título", + "size": "Tamanho", + "date": "Data", + "content-type": "Tipo de conteúdo", + "reset-sort": "redefinir classificação", + "open": "Abrir", + "delete": "Excluir", + "download": "Baixar", + "resume": "Retomar", + "pause": "Pausar", + "cancel": "Cancelar", + "apply": "Aplicar", + "port-for-local-kiwix-server-setting": "Porta para o servidor local Kiwix", + "zoom-level-setting": "Nível padrão de zoom", + "download-directory-setting": "Diretório de download", + "monitor-directory-setting": "Diretório de monitoramento", + "reset": "Redefinir", + "clear": "Limpar", + "browse": "Explorar", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "O Kiwix permite que você tenha toda a Wikipédia em mãos, onde quer que você vá! Em um barco, no meio do nada ou na cadeia, o Kiwix fornece acesso a todo o conhecimento humano. Você não precisa da Internet, tudo está armazenado no seu computador.", + "about-learn-more-about-kiwix": "Saiba mais sobre o Kiwix", + "about-release-title": "Lançamento", + "about-source-code": "Este software é lançado sob os termos da GNU General Public License version 3. Veja o código fonte em Github.", + "about-version": "Versão : {{VERSION}}", + "about-reporting-problem-title": "Reportando problema(s)", + "about-report-issue": "Para abrir um problema, visite Kiwix-desktop issue tracker e abra um problema descrevendo o seu problema.", + "about-report-issue-2": "Por favor mencione a versão na edição.", + "about-libraries-title": "Bibliotecas", + "kiwix-server-running-message": "O servidor Kiwix está em execução e pode ser acessado na rede local em:", + "kiwix-server-description": "Iniciar um servidor permite que outros computadores na rede local acessem sua biblioteca Kiwix com um navegador da Web padrão.", + "fullscreen-notification": "Agora você está no modo de tela cheia. Pressione ESC para sair!", + "online-files": "Arquivos Online", + "local-files": "Arquivos locais", + "category": "Categoria", + "language": "Idioma", + "hide": "Ocultar", + "open-in-browser": "Abrir no navegador", + "start-kiwix-server": "Inicie o servidor Kiwix", + "stop-kiwix-server": "Parar o servidor Kiwix", + "all": "Todos", + "fulltext-search": "Pesquisa de texto completo", + "pictures": "Fotografias", + "videos": "Vídeos", + "ftindex": "Índice de texto completo", + "details": "Artigo completo", + "yes": "sim", + "no": "não", + "ok": "ok", + "no-filter": "sem filtro", + "open-link-in-web-browser": "Abrir link no navegador da web", + "open-link-new-tab": "Abrir o link em uma nova guia", + "download-dir-dialog-title": "Tem certeza de que deseja alterar o diretório de download?", + "download-dir-dialog-msg": "O novo caminho do diretório de download será:\n{{DIRECTORY}}", + "invalid-port": "Porta inválida", + "zim-open-fail-title": "Arquivo inválido", + "zim-open-fail-text": "O arquivo ZIM {{ZIM}} não pôde ser aberto corretamente. Ele será removido da sua biblioteca.", + "monitor-dir-dialog-title": "Tem certeza de que deseja alterar o diretório de monitoramento?", + "monitor-dir-dialog-msg": "O novo caminho do diretório de monitoramento será:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Tem certeza de que deseja apagar o diretório de monitoramento?", + "path-was-copied": "Caminho foi copiado", + "monitor-clear-dir-dialog-msg": "Os arquivos pararão de serem checados no diretório de monitoramento para novos arquivos ZIM.", + "monitor-directory-tooltip": "Todos os arquivos ZIM nesse diretório serão adicionados na biblioteca automaticamente.", + "next-tab": "Mover para próxima guia", + "previous-tab": "Mover para guia anterior", + "cancel-download": "Cancelar download", + "cancel-download-text": "Tem certeza de que deseja cancelar o download de {{ZIM}}?", + "delete-book": "Apagar livro", + "delete-book-text": "Tem certeza de que deseja apagar {{ZIM}}?", + "download-storage-error": "Erro de Armazenamento", + "download-storage-error-text": "O sistema não tem armazenamento suficiente.", + "download-exceeds-max-file-size": "O tamanho do download excede o tamanho máximo do arquivo suportado pelo sistema.", + "download-dir-missing": "O diretório de download não existe.", + "download-dir-not-writable": "O diretório Download não pode ser escrito.", + "download-unavailable": "Download Indisponível", + "download-unavailable-text": "Esse download está indisponível", + "open-book": "Abrir livro", + "download-book": "Baixar livro", + "pause-download": "Pausar download", + "resume-download": "Continuar download", + "open-folder": "Abrir pasta", + "couldnt-open-location": "Não foi possível abrir o destino", + "couldnt-open-location-text": "Kiwix não possui permissão para abrir a pasta {{FOLDER}}", + "move-files-to-trash": "Mover arquivos apagados para lixeira", + "move-files-to-trash-text": "Essa ação irá mover o arquivo para lixeira.", + "perma-delete-files-text": "Essa ação irá apagar o arquivo permanentemente.", + "clear-filter": "Limpar as configurações de filtros atuais", + "language-searcher-placeholder": "Filtrar por idioma", + "category-searcher-placeholder": "Filtrar por categoria", + "content-type-searcher-placeholder": "Filtrar por tipo de conteúdo", + "no-details": "Apenas introdução", + "no-pictures": "Sem Imagens", + "no-videos": "Sem Vídeos", + "open-previous-tabs-at-startup": "Abrir guias anteriores ao iniciar", + "preview-book-in-web-browser": "Pré-visualizar livro no navegador", + "file-not-found-title": "Arquivo ZIM não encontrado", + "file-not-found-text": "O arquivo ZIM não existe ou não pode ser lido", + "zim-id": "Id do ZIM", + "zim-name": "Nome do ZIM", + "zim-path": "Caminho do arquivo ZIM", + "bad-zim-file-error-page-title": "Arquivo ZIM inválido", + "bad-zim-file-error-page-text": "O arquivo ZIM é inválido ou está corrompido.", + "zim-entry-path": "Caminho de entrada do ZIM", + "export-reading-list": "Exportar a lista de leitura", + "export-reading-list-error": "Ocorreu um erro ao exportar a lista de leitura.", + "import-reading-list": "Importar a lista de leitura", + "import-reading-list-error": "Ocorreu um erro ao importar a lista de leitura.", + "disable-sandbox": "O Kiwix foi aberto por um drive na rede. Isso é conhecido por causar problemas de compatibilidade ao navegar na sandbox. Como resultado, a sandbox vai ser desativada. Você quer prosseguir?", + "save-page-as": "Salvar como...", + "portable-disabled-tooltip": "Função desativada no modo portátil", + "scroll-next-tab": "Rolar para a próxima aba", + "scroll-previous-tab": "Mover para a aba anterior" +} diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index 88a6693db..6c66d614d 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -1,52 +1,52 @@ -{ - "@metadata": { - "authors": [ - "B3rnas", - "Unamane" - ] - }, - "name": "português", - "open-zim": "Abrir Zim", - "local-kiwix-server": "Servidor Kiwix local", - "random-article": "Artigo aleatório", - "home-page": "Página principal", - "main-menu": "Menu principal", - "print": "Imprimir", - "browse-library": "Navegar na biblioteca", - "open-file": "Abrir ficheiro", - "open-recent": "Abrir recente", - "search-article": "Pesquisar artigo", - "search-in-library": "Pesquisar na biblioteca", - "find-in-page": "Procura na página", - "set-fullscreen": "Ver em ecrã completo", - "quit-fullscreen": "Sair do ecrã completo", - "table-of-content": "Índice", - "add-bookmark": "Adicionar à lista de leitura", - "remove-bookmark": "Remover da lista de leitura", - "reading-list": "Lista de leitura", - "zoom-in": "Ampliar", - "zoom-out": "Reduzir", - "help": "Ajuda", - "feedback": "Comentários", - "report-a-bug": "Reportar um erro", - "request-a-feature": "Solicitar uma funcionalidade", - "about-kiwix": "Sobre o Kiwix", - "donate-to-support-kiwix": "Doar para apoiar o Kiwix", - "exit": "Sair", - "save-file-as-window-title": "Guardar ficheiro como", - "download-finished-message": "O documento foi descarregado.", - "file": "Ficheiro", - "edit": "Editar", - "view": "Ver", - "tools": "Ferramentas", - "window-title": "Biblioteca - Kiwix", - "search": "Pesquisar", - "date": "Data", - "content-type": "Tipo de conteúdo", - "open": "Abrir", - "delete": "Eliminar", - "download": "Descarregar", - "resume": "Continuar", - "pause": "Pausar", - "cancel": "Cancelar" -} +{ + "@metadata": { + "authors": [ + "B3rnas", + "Unamane" + ] + }, + "name": "português", + "open-zim": "Abrir Zim", + "local-kiwix-server": "Servidor Kiwix local", + "random-article": "Artigo aleatório", + "home-page": "Página principal", + "main-menu": "Menu principal", + "print": "Imprimir", + "browse-library": "Navegar na biblioteca", + "open-file": "Abrir ficheiro", + "open-recent": "Abrir recente", + "search-article": "Pesquisar artigo", + "search-in-library": "Pesquisar na biblioteca", + "find-in-page": "Procura na página", + "set-fullscreen": "Ver em ecrã completo", + "quit-fullscreen": "Sair do ecrã completo", + "table-of-content": "Índice", + "add-bookmark": "Adicionar à lista de leitura", + "remove-bookmark": "Remover da lista de leitura", + "reading-list": "Lista de leitura", + "zoom-in": "Ampliar", + "zoom-out": "Reduzir", + "help": "Ajuda", + "feedback": "Comentários", + "report-a-bug": "Reportar um erro", + "request-a-feature": "Solicitar uma funcionalidade", + "about-kiwix": "Sobre o Kiwix", + "donate-to-support-kiwix": "Doar para apoiar o Kiwix", + "exit": "Sair", + "save-file-as-window-title": "Guardar ficheiro como", + "download-finished-message": "O documento foi descarregado.", + "file": "Ficheiro", + "edit": "Editar", + "view": "Ver", + "tools": "Ferramentas", + "window-title": "Biblioteca - Kiwix", + "search": "Pesquisar", + "date": "Data", + "content-type": "Tipo de conteúdo", + "open": "Abrir", + "delete": "Eliminar", + "download": "Descarregar", + "resume": "Continuar", + "pause": "Pausar", + "cancel": "Cancelar" +} diff --git a/resources/i18n/qqq.json b/resources/i18n/qqq.json index d23d6c80e..e21efc0f2 100644 --- a/resources/i18n/qqq.json +++ b/resources/i18n/qqq.json @@ -1,199 +1,199 @@ -{ - "@metadata": { - "authors": [ - "Abijeet Patro", - "Ajeje Brazorf", - "Amire80", - "Eihel", - "Kuldeepburjbhalaike", - "McDutchie", - "Robby", - "Verdy p" - ] - }, - "name": "{{Doc-important|Don't write \"English\" in your language!}}\n\n'''Write the name of ''your'' language in its native script.'''\n\nCurrent language to which the string is being translated to.\n\nFor example, write \"français\" when translating to French, or \"Deutsch\" when translating to German.\n\n'''Important:''' Do not use your language’s word for “English”. Use the word that your language uses to refer to itself. If you translate this message to mean “English” in your language, your change will be reverted.", - "error-title": "{{Identical|Error}}", - "error-downloader-window-title": "Error text displayed when the ZIM file downloader failed to start on the local system.", - "error-downloader-launch-message": "Error text displayed to elaborate on the effect of the downloader failure.", - "error-launch-server-message": "Error text displayed when Kiwix server failed to launch.", - "error-archive": "Error text displayed when the retrieving the archive of a ZIM file failed.", - "error-opening-file": "Error text displayed when opening a downloaded file with an external application fails.", - "open-zim": "Caption text for file explorer window used to choose a ZIM file.", - "local-kiwix-server": "Describes a server hosted locally that contains the local kiwix library.", - "random-article": "A randomly chosen article in a ZIM file.", - "random-article-error": "Error text displayed when opening a random article in a ZIM file failed.", - "home-page": "Return to the home or landing page of the ZIM file.", - "main-menu": "Describes the menu that contains all user actions.", - "print": "{{identical|Print}}", - "print-page-error": "Error text displayed when printing the current web page into a file.", - "new-tab": "Represents the action of opening a new, blank tab on the tab bar.", - "close-tab": "Represents the action of closing a tab on the tab bar.", - "close": "Represents the action of closing a displayed window.", - "reopen-closed-tab": "Represents the action of reopen a tab that has just been closed.", - "browse-library": "Represents the action of browsing the local kiwix library.", - "open-file": "Represents the action of opening a file from the local file system.", - "save-file": "Represents the action of saving a file to the local file system.", - "open-recent": "Represents the action of choosing to open a file from those recently opened.", - "search-article": "Represents the action of Searching for a article in the current ZIM file.", - "search-in-library": "Represents the action of Searching for a text in the local Kiwix library.", - "find-in-page": "Represents the action of searching for a text in the current article/page.", - "set-fullscreen": "Represents the action of setting this desktop application to fill the entire computer screen.", - "quit-fullscreen": "Represents the action of setting this desktop application back to its original, non-fullscreen dimensions.", - "table-of-content": "Describes the table of content of an article.", - "add-bookmark": "Represents the action of adding a page as a bookmark", - "remove-bookmark": "Represents the action of removing a page from the bookmarks", - "reading-list": "Describes a list of bookmarked articles.", - "zoom-in": "Represents the action of zooming into the article, making the article content appear larger.", - "zoom-out": "Represents the action of zooming out of the article, making the article content appear smaller.", - "zoom-reset": "Represents the action of resetting the article zoom, changing the article content size back to default", - "help": "{{identical|Help}}", - "feedback": "{{identical|Feedback}}", - "report-a-bug": "Represents the action of reporting a bug in this desktop application.", - "request-a-feature": "Represents the action of requesting a new feature in this desktop application.", - "about-kiwix": "Describes the about page of Kiwix, containing information of the organization.", - "donate-to-support-kiwix": "Represents the action of donating to support the Kiwix Organization.", - "exit": "Represents the action of exiting the desktop application", - "save-file-as-window-title": "Title text of the window prompting user to save as a new file.", - "download-finished": "Title of the message box announcing the completion of a download", - "download-finished-message": "Text displayed when the download action has been completed", - "file": "{{Identical|File}}", - "edit": "{{Identical|Edit}}", - "view": "{{Identical|View}}", - "tools": "{{Identical|Tools}}", - "window-title": "The title text displayed when the library browser is present", - "search": "{{identical|Search}}", - "browse-directory": "Represents the action of browsing the local file system for a directory.", - "settings": "{{Identical|Settings}}", - "back": "{{identical|Back}}", - "forward": "{{Identical|Forward}}", - "search-files": "Represents the action of searching online or locally for ZIM files.", - "title": "{{identical|title}}", - "size": "{{identical|size}}", - "date": "{{identical|Date}}", - "content-type": "Describes the type of contents available on a ZIM file, for example, text or pictures.", - "reset-sort": "Represents the action of resetting the sorting of the items to default order.", - "open": "{{Identical|Open}}", - "delete": "{{identical|Delete}}", - "download": "{{Identical|Download}}", - "resume": "\"Resume\" here refers to continuing a task.", - "pause": "{{identical|Pause}}", - "cancel": "{{identical|Cancel}}", - "apply": "{{identical|apply}}", - "port-for-local-kiwix-server-setting": "Refers to the network port number on which the local Kiwix server will run.", - "zoom-level-setting": "Describes the default zoom level of articles displayed on this desktop application.", - "download-directory-setting": "Describes the directory in the file system where ZIM files will be downloaded to.", - "monitor-directory-setting": "Describes the directory in the file system where we will monitor for ZIM file changes.", - "reset": "{{Identical|reset}}", - "clear": "\"Clear\" here refers to emptying something.", - "browse": "{{Identical|Browse}}", - "about-kiwix-desktop-title": "Describes the title of this desktop application", - "about-kiwix-desktop-description": "Brief description of this desktop application.", - "about-learn-more-about-kiwix": "Hyperlink text directing to more details about Kiwix.", - "about-release-title": "Title text for the section on the release stages of this desktop application.", - "about-source-code": "Describes the licensing of the source code used for this desktop application.", - "about-version": "Describes the release version of this desktop application.", - "about-reporting-problem-title": "Title text for the section for how to report a problem for this desktop applciation.", - "about-report-issue": "Description of instructions on how to report an issue.", - "about-report-issue-2": "Description of instructions on how to report an issue.", - "about-libraries-title": "Title text for the section on what libraries are used in this desktop application.", - "kiwix-server-running-message": "Text displayed when kiwix server has started running.", - "kiwix-server-description": "Description text about capabilities of Kiwix server", - "fullscreen-notification": "Notification Text displayed when the desktop application is set to fullscreen.", - "online-files": "The ZIM files that can be found online.", - "local-files": "The ZIM files that are stored locally.", - "category": "{{Identical|Category}}", - "language": "{{Identical|Language}}", - "hide": "{{Identical|Hide}}", - "open-in-browser": "Represents the action of opening a link in the native browser.", - "start-kiwix-server": "Represents the action of starting the kiwix server.", - "stop-kiwix-server": "Represents the action of stopping the running kiwix server.", - "all": "{{Identical|All}}", - "all_ips": "Indicates using all ips - dual stack mode on local server. \"Stack\" refers to the networking protocol standard to use: in this case, both IPv4 and IPv6.", - "ipv4": "Indicates IPv4 mode on local server.", - "ipv6": "Indicates IPv6 mode on local server", - "fulltext-search": "Represents the action of searching the text appearances in all the articles in a ZIM file.", - "pictures": "{{identical|Picture}}", - "videos": "{{identical|Video}}", - "ftindex": "Text indicating that fulltext search is supported", - "details": "A content type for ZIM files representing it has more than just an introduction.", - "yes": "{{identical|yes}}", - "no": "{{identical|no}}", - "ok": "{{Identical|Ok}}", - "no-filter": "No filter selected for searches.", - "open-link-in-web-browser": "Ouvrir le lien dans le navigateur", - "open-link-new-tab": "Represents the action of opening a hyperlink in an article in a new tab.", - "download-dir-dialog-title": "A question to confirm the action to change the download directory.", - "download-dir-dialog-msg": "Please note that {{DIRECTORY}} should be placed on a newline.", - "invalid-port": "Error text displayed when the port selected is invalid in the local system.", - "zim-open-fail-title": "Error title text for the situation when opening a file as a ZIM file failed.", - "zim-open-fail-text": "Error description text for when opening a file as a ZIM file failed.", - "monitor-dir-dialog-title": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", - "monitor-dir-dialog-msg": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", - "monitor-clear-dir-dialog-title": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", - "path-was-copied": "Tooltip confirming that the download path from settings was copied.", - "monitor-clear-dir-dialog-msg": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", - "monitor-directory-tooltip": "Description text on what the monitor directory does.", - "next-tab": "Represents the action of switching to the next tab with respect to the current tab.", - "previous-tab": "Represents the action of switching to the previous tab with respect to the current tab.", - "cancel-download": "Represents the action of cancelling an on-going download of a ZIM file.", - "cancel-download-text": "A question to confirm the action to cancel the download of a ZIM file.", - "delete-book": "Represents the action of deleting an existing ZIM file.", - "delete-book-text": "A question to confirm the action to delete an existing ZIM file.", - "download-storage-error": "Error title text displayed when something is wrong with the directory of storage for ZIM files", - "download-storage-error-text": "Error description text for when something is wrong with the directory of storage for ZIM files.", - "download-exceeds-max-file-size": "Error text for the case when the download size exceeds the maximum files size of the filesystem where the download is going to be saved. Reported before the download is started.", - "download-dir-missing": "Error text displayed when it turns out that the download directory doesn't exist.", - "download-dir-not-writable": "Error text displayed when files cannot be created/saved in the download directory due to permissions", - "download-unavailable": "Error title text displayed when the downloading functionality is not available.", - "download-unavailable-text": "Error description text displayed when the downloading functionality is not available.", - "open-book": "\"Open\" is a imperative, not an adjective.", - "download-book": "Represents the action of downloading a ZIM file book.", - "pause-download": "Represents the action of pausing an on-going download of a ZIM file.", - "resume-download": "Represents the action of resuming a paused download of a ZIM file.", - "open-folder": "Represents the action of opening a folder in the file system.", - "couldnt-open-location": "Error title text displayed when opening a folder in the file system failed.", - "couldnt-open-location-text": "Error description text for when opening a folder in the file system failed.", - "move-files-to-trash": "Represents the action of moving ZIM files to trash, similar to deleting them.", - "move-files-to-trash-text": "Description text for the action of moving ZIM files to trash, similar to deleting them.", - "perma-delete-files-text": "Description text for the action of permanently deleting ZIM files.", - "clear-filter": "Represents the action of clearing the filters selected for a filter type.", - "language-searcher-placeholder": "Placeholder text displayed to indicate the ZIM file searcher will filter by language", - "category-searcher-placeholder": "Placeholder text displayed to indicate the ZIM file searcher will filter by category", - "content-type-searcher-placeholder": "Placeholder text displayed to indicate the ZIM file searcher will filter by content type", - "no-details": "A content type for ZIM files representing it only has an introduction.", - "no-pictures": "A content type for ZIM files that does not contain pictures.", - "no-videos": "A content type for ZIM files that does not contain videos.", - "open-previous-tabs-at-startup": "The tabs that were open when the user closed the application is opened again when the application is restarted.", - "preview-book-in-web-browser": "Preview this book by opening the link to its preview website in the native web browser", - "file-not-found-title": "Error title text displayed when the desktop application cannot find the ZIM file needed to display the web page.", - "file-not-found-text": "Error description text for when the desktop application cannot find the ZIM file needed to display the web page.", - "zim-id": "The term for the unique identifier of a ZIM file.", - "zim-name": "The term for the name of a ZIM file", - "zim-path": "The term for the path of a ZIM file", - "bad-zim-file-error-page-title": "Error title text displayed when the ZIM file appears to be invalid or corrupted", - "bad-zim-file-error-page-text": "Error description text displayed when the ZIM file appears to be invalid or corrupted", - "zim-entry-path": "The term for the path of an entry inside a ZIM file", - "export-reading-list": "Represents the action of exporting the reading list to a file.", - "export-reading-list-error": "Error description text for when exporting the reading list to a file failed.", - "import-reading-list": "Represents the action of importing a reading list from a file.", - "import-reading-list-error": "Error description text for when importing a reading list from a file failed.", - "disable-sandbox": "Question description text for when disabling the sandbox feature (Windows network drive), asking the user if he wants to proceed.", - "save-page-as": "Represents the action of saving the current tab content to a file chosen by the user.", - "portable-disabled-tooltip": "Tooltip used to explain disabled components in the portable version.", - "scroll-next-tab": "Represents the action of scrolling to the next tab of the current tab which toward the end of the tab bar.", - "scroll-previous-tab": "Represents the action of scrolling to the previous tab of the current tab which toward the start of the tab bar.", - "kiwix-search": "Title text for a list of search results, which notes to the user those are from Kiwix's Search Engine", - "search-options": "The term for a collection of additional search filtering options to help users narrow down search results.", - "read-article": "Represents the action of letting the computer read aloud all the text in the current article.", - "read-text": "Represents the action of letting the computer read out the selected text on the article.", - "read-stop": "Represents the action of stopping the on-going reading.", - "stop": "{{Identical|Stop}}", - "voice": "{{Identical|Voice}}", - "select-read-voice": "Represents the action of opening the voice selection for text-to-speech.", - "select-read-language": "Represents the action of opening the language selection for text-to-speech.", - "save-or-open": "Title of the message box allowing to choose whether a remote resource should be saved to disk or opened with a respective application", - "save-or-open-text": "Text of the message box allowing to choose whether a remote resource should be saved to disk or opened with a respective application", - "speed": "Label for text-to-speech speed adjustment control.", - "increase-tts-speed": "Represents the action of increasing the speed of the text-to-speech.", - "decrease-tts-speed": "Represents the action of decreasing the speed of the text-to-speech." -} +{ + "@metadata": { + "authors": [ + "Abijeet Patro", + "Ajeje Brazorf", + "Amire80", + "Eihel", + "Kuldeepburjbhalaike", + "McDutchie", + "Robby", + "Verdy p" + ] + }, + "name": "{{Doc-important|Don't write \"English\" in your language!}}\n\n'''Write the name of ''your'' language in its native script.'''\n\nCurrent language to which the string is being translated to.\n\nFor example, write \"français\" when translating to French, or \"Deutsch\" when translating to German.\n\n'''Important:''' Do not use your language’s word for “English”. Use the word that your language uses to refer to itself. If you translate this message to mean “English” in your language, your change will be reverted.", + "error-title": "{{Identical|Error}}", + "error-downloader-window-title": "Error text displayed when the ZIM file downloader failed to start on the local system.", + "error-downloader-launch-message": "Error text displayed to elaborate on the effect of the downloader failure.", + "error-launch-server-message": "Error text displayed when Kiwix server failed to launch.", + "error-archive": "Error text displayed when the retrieving the archive of a ZIM file failed.", + "error-opening-file": "Error text displayed when opening a downloaded file with an external application fails.", + "open-zim": "Caption text for file explorer window used to choose a ZIM file.", + "local-kiwix-server": "Describes a server hosted locally that contains the local kiwix library.", + "random-article": "A randomly chosen article in a ZIM file.", + "random-article-error": "Error text displayed when opening a random article in a ZIM file failed.", + "home-page": "Return to the home or landing page of the ZIM file.", + "main-menu": "Describes the menu that contains all user actions.", + "print": "{{identical|Print}}", + "print-page-error": "Error text displayed when printing the current web page into a file.", + "new-tab": "Represents the action of opening a new, blank tab on the tab bar.", + "close-tab": "Represents the action of closing a tab on the tab bar.", + "close": "Represents the action of closing a displayed window.", + "reopen-closed-tab": "Represents the action of reopen a tab that has just been closed.", + "browse-library": "Represents the action of browsing the local kiwix library.", + "open-file": "Represents the action of opening a file from the local file system.", + "save-file": "Represents the action of saving a file to the local file system.", + "open-recent": "Represents the action of choosing to open a file from those recently opened.", + "search-article": "Represents the action of Searching for a article in the current ZIM file.", + "search-in-library": "Represents the action of Searching for a text in the local Kiwix library.", + "find-in-page": "Represents the action of searching for a text in the current article/page.", + "set-fullscreen": "Represents the action of setting this desktop application to fill the entire computer screen.", + "quit-fullscreen": "Represents the action of setting this desktop application back to its original, non-fullscreen dimensions.", + "table-of-content": "Describes the table of content of an article.", + "add-bookmark": "Represents the action of adding a page as a bookmark", + "remove-bookmark": "Represents the action of removing a page from the bookmarks", + "reading-list": "Describes a list of bookmarked articles.", + "zoom-in": "Represents the action of zooming into the article, making the article content appear larger.", + "zoom-out": "Represents the action of zooming out of the article, making the article content appear smaller.", + "zoom-reset": "Represents the action of resetting the article zoom, changing the article content size back to default", + "help": "{{identical|Help}}", + "feedback": "{{identical|Feedback}}", + "report-a-bug": "Represents the action of reporting a bug in this desktop application.", + "request-a-feature": "Represents the action of requesting a new feature in this desktop application.", + "about-kiwix": "Describes the about page of Kiwix, containing information of the organization.", + "donate-to-support-kiwix": "Represents the action of donating to support the Kiwix Organization.", + "exit": "Represents the action of exiting the desktop application", + "save-file-as-window-title": "Title text of the window prompting user to save as a new file.", + "download-finished": "Title of the message box announcing the completion of a download", + "download-finished-message": "Text displayed when the download action has been completed", + "file": "{{Identical|File}}", + "edit": "{{Identical|Edit}}", + "view": "{{Identical|View}}", + "tools": "{{Identical|Tools}}", + "window-title": "The title text displayed when the library browser is present", + "search": "{{identical|Search}}", + "browse-directory": "Represents the action of browsing the local file system for a directory.", + "settings": "{{Identical|Settings}}", + "back": "{{identical|Back}}", + "forward": "{{Identical|Forward}}", + "search-files": "Represents the action of searching online or locally for ZIM files.", + "title": "{{identical|title}}", + "size": "{{identical|size}}", + "date": "{{identical|Date}}", + "content-type": "Describes the type of contents available on a ZIM file, for example, text or pictures.", + "reset-sort": "Represents the action of resetting the sorting of the items to default order.", + "open": "{{Identical|Open}}", + "delete": "{{identical|Delete}}", + "download": "{{Identical|Download}}", + "resume": "\"Resume\" here refers to continuing a task.", + "pause": "{{identical|Pause}}", + "cancel": "{{identical|Cancel}}", + "apply": "{{identical|apply}}", + "port-for-local-kiwix-server-setting": "Refers to the network port number on which the local Kiwix server will run.", + "zoom-level-setting": "Describes the default zoom level of articles displayed on this desktop application.", + "download-directory-setting": "Describes the directory in the file system where ZIM files will be downloaded to.", + "monitor-directory-setting": "Describes the directory in the file system where we will monitor for ZIM file changes.", + "reset": "{{Identical|reset}}", + "clear": "\"Clear\" here refers to emptying something.", + "browse": "{{Identical|Browse}}", + "about-kiwix-desktop-title": "Describes the title of this desktop application", + "about-kiwix-desktop-description": "Brief description of this desktop application.", + "about-learn-more-about-kiwix": "Hyperlink text directing to more details about Kiwix.", + "about-release-title": "Title text for the section on the release stages of this desktop application.", + "about-source-code": "Describes the licensing of the source code used for this desktop application.", + "about-version": "Describes the release version of this desktop application.", + "about-reporting-problem-title": "Title text for the section for how to report a problem for this desktop applciation.", + "about-report-issue": "Description of instructions on how to report an issue.", + "about-report-issue-2": "Description of instructions on how to report an issue.", + "about-libraries-title": "Title text for the section on what libraries are used in this desktop application.", + "kiwix-server-running-message": "Text displayed when kiwix server has started running.", + "kiwix-server-description": "Description text about capabilities of Kiwix server", + "fullscreen-notification": "Notification Text displayed when the desktop application is set to fullscreen.", + "online-files": "The ZIM files that can be found online.", + "local-files": "The ZIM files that are stored locally.", + "category": "{{Identical|Category}}", + "language": "{{Identical|Language}}", + "hide": "{{Identical|Hide}}", + "open-in-browser": "Represents the action of opening a link in the native browser.", + "start-kiwix-server": "Represents the action of starting the kiwix server.", + "stop-kiwix-server": "Represents the action of stopping the running kiwix server.", + "all": "{{Identical|All}}", + "all_ips": "Indicates using all ips - dual stack mode on local server. \"Stack\" refers to the networking protocol standard to use: in this case, both IPv4 and IPv6.", + "ipv4": "Indicates IPv4 mode on local server.", + "ipv6": "Indicates IPv6 mode on local server", + "fulltext-search": "Represents the action of searching the text appearances in all the articles in a ZIM file.", + "pictures": "{{identical|Picture}}", + "videos": "{{identical|Video}}", + "ftindex": "Text indicating that fulltext search is supported", + "details": "A content type for ZIM files representing it has more than just an introduction.", + "yes": "{{identical|yes}}", + "no": "{{identical|no}}", + "ok": "{{Identical|Ok}}", + "no-filter": "No filter selected for searches.", + "open-link-in-web-browser": "Ouvrir le lien dans le navigateur", + "open-link-new-tab": "Represents the action of opening a hyperlink in an article in a new tab.", + "download-dir-dialog-title": "A question to confirm the action to change the download directory.", + "download-dir-dialog-msg": "Please note that {{DIRECTORY}} should be placed on a newline.", + "invalid-port": "Error text displayed when the port selected is invalid in the local system.", + "zim-open-fail-title": "Error title text for the situation when opening a file as a ZIM file failed.", + "zim-open-fail-text": "Error description text for when opening a file as a ZIM file failed.", + "monitor-dir-dialog-title": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", + "monitor-dir-dialog-msg": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", + "monitor-clear-dir-dialog-title": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", + "path-was-copied": "Tooltip confirming that the download path from settings was copied.", + "monitor-clear-dir-dialog-msg": "\"Monitor\" means \"watch\" in this context. The monitor directory is monitored/watched for new ZIM files.", + "monitor-directory-tooltip": "Description text on what the monitor directory does.", + "next-tab": "Represents the action of switching to the next tab with respect to the current tab.", + "previous-tab": "Represents the action of switching to the previous tab with respect to the current tab.", + "cancel-download": "Represents the action of cancelling an on-going download of a ZIM file.", + "cancel-download-text": "A question to confirm the action to cancel the download of a ZIM file.", + "delete-book": "Represents the action of deleting an existing ZIM file.", + "delete-book-text": "A question to confirm the action to delete an existing ZIM file.", + "download-storage-error": "Error title text displayed when something is wrong with the directory of storage for ZIM files", + "download-storage-error-text": "Error description text for when something is wrong with the directory of storage for ZIM files.", + "download-exceeds-max-file-size": "Error text for the case when the download size exceeds the maximum files size of the filesystem where the download is going to be saved. Reported before the download is started.", + "download-dir-missing": "Error text displayed when it turns out that the download directory doesn't exist.", + "download-dir-not-writable": "Error text displayed when files cannot be created/saved in the download directory due to permissions", + "download-unavailable": "Error title text displayed when the downloading functionality is not available.", + "download-unavailable-text": "Error description text displayed when the downloading functionality is not available.", + "open-book": "\"Open\" is a imperative, not an adjective.", + "download-book": "Represents the action of downloading a ZIM file book.", + "pause-download": "Represents the action of pausing an on-going download of a ZIM file.", + "resume-download": "Represents the action of resuming a paused download of a ZIM file.", + "open-folder": "Represents the action of opening a folder in the file system.", + "couldnt-open-location": "Error title text displayed when opening a folder in the file system failed.", + "couldnt-open-location-text": "Error description text for when opening a folder in the file system failed.", + "move-files-to-trash": "Represents the action of moving ZIM files to trash, similar to deleting them.", + "move-files-to-trash-text": "Description text for the action of moving ZIM files to trash, similar to deleting them.", + "perma-delete-files-text": "Description text for the action of permanently deleting ZIM files.", + "clear-filter": "Represents the action of clearing the filters selected for a filter type.", + "language-searcher-placeholder": "Placeholder text displayed to indicate the ZIM file searcher will filter by language", + "category-searcher-placeholder": "Placeholder text displayed to indicate the ZIM file searcher will filter by category", + "content-type-searcher-placeholder": "Placeholder text displayed to indicate the ZIM file searcher will filter by content type", + "no-details": "A content type for ZIM files representing it only has an introduction.", + "no-pictures": "A content type for ZIM files that does not contain pictures.", + "no-videos": "A content type for ZIM files that does not contain videos.", + "open-previous-tabs-at-startup": "The tabs that were open when the user closed the application is opened again when the application is restarted.", + "preview-book-in-web-browser": "Preview this book by opening the link to its preview website in the native web browser", + "file-not-found-title": "Error title text displayed when the desktop application cannot find the ZIM file needed to display the web page.", + "file-not-found-text": "Error description text for when the desktop application cannot find the ZIM file needed to display the web page.", + "zim-id": "The term for the unique identifier of a ZIM file.", + "zim-name": "The term for the name of a ZIM file", + "zim-path": "The term for the path of a ZIM file", + "bad-zim-file-error-page-title": "Error title text displayed when the ZIM file appears to be invalid or corrupted", + "bad-zim-file-error-page-text": "Error description text displayed when the ZIM file appears to be invalid or corrupted", + "zim-entry-path": "The term for the path of an entry inside a ZIM file", + "export-reading-list": "Represents the action of exporting the reading list to a file.", + "export-reading-list-error": "Error description text for when exporting the reading list to a file failed.", + "import-reading-list": "Represents the action of importing a reading list from a file.", + "import-reading-list-error": "Error description text for when importing a reading list from a file failed.", + "disable-sandbox": "Question description text for when disabling the sandbox feature (Windows network drive), asking the user if he wants to proceed.", + "save-page-as": "Represents the action of saving the current tab content to a file chosen by the user.", + "portable-disabled-tooltip": "Tooltip used to explain disabled components in the portable version.", + "scroll-next-tab": "Represents the action of scrolling to the next tab of the current tab which toward the end of the tab bar.", + "scroll-previous-tab": "Represents the action of scrolling to the previous tab of the current tab which toward the start of the tab bar.", + "kiwix-search": "Title text for a list of search results, which notes to the user those are from Kiwix's Search Engine", + "search-options": "The term for a collection of additional search filtering options to help users narrow down search results.", + "read-article": "Represents the action of letting the computer read aloud all the text in the current article.", + "read-text": "Represents the action of letting the computer read out the selected text on the article.", + "read-stop": "Represents the action of stopping the on-going reading.", + "stop": "{{Identical|Stop}}", + "voice": "{{Identical|Voice}}", + "select-read-voice": "Represents the action of opening the voice selection for text-to-speech.", + "select-read-language": "Represents the action of opening the language selection for text-to-speech.", + "save-or-open": "Title of the message box allowing to choose whether a remote resource should be saved to disk or opened with a respective application", + "save-or-open-text": "Text of the message box allowing to choose whether a remote resource should be saved to disk or opened with a respective application", + "speed": "Label for text-to-speech speed adjustment control.", + "increase-tts-speed": "Represents the action of increasing the speed of the text-to-speech.", + "decrease-tts-speed": "Represents the action of decreasing the speed of the text-to-speech." +} diff --git a/resources/i18n/ro.json b/resources/i18n/ro.json index 8b897076c..143ef2eee 100644 --- a/resources/i18n/ro.json +++ b/resources/i18n/ro.json @@ -1,107 +1,107 @@ -{ - "@metadata": { - "authors": [ - "Andreyyshore", - "MSClaudiu", - "Trotinel Iftode" - ] - }, - "name": "Română", - "error-title": "Eroare", - "error-downloader-window-title": "Nu se poate lansa sistemul de descărcare", - "error-downloader-launch-message": "Imposibil de lansat programul de descărcare, Kiwix-desktop va începe, dar toate funcțiile de descărcare nu vor funcționa !", - "error-launch-server-message": "A apărut o eroare !", - "open-zim": "Deschide Zim", - "local-kiwix-server": "Server Kiwix Local", - "random-article": "Articol Aleatoriu", - "home-page": "Pagina principală", - "main-menu": "Meniu principal", - "print": "Imprimă", - "new-tab": "Filă nouă", - "close-tab": "Închide fila", - "close": "Închide", - "reopen-closed-tab": "Redeschideți fila închisă", - "browse-library": "Căutați în bibliotecă", - "open-file": "Deschide fișierul", - "open-recent": "Deschideți recent", - "search-article": "Caută articol", - "search-in-library": "Căutați în bibliotecă", - "find-in-page": "Găsește în pagină", - "set-fullscreen": "Setează ecran complet", - "quit-fullscreen": "Ieșire ecran complet", - "table-of-content": "Cuprins", - "reading-list": "Listă de citit", - "zoom-in": "Mărește", - "zoom-out": "Micșorează", - "zoom-reset": "Reset zoom", - "help": "Ajutor", - "feedback": "Părere", - "report-a-bug": "Raportează un bug", - "request-a-feature": "Solicitați o caracteristică", - "about-kiwix": "Despre Kiwix", - "donate-to-support-kiwix": "Donează pentru a sprijini Kiwix", - "exit": "Ieșire", - "save-file-as-window-title": "Salvați fișierul ca", - "download-finished-message": "Documentul a fost descărcat.", - "file": "Fișier", - "edit": "Modifică", - "view": "Vezi", - "tools": "Unelte", - "window-title": "Biblioteca - Kiwix", - "search": "Caută", - "browse-directory": "Căutați în Director", - "settings": "Setări", - "back": "Înapoi", - "forward": "Înainte", - "search-files": "Caută fișiere", - "title": "Titlu", - "size": "Mărime", - "date": "Data", - "content-type": "Tipul de conținut", - "reset-sort": "resetare sort", - "open": "Deschis", - "delete": "Șterge", - "download": "Descarcă", - "resume": "Reia", - "pause": "Pauză", - "cancel": "Anulare", - "apply": "Aplică", - "port-for-local-kiwix-server-setting": "Port pentru serverul local Kiwix:", - "zoom-level-setting": "Nivel Zoom :", - "download-directory-setting": "Director de descărcare :", - "reset": "Resetează", - "browse": "Caută", - "about-kiwix-desktop-title": "Kiwix pentru PC", - "about-kiwix-desktop-description": "Kiwix vă permite să aveți toată Wikipedia la îndemână oriunde mergeți! Pe o barcă, în mijlocul la pustietății sau în închisoare, Kiwix vă oferă acces la toată cunoașterea umană. Nu aveți nevoie de internet, totul este descărcat în computerul dumneavoastră.", - "about-learn-more-about-kiwix": "Aflați mai multe despre Kiwix", - "about-release-title": "Lansat", - "about-source-code": "Acest software este lansat în conformitate cu licența GNU General Public versiunea 3. Vizualizați codul sursă pe Github .", - "about-version": "Versiunea : {{VERSION}}", - "about-reporting-problem-title": "Semnalare problemă", - "about-report-issue": "Pentru a transmite o problemă, vizitează Kiwix-urmărește probleme computer și deschide o discuție care descrie problema ta.", - "about-report-issue-2": "Vă rugăm să menționați versiunea în problemă.", - "about-libraries-title": "Biblioteci", - "kiwix-server-running-message": "Serverul Kiwix rulează și poate fi accesat în rețeaua locală la:", - "kiwix-server-description": "Pornirea unui server permite altor computere din rețeaua locală să acceseze biblioteca dvs. Kiwix cu un browser web standard.", - "fullscreen-notification": "Acum sunteți în modul ecran complet. Apăsați ESC pentru a renunța!", - "local-files": "Fișiere Locale", - "hide": "Ascunde", - "open-in-browser": "Deschide în browser", - "start-kiwix-server": "Porniți Serverul Kiwix", - "stop-kiwix-server": "Oprește Serverul Kiwix", - "all": "Toate", - "fulltext-search": "Caută text întreg", - "pictures": "Fotografii", - "videos": "Videouri", - "ftindex": "Index complet", - "details": "Articol complet", - "yes": "da", - "no": "nu", - "no-filter": "fără filtru", - "open-link-in-web-browser": "Deschide legătura în browserul web", - "download-dir-dialog-title": "Sunteți sigur(ă) că vreți să schimbați folderul de descărcare?", - "download-dir-dialog-msg": "Noua cale pentru folderul de descărcare va fi: {{DIRECTORY}}", - "invalid-port": "Port invalid", - "read-text": "Citiți textul selectat", - "select-read-voice": "Selectați vocea de citire" -} +{ + "@metadata": { + "authors": [ + "Andreyyshore", + "MSClaudiu", + "Trotinel Iftode" + ] + }, + "name": "Română", + "error-title": "Eroare", + "error-downloader-window-title": "Nu se poate lansa sistemul de descărcare", + "error-downloader-launch-message": "Imposibil de lansat programul de descărcare, Kiwix-desktop va începe, dar toate funcțiile de descărcare nu vor funcționa !", + "error-launch-server-message": "A apărut o eroare !", + "open-zim": "Deschide Zim", + "local-kiwix-server": "Server Kiwix Local", + "random-article": "Articol Aleatoriu", + "home-page": "Pagina principală", + "main-menu": "Meniu principal", + "print": "Imprimă", + "new-tab": "Filă nouă", + "close-tab": "Închide fila", + "close": "Închide", + "reopen-closed-tab": "Redeschideți fila închisă", + "browse-library": "Căutați în bibliotecă", + "open-file": "Deschide fișierul", + "open-recent": "Deschideți recent", + "search-article": "Caută articol", + "search-in-library": "Căutați în bibliotecă", + "find-in-page": "Găsește în pagină", + "set-fullscreen": "Setează ecran complet", + "quit-fullscreen": "Ieșire ecran complet", + "table-of-content": "Cuprins", + "reading-list": "Listă de citit", + "zoom-in": "Mărește", + "zoom-out": "Micșorează", + "zoom-reset": "Reset zoom", + "help": "Ajutor", + "feedback": "Părere", + "report-a-bug": "Raportează un bug", + "request-a-feature": "Solicitați o caracteristică", + "about-kiwix": "Despre Kiwix", + "donate-to-support-kiwix": "Donează pentru a sprijini Kiwix", + "exit": "Ieșire", + "save-file-as-window-title": "Salvați fișierul ca", + "download-finished-message": "Documentul a fost descărcat.", + "file": "Fișier", + "edit": "Modifică", + "view": "Vezi", + "tools": "Unelte", + "window-title": "Biblioteca - Kiwix", + "search": "Caută", + "browse-directory": "Căutați în Director", + "settings": "Setări", + "back": "Înapoi", + "forward": "Înainte", + "search-files": "Caută fișiere", + "title": "Titlu", + "size": "Mărime", + "date": "Data", + "content-type": "Tipul de conținut", + "reset-sort": "resetare sort", + "open": "Deschis", + "delete": "Șterge", + "download": "Descarcă", + "resume": "Reia", + "pause": "Pauză", + "cancel": "Anulare", + "apply": "Aplică", + "port-for-local-kiwix-server-setting": "Port pentru serverul local Kiwix:", + "zoom-level-setting": "Nivel Zoom :", + "download-directory-setting": "Director de descărcare :", + "reset": "Resetează", + "browse": "Caută", + "about-kiwix-desktop-title": "Kiwix pentru PC", + "about-kiwix-desktop-description": "Kiwix vă permite să aveți toată Wikipedia la îndemână oriunde mergeți! Pe o barcă, în mijlocul la pustietății sau în închisoare, Kiwix vă oferă acces la toată cunoașterea umană. Nu aveți nevoie de internet, totul este descărcat în computerul dumneavoastră.", + "about-learn-more-about-kiwix": "Aflați mai multe despre Kiwix", + "about-release-title": "Lansat", + "about-source-code": "Acest software este lansat în conformitate cu licența GNU General Public versiunea 3. Vizualizați codul sursă pe Github .", + "about-version": "Versiunea : {{VERSION}}", + "about-reporting-problem-title": "Semnalare problemă", + "about-report-issue": "Pentru a transmite o problemă, vizitează Kiwix-urmărește probleme computer și deschide o discuție care descrie problema ta.", + "about-report-issue-2": "Vă rugăm să menționați versiunea în problemă.", + "about-libraries-title": "Biblioteci", + "kiwix-server-running-message": "Serverul Kiwix rulează și poate fi accesat în rețeaua locală la:", + "kiwix-server-description": "Pornirea unui server permite altor computere din rețeaua locală să acceseze biblioteca dvs. Kiwix cu un browser web standard.", + "fullscreen-notification": "Acum sunteți în modul ecran complet. Apăsați ESC pentru a renunța!", + "local-files": "Fișiere Locale", + "hide": "Ascunde", + "open-in-browser": "Deschide în browser", + "start-kiwix-server": "Porniți Serverul Kiwix", + "stop-kiwix-server": "Oprește Serverul Kiwix", + "all": "Toate", + "fulltext-search": "Caută text întreg", + "pictures": "Fotografii", + "videos": "Videouri", + "ftindex": "Index complet", + "details": "Articol complet", + "yes": "da", + "no": "nu", + "no-filter": "fără filtru", + "open-link-in-web-browser": "Deschide legătura în browserul web", + "download-dir-dialog-title": "Sunteți sigur(ă) că vreți să schimbați folderul de descărcare?", + "download-dir-dialog-msg": "Noua cale pentru folderul de descărcare va fi: {{DIRECTORY}}", + "invalid-port": "Port invalid", + "read-text": "Citiți textul selectat", + "select-read-voice": "Selectați vocea de citire" +} diff --git a/resources/i18n/roa-tara.json b/resources/i18n/roa-tara.json index ec58fc6ff..d1a981479 100644 --- a/resources/i18n/roa-tara.json +++ b/resources/i18n/roa-tara.json @@ -1,60 +1,60 @@ -{ - "@metadata": { - "authors": [ - "Joetaras", - "McDutchie" - ] - }, - "name": "Tarandíne", - "open-zim": "Iapre fail Zim", - "local-kiwix-server": "Server locale de Kiwix", - "home-page": "Pàgena prengepàle", - "print": "Stambe", - "new-tab": "Scheda nove", - "close-tab": "Achiude 'a schede", - "browse-library": "Sfogghie 'a libbrerie", - "open-file": "Iapre 'u file", - "open-recent": "Iapre le urteme", - "search-article": "Cirche 'a vôsce", - "set-fullscreen": "Tràse jndr'à 'u scherme indere", - "quit-fullscreen": "Isse da 'u scherme indere", - "zoom-in": "Cchiù granne", - "zoom-out": "Cchiù peccinne", - "zoom-reset": "Azzere 'u zoom", - "help": "Aijute", - "feedback": "Segnalazione", - "report-a-bug": "Segnale 'n'errore", - "about-kiwix": "Sus a Kiwix", - "exit": "Isse", - "file": "File", - "edit": "Cange", - "view": "'Ndruche", - "tools": "Struminde", - "search": "Cirche", - "settings": "'Mbostaziune", - "back": "Rrete", - "forward": "Inoltre", - "search-files": "Cirche le file", - "title": "Titole", - "size": "Dimenzione", - "content-type": "Tipe de condenute", - "reset-sort": "azzere l'ordenamende", - "open": "Iapre", - "delete": "Scangìlle", - "resume": "Repigghie", - "pause": "Pause", - "cancel": "Annulle", - "apply": "Appleche", - "zoom-level-setting": "Levèlle de zoom predefinite", - "download-directory-setting": "Cartelle de le scarecamiende", - "reset": "Azzere", - "browse": "Sfoglie", - "about-version": "Versione : {{VERSION}}", - "about-reporting-problem-title": "Segnale 'nu probbleme", - "about-libraries-title": "Libbrerie", - "local-files": "File locale", - "hide": "Scunne", - "open-in-browser": "Iapre jndr'à 'u browser", - "all": "Tutte", - "fulltext-search": "Recerche jdnr'à tutte 'u teste" -} +{ + "@metadata": { + "authors": [ + "Joetaras", + "McDutchie" + ] + }, + "name": "Tarandíne", + "open-zim": "Iapre fail Zim", + "local-kiwix-server": "Server locale de Kiwix", + "home-page": "Pàgena prengepàle", + "print": "Stambe", + "new-tab": "Scheda nove", + "close-tab": "Achiude 'a schede", + "browse-library": "Sfogghie 'a libbrerie", + "open-file": "Iapre 'u file", + "open-recent": "Iapre le urteme", + "search-article": "Cirche 'a vôsce", + "set-fullscreen": "Tràse jndr'à 'u scherme indere", + "quit-fullscreen": "Isse da 'u scherme indere", + "zoom-in": "Cchiù granne", + "zoom-out": "Cchiù peccinne", + "zoom-reset": "Azzere 'u zoom", + "help": "Aijute", + "feedback": "Segnalazione", + "report-a-bug": "Segnale 'n'errore", + "about-kiwix": "Sus a Kiwix", + "exit": "Isse", + "file": "File", + "edit": "Cange", + "view": "'Ndruche", + "tools": "Struminde", + "search": "Cirche", + "settings": "'Mbostaziune", + "back": "Rrete", + "forward": "Inoltre", + "search-files": "Cirche le file", + "title": "Titole", + "size": "Dimenzione", + "content-type": "Tipe de condenute", + "reset-sort": "azzere l'ordenamende", + "open": "Iapre", + "delete": "Scangìlle", + "resume": "Repigghie", + "pause": "Pause", + "cancel": "Annulle", + "apply": "Appleche", + "zoom-level-setting": "Levèlle de zoom predefinite", + "download-directory-setting": "Cartelle de le scarecamiende", + "reset": "Azzere", + "browse": "Sfoglie", + "about-version": "Versione : {{VERSION}}", + "about-reporting-problem-title": "Segnale 'nu probbleme", + "about-libraries-title": "Libbrerie", + "local-files": "File locale", + "hide": "Scunne", + "open-in-browser": "Iapre jndr'à 'u browser", + "all": "Tutte", + "fulltext-search": "Recerche jdnr'à tutte 'u teste" +} diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index 2203ec213..d7c797822 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -1,191 +1,191 @@ -{ - "@metadata": { - "authors": [ - "Adam-Yourist", - "DDPAT", - "Diman Russkov", - "Dirruw'o", - "Firapinch", - "Helpau", - "Ice bulldog", - "Kareyac", - "Lutece398", - "Okras", - "Pacha Tchernof", - "Razno0", - "Rofiatmustapha12", - "Smavrina", - "StarryFairy", - "Tanzun", - "Артём 13327" - ] - }, - "name": "русский", - "error-title": "Ошибка", - "error-downloader-window-title": "Невозможно создать загрузчик", - "error-downloader-launch-message": "Невозможно запустить загрузчик, Kiwix для рабочего стола будет запущен, но все функции скачивания будут недоступны!", - "error-launch-server-message": "Произошла ошибка!", - "error-archive": "Не удалось получить архив", - "open-zim": "Открыть Zim-файл", - "local-kiwix-server": "Локальный сервер Kiwix", - "random-article": "Случайная статья", - "random-article-error": "Не удалось открыть случайную статью.", - "home-page": "Домашняя страница", - "main-menu": "Главное меню", - "print": "Печать", - "print-page-error": "Во время печати возникла ошибка.", - "new-tab": "Новая вкладка", - "close-tab": "Закрыть вкладку", - "close": "Закрыть", - "reopen-closed-tab": "Открыть закрытую вкладку снова", - "browse-library": "Просмотр библиотеки", - "open-file": "Открыть файл", - "save-file": "Сохранить файл", - "open-recent": "Открыть недавние", - "search-article": "Искать статью", - "search-in-library": "Поиск в библиотеке", - "find-in-page": "Найти на странице", - "set-fullscreen": "Установить полноэкранный режим", - "quit-fullscreen": "Выйти из полноэкранного режима", - "table-of-content": "Содержание", - "add-bookmark": "Добавить список для чтения", - "remove-bookmark": "Удалить из списка для чтения", - "reading-list": "Список для чтения", - "zoom-in": "Приблизить", - "zoom-out": "Отдалить", - "zoom-reset": "Сбросить масштаб", - "help": "Справка", - "feedback": "Обратная связь", - "report-a-bug": "Сообщить об ошибке", - "request-a-feature": "Предложить новую функцию", - "about-kiwix": "О Kiwix", - "donate-to-support-kiwix": "Поддержать Kiwix", - "exit": "Выйти", - "save-file-as-window-title": "Сохранить файл как", - "download-finished": "Загрузка завершена", - "download-finished-message": "Документ был загружен.", - "file": "Файл", - "edit": "Изменить", - "view": "Посмотреть", - "tools": "Инструменты", - "window-title": "Библиотека - Kiwix", - "search": "Поиск", - "browse-directory": "Просмотр Папки", - "settings": "Настройки", - "back": "Назад", - "forward": "Вперёд", - "search-files": "Искать файлы", - "title": "Заголовок", - "size": "Размер", - "date": "Дата", - "content-type": "Тип контента", - "reset-sort": "сбросить сортировку", - "open": "Открыть", - "delete": "Удалить", - "download": "Скачать", - "resume": "Продолжить", - "pause": "Пауза", - "cancel": "Отменить", - "apply": "Применить", - "port-for-local-kiwix-server-setting": "Порт для локального сервера Kiwix", - "zoom-level-setting": "Уровень масштабирования по умолчанию", - "download-directory-setting": "Папка для загрузок", - "monitor-directory-setting": "Директория на дисплее", - "reset": "Сбросить", - "clear": "Очистить", - "browse": "Обзор", - "about-kiwix-desktop-title": "Kiwix для ПК", - "about-kiwix-desktop-description": "С Kiwix вся Википедия у вас под рукой, куда бы вы не отправились! На корабле, в далёкой глуши или даже в тюрьме, с Kiwix вам доступны все знания человечества. Для доступа вам не понадобится интернет, всё сохранено на вашем компьютере.", - "about-learn-more-about-kiwix": "Подробнее о Kiwix", - "about-release-title": "Релиз", - "about-source-code": "Это программное обеспечение выпущено под условиями GNU General Public License версии 3. Исходный код доступен по адресу Github.", - "about-version": "Версия: {{VERSION}}", - "about-reporting-problem-title": "Сообщить о проблемах", - "about-report-issue": "Чтобы сообщить об ошибке, перейдите по адресу Kiwix-desktop issue tracker и создайте новое сообщение, описав вашу проблему.", - "about-report-issue-2": "Пожалуйста, укажите версию в вашем сообщении.", - "about-libraries-title": "Библиотеки", - "kiwix-server-running-message": "Сервер Kiwix запущен и доступен в локальной сети как:", - "kiwix-server-description": "Запуск сервера позволит другим компьютерам в локальной сети получить доступ к вашей библиотеке Kiwix через обычный веб-браузер.", - "fullscreen-notification": "Вы находитесь в полноэкранном режиме. Нажмите ESC для выхода!", - "online-files": "Онлайн-файлы", - "local-files": "Локальный файлы", - "category": "Категория", - "language": "Язык", - "hide": "Скрыть", - "open-in-browser": "Открыть в браузере", - "start-kiwix-server": "Запустить сервер Kiwix", - "stop-kiwix-server": "Остановить сервер Kiwix", - "all": "Все", - "fulltext-search": "Полнотекстовый поиск", - "pictures": "Изображения", - "videos": "Видео", - "ftindex": "Полнотекстовый индекс", - "details": "Полная статья", - "yes": "да", - "no": "нет", - "ok": "ок", - "no-filter": "без фильтра", - "open-link-in-web-browser": "Открыть ссылку в веб-браузере", - "open-link-new-tab": "Открыть в новой вкладке", - "download-dir-dialog-title": "Вы уверены, что хотите изменить папку для загрузок?", - "download-dir-dialog-msg": "Путь к новой папке для загрузок:\n{{DIRECTORY}}", - "invalid-port": "Некорректный порт", - "zim-open-fail-title": "Недопустимый файл", - "zim-open-fail-text": "Указанный ZIM-файл {{ZIM}} не может быть открыт корректно. Он будет удалён из вашей библиотеки.", - "monitor-dir-dialog-title": "Вы действительно хотите изменить директорию монитора?", - "monitor-dir-dialog-msg": "Путь к новой директории монитора: \n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Вы действительно хотите очистить директорию монитора?", - "path-was-copied": "Путь скопирован", - "monitor-clear-dir-dialog-msg": "Это остановит проверку директории монитора на наличие новых файлов в формате ZIM.", - "monitor-directory-tooltip": "Все файлы ZIM в этой директории будут автоматически добавлены в библиотеку.", - "next-tab": "Перейти к следующей вкладке", - "previous-tab": "Перейти к предыдущей вкладке", - "cancel-download": "Отменить загрузку", - "cancel-download-text": "Вы уверены, что хотите отменить загрузку {{ZIM}}?", - "delete-book": "Удалить книгу", - "delete-book-text": "Вы уверены, что хотите удалить {{ZIM}}?", - "download-storage-error": "Ошибка хранилища", - "download-storage-error-text": "В системе недостаточно свободного места.", - "download-exceeds-max-file-size": "Размер загружаемого файла превышает максимальный размер файла, поддерживаемый целевой файловой системой.", - "download-dir-missing": "Каталог загрузки не существует.", - "download-dir-not-writable": "Каталог загрузки недоступен для записи.", - "download-unavailable": "Загрузка недоступна", - "download-unavailable-text": "Эта загрузка недоступна.", - "open-book": "Открыть книгу", - "download-book": "Скачать книгу", - "pause-download": "Приостановить загрузку", - "resume-download": "Возобновить загрузку", - "open-folder": "Открыть папку", - "couldnt-open-location": "Не удалось открыть местоположение", - "couldnt-open-location-text": "Kiwix не может открыть папку {{FOLDER}}", - "move-files-to-trash": "Переместить удаленные файлы в корзину", - "move-files-to-trash-text": "Это действие переместит файл в корзину.", - "perma-delete-files-text": "Это действие навсегда удалит файл.", - "clear-filter": "Очистить текущие установленные фильтры", - "language-searcher-placeholder": "Фильтр по языку", - "category-searcher-placeholder": "Фильтр по категории", - "content-type-searcher-placeholder": "Фильтр по типу контента", - "no-details": "Только введение", - "no-pictures": "Без картинок", - "no-videos": "Без видео", - "open-previous-tabs-at-startup": "Открыть предыдущие вкладки при запуске", - "preview-book-in-web-browser": "Предварительный просмотр книги в веб-браузере", - "file-not-found-title": "Файл ZIM не найден", - "file-not-found-text": "ZIM-файл не существует или не может быть прочитан", - "zim-id": "ZIM-идентификатор", - "zim-name": "Имя ZIM-файла", - "zim-path": "Путь к ZIM-файлу", - "bad-zim-file-error-page-title": "Недействительный ZIM-файл", - "bad-zim-file-error-page-text": "ZIM-файл недействителен или поврежден.", - "zim-entry-path": "Путь входа ZIM", - "export-reading-list": "Экспортировать список чтения", - "export-reading-list-error": "Ошибка при экспорте списка чтения.", - "import-reading-list": "Импортировать список чтения", - "import-reading-list-error": "Ошибка при импорте списка чтения.", - "disable-sandbox": "Приложение было запущено с сетевого диска. Известно, что это может вызвать проблемы с совместимостью из-за песочницы. Хотите ли вы рискнуть и отключить его?", - "save-page-as": "Сохранить как...", - "portable-disabled-tooltip": "В портативном режиме функция отключена", - "scroll-next-tab": "Перейти к следующей вкладке", - "stop": "Остановить", - "voice": "Голос" -} +{ + "@metadata": { + "authors": [ + "Adam-Yourist", + "DDPAT", + "Diman Russkov", + "Dirruw'o", + "Firapinch", + "Helpau", + "Ice bulldog", + "Kareyac", + "Lutece398", + "Okras", + "Pacha Tchernof", + "Razno0", + "Rofiatmustapha12", + "Smavrina", + "StarryFairy", + "Tanzun", + "Артём 13327" + ] + }, + "name": "русский", + "error-title": "Ошибка", + "error-downloader-window-title": "Невозможно создать загрузчик", + "error-downloader-launch-message": "Невозможно запустить загрузчик, Kiwix для рабочего стола будет запущен, но все функции скачивания будут недоступны!", + "error-launch-server-message": "Произошла ошибка!", + "error-archive": "Не удалось получить архив", + "open-zim": "Открыть Zim-файл", + "local-kiwix-server": "Локальный сервер Kiwix", + "random-article": "Случайная статья", + "random-article-error": "Не удалось открыть случайную статью.", + "home-page": "Домашняя страница", + "main-menu": "Главное меню", + "print": "Печать", + "print-page-error": "Во время печати возникла ошибка.", + "new-tab": "Новая вкладка", + "close-tab": "Закрыть вкладку", + "close": "Закрыть", + "reopen-closed-tab": "Открыть закрытую вкладку снова", + "browse-library": "Просмотр библиотеки", + "open-file": "Открыть файл", + "save-file": "Сохранить файл", + "open-recent": "Открыть недавние", + "search-article": "Искать статью", + "search-in-library": "Поиск в библиотеке", + "find-in-page": "Найти на странице", + "set-fullscreen": "Установить полноэкранный режим", + "quit-fullscreen": "Выйти из полноэкранного режима", + "table-of-content": "Содержание", + "add-bookmark": "Добавить список для чтения", + "remove-bookmark": "Удалить из списка для чтения", + "reading-list": "Список для чтения", + "zoom-in": "Приблизить", + "zoom-out": "Отдалить", + "zoom-reset": "Сбросить масштаб", + "help": "Справка", + "feedback": "Обратная связь", + "report-a-bug": "Сообщить об ошибке", + "request-a-feature": "Предложить новую функцию", + "about-kiwix": "О Kiwix", + "donate-to-support-kiwix": "Поддержать Kiwix", + "exit": "Выйти", + "save-file-as-window-title": "Сохранить файл как", + "download-finished": "Загрузка завершена", + "download-finished-message": "Документ был загружен.", + "file": "Файл", + "edit": "Изменить", + "view": "Посмотреть", + "tools": "Инструменты", + "window-title": "Библиотека - Kiwix", + "search": "Поиск", + "browse-directory": "Просмотр Папки", + "settings": "Настройки", + "back": "Назад", + "forward": "Вперёд", + "search-files": "Искать файлы", + "title": "Заголовок", + "size": "Размер", + "date": "Дата", + "content-type": "Тип контента", + "reset-sort": "сбросить сортировку", + "open": "Открыть", + "delete": "Удалить", + "download": "Скачать", + "resume": "Продолжить", + "pause": "Пауза", + "cancel": "Отменить", + "apply": "Применить", + "port-for-local-kiwix-server-setting": "Порт для локального сервера Kiwix", + "zoom-level-setting": "Уровень масштабирования по умолчанию", + "download-directory-setting": "Папка для загрузок", + "monitor-directory-setting": "Директория на дисплее", + "reset": "Сбросить", + "clear": "Очистить", + "browse": "Обзор", + "about-kiwix-desktop-title": "Kiwix для ПК", + "about-kiwix-desktop-description": "С Kiwix вся Википедия у вас под рукой, куда бы вы не отправились! На корабле, в далёкой глуши или даже в тюрьме, с Kiwix вам доступны все знания человечества. Для доступа вам не понадобится интернет, всё сохранено на вашем компьютере.", + "about-learn-more-about-kiwix": "Подробнее о Kiwix", + "about-release-title": "Релиз", + "about-source-code": "Это программное обеспечение выпущено под условиями GNU General Public License версии 3. Исходный код доступен по адресу Github.", + "about-version": "Версия: {{VERSION}}", + "about-reporting-problem-title": "Сообщить о проблемах", + "about-report-issue": "Чтобы сообщить об ошибке, перейдите по адресу Kiwix-desktop issue tracker и создайте новое сообщение, описав вашу проблему.", + "about-report-issue-2": "Пожалуйста, укажите версию в вашем сообщении.", + "about-libraries-title": "Библиотеки", + "kiwix-server-running-message": "Сервер Kiwix запущен и доступен в локальной сети как:", + "kiwix-server-description": "Запуск сервера позволит другим компьютерам в локальной сети получить доступ к вашей библиотеке Kiwix через обычный веб-браузер.", + "fullscreen-notification": "Вы находитесь в полноэкранном режиме. Нажмите ESC для выхода!", + "online-files": "Онлайн-файлы", + "local-files": "Локальный файлы", + "category": "Категория", + "language": "Язык", + "hide": "Скрыть", + "open-in-browser": "Открыть в браузере", + "start-kiwix-server": "Запустить сервер Kiwix", + "stop-kiwix-server": "Остановить сервер Kiwix", + "all": "Все", + "fulltext-search": "Полнотекстовый поиск", + "pictures": "Изображения", + "videos": "Видео", + "ftindex": "Полнотекстовый индекс", + "details": "Полная статья", + "yes": "да", + "no": "нет", + "ok": "ок", + "no-filter": "без фильтра", + "open-link-in-web-browser": "Открыть ссылку в веб-браузере", + "open-link-new-tab": "Открыть в новой вкладке", + "download-dir-dialog-title": "Вы уверены, что хотите изменить папку для загрузок?", + "download-dir-dialog-msg": "Путь к новой папке для загрузок:\n{{DIRECTORY}}", + "invalid-port": "Некорректный порт", + "zim-open-fail-title": "Недопустимый файл", + "zim-open-fail-text": "Указанный ZIM-файл {{ZIM}} не может быть открыт корректно. Он будет удалён из вашей библиотеки.", + "monitor-dir-dialog-title": "Вы действительно хотите изменить директорию монитора?", + "monitor-dir-dialog-msg": "Путь к новой директории монитора: \n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Вы действительно хотите очистить директорию монитора?", + "path-was-copied": "Путь скопирован", + "monitor-clear-dir-dialog-msg": "Это остановит проверку директории монитора на наличие новых файлов в формате ZIM.", + "monitor-directory-tooltip": "Все файлы ZIM в этой директории будут автоматически добавлены в библиотеку.", + "next-tab": "Перейти к следующей вкладке", + "previous-tab": "Перейти к предыдущей вкладке", + "cancel-download": "Отменить загрузку", + "cancel-download-text": "Вы уверены, что хотите отменить загрузку {{ZIM}}?", + "delete-book": "Удалить книгу", + "delete-book-text": "Вы уверены, что хотите удалить {{ZIM}}?", + "download-storage-error": "Ошибка хранилища", + "download-storage-error-text": "В системе недостаточно свободного места.", + "download-exceeds-max-file-size": "Размер загружаемого файла превышает максимальный размер файла, поддерживаемый целевой файловой системой.", + "download-dir-missing": "Каталог загрузки не существует.", + "download-dir-not-writable": "Каталог загрузки недоступен для записи.", + "download-unavailable": "Загрузка недоступна", + "download-unavailable-text": "Эта загрузка недоступна.", + "open-book": "Открыть книгу", + "download-book": "Скачать книгу", + "pause-download": "Приостановить загрузку", + "resume-download": "Возобновить загрузку", + "open-folder": "Открыть папку", + "couldnt-open-location": "Не удалось открыть местоположение", + "couldnt-open-location-text": "Kiwix не может открыть папку {{FOLDER}}", + "move-files-to-trash": "Переместить удаленные файлы в корзину", + "move-files-to-trash-text": "Это действие переместит файл в корзину.", + "perma-delete-files-text": "Это действие навсегда удалит файл.", + "clear-filter": "Очистить текущие установленные фильтры", + "language-searcher-placeholder": "Фильтр по языку", + "category-searcher-placeholder": "Фильтр по категории", + "content-type-searcher-placeholder": "Фильтр по типу контента", + "no-details": "Только введение", + "no-pictures": "Без картинок", + "no-videos": "Без видео", + "open-previous-tabs-at-startup": "Открыть предыдущие вкладки при запуске", + "preview-book-in-web-browser": "Предварительный просмотр книги в веб-браузере", + "file-not-found-title": "Файл ZIM не найден", + "file-not-found-text": "ZIM-файл не существует или не может быть прочитан", + "zim-id": "ZIM-идентификатор", + "zim-name": "Имя ZIM-файла", + "zim-path": "Путь к ZIM-файлу", + "bad-zim-file-error-page-title": "Недействительный ZIM-файл", + "bad-zim-file-error-page-text": "ZIM-файл недействителен или поврежден.", + "zim-entry-path": "Путь входа ZIM", + "export-reading-list": "Экспортировать список чтения", + "export-reading-list-error": "Ошибка при экспорте списка чтения.", + "import-reading-list": "Импортировать список чтения", + "import-reading-list-error": "Ошибка при импорте списка чтения.", + "disable-sandbox": "Приложение было запущено с сетевого диска. Известно, что это может вызвать проблемы с совместимостью из-за песочницы. Хотите ли вы рискнуть и отключить его?", + "save-page-as": "Сохранить как...", + "portable-disabled-tooltip": "В портативном режиме функция отключена", + "scroll-next-tab": "Перейти к следующей вкладке", + "stop": "Остановить", + "voice": "Голос" +} diff --git a/resources/i18n/sc.json b/resources/i18n/sc.json index 314e4d169..c29ea0bac 100644 --- a/resources/i18n/sc.json +++ b/resources/i18n/sc.json @@ -1,146 +1,146 @@ -{ - "@metadata": { - "authors": [ - "L2212" - ] - }, - "name": "Sardu", - "error-title": "Faddina", - "error-downloader-window-title": "Impossìbile creare s'iscarrigadore", - "error-downloader-launch-message": "Impossìbile carrigare s'iscarrigadore, Kiwix-desktop s'at a abèrrere ma totu sas funtzionalidades de iscarrigamentu no ant a funtzionare!", - "error-launch-server-message": "B'at àpidu una faddina|", - "error-archive": "No at fatu a otènnere s'archìviu", - "open-zim": "Aberi un'archìviu Zim", - "local-kiwix-server": "Serbidore Kiwix locale", - "random-article": "Artìculu a sa tzurpa", - "random-article-error": "Abertura de un'artìculu a casu fallida.", - "home-page": "Pàgina printzipale", - "main-menu": "Menù printzipale", - "print": "Imprenta", - "print-page-error": "B'at àpidu un'errore imprentende.", - "new-tab": "Ischeda noa", - "close-tab": "Serra s'ischeda", - "close": "Serra", - "reopen-closed-tab": "Torra a abèrrere s'ischeda serrada", - "browse-library": "Esplora sa biblioteca", - "open-file": "Aberi unu documentu", - "open-recent": "Aberi documentos reghentes", - "search-article": "Chirca un'artìculu", - "search-in-library": "Chirca in sa biblioteca", - "find-in-page": "Agata in sa pàgina", - "set-fullscreen": "Imposta a ischermu intreu", - "quit-fullscreen": "Essi dae s'ischermu intreu", - "table-of-content": "Summàriu", - "reading-list": "Lista de leghidura", - "zoom-in": "Ismànnia", - "zoom-out": "Mìnima", - "zoom-reset": "Reimposta s'ismanniamentu", - "help": "Agiudu", - "feedback": "Cummentos", - "report-a-bug": "Sinnala un'errore", - "request-a-feature": "Pedi una funtzionalidade", - "about-kiwix": "In subra de Kiwix", - "donate-to-support-kiwix": "Dona pro sustentare Kiwix", - "exit": "Essi", - "save-file-as-window-title": "Sarva su documentu comente", - "download-finished-message": "Su documentu est istadu iscarrigadu.", - "file": "Documentu", - "edit": "Modìfica", - "view": "Ammustra", - "tools": "Ainas", - "window-title": "Biblioteca - Kiwix", - "search": "Chirca", - "browse-directory": "Esplora una cartella", - "settings": "Impostatziones", - "back": "In segus", - "forward": "In antis", - "search-files": "Chirca documentos", - "title": "Tìtulu", - "size": "Mannària", - "date": "Data", - "content-type": "Casta de cuntenutu", - "reset-sort": "reimposta s'ordinamentu", - "open": "Aberi", - "delete": "Iscantzella", - "download": "Iscàrriga", - "resume": "Sighi", - "pause": "Pàusa", - "cancel": "Annulla", - "apply": "Àplica", - "port-for-local-kiwix-server-setting": "Ghenna pro su serbidore locale de Kiwix", - "zoom-level-setting": "Livellu de ismanniamentu predefinidu", - "download-directory-setting": "Cartella de iscarrigamentu", - "monitor-directory-setting": "Cartella de compidada", - "reset": "Reseta", - "clear": "Isbòida", - "browse": "Esplora", - "about-kiwix-desktop-title": "Kiwix pro iscrivania", - "about-kiwix-desktop-description": "Kiwix ti permitit de tènnere sa Wikipedia intrea a portada de manu in cale si siat logu tue ue as a andare! In una barca, in mesu a su nudda o in presone, Kiwix ti dat atzessu a totu sa connoschèntzia umana. Non bisòngias de ìnternet, totu est sarvadu in s'elaboradore tuo.", - "about-learn-more-about-kiwix": "Impara de prus subra de Kiwix", - "about-release-title": "Intregada", - "about-source-code": "Custu programma benit intregadu sighende sos tèrmines de sa litzèntzia GNU General Public License versione 3. Pòmpia·ti su còdighe de orìgine in Github.", - "about-version": "Versione: {{VERSION}}", - "about-reporting-problem-title": "Sinnalare problemas", - "about-report-issue": "Pro abèrrere una sinnalatzione, bìsita s'arrastadore pro Kiwix pro elaboradores de iscrivania (Kiwix-desktop) e aberi una sinnalatzione in ue descries su problema tuo.", - "about-report-issue-2": "Pro praghere mentova sa versione in sa sinnalatzione.", - "about-libraries-title": "Librerias", - "kiwix-server-running-message": "Su serbidore de Kiwix est in funtzione e bi podes atzèdere in sa retza locale dae:", - "kiwix-server-description": "Aviare unu serbidore permitit a sos àteros eleboradores in sa retza locale de tènnere atzessu a sa biblioteca locale de Kiwix tua cun unu navigadore (browser) istandard.", - "fullscreen-notification": "Como ses in modalidade a ischermu intreu. Incarca ESC pro essire!", - "online-files": "Documentos in lìnia", - "local-files": "Documentos locales", - "category": "Categoria", - "language": "Limba", - "hide": "Cua", - "open-in-browser": "Aberi in su navigadore (browser)", - "start-kiwix-server": "Avia su serbidore de Kiwix", - "stop-kiwix-server": "Firma su serbidore de Kiwix", - "all": "Totu", - "fulltext-search": "Chirca in su testu intreu", - "pictures": "Immàgines", - "videos": "Vìdeos", - "ftindex": "Ìnditze in testu intreu", - "details": "Artìculu intreu", - "yes": "eja", - "no": "nono", - "ok": "ab", - "no-filter": "perunu filtru", - "open-link-in-web-browser": "Aberi su ligàmene in su navigadore web", - "download-dir-dialog-title": "Ses seguru de chèrrere mudare sa cartella de iscarrigamentu?", - "download-dir-dialog-msg": "S'àndala pro sa cartella de iscarrigamentu noa at a èssere:\n{{DIRECTORY}}", - "invalid-port": "Ghenna non bàlida", - "zim-open-fail-title": "Documentu non bàlidu", - "zim-open-fail-text": "Su documentu ZIM {{ZIM}} non si podet abèrrere comente si tocat. S'at a bogare dae sa biblioteca tua.", - "monitor-dir-dialog-title": "Ses seguru de chèrrere mudare sa cartella de compidada?", - "monitor-dir-dialog-msg": "S'àndala pro sa cartella de compidada noa at a èssere:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Ses seguru de chèrrere isboidare sa cartella de compidada?", - "monitor-clear-dir-dialog-msg": "Custu at a firmare sa chirca de archìvios ZIM noos in sa cartella de compidada.", - "monitor-directory-tooltip": "Totu sos archìvios ZIM in custa cartella s'ant a annànghere in automàticu a sa biblioteca.", - "next-tab": "Cola a s'ischeda imbeniente", - "previous-tab": "Cola a s'ischeda anteposta", - "cancel-download": "Annulla s'iscarrigamentu", - "cancel-download-text": "Ses seguru de chèrrere annullare s'iscarrigamentu de {{ZIM}}?", - "delete-book": "Iscantzella su libru", - "delete-book-text": "Ses seguru de chèrrere iscantzellare {{ZIM}}?", - "download-storage-error": "Errore de archiviatzione", - "download-storage-error-text": "Su sistema non tenet memòria bastante a disponimentu.", - "download-unavailable": "Iscarrigamentu no a disponimentu", - "download-unavailable-text": "Custu iscarrigamentu no est a disponimentu.", - "open-book": "Aberi su libru", - "download-book": "Iscàrriga su libru", - "pause-download": "Pone in pàusa s'iscarrigamentu", - "resume-download": "Faghe torrare a incumintzare s'iscarrigamentu", - "open-folder": "Aberi sa cartella", - "couldnt-open-location": "Non faghiat a abèrrere sa positzione", - "couldnt-open-location-text": "Kiwix no est in gradu de abèrrere sa cartella {{FOLDER}}", - "move-files-to-trash": "Move sos documentos iscarrigados a s'arga", - "move-files-to-trash-text": "Cussta atzione at a mòvere su documentos a su boto de s'arga.", - "perma-delete-files-text": "Custa atzione at a iscantzellare in manera permanente su documentu.", - "clear-filter": "Isbòida sos filtros impostados atuales", - "language-searcher-placeholder": "Filtra pro limba", - "category-searcher-placeholder": "Filtra pro categoria", - "content-type-searcher-placeholder": "Filtra pro casta de cuntenutu", - "no-details": "Introdutzione ebbia", - "no-pictures": "Peruna immàgine", - "no-videos": "Perunu vìdeu" -} +{ + "@metadata": { + "authors": [ + "L2212" + ] + }, + "name": "Sardu", + "error-title": "Faddina", + "error-downloader-window-title": "Impossìbile creare s'iscarrigadore", + "error-downloader-launch-message": "Impossìbile carrigare s'iscarrigadore, Kiwix-desktop s'at a abèrrere ma totu sas funtzionalidades de iscarrigamentu no ant a funtzionare!", + "error-launch-server-message": "B'at àpidu una faddina|", + "error-archive": "No at fatu a otènnere s'archìviu", + "open-zim": "Aberi un'archìviu Zim", + "local-kiwix-server": "Serbidore Kiwix locale", + "random-article": "Artìculu a sa tzurpa", + "random-article-error": "Abertura de un'artìculu a casu fallida.", + "home-page": "Pàgina printzipale", + "main-menu": "Menù printzipale", + "print": "Imprenta", + "print-page-error": "B'at àpidu un'errore imprentende.", + "new-tab": "Ischeda noa", + "close-tab": "Serra s'ischeda", + "close": "Serra", + "reopen-closed-tab": "Torra a abèrrere s'ischeda serrada", + "browse-library": "Esplora sa biblioteca", + "open-file": "Aberi unu documentu", + "open-recent": "Aberi documentos reghentes", + "search-article": "Chirca un'artìculu", + "search-in-library": "Chirca in sa biblioteca", + "find-in-page": "Agata in sa pàgina", + "set-fullscreen": "Imposta a ischermu intreu", + "quit-fullscreen": "Essi dae s'ischermu intreu", + "table-of-content": "Summàriu", + "reading-list": "Lista de leghidura", + "zoom-in": "Ismànnia", + "zoom-out": "Mìnima", + "zoom-reset": "Reimposta s'ismanniamentu", + "help": "Agiudu", + "feedback": "Cummentos", + "report-a-bug": "Sinnala un'errore", + "request-a-feature": "Pedi una funtzionalidade", + "about-kiwix": "In subra de Kiwix", + "donate-to-support-kiwix": "Dona pro sustentare Kiwix", + "exit": "Essi", + "save-file-as-window-title": "Sarva su documentu comente", + "download-finished-message": "Su documentu est istadu iscarrigadu.", + "file": "Documentu", + "edit": "Modìfica", + "view": "Ammustra", + "tools": "Ainas", + "window-title": "Biblioteca - Kiwix", + "search": "Chirca", + "browse-directory": "Esplora una cartella", + "settings": "Impostatziones", + "back": "In segus", + "forward": "In antis", + "search-files": "Chirca documentos", + "title": "Tìtulu", + "size": "Mannària", + "date": "Data", + "content-type": "Casta de cuntenutu", + "reset-sort": "reimposta s'ordinamentu", + "open": "Aberi", + "delete": "Iscantzella", + "download": "Iscàrriga", + "resume": "Sighi", + "pause": "Pàusa", + "cancel": "Annulla", + "apply": "Àplica", + "port-for-local-kiwix-server-setting": "Ghenna pro su serbidore locale de Kiwix", + "zoom-level-setting": "Livellu de ismanniamentu predefinidu", + "download-directory-setting": "Cartella de iscarrigamentu", + "monitor-directory-setting": "Cartella de compidada", + "reset": "Reseta", + "clear": "Isbòida", + "browse": "Esplora", + "about-kiwix-desktop-title": "Kiwix pro iscrivania", + "about-kiwix-desktop-description": "Kiwix ti permitit de tènnere sa Wikipedia intrea a portada de manu in cale si siat logu tue ue as a andare! In una barca, in mesu a su nudda o in presone, Kiwix ti dat atzessu a totu sa connoschèntzia umana. Non bisòngias de ìnternet, totu est sarvadu in s'elaboradore tuo.", + "about-learn-more-about-kiwix": "Impara de prus subra de Kiwix", + "about-release-title": "Intregada", + "about-source-code": "Custu programma benit intregadu sighende sos tèrmines de sa litzèntzia GNU General Public License versione 3. Pòmpia·ti su còdighe de orìgine in Github.", + "about-version": "Versione: {{VERSION}}", + "about-reporting-problem-title": "Sinnalare problemas", + "about-report-issue": "Pro abèrrere una sinnalatzione, bìsita s'arrastadore pro Kiwix pro elaboradores de iscrivania (Kiwix-desktop) e aberi una sinnalatzione in ue descries su problema tuo.", + "about-report-issue-2": "Pro praghere mentova sa versione in sa sinnalatzione.", + "about-libraries-title": "Librerias", + "kiwix-server-running-message": "Su serbidore de Kiwix est in funtzione e bi podes atzèdere in sa retza locale dae:", + "kiwix-server-description": "Aviare unu serbidore permitit a sos àteros eleboradores in sa retza locale de tènnere atzessu a sa biblioteca locale de Kiwix tua cun unu navigadore (browser) istandard.", + "fullscreen-notification": "Como ses in modalidade a ischermu intreu. Incarca ESC pro essire!", + "online-files": "Documentos in lìnia", + "local-files": "Documentos locales", + "category": "Categoria", + "language": "Limba", + "hide": "Cua", + "open-in-browser": "Aberi in su navigadore (browser)", + "start-kiwix-server": "Avia su serbidore de Kiwix", + "stop-kiwix-server": "Firma su serbidore de Kiwix", + "all": "Totu", + "fulltext-search": "Chirca in su testu intreu", + "pictures": "Immàgines", + "videos": "Vìdeos", + "ftindex": "Ìnditze in testu intreu", + "details": "Artìculu intreu", + "yes": "eja", + "no": "nono", + "ok": "ab", + "no-filter": "perunu filtru", + "open-link-in-web-browser": "Aberi su ligàmene in su navigadore web", + "download-dir-dialog-title": "Ses seguru de chèrrere mudare sa cartella de iscarrigamentu?", + "download-dir-dialog-msg": "S'àndala pro sa cartella de iscarrigamentu noa at a èssere:\n{{DIRECTORY}}", + "invalid-port": "Ghenna non bàlida", + "zim-open-fail-title": "Documentu non bàlidu", + "zim-open-fail-text": "Su documentu ZIM {{ZIM}} non si podet abèrrere comente si tocat. S'at a bogare dae sa biblioteca tua.", + "monitor-dir-dialog-title": "Ses seguru de chèrrere mudare sa cartella de compidada?", + "monitor-dir-dialog-msg": "S'àndala pro sa cartella de compidada noa at a èssere:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Ses seguru de chèrrere isboidare sa cartella de compidada?", + "monitor-clear-dir-dialog-msg": "Custu at a firmare sa chirca de archìvios ZIM noos in sa cartella de compidada.", + "monitor-directory-tooltip": "Totu sos archìvios ZIM in custa cartella s'ant a annànghere in automàticu a sa biblioteca.", + "next-tab": "Cola a s'ischeda imbeniente", + "previous-tab": "Cola a s'ischeda anteposta", + "cancel-download": "Annulla s'iscarrigamentu", + "cancel-download-text": "Ses seguru de chèrrere annullare s'iscarrigamentu de {{ZIM}}?", + "delete-book": "Iscantzella su libru", + "delete-book-text": "Ses seguru de chèrrere iscantzellare {{ZIM}}?", + "download-storage-error": "Errore de archiviatzione", + "download-storage-error-text": "Su sistema non tenet memòria bastante a disponimentu.", + "download-unavailable": "Iscarrigamentu no a disponimentu", + "download-unavailable-text": "Custu iscarrigamentu no est a disponimentu.", + "open-book": "Aberi su libru", + "download-book": "Iscàrriga su libru", + "pause-download": "Pone in pàusa s'iscarrigamentu", + "resume-download": "Faghe torrare a incumintzare s'iscarrigamentu", + "open-folder": "Aberi sa cartella", + "couldnt-open-location": "Non faghiat a abèrrere sa positzione", + "couldnt-open-location-text": "Kiwix no est in gradu de abèrrere sa cartella {{FOLDER}}", + "move-files-to-trash": "Move sos documentos iscarrigados a s'arga", + "move-files-to-trash-text": "Cussta atzione at a mòvere su documentos a su boto de s'arga.", + "perma-delete-files-text": "Custa atzione at a iscantzellare in manera permanente su documentu.", + "clear-filter": "Isbòida sos filtros impostados atuales", + "language-searcher-placeholder": "Filtra pro limba", + "category-searcher-placeholder": "Filtra pro categoria", + "content-type-searcher-placeholder": "Filtra pro casta de cuntenutu", + "no-details": "Introdutzione ebbia", + "no-pictures": "Peruna immàgine", + "no-videos": "Perunu vìdeu" +} diff --git a/resources/i18n/scn.json b/resources/i18n/scn.json index b04f2a5f3..0de73d01e 100644 --- a/resources/i18n/scn.json +++ b/resources/i18n/scn.json @@ -1,32 +1,32 @@ -{ - "@metadata": { - "authors": [ - "Ajeje Brazorf", - "GianAntonucci", - "Gmelfi" - ] - }, - "name": "Sicilianu", - "error-title": "Erruri", - "home-page": "Pàggina principali", - "main-menu": "Menu principali", - "close": "Chiudi", - "help": "Aiutu", - "about-kiwix": "Nfurmazzioni supra Kiwix", - "exit": "Nesci", - "save-file-as-window-title": "Sarva file comu", - "file": "File", - "edit": "Cancia", - "view": "Talìa", - "tools": "Strummenta", - "settings": "Mpustazzioni", - "title": "Tìtulu", - "delete": "Cancella", - "cancel": "Annulla", - "about-version": "Virsioni: {{VERSION}}", - "hide": "Ammuccia", - "all": "Tuttu", - "yes": "sì", - "no": "no", - "zim-open-fail-title": "File nun vàlidu" -} +{ + "@metadata": { + "authors": [ + "Ajeje Brazorf", + "GianAntonucci", + "Gmelfi" + ] + }, + "name": "Sicilianu", + "error-title": "Erruri", + "home-page": "Pàggina principali", + "main-menu": "Menu principali", + "close": "Chiudi", + "help": "Aiutu", + "about-kiwix": "Nfurmazzioni supra Kiwix", + "exit": "Nesci", + "save-file-as-window-title": "Sarva file comu", + "file": "File", + "edit": "Cancia", + "view": "Talìa", + "tools": "Strummenta", + "settings": "Mpustazzioni", + "title": "Tìtulu", + "delete": "Cancella", + "cancel": "Annulla", + "about-version": "Virsioni: {{VERSION}}", + "hide": "Ammuccia", + "all": "Tuttu", + "yes": "sì", + "no": "no", + "zim-open-fail-title": "File nun vàlidu" +} diff --git a/resources/i18n/sdc.json b/resources/i18n/sdc.json index 542e1b08a..765250ecd 100644 --- a/resources/i18n/sdc.json +++ b/resources/i18n/sdc.json @@ -1,34 +1,34 @@ -{ - "@metadata": { - "authors": [ - "F Samaritani" - ] - }, - "name": "Sassaresu", - "error-title": "Errori", - "print": "Sthampa", - "find-in-page": "Acciappà in pàgina", - "help": "Aggiuddu", - "feedback": "Feedback", - "file": "File", - "view": "Liggì", - "tools": "Isthrumenti", - "search": "Zercha", - "settings": "Prifirènzi", - "back": "Indareddu", - "search-files": "Zirchà dati", - "title": "Tìturu", - "size": "Misuri in byte", - "date": "Data", - "content-type": "Pagina di cuntinuddu", - "delete": "Canzella", - "download": "Ischarriggà", - "cancel": "Annullà", - "clear": "Ibbiuddà", - "browse": "Iffuglia", - "hide": "Cuà", - "videos": "Vìdeo", - "yes": "eja", - "no": "no", - "zim-open-fail-title": "Tìturu nò vàriddu" -} +{ + "@metadata": { + "authors": [ + "F Samaritani" + ] + }, + "name": "Sassaresu", + "error-title": "Errori", + "print": "Sthampa", + "find-in-page": "Acciappà in pàgina", + "help": "Aggiuddu", + "feedback": "Feedback", + "file": "File", + "view": "Liggì", + "tools": "Isthrumenti", + "search": "Zercha", + "settings": "Prifirènzi", + "back": "Indareddu", + "search-files": "Zirchà dati", + "title": "Tìturu", + "size": "Misuri in byte", + "date": "Data", + "content-type": "Pagina di cuntinuddu", + "delete": "Canzella", + "download": "Ischarriggà", + "cancel": "Annullà", + "clear": "Ibbiuddà", + "browse": "Iffuglia", + "hide": "Cuà", + "videos": "Vìdeo", + "yes": "eja", + "no": "no", + "zim-open-fail-title": "Tìturu nò vàriddu" +} diff --git a/resources/i18n/se.json b/resources/i18n/se.json index c2d8740b7..641d79548 100644 --- a/resources/i18n/se.json +++ b/resources/i18n/se.json @@ -1,52 +1,52 @@ -{ - "@metadata": { - "authors": [ - "Kimberli Mäkäräinen (WMNO)", - "Yupik" - ] - }, - "name": "davvisámegillii", - "error-title": "Meattáhus", - "open-zim": "Raba ZIM-fiilla", - "home-page": "Váldosiidu", - "main-menu": "Váldofállu", - "close-tab": "Gidde leavgga", - "open-file": "Raba fiilla", - "search-in-library": "Oza girjerájus", - "find-in-page": "Oza siiddus", - "add-bookmark": "Lasit lohkanlistui", - "remove-bookmark": "Sihko lohkanlisttus", - "reading-list": "Lohkanlistu", - "help": "Rávvagat", - "feedback": "Máhcahat", - "about-kiwix": "Dieđut Kiwix birra", - "save-file-as-window-title": "Vurke fiilla namain", - "file": "Fiila", - "edit": "Rievdat", - "view": "Čájet", - "tools": "Neavvut", - "window-title": "Girjerádju - Kiwix", - "search": "Oza", - "settings": "Ásahusat", - "back": "Ruovttoluotta", - "search-files": "Oza fiillaid", - "size": "Sturrodat", - "date": "Beaivemearri", - "open": "Raba", - "delete": "Sihko", - "download": "Viečča", - "resume": "Joatkke", - "cancel": "Gaskkalduhte", - "apply": "Geavat", - "download-directory-setting": "Viečča máhpa", - "clear": "Gurre", - "browse": "Bláđe", - "about-learn-more-about-kiwix": "Lassedieđut Kiwix birra", - "about-version": "Veršuvdna: {{VERSION}}", - "about-libraries-title": "Girjerájut", - "hide": "Čiega", - "all": "Buot", - "pictures": "Govat", - "videos": "Videot", - "zim-open-fail-title": "Gustohis fiila" -} +{ + "@metadata": { + "authors": [ + "Kimberli Mäkäräinen (WMNO)", + "Yupik" + ] + }, + "name": "davvisámegillii", + "error-title": "Meattáhus", + "open-zim": "Raba ZIM-fiilla", + "home-page": "Váldosiidu", + "main-menu": "Váldofállu", + "close-tab": "Gidde leavgga", + "open-file": "Raba fiilla", + "search-in-library": "Oza girjerájus", + "find-in-page": "Oza siiddus", + "add-bookmark": "Lasit lohkanlistui", + "remove-bookmark": "Sihko lohkanlisttus", + "reading-list": "Lohkanlistu", + "help": "Rávvagat", + "feedback": "Máhcahat", + "about-kiwix": "Dieđut Kiwix birra", + "save-file-as-window-title": "Vurke fiilla namain", + "file": "Fiila", + "edit": "Rievdat", + "view": "Čájet", + "tools": "Neavvut", + "window-title": "Girjerádju - Kiwix", + "search": "Oza", + "settings": "Ásahusat", + "back": "Ruovttoluotta", + "search-files": "Oza fiillaid", + "size": "Sturrodat", + "date": "Beaivemearri", + "open": "Raba", + "delete": "Sihko", + "download": "Viečča", + "resume": "Joatkke", + "cancel": "Gaskkalduhte", + "apply": "Geavat", + "download-directory-setting": "Viečča máhpa", + "clear": "Gurre", + "browse": "Bláđe", + "about-learn-more-about-kiwix": "Lassedieđut Kiwix birra", + "about-version": "Veršuvdna: {{VERSION}}", + "about-libraries-title": "Girjerájut", + "hide": "Čiega", + "all": "Buot", + "pictures": "Govat", + "videos": "Videot", + "zim-open-fail-title": "Gustohis fiila" +} diff --git a/resources/i18n/sk.json b/resources/i18n/sk.json index c766a97e5..82fd142d5 100644 --- a/resources/i18n/sk.json +++ b/resources/i18n/sk.json @@ -1,116 +1,116 @@ -{ - "@metadata": { - "authors": [ - "McDutchie", - "Yardom78" - ] - }, - "name": "Slovenčina", - "error-title": "Chyba", - "error-downloader-window-title": "Nepodarilo za vytvoriť sťahovač", - "error-downloader-launch-message": "Nie je možné spustiť sťahovač, Kiwix sa spustí, ale všetky funkcie sťahovania budú nefunkčné.", - "error-launch-server-message": "Vyskytla sa chyba!", - "error-archive": "Archív sa nedá získať", - "open-zim": "Otvoriť Zim", - "local-kiwix-server": "Lokálny Kiwix server", - "random-article": "Náhodný článok", - "random-article-error": "Nepodarilo sa otvoriť náhodný článok.", - "home-page": "Domovská stránka", - "main-menu": "Hlavné menu", - "print": "Tlač", - "print-page-error": "Počas tlači došlo k chybe.", - "new-tab": "Nová karta", - "close-tab": "Zavrieť kartu", - "close": "Zavrieť", - "reopen-closed-tab": "Znovu otvoriť zatvorenú kartu", - "browse-library": "Prehliadať knižnicu", - "open-file": "Otvoriť súbor", - "open-recent": "Otvoriť nedávny", - "search-article": "Hľadať článok", - "search-in-library": "Hľadať v knižnici", - "find-in-page": "Nájsť na stránke", - "set-fullscreen": "Spustiť režim celej obrazovky", - "quit-fullscreen": "Ukončiť režim celej obrazovky", - "table-of-content": "Obsah", - "reading-list": "Zoznamy na prečítanie", - "zoom-in": "Priblížiť", - "zoom-out": "Oddialiť", - "zoom-reset": "Vynulovať zoom", - "help": "Pomocník", - "feedback": "Spätná väzba", - "report-a-bug": "Nahlásiť chybu", - "request-a-feature": "Požiadať o funkciu", - "about-kiwix": "O Kiwixe", - "donate-to-support-kiwix": "Podpore Kiwix darom", - "exit": "Ukončiť", - "save-file-as-window-title": "Uložiť súbor ako", - "download-finished-message": "Dokument bol stiahnutý.", - "file": "Súbor", - "edit": "Upraviť", - "view": "Zobrazenie", - "tools": "Nástroje", - "window-title": "Knižnica - Kiwix", - "search": "Hľadať", - "browse-directory": "Prehliadať adresár", - "settings": "Nastavenia", - "back": "Späť", - "forward": "Dopredu", - "search-files": "Hľadať súbory", - "title": "Názov", - "size": "Veľkosť", - "date": "Dátum", - "content-type": "Typ obsahu", - "reset-sort": "Vynulovať triedenie", - "open": "Otvoriť", - "delete": "Vymazať", - "download": "Stiahnuť", - "resume": "Pokračovať", - "pause": "Pozastaviť", - "cancel": "Zrušiť", - "apply": "Použiť", - "port-for-local-kiwix-server-setting": "Port pre miestny Kiwix server", - "zoom-level-setting": "Predvolená úroveň priblíženia", - "download-directory-setting": "Adresár sťahovania", - "monitor-directory-setting": "Adresár monitora", - "reset": "Vynulovať", - "clear": "Vyčistiť", - "browse": "Prehliadať", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix Vám umožňuje mať Wikipédiu po ruke kamkoľvek idete! Na lodi, uprostred ničoho alebo vo väzení, Kiwix Vám poskytuje prístup k celému ľudskému poznaniu. Nepotrebujete internet, všetko máte uložené na Vašom počítači.", - "about-learn-more-about-kiwix": "Dozvedieť sa o Kiwixe viac", - "about-release-title": "Vydanie", - "about-source-code": "Tento softvér je vydaný pod podmienkami GNU General Public License version 3. Zdrojová kód sa dá nájsť na Github.", - "about-version": "Verzia: {{VERSION}}", - "about-reporting-problem-title": "Nahlásenie problému", - "about-report-issue": "Pre vytvorenie hlásenia navštívte sledovač problémov Kixiw a otvorte hlásenie a opíšte problém.", - "about-report-issue-2": "Prosím uveďte aj verziu Kiwixu v opise problému.", - "about-libraries-title": "Knižnice", - "kiwix-server-running-message": "Server Kiwixu pracuje a dá sa naň vstúpiť na miestnej sieti cez:", - "kiwix-server-description": "Spustenie serveru umožňuje ostatným počítačom v lokálnej sieti pristupovať do Vašej Kiwix knižnice pomocou štandardného webového prehliadača.", - "fullscreen-notification": "Ste v režime celej obrazovky. Pre ukončenie stlačte ESC!", - "local-files": "Lokálne súbory", - "hide": "Skryť", - "open-in-browser": "Otvoriť v prehliadači", - "start-kiwix-server": "Spustiť Kiwix server", - "stop-kiwix-server": "Zastaviť Kiwix server", - "all": "Všetko", - "fulltext-search": "Hľadanie celého textu", - "pictures": "Obrázky", - "videos": "Videá", - "ftindex": "Index celého textu", - "details": "Celý článok", - "yes": "áno", - "no": "nie", - "no-filter": "žiaden filter", - "open-link-in-web-browser": "Otvoriť odkaz vo webovom prehliadači", - "download-dir-dialog-title": "Ste si istý/á, že chcete zmeniť adresár pre sťahovanie?", - "download-dir-dialog-msg": "Nová cesta k adresáru pre sťahovanie bude: {{DIRECTORY}}", - "invalid-port": "Neplatný port", - "zim-open-fail-title": "Neplatný súbor", - "zim-open-fail-text": "Súbor ZIM {{ZIM}}sa nedá správne otvoriť. Bude v vašej knižnice odstránený.", - "monitor-dir-dialog-title": "Ste si istý/á, že chcete zmeniť adresár monitora?", - "monitor-dir-dialog-msg": "Nová cesta do adresára bude: {{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Ste si istý/á, že chcete adresár vyčistiť?", - "monitor-clear-dir-dialog-msg": "Toto zastaví kontrolu adresára pre nové súbory ZIM.", - "monitor-directory-tooltip": "Všetky súbory ZIM budú automaticky pridané do knižnice." -} +{ + "@metadata": { + "authors": [ + "McDutchie", + "Yardom78" + ] + }, + "name": "Slovenčina", + "error-title": "Chyba", + "error-downloader-window-title": "Nepodarilo za vytvoriť sťahovač", + "error-downloader-launch-message": "Nie je možné spustiť sťahovač, Kiwix sa spustí, ale všetky funkcie sťahovania budú nefunkčné.", + "error-launch-server-message": "Vyskytla sa chyba!", + "error-archive": "Archív sa nedá získať", + "open-zim": "Otvoriť Zim", + "local-kiwix-server": "Lokálny Kiwix server", + "random-article": "Náhodný článok", + "random-article-error": "Nepodarilo sa otvoriť náhodný článok.", + "home-page": "Domovská stránka", + "main-menu": "Hlavné menu", + "print": "Tlač", + "print-page-error": "Počas tlači došlo k chybe.", + "new-tab": "Nová karta", + "close-tab": "Zavrieť kartu", + "close": "Zavrieť", + "reopen-closed-tab": "Znovu otvoriť zatvorenú kartu", + "browse-library": "Prehliadať knižnicu", + "open-file": "Otvoriť súbor", + "open-recent": "Otvoriť nedávny", + "search-article": "Hľadať článok", + "search-in-library": "Hľadať v knižnici", + "find-in-page": "Nájsť na stránke", + "set-fullscreen": "Spustiť režim celej obrazovky", + "quit-fullscreen": "Ukončiť režim celej obrazovky", + "table-of-content": "Obsah", + "reading-list": "Zoznamy na prečítanie", + "zoom-in": "Priblížiť", + "zoom-out": "Oddialiť", + "zoom-reset": "Vynulovať zoom", + "help": "Pomocník", + "feedback": "Spätná väzba", + "report-a-bug": "Nahlásiť chybu", + "request-a-feature": "Požiadať o funkciu", + "about-kiwix": "O Kiwixe", + "donate-to-support-kiwix": "Podpore Kiwix darom", + "exit": "Ukončiť", + "save-file-as-window-title": "Uložiť súbor ako", + "download-finished-message": "Dokument bol stiahnutý.", + "file": "Súbor", + "edit": "Upraviť", + "view": "Zobrazenie", + "tools": "Nástroje", + "window-title": "Knižnica - Kiwix", + "search": "Hľadať", + "browse-directory": "Prehliadať adresár", + "settings": "Nastavenia", + "back": "Späť", + "forward": "Dopredu", + "search-files": "Hľadať súbory", + "title": "Názov", + "size": "Veľkosť", + "date": "Dátum", + "content-type": "Typ obsahu", + "reset-sort": "Vynulovať triedenie", + "open": "Otvoriť", + "delete": "Vymazať", + "download": "Stiahnuť", + "resume": "Pokračovať", + "pause": "Pozastaviť", + "cancel": "Zrušiť", + "apply": "Použiť", + "port-for-local-kiwix-server-setting": "Port pre miestny Kiwix server", + "zoom-level-setting": "Predvolená úroveň priblíženia", + "download-directory-setting": "Adresár sťahovania", + "monitor-directory-setting": "Adresár monitora", + "reset": "Vynulovať", + "clear": "Vyčistiť", + "browse": "Prehliadať", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix Vám umožňuje mať Wikipédiu po ruke kamkoľvek idete! Na lodi, uprostred ničoho alebo vo väzení, Kiwix Vám poskytuje prístup k celému ľudskému poznaniu. Nepotrebujete internet, všetko máte uložené na Vašom počítači.", + "about-learn-more-about-kiwix": "Dozvedieť sa o Kiwixe viac", + "about-release-title": "Vydanie", + "about-source-code": "Tento softvér je vydaný pod podmienkami GNU General Public License version 3. Zdrojová kód sa dá nájsť na Github.", + "about-version": "Verzia: {{VERSION}}", + "about-reporting-problem-title": "Nahlásenie problému", + "about-report-issue": "Pre vytvorenie hlásenia navštívte sledovač problémov Kixiw a otvorte hlásenie a opíšte problém.", + "about-report-issue-2": "Prosím uveďte aj verziu Kiwixu v opise problému.", + "about-libraries-title": "Knižnice", + "kiwix-server-running-message": "Server Kiwixu pracuje a dá sa naň vstúpiť na miestnej sieti cez:", + "kiwix-server-description": "Spustenie serveru umožňuje ostatným počítačom v lokálnej sieti pristupovať do Vašej Kiwix knižnice pomocou štandardného webového prehliadača.", + "fullscreen-notification": "Ste v režime celej obrazovky. Pre ukončenie stlačte ESC!", + "local-files": "Lokálne súbory", + "hide": "Skryť", + "open-in-browser": "Otvoriť v prehliadači", + "start-kiwix-server": "Spustiť Kiwix server", + "stop-kiwix-server": "Zastaviť Kiwix server", + "all": "Všetko", + "fulltext-search": "Hľadanie celého textu", + "pictures": "Obrázky", + "videos": "Videá", + "ftindex": "Index celého textu", + "details": "Celý článok", + "yes": "áno", + "no": "nie", + "no-filter": "žiaden filter", + "open-link-in-web-browser": "Otvoriť odkaz vo webovom prehliadači", + "download-dir-dialog-title": "Ste si istý/á, že chcete zmeniť adresár pre sťahovanie?", + "download-dir-dialog-msg": "Nová cesta k adresáru pre sťahovanie bude: {{DIRECTORY}}", + "invalid-port": "Neplatný port", + "zim-open-fail-title": "Neplatný súbor", + "zim-open-fail-text": "Súbor ZIM {{ZIM}}sa nedá správne otvoriť. Bude v vašej knižnice odstránený.", + "monitor-dir-dialog-title": "Ste si istý/á, že chcete zmeniť adresár monitora?", + "monitor-dir-dialog-msg": "Nová cesta do adresára bude: {{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Ste si istý/á, že chcete adresár vyčistiť?", + "monitor-clear-dir-dialog-msg": "Toto zastaví kontrolu adresára pre nové súbory ZIM.", + "monitor-directory-tooltip": "Všetky súbory ZIM budú automaticky pridané do knižnice." +} diff --git a/resources/i18n/skr-arab.json b/resources/i18n/skr-arab.json index ff7598976..dd64e2fea 100644 --- a/resources/i18n/skr-arab.json +++ b/resources/i18n/skr-arab.json @@ -1,82 +1,82 @@ -{ - "@metadata": { - "authors": [ - "Saraiki" - ] - }, - "name": "سرائیکی", - "error-title": "نقص", - "error-launch-server-message": "خرابی تھی ڳئی ہے!", - "random-article": "پوکھواں مضمون", - "home-page": "گھر ورقہ", - "main-menu": "اصل مینیو", - "print": "چھاپو", - "new-tab": "نویں ٹیب", - "close-tab": "ٹیب بند کرو", - "close": "بند کرو", - "browse-library": "لائبریری براؤز کرو", - "open-file": "فائل کھولو", - "open-recent": "حالیہ کھولو", - "search-article": "مضمون ڳولو", - "search-in-library": "لائبریری وچ ڳولو", - "find-in-page": "ورقے وچ لبھو", - "quit-fullscreen": "پوری سکرین توں نکلو", - "table-of-content": "تندیر", - "reading-list": "مطالعہ تندیر", - "zoom-in": "وݙا کرو", - "zoom-out": "چھوٹا کرو", - "help": "مدد", - "feedback": "فیڈ بیک", - "report-a-bug": "بگ رپورٹ کرو", - "exit": "نکلو", - "file": "فائل", - "edit": "لکھو", - "view": "ݙیکھو", - "tools": "اوزار", - "search": "ڳولو", - "settings": "ترتیباں", - "back": "پچھوں", - "forward": "اڳوں تے بھیڄو", - "search-files": "فائلاں ڳولو", - "title": "عنوان", - "size": "سائز", - "date": "تریخ", - "content-type": "مواد قسم", - "open": "کھولو", - "delete": "مٹاؤ", - "download": "ڈاؤن لوڈ", - "resume": "ولدا شروع کرو", - "pause": "اجھکو", - "cancel": "منسوخ", - "apply": "اطلاق", - "reset": "ولدا ٹھیک کرو", - "clear": "صاف", - "browse": "براؤز", - "about-privacy-policy": "رازداری پالیسی", - "about-release-title": "ریلیز", - "about-version": "ورژن: {{VERSION}}", - "about-libraries-title": "لائبریریاں", - "online-files": "آن لائن فائلاں", - "local-files": "مقامی فائلاں", - "category": "ونکی", - "language": "زبان", - "hide": "لُکاؤ", - "open-in-browser": "براؤزر وچ کھولو", - "all": "یکے", - "pictures": "تصویراں", - "videos": "وڈیو", - "yes": "جیا", - "no": "کو", - "ok": "ٹھیک ہے", - "invalid-port": "غلط پورٹ", - "zim-open-fail-title": "غلط فائل", - "delete-book": "کتاب مٹاؤ", - "open-book": "کتاب کھولو", - "open-folder": "فولڈر کھولو", - "no-videos": "ویڈیو کائنی", - "zim-id": "ZIM آئی ڈی", - "zim-name": "Zim ناں", - "scroll-next-tab": "اڳلی ٹیب تے سکرول کرو", - "scroll-previous-tab": "پچھلی ٹیب تے سکرول کرو", - "stop": "اختتام" -} +{ + "@metadata": { + "authors": [ + "Saraiki" + ] + }, + "name": "سرائیکی", + "error-title": "نقص", + "error-launch-server-message": "خرابی تھی ڳئی ہے!", + "random-article": "پوکھواں مضمون", + "home-page": "گھر ورقہ", + "main-menu": "اصل مینیو", + "print": "چھاپو", + "new-tab": "نویں ٹیب", + "close-tab": "ٹیب بند کرو", + "close": "بند کرو", + "browse-library": "لائبریری براؤز کرو", + "open-file": "فائل کھولو", + "open-recent": "حالیہ کھولو", + "search-article": "مضمون ڳولو", + "search-in-library": "لائبریری وچ ڳولو", + "find-in-page": "ورقے وچ لبھو", + "quit-fullscreen": "پوری سکرین توں نکلو", + "table-of-content": "تندیر", + "reading-list": "مطالعہ تندیر", + "zoom-in": "وݙا کرو", + "zoom-out": "چھوٹا کرو", + "help": "مدد", + "feedback": "فیڈ بیک", + "report-a-bug": "بگ رپورٹ کرو", + "exit": "نکلو", + "file": "فائل", + "edit": "لکھو", + "view": "ݙیکھو", + "tools": "اوزار", + "search": "ڳولو", + "settings": "ترتیباں", + "back": "پچھوں", + "forward": "اڳوں تے بھیڄو", + "search-files": "فائلاں ڳولو", + "title": "عنوان", + "size": "سائز", + "date": "تریخ", + "content-type": "مواد قسم", + "open": "کھولو", + "delete": "مٹاؤ", + "download": "ڈاؤن لوڈ", + "resume": "ولدا شروع کرو", + "pause": "اجھکو", + "cancel": "منسوخ", + "apply": "اطلاق", + "reset": "ولدا ٹھیک کرو", + "clear": "صاف", + "browse": "براؤز", + "about-privacy-policy": "رازداری پالیسی", + "about-release-title": "ریلیز", + "about-version": "ورژن: {{VERSION}}", + "about-libraries-title": "لائبریریاں", + "online-files": "آن لائن فائلاں", + "local-files": "مقامی فائلاں", + "category": "ونکی", + "language": "زبان", + "hide": "لُکاؤ", + "open-in-browser": "براؤزر وچ کھولو", + "all": "یکے", + "pictures": "تصویراں", + "videos": "وڈیو", + "yes": "جیا", + "no": "کو", + "ok": "ٹھیک ہے", + "invalid-port": "غلط پورٹ", + "zim-open-fail-title": "غلط فائل", + "delete-book": "کتاب مٹاؤ", + "open-book": "کتاب کھولو", + "open-folder": "فولڈر کھولو", + "no-videos": "ویڈیو کائنی", + "zim-id": "ZIM آئی ڈی", + "zim-name": "Zim ناں", + "scroll-next-tab": "اڳلی ٹیب تے سکرول کرو", + "scroll-previous-tab": "پچھلی ٹیب تے سکرول کرو", + "stop": "اختتام" +} diff --git a/resources/i18n/sl.json b/resources/i18n/sl.json index cf16cabb6..91f2906b3 100644 --- a/resources/i18n/sl.json +++ b/resources/i18n/sl.json @@ -1,148 +1,148 @@ -{ - "@metadata": { - "authors": [ - "Eleassar" - ] - }, - "name": "slovenščina", - "error-title": "Napaka", - "error-downloader-window-title": "Prenašalnika ni mogoče ustvariti", - "error-downloader-launch-message": "Prenašalnika ni mogoče zagnati. Zagnal se bo Kiwix-desktop, vendar vse funkcije prenosa ne bodo delovale!", - "error-launch-server-message": "Prišlo je do napake!", - "error-archive": "Arhiva ni mogoče pridobiti", - "open-zim": "Odpri datoteko ZIM", - "local-kiwix-server": "Lokalni strežnik Kiwix", - "random-article": "Naključni članek", - "random-article-error": "Odpiranje naključnega članka ni uspelo.", - "home-page": "Domača stran", - "main-menu": "Glavni meni", - "print": "Natisni", - "print-page-error": "Pri tiskanju je prišlo do napake.", - "new-tab": "Nov zavihek", - "close-tab": "Zapri zavihek", - "close": "Zapri", - "reopen-closed-tab": "Znova odpri zaprti zavihek", - "browse-library": "Brskanje po knjižnici", - "open-file": "Odpri datoteko", - "open-recent": "Odpri nedavno", - "search-article": "Poišči članek", - "search-in-library": "Poišči v knjižnici", - "find-in-page": "Poišči na strani", - "set-fullscreen": "Nastavi celozaslonski način", - "quit-fullscreen": "Izhod iz celozaslonskega načina", - "table-of-content": "Kazalo vsebine", - "reading-list": "Bralni seznam", - "zoom-in": "Povečaj", - "zoom-out": "Pomanjšaj", - "zoom-reset": "Ponastavitev povečave", - "help": "Pomoč", - "feedback": "Povratne informacije", - "report-a-bug": "Sporoči hrošča", - "request-a-feature": "Predlagaj možnost", - "about-kiwix": "O Kiwixu", - "donate-to-support-kiwix": "Donirajte za podporo Kiwixu", - "exit": "Izhod", - "save-file-as-window-title": "Shrani datoteko kot", - "download-finished-message": "Dokument je prenesen.", - "file": "Datoteka", - "edit": "Uredi", - "view": "Ogled", - "tools": "Orodja", - "window-title": "Knjižnica – Kiwix", - "search": "Poišči", - "browse-directory": "Brskanje po imeniku", - "settings": "Nastavitve", - "back": "Nazaj", - "forward": "Naprej", - "search-files": "Poišči opombo", - "title": "Naslov", - "size": "Velikost", - "date": "Datum", - "content-type": "Vrata vsebine", - "reset-sort": "ponastavi razvrščanje", - "open": "Odpri", - "delete": "Odstrani", - "download": "Prenesi", - "resume": "Nadaljuj", - "pause": "Premor", - "cancel": "Prekliči", - "apply": "Uporabi", - "port-for-local-kiwix-server-setting": "Vrata za lokalni strežnik Kiwix", - "zoom-level-setting": "Privzeta stopnja povečave", - "download-directory-setting": "Imenik za prenose", - "monitor-directory-setting": "Nadzorni imenik", - "reset": "Ponastavi", - "clear": "Počisti", - "browse": "Prebrskaj", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix vam zagotavlja, da imate pri roki vso Wikipedijo ne glede na to, kje ste! Na čolnu, sredi ničesar ali v zaporu vam Kiwix omogoča dostop do vsega človeškega znanja. Ne potrebujete interneta, vse je shranjeno na vašem računalniku.", - "about-learn-more-about-kiwix": "Več o Kiwixu", - "about-release-title": "Izdaja", - "about-source-code": "Ta program je izdan pod pogoji licence GNU Geneeal Public License različice 3. Izvorno kodo si lahko ogledate v Githubu .", - "about-version": "Različica: {{VERSION}}", - "about-reporting-problem-title": "Sporočanje težav", - "about-report-issue": "Če želite sporočiti težavo, obiščite sledilnik težav Kiwix-desktop in tam ustvarite temo z opisom vaše težave.", - "about-report-issue-2": "Pri tem navedite različico s težavo.", - "about-libraries-title": "Knjižnice", - "kiwix-server-running-message": "Strežnik Kiwix deluje in je dostopen v lokalnem omrežju na:", - "kiwix-server-description": "Zagon strežnika omogoča drugim računalnikom v lokalnem omrežju dostop do vaše knjižnice Kiwix z običajnim spletnim brskalnikom.", - "fullscreen-notification": "Zdaj ste v celozaslonskem načinu. Pritisnite ESC za izhod!", - "online-files": "Spletne datoteke", - "local-files": "Lokalne datoteke", - "category": "Kategorija", - "language": "Jezik", - "hide": "Skrij", - "open-in-browser": "Odpri v brskalniku", - "start-kiwix-server": "Zaženi strežnik Kiwix", - "stop-kiwix-server": "Ustavi strežnik Kiwix", - "all": "Vse", - "fulltext-search": "Iskanje po celotnem besedilu", - "pictures": "Slike", - "videos": "Videi", - "ftindex": "Kazalo celotnega besedila", - "details": "Celotni članek", - "yes": "da", - "no": "ne", - "ok": "v redu", - "no-filter": "brez filtra", - "open-link-in-web-browser": "Odpri povezavo v spletnem brskalniku", - "open-link-new-tab": "Odpri povezavo na novem zavihku", - "download-dir-dialog-title": "Ali res želite spremeniti imenik za prenose?", - "download-dir-dialog-msg": "Pot novega imenika za prenose bo:\n{{DIRECTORY}}", - "invalid-port": "Neveljavna vrata", - "zim-open-fail-title": "Neveljavna datoteka", - "zim-open-fail-text": "Datoteke ZIM {{ZIM}} ni mogoče pravilno odpreti. Iz vaše knjižnice bo odstranjena.", - "monitor-dir-dialog-title": "Ali res želite spremeniti nadzorni imenik?", - "monitor-dir-dialog-msg": "Pot novega nadzornega imenika bo:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Ali res želite počistiti nadzorni imenik?", - "monitor-clear-dir-dialog-msg": "S tem boste ustavili preverjanje nadzornega imenika za nove datoteke ZIM.", - "monitor-directory-tooltip": "Vse datoteke ZIM v tem imeniku bodo samodejno dodane v knjižnico.", - "next-tab": "Pojdi na naslednji zavihek", - "previous-tab": "Pojdi na prejšnji zavihek", - "cancel-download": "Prekliči prenos", - "cancel-download-text": "Ste prepričani, da želite preklicati prenos {{ZIM}}?", - "delete-book": "Izbriši knjigo", - "delete-book-text": "Ste prepričani, da želite izbrisati {{ZIM}}?", - "download-storage-error": "Napaka pri shranjevanju", - "download-storage-error-text": "Sistem nima na voljo dovolj prostora za shranjevanje.", - "download-unavailable": "Prenos ni na voljo", - "download-unavailable-text": "Ta prenos ni na voljo.", - "open-book": "Odpri knjigo", - "download-book": "Prenesi knjigo", - "pause-download": "Premor prenosa", - "resume-download": "Nadaljuj prenos", - "open-folder": "Odpri mapo", - "couldnt-open-location": "Lokacije ni bilo mogoče odpreti", - "couldnt-open-location-text": "Kiwix ne more odpreti mape {{FOLDER}}", - "move-files-to-trash": "Prestavi izbrisane datoteke v koš", - "move-files-to-trash-text": "To dejanje bo datoteko prestavilo v koš.", - "perma-delete-files-text": "To dejanje bo datoteko trajno izbrisalo.", - "clear-filter": "Počistite trenutno nastavljene filtre", - "language-searcher-placeholder": "Filtriranje jezika", - "category-searcher-placeholder": "Filtriranje kategorije", - "content-type-searcher-placeholder": "Filtriranje vrste vsebine", - "no-details": "Samo uvod", - "no-pictures": "Ni slik", - "no-videos": "Ni videoposnetkov", - "disable-sandbox": "Kiwix je bil zagnan z omrežnega pogona. Znano je, da to povzroča težave z združljivostjo s peskovnikom za brskanje. Peskovnik bo zato onemogočen. Želite nadaljevati?" -} +{ + "@metadata": { + "authors": [ + "Eleassar" + ] + }, + "name": "slovenščina", + "error-title": "Napaka", + "error-downloader-window-title": "Prenašalnika ni mogoče ustvariti", + "error-downloader-launch-message": "Prenašalnika ni mogoče zagnati. Zagnal se bo Kiwix-desktop, vendar vse funkcije prenosa ne bodo delovale!", + "error-launch-server-message": "Prišlo je do napake!", + "error-archive": "Arhiva ni mogoče pridobiti", + "open-zim": "Odpri datoteko ZIM", + "local-kiwix-server": "Lokalni strežnik Kiwix", + "random-article": "Naključni članek", + "random-article-error": "Odpiranje naključnega članka ni uspelo.", + "home-page": "Domača stran", + "main-menu": "Glavni meni", + "print": "Natisni", + "print-page-error": "Pri tiskanju je prišlo do napake.", + "new-tab": "Nov zavihek", + "close-tab": "Zapri zavihek", + "close": "Zapri", + "reopen-closed-tab": "Znova odpri zaprti zavihek", + "browse-library": "Brskanje po knjižnici", + "open-file": "Odpri datoteko", + "open-recent": "Odpri nedavno", + "search-article": "Poišči članek", + "search-in-library": "Poišči v knjižnici", + "find-in-page": "Poišči na strani", + "set-fullscreen": "Nastavi celozaslonski način", + "quit-fullscreen": "Izhod iz celozaslonskega načina", + "table-of-content": "Kazalo vsebine", + "reading-list": "Bralni seznam", + "zoom-in": "Povečaj", + "zoom-out": "Pomanjšaj", + "zoom-reset": "Ponastavitev povečave", + "help": "Pomoč", + "feedback": "Povratne informacije", + "report-a-bug": "Sporoči hrošča", + "request-a-feature": "Predlagaj možnost", + "about-kiwix": "O Kiwixu", + "donate-to-support-kiwix": "Donirajte za podporo Kiwixu", + "exit": "Izhod", + "save-file-as-window-title": "Shrani datoteko kot", + "download-finished-message": "Dokument je prenesen.", + "file": "Datoteka", + "edit": "Uredi", + "view": "Ogled", + "tools": "Orodja", + "window-title": "Knjižnica – Kiwix", + "search": "Poišči", + "browse-directory": "Brskanje po imeniku", + "settings": "Nastavitve", + "back": "Nazaj", + "forward": "Naprej", + "search-files": "Poišči opombo", + "title": "Naslov", + "size": "Velikost", + "date": "Datum", + "content-type": "Vrata vsebine", + "reset-sort": "ponastavi razvrščanje", + "open": "Odpri", + "delete": "Odstrani", + "download": "Prenesi", + "resume": "Nadaljuj", + "pause": "Premor", + "cancel": "Prekliči", + "apply": "Uporabi", + "port-for-local-kiwix-server-setting": "Vrata za lokalni strežnik Kiwix", + "zoom-level-setting": "Privzeta stopnja povečave", + "download-directory-setting": "Imenik za prenose", + "monitor-directory-setting": "Nadzorni imenik", + "reset": "Ponastavi", + "clear": "Počisti", + "browse": "Prebrskaj", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix vam zagotavlja, da imate pri roki vso Wikipedijo ne glede na to, kje ste! Na čolnu, sredi ničesar ali v zaporu vam Kiwix omogoča dostop do vsega človeškega znanja. Ne potrebujete interneta, vse je shranjeno na vašem računalniku.", + "about-learn-more-about-kiwix": "Več o Kiwixu", + "about-release-title": "Izdaja", + "about-source-code": "Ta program je izdan pod pogoji licence GNU Geneeal Public License različice 3. Izvorno kodo si lahko ogledate v Githubu .", + "about-version": "Različica: {{VERSION}}", + "about-reporting-problem-title": "Sporočanje težav", + "about-report-issue": "Če želite sporočiti težavo, obiščite sledilnik težav Kiwix-desktop in tam ustvarite temo z opisom vaše težave.", + "about-report-issue-2": "Pri tem navedite različico s težavo.", + "about-libraries-title": "Knjižnice", + "kiwix-server-running-message": "Strežnik Kiwix deluje in je dostopen v lokalnem omrežju na:", + "kiwix-server-description": "Zagon strežnika omogoča drugim računalnikom v lokalnem omrežju dostop do vaše knjižnice Kiwix z običajnim spletnim brskalnikom.", + "fullscreen-notification": "Zdaj ste v celozaslonskem načinu. Pritisnite ESC za izhod!", + "online-files": "Spletne datoteke", + "local-files": "Lokalne datoteke", + "category": "Kategorija", + "language": "Jezik", + "hide": "Skrij", + "open-in-browser": "Odpri v brskalniku", + "start-kiwix-server": "Zaženi strežnik Kiwix", + "stop-kiwix-server": "Ustavi strežnik Kiwix", + "all": "Vse", + "fulltext-search": "Iskanje po celotnem besedilu", + "pictures": "Slike", + "videos": "Videi", + "ftindex": "Kazalo celotnega besedila", + "details": "Celotni članek", + "yes": "da", + "no": "ne", + "ok": "v redu", + "no-filter": "brez filtra", + "open-link-in-web-browser": "Odpri povezavo v spletnem brskalniku", + "open-link-new-tab": "Odpri povezavo na novem zavihku", + "download-dir-dialog-title": "Ali res želite spremeniti imenik za prenose?", + "download-dir-dialog-msg": "Pot novega imenika za prenose bo:\n{{DIRECTORY}}", + "invalid-port": "Neveljavna vrata", + "zim-open-fail-title": "Neveljavna datoteka", + "zim-open-fail-text": "Datoteke ZIM {{ZIM}} ni mogoče pravilno odpreti. Iz vaše knjižnice bo odstranjena.", + "monitor-dir-dialog-title": "Ali res želite spremeniti nadzorni imenik?", + "monitor-dir-dialog-msg": "Pot novega nadzornega imenika bo:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Ali res želite počistiti nadzorni imenik?", + "monitor-clear-dir-dialog-msg": "S tem boste ustavili preverjanje nadzornega imenika za nove datoteke ZIM.", + "monitor-directory-tooltip": "Vse datoteke ZIM v tem imeniku bodo samodejno dodane v knjižnico.", + "next-tab": "Pojdi na naslednji zavihek", + "previous-tab": "Pojdi na prejšnji zavihek", + "cancel-download": "Prekliči prenos", + "cancel-download-text": "Ste prepričani, da želite preklicati prenos {{ZIM}}?", + "delete-book": "Izbriši knjigo", + "delete-book-text": "Ste prepričani, da želite izbrisati {{ZIM}}?", + "download-storage-error": "Napaka pri shranjevanju", + "download-storage-error-text": "Sistem nima na voljo dovolj prostora za shranjevanje.", + "download-unavailable": "Prenos ni na voljo", + "download-unavailable-text": "Ta prenos ni na voljo.", + "open-book": "Odpri knjigo", + "download-book": "Prenesi knjigo", + "pause-download": "Premor prenosa", + "resume-download": "Nadaljuj prenos", + "open-folder": "Odpri mapo", + "couldnt-open-location": "Lokacije ni bilo mogoče odpreti", + "couldnt-open-location-text": "Kiwix ne more odpreti mape {{FOLDER}}", + "move-files-to-trash": "Prestavi izbrisane datoteke v koš", + "move-files-to-trash-text": "To dejanje bo datoteko prestavilo v koš.", + "perma-delete-files-text": "To dejanje bo datoteko trajno izbrisalo.", + "clear-filter": "Počistite trenutno nastavljene filtre", + "language-searcher-placeholder": "Filtriranje jezika", + "category-searcher-placeholder": "Filtriranje kategorije", + "content-type-searcher-placeholder": "Filtriranje vrste vsebine", + "no-details": "Samo uvod", + "no-pictures": "Ni slik", + "no-videos": "Ni videoposnetkov", + "disable-sandbox": "Kiwix je bil zagnan z omrežnega pogona. Znano je, da to povzroča težave z združljivostjo s peskovnikom za brskanje. Peskovnik bo zato onemogočen. Želite nadaljevati?" +} diff --git a/resources/i18n/sms.json b/resources/i18n/sms.json index 801ed5092..ae772de49 100644 --- a/resources/i18n/sms.json +++ b/resources/i18n/sms.json @@ -1,57 +1,57 @@ -{ - "@metadata": { - "authors": [ - "Yupik" - ] - }, - "name": "sääʹmǩiõll", - "error-title": "Vââʹǩǩ", - "error-archive": "Arkiiv viǯǯmõš ij oʹnnstam", - "open-zim": "Ääʹved ZIM-teâttõõzz", - "random-article": "Håʹt mii artikkel", - "home-page": "Ouddseidd", - "main-menu": "Väʹlddvaʹlljõk", - "print": "Printtâd", - "new-tab": "Ođđ kõskklõstt", - "close-tab": "Piij kõskklõõst ǩidd", - "browse-library": "Ǩiõčč ǩeʹrjjpõõrt", - "open-file": "Ääʹved teâttõõzz", - "search-article": "Ooʒʒ artikkeeʹl", - "search-in-library": "Ooʒʒ ǩeʹrjjpõõrtâst", - "find-in-page": "Ooʒʒ seeidast", - "table-of-content": "Siiskâžlooǥǥtõs", - "add-bookmark": "Lââʹzzet lookkâmliʹstte", - "remove-bookmark": "Jaukkâd lookkâmliistâst", - "reading-list": "Lookkâmlistt", - "help": "Vuäʹpstõõzz", - "feedback": "Maacctõs", - "report-a-bug": "Iʹlmmet čuõlmâst", - "about-kiwix": "Teâđ Kiwix pirr", - "save-file-as-window-title": "Ruõkk teâttõõzz nõõmin", - "file": "Teâttõs", - "edit": "Muuʹtt", - "view": "Čuäʹjet", - "tools": "Neävv", - "window-title": "Ǩeʹrjjpõrtt - Kiwix", - "search": "Ooʒʒ", - "settings": "Asetõõzz", - "search-files": "Ooʒʒ teâttõõzzid", - "size": "Šorradvuõtt", - "date": "Peiʹvvmieʹrr", - "open": "Ääʹved", - "delete": "Jaukkâd", - "download": "Laadd", - "cancel": "Jõõsk", - "clear": "Kuârad", - "browse": "Ǩiõčč", - "about-learn-more-about-kiwix": "Lââʹssteâđ Kiwix pirr", - "about-version": "Versio: {{VERSION}}", - "local-files": "Pääiklaž teâttõõzz", - "hide": "Čiõǥǥ", - "open-in-browser": "Ääʹved neʹttlookkjest", - "all": "Puk", - "pictures": "Kaart da snimldõõǥǥ", - "videos": "Video", - "open-link-new-tab": "Ääʹved liiŋk ođđ kõskklõʹstte", - "zim-open-fail-title": "Vââǥǥlaž teâttõs" -} +{ + "@metadata": { + "authors": [ + "Yupik" + ] + }, + "name": "sääʹmǩiõll", + "error-title": "Vââʹǩǩ", + "error-archive": "Arkiiv viǯǯmõš ij oʹnnstam", + "open-zim": "Ääʹved ZIM-teâttõõzz", + "random-article": "Håʹt mii artikkel", + "home-page": "Ouddseidd", + "main-menu": "Väʹlddvaʹlljõk", + "print": "Printtâd", + "new-tab": "Ođđ kõskklõstt", + "close-tab": "Piij kõskklõõst ǩidd", + "browse-library": "Ǩiõčč ǩeʹrjjpõõrt", + "open-file": "Ääʹved teâttõõzz", + "search-article": "Ooʒʒ artikkeeʹl", + "search-in-library": "Ooʒʒ ǩeʹrjjpõõrtâst", + "find-in-page": "Ooʒʒ seeidast", + "table-of-content": "Siiskâžlooǥǥtõs", + "add-bookmark": "Lââʹzzet lookkâmliʹstte", + "remove-bookmark": "Jaukkâd lookkâmliistâst", + "reading-list": "Lookkâmlistt", + "help": "Vuäʹpstõõzz", + "feedback": "Maacctõs", + "report-a-bug": "Iʹlmmet čuõlmâst", + "about-kiwix": "Teâđ Kiwix pirr", + "save-file-as-window-title": "Ruõkk teâttõõzz nõõmin", + "file": "Teâttõs", + "edit": "Muuʹtt", + "view": "Čuäʹjet", + "tools": "Neävv", + "window-title": "Ǩeʹrjjpõrtt - Kiwix", + "search": "Ooʒʒ", + "settings": "Asetõõzz", + "search-files": "Ooʒʒ teâttõõzzid", + "size": "Šorradvuõtt", + "date": "Peiʹvvmieʹrr", + "open": "Ääʹved", + "delete": "Jaukkâd", + "download": "Laadd", + "cancel": "Jõõsk", + "clear": "Kuârad", + "browse": "Ǩiõčč", + "about-learn-more-about-kiwix": "Lââʹssteâđ Kiwix pirr", + "about-version": "Versio: {{VERSION}}", + "local-files": "Pääiklaž teâttõõzz", + "hide": "Čiõǥǥ", + "open-in-browser": "Ääʹved neʹttlookkjest", + "all": "Puk", + "pictures": "Kaart da snimldõõǥǥ", + "videos": "Video", + "open-link-new-tab": "Ääʹved liiŋk ođđ kõskklõʹstte", + "zim-open-fail-title": "Vââǥǥlaž teâttõs" +} diff --git a/resources/i18n/sq.json b/resources/i18n/sq.json index 54d518996..f19f052c8 100644 --- a/resources/i18n/sq.json +++ b/resources/i18n/sq.json @@ -1,178 +1,178 @@ -{ - "@metadata": { - "authors": [ - "Besnik b", - "McDutchie" - ] - }, - "name": "Shqip", - "error-title": "Gabim", - "error-downloader-window-title": "S’krijohet dot shkarkuesi", - "error-downloader-launch-message": "E pamundur të niset shkarkuesi, Kiwix-desktop do të niset, por s’do të punojnë krejt funksionet e shkarkimit!", - "error-launch-server-message": "Ndodhi një gabim!", - "error-archive": "S’merret dor arkivi", - "open-zim": "Hap Kartelë Zim", - "local-kiwix-server": "Shërbyes Kiwix Vendor", - "random-article": "Artikull Kuturu", - "random-article-error": "S’u arrit të hapet një artikull kuturu.", - "home-page": "Faqe hyrëse", - "main-menu": "Menuja kryesore", - "print": "Shtype", - "print-page-error": "Ndodhi një gabim gjatë shtypjes.", - "new-tab": "Skedë e re", - "close-tab": "Mbylle skedën", - "close": "Mbylle", - "reopen-closed-tab": "Rihape skedën e mbyllyr", - "browse-library": "Shfletoni bibliotekën", - "open-file": "Hapni kartelë", - "open-recent": "Hap së fundi", - "search-article": "Kërkoni artikull", - "search-in-library": "Kërkoni në bibliotekë", - "find-in-page": "Gjej në faqe", - "set-fullscreen": "Vëre sa krejt ekrani", - "quit-fullscreen": "Dil nga “Sa krejt ekrani”", - "table-of-content": "Tryezë e lëndës", - "add-bookmark": "Shtoje te lista e leximeve", - "remove-bookmark": "Hiqe nga lista e leximeve", - "reading-list": "Listë leximesh", - "zoom-in": "Zmadhoje", - "zoom-out": "Zvogëloje", - "zoom-reset": "Zerim Zoom-i", - "help": "Ndihmë", - "feedback": "Përshtypje", - "report-a-bug": "Njoftoni një të metë", - "request-a-feature": "Kërkoni një veçori", - "about-kiwix": "Mbi Kiwix-in", - "donate-to-support-kiwix": "Dhuroni që të përkrahni Kiwix-in", - "exit": "Dalje", - "save-file-as-window-title": "Ruaje Kartelën si", - "download-finished-message": "Dokumenti u shkarkua.", - "file": "Kartelë", - "edit": "Përpunoni", - "view": "Shiheni", - "tools": "Mjete", - "window-title": "Bibliotekë - Kiwix", - "search": "Kërko", - "browse-directory": "Shfletoni Drejtorinë", - "settings": "Rregullime", - "back": "Mbrapsht", - "forward": "Përpara", - "search-files": "Kërkoni kartela", - "title": "Titull", - "size": "Madhësi", - "date": "Datë", - "content-type": "Lloj lënde", - "reset-sort": "zeroni renditjen", - "open": "Hape", - "delete": "Fshije", - "download": "Shkarkoje", - "resume": "Rimerre", - "pause": "Ndalesë", - "cancel": "Anuloje", - "apply": "Zbatoje", - "port-for-local-kiwix-server-setting": "Portë për shërbyes Kiwix vendor", - "zoom-level-setting": "Shkallë parazgjedhje për zoom-in", - "download-directory-setting": "Drejtori shkarkimesh", - "monitor-directory-setting": "Drejtori mbikëqyrjesh", - "reset": "Riktheje te parazgjedhja", - "clear": "Spastroje", - "browse": "Shfletoni", - "about-kiwix-desktop-title": "Desktopi Kiwix", - "about-kiwix-desktop-description": "Kiwix-i ju lejon të keni në pëllëmbë të dorës krejt Wikipedia-n, kudo që shkoni! Në një barkë, në mes të asgjëkundit apo në burg, Kiwix ju lejon të hyni në krejt dijen njerëzore. S’keni nevojë për Internet, gjithçka depozitohet në kompjuterin tuaj.", - "about-learn-more-about-kiwix": "Mësoni më tepër mbi Kiwix-in", - "about-release-title": "Hedhje në qarkullim", - "about-source-code": "Ky program hidhet në qarkullim sipas termave të licencës GNU General Public License version 3. Shihini kodin burim te Github.", - "about-version": "Version: {{VERSION}}", - "about-reporting-problem-title": "Njoftim problemesh", - "about-report-issue": "Që të hapni një çështje, vizitoni ndjekësin e çështjeve të desktopit Kiwix dhe hapni një çështje që përshkruan problemin tuaj.", - "about-report-issue-2": "Ju lutemi, te çështja përmendni versionin.", - "about-libraries-title": "Biblioteka", - "kiwix-server-running-message": "Shërbyesi Kiwix Server është në punë dhe mund të përdoret te rrjeti vendor në:", - "kiwix-server-description": "Nisja e një shërbyesi u lejon kompjuterave të tjerë në rrjetin vendor të përdorin bibliotekën tuaj Kiwix përmes një shfletuesi standard.", - "fullscreen-notification": "Gjendeni nën mënyrën “sa krejt ekrani”. Që të dilni, shtypni tastin ESC!", - "online-files": "Kartela Në Linjë", - "local-files": "Kartela Vendore", - "category": "Kategori", - "language": "Gjuhë", - "hide": "Fshihe", - "open-in-browser": "Hape në shfletues", - "start-kiwix-server": "Nis Shërbyesin Kiwix", - "stop-kiwix-server": "Ndale Shërbyesin Kiwix", - "all": "Krejt", - "all_ips": "Krejt - Mënyra “Kapicë Duale”", - "ipv4": "Mënyra Vetëm IPv4", - "ipv6": "Mënyra Vetëm IPv6", - "fulltext-search": "Kërkim në krejt tekstin", - "pictures": "Foto", - "videos": "Video", - "ftindex": "Tregues “fulltext”", - "details": "Artikull i plotë", - "yes": "po", - "no": "jo", - "ok": "ok", - "no-filter": "pa filtër", - "open-link-in-web-browser": "Hape lidhjen në shfletues", - "open-link-new-tab": "Hape lidhjen në skedë të re", - "download-dir-dialog-title": "Jeni i sigurt se doni të ndryshoni drejtorinë e shkarkimeve?", - "download-dir-dialog-msg": "Shtegu i drejtorisë së re do të jetë:\n{{DIRECTORY}}", - "invalid-port": "Portë e pavlefshme", - "zim-open-fail-title": "Kartelë e pavlefshme", - "zim-open-fail-text": "Kartela ZIM {{ZIM}} s’mund të hapet si duhet. Do të hiqet nga biblioteka juaj.", - "monitor-dir-dialog-title": "Jeni i sigurt se doni të ndryshohet drejtoria e mbikëqyrjeve?", - "monitor-dir-dialog-msg": "Shtegu i drejtorisë së re të mbikëqyrjeve do të jetë :\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Jeni i sigurt se doni të spastrohet drejtoria e mbikëqyrjeve?", - "path-was-copied": "Shtegu u kopjua", - "monitor-clear-dir-dialog-msg": "Kjo do të ndalë kontrollimin e drejtorisë së mbikëqyrjeve për kartela të reja ZIM.", - "monitor-directory-tooltip": "Krejt kartelat ZIM në këtë drejtori do të shtohen automatikisht te biblioteka.", - "next-tab": "Kalo te skeda pasuese", - "previous-tab": "Kalo te skeda e mëparshme", - "cancel-download": "Anuloje shkarkimin", - "cancel-download-text": "Jeni i sigurt se doni të anulohet shkarkimi i {{ZIM}}?", - "delete-book": "Fshije librin", - "delete-book-text": "Jeni i sigurt se doni të fshihet {{ZIM}}?", - "download-storage-error": "Gabim Depozitimi", - "download-storage-error-text": "Sistemi s’ka hapësirë të lirë të mjaftueshme.", - "download-exceeds-max-file-size": "Madhësia e shkarkimit tejkalon madhësinë maksimum të kartelave që mbulohet nga sistemi i synuar i kartelave.", - "download-dir-missing": "Drejtoria e shkarkimeve s’ekziston.", - "download-dir-not-writable": "Drejtoria e shkarkimeve s’është e shkrueshme.", - "download-unavailable": "Shkarkim Jo i Passhëm", - "download-unavailable-text": "Ky shkarkim s’është gati.", - "open-book": "Hapni libër", - "download-book": "Shkarkoni libër", - "pause-download": "Pauzë shkarkimi", - "resume-download": "Rimerre shkarkimin", - "open-folder": "Hap dosje", - "couldnt-open-location": "S’u hap dot vendndodhje", - "couldnt-open-location-text": "Kiwix-i s’është në gjendje të hapë dosjen {{FOLDER}}", - "move-files-to-trash": "Shpjeri kartelat e fshira te hedhurinat", - "move-files-to-trash-text": "Ky veprim do ta shpjerë kartelën te hedhurinat.", - "perma-delete-files-text": "Ky veprim do ta fshijë përgjithmonë kartelën.", - "clear-filter": "Spastro filtrat e ujdisur aktualisht", - "language-searcher-placeholder": "Filtroji sipas gjuhësh", - "category-searcher-placeholder": "Filtroji sipas kategorish", - "content-type-searcher-placeholder": "Filtroji sipas llojesh lënde", - "no-details": "Vetëm hyrje", - "no-pictures": "Pa Foto", - "no-videos": "Pa Video", - "open-previous-tabs-at-startup": "Gjatë nisjes, hap skedat e mëparshme", - "preview-book-in-web-browser": "Bëjini librit paraparje në shfletues", - "file-not-found-title": "S’u Gjet Kartelë ZIM", - "file-not-found-text": "Kartela ZIM s’ekziston, ose s’është e lexueshme", - "zim-id": "ID ZIM", - "zim-name": "Emër ZIM", - "zim-path": "Shteg Kartele ZIM", - "bad-zim-file-error-page-title": "Kartelë ZIM e Pavlefshme", - "bad-zim-file-error-page-text": "Kartela ZIM është e pavlefshme, ose e dëmtuar.", - "zim-entry-path": "Shteg Zëri ZIM", - "export-reading-list": "Eksporto listë leximesh", - "export-reading-list-error": "Ndodhi një gabim gjatë eksportimit të listës së leximeve.", - "import-reading-list": "Importo listë leximesh", - "import-reading-list-error": "Ndodhi një gabim gjatë importimit të listës së leximeve.", - "disable-sandbox": "Kiwix-i është nisur që nga një disk rrjeti. Kjo dihet se shkakton probleme përputhjeje me shfletimin që nga bankëprovë. Për pasojë, bankëprova është çaktivizuar. Doni të vazhdohet?", - "save-page-as": "Ruajeni Si…", - "portable-disabled-tooltip": "Funksion i çaktivizuar për mënyrën e bartshme", - "scroll-next-tab": "Rrëshqit te skeda pasuese", - "scroll-previous-tab": "Rrëshqit te skeda e mëparshme", - "kiwix-search": "Kërkim Kiwix", - "search-options": "Mundësi Kërkimi" -} +{ + "@metadata": { + "authors": [ + "Besnik b", + "McDutchie" + ] + }, + "name": "Shqip", + "error-title": "Gabim", + "error-downloader-window-title": "S’krijohet dot shkarkuesi", + "error-downloader-launch-message": "E pamundur të niset shkarkuesi, Kiwix-desktop do të niset, por s’do të punojnë krejt funksionet e shkarkimit!", + "error-launch-server-message": "Ndodhi një gabim!", + "error-archive": "S’merret dor arkivi", + "open-zim": "Hap Kartelë Zim", + "local-kiwix-server": "Shërbyes Kiwix Vendor", + "random-article": "Artikull Kuturu", + "random-article-error": "S’u arrit të hapet një artikull kuturu.", + "home-page": "Faqe hyrëse", + "main-menu": "Menuja kryesore", + "print": "Shtype", + "print-page-error": "Ndodhi një gabim gjatë shtypjes.", + "new-tab": "Skedë e re", + "close-tab": "Mbylle skedën", + "close": "Mbylle", + "reopen-closed-tab": "Rihape skedën e mbyllyr", + "browse-library": "Shfletoni bibliotekën", + "open-file": "Hapni kartelë", + "open-recent": "Hap së fundi", + "search-article": "Kërkoni artikull", + "search-in-library": "Kërkoni në bibliotekë", + "find-in-page": "Gjej në faqe", + "set-fullscreen": "Vëre sa krejt ekrani", + "quit-fullscreen": "Dil nga “Sa krejt ekrani”", + "table-of-content": "Tryezë e lëndës", + "add-bookmark": "Shtoje te lista e leximeve", + "remove-bookmark": "Hiqe nga lista e leximeve", + "reading-list": "Listë leximesh", + "zoom-in": "Zmadhoje", + "zoom-out": "Zvogëloje", + "zoom-reset": "Zerim Zoom-i", + "help": "Ndihmë", + "feedback": "Përshtypje", + "report-a-bug": "Njoftoni një të metë", + "request-a-feature": "Kërkoni një veçori", + "about-kiwix": "Mbi Kiwix-in", + "donate-to-support-kiwix": "Dhuroni që të përkrahni Kiwix-in", + "exit": "Dalje", + "save-file-as-window-title": "Ruaje Kartelën si", + "download-finished-message": "Dokumenti u shkarkua.", + "file": "Kartelë", + "edit": "Përpunoni", + "view": "Shiheni", + "tools": "Mjete", + "window-title": "Bibliotekë - Kiwix", + "search": "Kërko", + "browse-directory": "Shfletoni Drejtorinë", + "settings": "Rregullime", + "back": "Mbrapsht", + "forward": "Përpara", + "search-files": "Kërkoni kartela", + "title": "Titull", + "size": "Madhësi", + "date": "Datë", + "content-type": "Lloj lënde", + "reset-sort": "zeroni renditjen", + "open": "Hape", + "delete": "Fshije", + "download": "Shkarkoje", + "resume": "Rimerre", + "pause": "Ndalesë", + "cancel": "Anuloje", + "apply": "Zbatoje", + "port-for-local-kiwix-server-setting": "Portë për shërbyes Kiwix vendor", + "zoom-level-setting": "Shkallë parazgjedhje për zoom-in", + "download-directory-setting": "Drejtori shkarkimesh", + "monitor-directory-setting": "Drejtori mbikëqyrjesh", + "reset": "Riktheje te parazgjedhja", + "clear": "Spastroje", + "browse": "Shfletoni", + "about-kiwix-desktop-title": "Desktopi Kiwix", + "about-kiwix-desktop-description": "Kiwix-i ju lejon të keni në pëllëmbë të dorës krejt Wikipedia-n, kudo që shkoni! Në një barkë, në mes të asgjëkundit apo në burg, Kiwix ju lejon të hyni në krejt dijen njerëzore. S’keni nevojë për Internet, gjithçka depozitohet në kompjuterin tuaj.", + "about-learn-more-about-kiwix": "Mësoni më tepër mbi Kiwix-in", + "about-release-title": "Hedhje në qarkullim", + "about-source-code": "Ky program hidhet në qarkullim sipas termave të licencës GNU General Public License version 3. Shihini kodin burim te Github.", + "about-version": "Version: {{VERSION}}", + "about-reporting-problem-title": "Njoftim problemesh", + "about-report-issue": "Që të hapni një çështje, vizitoni ndjekësin e çështjeve të desktopit Kiwix dhe hapni një çështje që përshkruan problemin tuaj.", + "about-report-issue-2": "Ju lutemi, te çështja përmendni versionin.", + "about-libraries-title": "Biblioteka", + "kiwix-server-running-message": "Shërbyesi Kiwix Server është në punë dhe mund të përdoret te rrjeti vendor në:", + "kiwix-server-description": "Nisja e një shërbyesi u lejon kompjuterave të tjerë në rrjetin vendor të përdorin bibliotekën tuaj Kiwix përmes një shfletuesi standard.", + "fullscreen-notification": "Gjendeni nën mënyrën “sa krejt ekrani”. Që të dilni, shtypni tastin ESC!", + "online-files": "Kartela Në Linjë", + "local-files": "Kartela Vendore", + "category": "Kategori", + "language": "Gjuhë", + "hide": "Fshihe", + "open-in-browser": "Hape në shfletues", + "start-kiwix-server": "Nis Shërbyesin Kiwix", + "stop-kiwix-server": "Ndale Shërbyesin Kiwix", + "all": "Krejt", + "all_ips": "Krejt - Mënyra “Kapicë Duale”", + "ipv4": "Mënyra Vetëm IPv4", + "ipv6": "Mënyra Vetëm IPv6", + "fulltext-search": "Kërkim në krejt tekstin", + "pictures": "Foto", + "videos": "Video", + "ftindex": "Tregues “fulltext”", + "details": "Artikull i plotë", + "yes": "po", + "no": "jo", + "ok": "ok", + "no-filter": "pa filtër", + "open-link-in-web-browser": "Hape lidhjen në shfletues", + "open-link-new-tab": "Hape lidhjen në skedë të re", + "download-dir-dialog-title": "Jeni i sigurt se doni të ndryshoni drejtorinë e shkarkimeve?", + "download-dir-dialog-msg": "Shtegu i drejtorisë së re do të jetë:\n{{DIRECTORY}}", + "invalid-port": "Portë e pavlefshme", + "zim-open-fail-title": "Kartelë e pavlefshme", + "zim-open-fail-text": "Kartela ZIM {{ZIM}} s’mund të hapet si duhet. Do të hiqet nga biblioteka juaj.", + "monitor-dir-dialog-title": "Jeni i sigurt se doni të ndryshohet drejtoria e mbikëqyrjeve?", + "monitor-dir-dialog-msg": "Shtegu i drejtorisë së re të mbikëqyrjeve do të jetë :\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Jeni i sigurt se doni të spastrohet drejtoria e mbikëqyrjeve?", + "path-was-copied": "Shtegu u kopjua", + "monitor-clear-dir-dialog-msg": "Kjo do të ndalë kontrollimin e drejtorisë së mbikëqyrjeve për kartela të reja ZIM.", + "monitor-directory-tooltip": "Krejt kartelat ZIM në këtë drejtori do të shtohen automatikisht te biblioteka.", + "next-tab": "Kalo te skeda pasuese", + "previous-tab": "Kalo te skeda e mëparshme", + "cancel-download": "Anuloje shkarkimin", + "cancel-download-text": "Jeni i sigurt se doni të anulohet shkarkimi i {{ZIM}}?", + "delete-book": "Fshije librin", + "delete-book-text": "Jeni i sigurt se doni të fshihet {{ZIM}}?", + "download-storage-error": "Gabim Depozitimi", + "download-storage-error-text": "Sistemi s’ka hapësirë të lirë të mjaftueshme.", + "download-exceeds-max-file-size": "Madhësia e shkarkimit tejkalon madhësinë maksimum të kartelave që mbulohet nga sistemi i synuar i kartelave.", + "download-dir-missing": "Drejtoria e shkarkimeve s’ekziston.", + "download-dir-not-writable": "Drejtoria e shkarkimeve s’është e shkrueshme.", + "download-unavailable": "Shkarkim Jo i Passhëm", + "download-unavailable-text": "Ky shkarkim s’është gati.", + "open-book": "Hapni libër", + "download-book": "Shkarkoni libër", + "pause-download": "Pauzë shkarkimi", + "resume-download": "Rimerre shkarkimin", + "open-folder": "Hap dosje", + "couldnt-open-location": "S’u hap dot vendndodhje", + "couldnt-open-location-text": "Kiwix-i s’është në gjendje të hapë dosjen {{FOLDER}}", + "move-files-to-trash": "Shpjeri kartelat e fshira te hedhurinat", + "move-files-to-trash-text": "Ky veprim do ta shpjerë kartelën te hedhurinat.", + "perma-delete-files-text": "Ky veprim do ta fshijë përgjithmonë kartelën.", + "clear-filter": "Spastro filtrat e ujdisur aktualisht", + "language-searcher-placeholder": "Filtroji sipas gjuhësh", + "category-searcher-placeholder": "Filtroji sipas kategorish", + "content-type-searcher-placeholder": "Filtroji sipas llojesh lënde", + "no-details": "Vetëm hyrje", + "no-pictures": "Pa Foto", + "no-videos": "Pa Video", + "open-previous-tabs-at-startup": "Gjatë nisjes, hap skedat e mëparshme", + "preview-book-in-web-browser": "Bëjini librit paraparje në shfletues", + "file-not-found-title": "S’u Gjet Kartelë ZIM", + "file-not-found-text": "Kartela ZIM s’ekziston, ose s’është e lexueshme", + "zim-id": "ID ZIM", + "zim-name": "Emër ZIM", + "zim-path": "Shteg Kartele ZIM", + "bad-zim-file-error-page-title": "Kartelë ZIM e Pavlefshme", + "bad-zim-file-error-page-text": "Kartela ZIM është e pavlefshme, ose e dëmtuar.", + "zim-entry-path": "Shteg Zëri ZIM", + "export-reading-list": "Eksporto listë leximesh", + "export-reading-list-error": "Ndodhi një gabim gjatë eksportimit të listës së leximeve.", + "import-reading-list": "Importo listë leximesh", + "import-reading-list-error": "Ndodhi një gabim gjatë importimit të listës së leximeve.", + "disable-sandbox": "Kiwix-i është nisur që nga një disk rrjeti. Kjo dihet se shkakton probleme përputhjeje me shfletimin që nga bankëprovë. Për pasojë, bankëprova është çaktivizuar. Doni të vazhdohet?", + "save-page-as": "Ruajeni Si…", + "portable-disabled-tooltip": "Funksion i çaktivizuar për mënyrën e bartshme", + "scroll-next-tab": "Rrëshqit te skeda pasuese", + "scroll-previous-tab": "Rrëshqit te skeda e mëparshme", + "kiwix-search": "Kërkim Kiwix", + "search-options": "Mundësi Kërkimi" +} diff --git a/resources/i18n/sr-ec.json b/resources/i18n/sr-ec.json index 6f4de6556..4d4ede431 100644 --- a/resources/i18n/sr-ec.json +++ b/resources/i18n/sr-ec.json @@ -1,67 +1,67 @@ -{ - "@metadata": { - "authors": [ - "BadDog", - "Kizule", - "Milicevic01" - ] - }, - "name": "српски", - "random-article": "Насумичан чланак", - "home-page": "Почетна странциа", - "print": "Штампање", - "new-tab": "Нова картица", - "close-tab": "Затвори картицу", - "open-file": "Отвори датотеку", - "search-article": "Претражите чланак", - "find-in-page": "Пронађи на страници", - "quit-fullscreen": "Изађи из целог екрана", - "table-of-content": "Садржај", - "reading-list": "Листа за читање", - "zoom-in": "Увећај", - "zoom-out": "Умањи", - "help": "Помоћ", - "feedback": "Повратне информације", - "report-a-bug": "Пријавите грешку", - "request-a-feature": "Затражите функцију", - "about-kiwix": "О Kiwix-у", - "donate-to-support-kiwix": "Донирајте Kiwix-у", - "exit": "Изађи", - "download-finished-message": "Документ је преузет.", - "file": "Датотека", - "edit": "Уреди", - "view": "Прикажи", - "tools": "Алатке", - "window-title": "Библиотека: Kiwix", - "search": "Претражи", - "browse-directory": "Прегледајте директоријум", - "settings": "Подешавања", - "back": "Назад", - "search-files": "Претрага датотека", - "title": "Наслов", - "size": "Величина", - "date": "Датум", - "content-type": "Тип садржаја", - "open": "Отвори", - "delete": "Избриши", - "download": "Преузми", - "resume": "Настави", - "pause": "Паузирај", - "cancel": "Откажи", - "apply": "Примени", - "zoom-level-setting": "Подразумевани ниво зумирања:", - "reset": "Ресетуј", - "browse": "Прегледај", - "about-learn-more-about-kiwix": "Детаљније о Kiwix-у", - "about-privacy-policy": "Политика приватности", - "about-version": "Верзија: {{VERSION}}", - "about-reporting-problem-title": "Пријављивање проблема", - "about-libraries-title": "Библиотеке", - "fullscreen-notification": "Сада сте у режиму целог екрана. Притисните Esc да бисте изашли.", - "local-files": "Локалне датотеке", - "hide": "Сакриј", - "open-in-browser": "Отвори у прегледачу", - "stop-kiwix-server": "Заустави Kiwix сервер", - "all": "Све", - "fulltext-search": "Претражи цео текст" -} +{ + "@metadata": { + "authors": [ + "BadDog", + "Kizule", + "Milicevic01" + ] + }, + "name": "српски", + "random-article": "Насумичан чланак", + "home-page": "Почетна странциа", + "print": "Штампање", + "new-tab": "Нова картица", + "close-tab": "Затвори картицу", + "open-file": "Отвори датотеку", + "search-article": "Претражите чланак", + "find-in-page": "Пронађи на страници", + "quit-fullscreen": "Изађи из целог екрана", + "table-of-content": "Садржај", + "reading-list": "Листа за читање", + "zoom-in": "Увећај", + "zoom-out": "Умањи", + "help": "Помоћ", + "feedback": "Повратне информације", + "report-a-bug": "Пријавите грешку", + "request-a-feature": "Затражите функцију", + "about-kiwix": "О Kiwix-у", + "donate-to-support-kiwix": "Донирајте Kiwix-у", + "exit": "Изађи", + "download-finished-message": "Документ је преузет.", + "file": "Датотека", + "edit": "Уреди", + "view": "Прикажи", + "tools": "Алатке", + "window-title": "Библиотека: Kiwix", + "search": "Претражи", + "browse-directory": "Прегледајте директоријум", + "settings": "Подешавања", + "back": "Назад", + "search-files": "Претрага датотека", + "title": "Наслов", + "size": "Величина", + "date": "Датум", + "content-type": "Тип садржаја", + "open": "Отвори", + "delete": "Избриши", + "download": "Преузми", + "resume": "Настави", + "pause": "Паузирај", + "cancel": "Откажи", + "apply": "Примени", + "zoom-level-setting": "Подразумевани ниво зумирања:", + "reset": "Ресетуј", + "browse": "Прегледај", + "about-learn-more-about-kiwix": "Детаљније о Kiwix-у", + "about-privacy-policy": "Политика приватности", + "about-version": "Верзија: {{VERSION}}", + "about-reporting-problem-title": "Пријављивање проблема", + "about-libraries-title": "Библиотеке", + "fullscreen-notification": "Сада сте у режиму целог екрана. Притисните Esc да бисте изашли.", + "local-files": "Локалне датотеке", + "hide": "Сакриј", + "open-in-browser": "Отвори у прегледачу", + "stop-kiwix-server": "Заустави Kiwix сервер", + "all": "Све", + "fulltext-search": "Претражи цео текст" +} diff --git a/resources/i18n/sro.json b/resources/i18n/sro.json index b8ac94840..596667da7 100644 --- a/resources/i18n/sro.json +++ b/resources/i18n/sro.json @@ -1,33 +1,33 @@ -{ - "@metadata": { - "authors": [ - "F Samaritani" - ] - }, - "name": "Sardu campidanesu", - "new-tab": "Sceda noa", - "search-article": "Cumpartzi sa boxi", - "find-in-page": "Agata in sa pàgina", - "table-of-content": "Ìndixi", - "reading-list": "Listas de ligidura", - "help": "Agiudu", - "feedback": "Feedback", - "exit": "Muda", - "file": "File", - "edit": "Muda", - "view": "Càstia", - "tools": "Ainas", - "search": "Circa", - "settings": "Impostatzionis", - "back": "Acoa", - "forward": "Ainnantis", - "open": "Aberi", - "cancel": "Annudda", - "browse": "Browser", - "hide": "Cua", - "all": "Totu", - "pictures": "màginis", - "videos": "Videos", - "yes": "eja", - "no": "no" -} +{ + "@metadata": { + "authors": [ + "F Samaritani" + ] + }, + "name": "Sardu campidanesu", + "new-tab": "Sceda noa", + "search-article": "Cumpartzi sa boxi", + "find-in-page": "Agata in sa pàgina", + "table-of-content": "Ìndixi", + "reading-list": "Listas de ligidura", + "help": "Agiudu", + "feedback": "Feedback", + "exit": "Muda", + "file": "File", + "edit": "Muda", + "view": "Càstia", + "tools": "Ainas", + "search": "Circa", + "settings": "Impostatzionis", + "back": "Acoa", + "forward": "Ainnantis", + "open": "Aberi", + "cancel": "Annudda", + "browse": "Browser", + "hide": "Cua", + "all": "Totu", + "pictures": "màginis", + "videos": "Videos", + "yes": "eja", + "no": "no" +} diff --git a/resources/i18n/sv.json b/resources/i18n/sv.json index baed8afd7..aa5e8e0fa 100644 --- a/resources/i18n/sv.json +++ b/resources/i18n/sv.json @@ -1,195 +1,195 @@ -{ - "@metadata": { - "authors": [ - "Jopparn", - "Larsa", - "Marcusjehrlander", - "Sabelöga", - "Susann Schweden", - "WikiPhoenix" - ] - }, - "name": "Svenska", - "error-title": "Fel", - "error-downloader-window-title": "Kan inte skapa nedladdare", - "error-downloader-launch-message": "Omöjligt att starta nedladdare, Kiwix-desktop kommer starta men alla nedladdningsfunktioner kommer inte fungera!", - "error-launch-server-message": "Ett fel har uppstått!", - "error-archive": "Kan inte hämta arkivet", - "error-opening-file": "Det gick inte att öppna filen.", - "open-zim": "Öppna ZIM-fil", - "local-kiwix-server": "Lokal Kiwix-server", - "random-article": "Slumpartikel", - "random-article-error": "Misslyckades att öppna en slumpartikel.", - "home-page": "Hemsida", - "main-menu": "Huvudmeny", - "print": "Skriv ut", - "print-page-error": "Ett fel uppstod under utskrift.", - "new-tab": "Ny flik", - "close-tab": "Stäng flik", - "close": "Stäng", - "reopen-closed-tab": "Öppna stängd flik igen", - "browse-library": "Bläddra i bibliotek", - "open-file": "Öppna fil", - "save-file": "Spara fil", - "open-recent": "Öppna senaste", - "search-article": "Sök efter artikel", - "search-in-library": "Sök i bibliotek", - "find-in-page": "Hitta på sida", - "set-fullscreen": "Aktivera helskärm", - "quit-fullscreen": "Stäng helskärm", - "table-of-content": "Innehållsförteckning", - "add-bookmark": "Lägg till i läslistan", - "remove-bookmark": "Ta bort från läslistan", - "reading-list": "Läslista", - "zoom-in": "Zooma in", - "zoom-out": "Zooma ut", - "zoom-reset": "Återställ zoom", - "help": "Hjälp", - "feedback": "Återkoppling", - "report-a-bug": "Rapportera en bugg", - "request-a-feature": "Begär en funktion", - "about-kiwix": "Om Kiwix", - "donate-to-support-kiwix": "Donera för att stödja Kiwix", - "exit": "Avsluta", - "save-file-as-window-title": "Spara fil som", - "download-finished": "Nedladdningen är klar", - "download-finished-message": "Dokumentet laddades ned.", - "file": "Fil", - "edit": "Redigera", - "view": "Visa", - "tools": "Verktyg", - "window-title": "Bibliotek - Kiwix", - "search": "Sök", - "browse-directory": "Bläddra i katalogen", - "settings": "Inställningar", - "back": "Tillbaka", - "forward": "Fram", - "search-files": "Sök efter filer", - "title": "Titel", - "size": "Storlek", - "date": "Datum", - "content-type": "Innehållstyp", - "reset-sort": "återställ sortering", - "open": "Öppna", - "delete": "Radera", - "download": "Ladda ned", - "resume": "Återuppta", - "pause": "Pausa", - "cancel": "Avbryt", - "apply": "Verkställ", - "port-for-local-kiwix-server-setting": "Port för den lokala Kiwix-servern", - "zoom-level-setting": "Zoomnivå som standard", - "download-directory-setting": "Katalog för nedladdningar", - "monitor-directory-setting": "Övervaka katalogen", - "reset": "Återställ", - "clear": "Rensa", - "browse": "Bläddra", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix gör det möjligt för dig att ha hela Wikipedia till hands var du än är! På en båt, mitt i ingenstans eller i fängelse, Kiwix ger dig tillgång till mänsklighetens samlade kunskap. Du behöver inget internet, allt sparas i din dator.", - "about-learn-more-about-kiwix": "Lär dig mer om Kiwix", - "about-privacy-policy": "Integritetspolicy", - "about-release-title": "Utgåva", - "about-source-code": "Mjukvaran släpps under GNU General Public License version 3. Läs källkoden på Github.", - "about-version": "Version: {{VERSION}}", - "about-reporting-problem-title": "Rapportera problem", - "about-report-issue": "För att rapportera ett problem, besök Kiwix-desktop issue tracker och öppna ett ärende som beskriver ditt problem.", - "about-report-issue-2": "Vänligen nämn versionen problemet uppstod i.", - "about-libraries-title": "Bibliotek", - "kiwix-server-running-message": "Kiwix-servern fungerar och kan nås i det lokala nätverket via:", - "kiwix-server-description": "Att starta en server låter andra datorer i det lokala nätverket att få tillgång till ditt Kiwix-bibliotek med en vanlig webbläsare.", - "fullscreen-notification": "Du har aktiverat fullskärmsläge. Tryck ESC för att avsluta!", - "online-files": "Online-filer", - "local-files": "Lokala filer", - "category": "Kategori", - "language": "Språk", - "hide": "Dölj", - "open-in-browser": "Öppna i webbläsare", - "start-kiwix-server": "Starta Kiwix-server", - "stop-kiwix-server": "Stoppa Kiwix-server", - "all": "Alla", - "all_ips": "Alla - Dubbelstackläge", - "ipv4": "Enbart IPv4-läge", - "ipv6": "Enbart IPv6-läge", - "fulltext-search": "Fulltextsökning", - "pictures": "Bilder", - "videos": "Videor", - "ftindex": "Fulltextindex", - "details": "Fullständig artikel", - "yes": "ja", - "no": "nej", - "ok": "ok", - "no-filter": "inget filter", - "open-link-in-web-browser": "Öppna länk i webbläsaren", - "open-link-new-tab": "Öppna länk i ny flik", - "download-dir-dialog-title": "Är du säker på att du vill byta nedladdningskatalog?", - "download-dir-dialog-msg": "Den nya sökvägen till nedladdningskatalogen kommer bli:\n{{DIRECTORY}}", - "invalid-port": "Ogiltig port", - "zim-open-fail-title": "Ogiltig fil", - "zim-open-fail-text": "ZIM-filen {{ZIM}} kan inte öppnas ordentligt. Den kommer att tas bort från ditt bibliotek.", - "monitor-dir-dialog-title": "Är du säker på att du vill byta övervakningskatalog?", - "monitor-dir-dialog-msg": "Den nya katalogsökvägen kommer vara:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Är du säker på att du vill rensa övervakningskatalogen?", - "path-was-copied": "Vägen kopierades", - "monitor-clear-dir-dialog-msg": "Detta kommer sluta kontrollera övervakningskatalogen efter nya ZIM-filer.", - "monitor-directory-tooltip": "Alla ZIM-filer i denna katalog kommer automatiskt läggas till i biblioteket.", - "next-tab": "Flytta till nästa flik", - "previous-tab": "Flytta till föregående flik", - "cancel-download": "Avbryt nedladdning", - "cancel-download-text": "Är du säker på att du vill avbryta nedladdningen av {{ZIM}}?", - "delete-book": "Radera bok", - "delete-book-text": "Är du säker på att du vill radera {{ZIM}}?", - "download-storage-error": "Lagringsfel", - "download-storage-error-text": "Systemet har inte tillräckligt med lagringsutrymme.", - "download-exceeds-max-file-size": "Nedladdningsstorleken överskrider den maximala filstorleken som stöds av det valda filsystemet.", - "download-dir-missing": "Nedladdningskatalogen finns inte.", - "download-dir-not-writable": "Nedladdningskatalogen är inte skrivbar.", - "download-unavailable": "Nedladdning ej tillgänglig", - "download-unavailable-text": "Nedladdningen är inte tillgänglig.", - "open-book": "Öppna bok", - "download-book": "Ladda ner bok", - "pause-download": "Pausa nedladdning", - "resume-download": "Återuppta nedladdning", - "open-folder": "Öppna mapp", - "couldnt-open-location": "Kunde inte öppna plats", - "couldnt-open-location-text": "Kiwix kunde inte öppna mappen {{FOLDER}}", - "move-files-to-trash": "Flytta raderade filer till papperskorgen", - "move-files-to-trash-text": "Denna åtgärd flyttar filen till papperskorgen.", - "perma-delete-files-text": "Denna åtgärd raderar filen permanent.", - "clear-filter": "Rensa aktuella filter", - "language-searcher-placeholder": "Filtrera efter språk", - "category-searcher-placeholder": "Filtrera efter kategori", - "content-type-searcher-placeholder": "Filtrera efter innehållstyp", - "no-details": "Endast introduktion", - "no-pictures": "Inga bilder", - "no-videos": "Inga videor", - "open-previous-tabs-at-startup": "Öppna tidigare flikar vid start", - "preview-book-in-web-browser": "Förhandsgranska i webbläsare", - "file-not-found-title": "ZIM-fil hittades inte", - "file-not-found-text": "ZIM-filen finns inte eller kan inte läsas", - "zim-id": "Zim-id", - "zim-name": "Zim-namn", - "zim-path": "Zim-filväg", - "bad-zim-file-error-page-title": "Ogiltig ZIM-fil", - "bad-zim-file-error-page-text": "ZIM-filen är ogiltig eller skadad.", - "zim-entry-path": "ZIM-ingångsväg", - "export-reading-list": "Exportera läslista", - "export-reading-list-error": "Ett fel har uppstått under exporten av läslistan.", - "import-reading-list": "Importera läslista", - "import-reading-list-error": "Ett fel har uppstått under importen av läslistan.", - "disable-sandbox": "Kiwix har startats från en nätverksenhet. Det kan leda till kompatibilitetsproblem med surfsandboxing. Därför kommer sandlådan att inaktiveras. Vill du fortsätta?", - "save-page-as": "Spara som...", - "portable-disabled-tooltip": "Funktionen inaktiverad i portabelt läge", - "scroll-next-tab": "Gå till nästa flik", - "scroll-previous-tab": "Gå till föregående flik", - "kiwix-search": "Kiwix-sökning", - "search-options": "Sökalternativ", - "read-article": "Läs artikeln", - "read-text": "Läs markerad text", - "read-stop": "Sluta läsa", - "stop": "Stopp", - "voice": "Röst", - "select-read-voice": "Välj läsröst", - "select-read-language": "Välj lässpråk", - "save-or-open": "Spara eller Öppna fil", - "save-or-open-text": "Vad ska Kiwix göra med den här filen?" -} +{ + "@metadata": { + "authors": [ + "Jopparn", + "Larsa", + "Marcusjehrlander", + "Sabelöga", + "Susann Schweden", + "WikiPhoenix" + ] + }, + "name": "Svenska", + "error-title": "Fel", + "error-downloader-window-title": "Kan inte skapa nedladdare", + "error-downloader-launch-message": "Omöjligt att starta nedladdare, Kiwix-desktop kommer starta men alla nedladdningsfunktioner kommer inte fungera!", + "error-launch-server-message": "Ett fel har uppstått!", + "error-archive": "Kan inte hämta arkivet", + "error-opening-file": "Det gick inte att öppna filen.", + "open-zim": "Öppna ZIM-fil", + "local-kiwix-server": "Lokal Kiwix-server", + "random-article": "Slumpartikel", + "random-article-error": "Misslyckades att öppna en slumpartikel.", + "home-page": "Hemsida", + "main-menu": "Huvudmeny", + "print": "Skriv ut", + "print-page-error": "Ett fel uppstod under utskrift.", + "new-tab": "Ny flik", + "close-tab": "Stäng flik", + "close": "Stäng", + "reopen-closed-tab": "Öppna stängd flik igen", + "browse-library": "Bläddra i bibliotek", + "open-file": "Öppna fil", + "save-file": "Spara fil", + "open-recent": "Öppna senaste", + "search-article": "Sök efter artikel", + "search-in-library": "Sök i bibliotek", + "find-in-page": "Hitta på sida", + "set-fullscreen": "Aktivera helskärm", + "quit-fullscreen": "Stäng helskärm", + "table-of-content": "Innehållsförteckning", + "add-bookmark": "Lägg till i läslistan", + "remove-bookmark": "Ta bort från läslistan", + "reading-list": "Läslista", + "zoom-in": "Zooma in", + "zoom-out": "Zooma ut", + "zoom-reset": "Återställ zoom", + "help": "Hjälp", + "feedback": "Återkoppling", + "report-a-bug": "Rapportera en bugg", + "request-a-feature": "Begär en funktion", + "about-kiwix": "Om Kiwix", + "donate-to-support-kiwix": "Donera för att stödja Kiwix", + "exit": "Avsluta", + "save-file-as-window-title": "Spara fil som", + "download-finished": "Nedladdningen är klar", + "download-finished-message": "Dokumentet laddades ned.", + "file": "Fil", + "edit": "Redigera", + "view": "Visa", + "tools": "Verktyg", + "window-title": "Bibliotek - Kiwix", + "search": "Sök", + "browse-directory": "Bläddra i katalogen", + "settings": "Inställningar", + "back": "Tillbaka", + "forward": "Fram", + "search-files": "Sök efter filer", + "title": "Titel", + "size": "Storlek", + "date": "Datum", + "content-type": "Innehållstyp", + "reset-sort": "återställ sortering", + "open": "Öppna", + "delete": "Radera", + "download": "Ladda ned", + "resume": "Återuppta", + "pause": "Pausa", + "cancel": "Avbryt", + "apply": "Verkställ", + "port-for-local-kiwix-server-setting": "Port för den lokala Kiwix-servern", + "zoom-level-setting": "Zoomnivå som standard", + "download-directory-setting": "Katalog för nedladdningar", + "monitor-directory-setting": "Övervaka katalogen", + "reset": "Återställ", + "clear": "Rensa", + "browse": "Bläddra", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix gör det möjligt för dig att ha hela Wikipedia till hands var du än är! På en båt, mitt i ingenstans eller i fängelse, Kiwix ger dig tillgång till mänsklighetens samlade kunskap. Du behöver inget internet, allt sparas i din dator.", + "about-learn-more-about-kiwix": "Lär dig mer om Kiwix", + "about-privacy-policy": "Integritetspolicy", + "about-release-title": "Utgåva", + "about-source-code": "Mjukvaran släpps under GNU General Public License version 3. Läs källkoden på Github.", + "about-version": "Version: {{VERSION}}", + "about-reporting-problem-title": "Rapportera problem", + "about-report-issue": "För att rapportera ett problem, besök Kiwix-desktop issue tracker och öppna ett ärende som beskriver ditt problem.", + "about-report-issue-2": "Vänligen nämn versionen problemet uppstod i.", + "about-libraries-title": "Bibliotek", + "kiwix-server-running-message": "Kiwix-servern fungerar och kan nås i det lokala nätverket via:", + "kiwix-server-description": "Att starta en server låter andra datorer i det lokala nätverket att få tillgång till ditt Kiwix-bibliotek med en vanlig webbläsare.", + "fullscreen-notification": "Du har aktiverat fullskärmsläge. Tryck ESC för att avsluta!", + "online-files": "Online-filer", + "local-files": "Lokala filer", + "category": "Kategori", + "language": "Språk", + "hide": "Dölj", + "open-in-browser": "Öppna i webbläsare", + "start-kiwix-server": "Starta Kiwix-server", + "stop-kiwix-server": "Stoppa Kiwix-server", + "all": "Alla", + "all_ips": "Alla - Dubbelstackläge", + "ipv4": "Enbart IPv4-läge", + "ipv6": "Enbart IPv6-läge", + "fulltext-search": "Fulltextsökning", + "pictures": "Bilder", + "videos": "Videor", + "ftindex": "Fulltextindex", + "details": "Fullständig artikel", + "yes": "ja", + "no": "nej", + "ok": "ok", + "no-filter": "inget filter", + "open-link-in-web-browser": "Öppna länk i webbläsaren", + "open-link-new-tab": "Öppna länk i ny flik", + "download-dir-dialog-title": "Är du säker på att du vill byta nedladdningskatalog?", + "download-dir-dialog-msg": "Den nya sökvägen till nedladdningskatalogen kommer bli:\n{{DIRECTORY}}", + "invalid-port": "Ogiltig port", + "zim-open-fail-title": "Ogiltig fil", + "zim-open-fail-text": "ZIM-filen {{ZIM}} kan inte öppnas ordentligt. Den kommer att tas bort från ditt bibliotek.", + "monitor-dir-dialog-title": "Är du säker på att du vill byta övervakningskatalog?", + "monitor-dir-dialog-msg": "Den nya katalogsökvägen kommer vara:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Är du säker på att du vill rensa övervakningskatalogen?", + "path-was-copied": "Vägen kopierades", + "monitor-clear-dir-dialog-msg": "Detta kommer sluta kontrollera övervakningskatalogen efter nya ZIM-filer.", + "monitor-directory-tooltip": "Alla ZIM-filer i denna katalog kommer automatiskt läggas till i biblioteket.", + "next-tab": "Flytta till nästa flik", + "previous-tab": "Flytta till föregående flik", + "cancel-download": "Avbryt nedladdning", + "cancel-download-text": "Är du säker på att du vill avbryta nedladdningen av {{ZIM}}?", + "delete-book": "Radera bok", + "delete-book-text": "Är du säker på att du vill radera {{ZIM}}?", + "download-storage-error": "Lagringsfel", + "download-storage-error-text": "Systemet har inte tillräckligt med lagringsutrymme.", + "download-exceeds-max-file-size": "Nedladdningsstorleken överskrider den maximala filstorleken som stöds av det valda filsystemet.", + "download-dir-missing": "Nedladdningskatalogen finns inte.", + "download-dir-not-writable": "Nedladdningskatalogen är inte skrivbar.", + "download-unavailable": "Nedladdning ej tillgänglig", + "download-unavailable-text": "Nedladdningen är inte tillgänglig.", + "open-book": "Öppna bok", + "download-book": "Ladda ner bok", + "pause-download": "Pausa nedladdning", + "resume-download": "Återuppta nedladdning", + "open-folder": "Öppna mapp", + "couldnt-open-location": "Kunde inte öppna plats", + "couldnt-open-location-text": "Kiwix kunde inte öppna mappen {{FOLDER}}", + "move-files-to-trash": "Flytta raderade filer till papperskorgen", + "move-files-to-trash-text": "Denna åtgärd flyttar filen till papperskorgen.", + "perma-delete-files-text": "Denna åtgärd raderar filen permanent.", + "clear-filter": "Rensa aktuella filter", + "language-searcher-placeholder": "Filtrera efter språk", + "category-searcher-placeholder": "Filtrera efter kategori", + "content-type-searcher-placeholder": "Filtrera efter innehållstyp", + "no-details": "Endast introduktion", + "no-pictures": "Inga bilder", + "no-videos": "Inga videor", + "open-previous-tabs-at-startup": "Öppna tidigare flikar vid start", + "preview-book-in-web-browser": "Förhandsgranska i webbläsare", + "file-not-found-title": "ZIM-fil hittades inte", + "file-not-found-text": "ZIM-filen finns inte eller kan inte läsas", + "zim-id": "Zim-id", + "zim-name": "Zim-namn", + "zim-path": "Zim-filväg", + "bad-zim-file-error-page-title": "Ogiltig ZIM-fil", + "bad-zim-file-error-page-text": "ZIM-filen är ogiltig eller skadad.", + "zim-entry-path": "ZIM-ingångsväg", + "export-reading-list": "Exportera läslista", + "export-reading-list-error": "Ett fel har uppstått under exporten av läslistan.", + "import-reading-list": "Importera läslista", + "import-reading-list-error": "Ett fel har uppstått under importen av läslistan.", + "disable-sandbox": "Kiwix har startats från en nätverksenhet. Det kan leda till kompatibilitetsproblem med surfsandboxing. Därför kommer sandlådan att inaktiveras. Vill du fortsätta?", + "save-page-as": "Spara som...", + "portable-disabled-tooltip": "Funktionen inaktiverad i portabelt läge", + "scroll-next-tab": "Gå till nästa flik", + "scroll-previous-tab": "Gå till föregående flik", + "kiwix-search": "Kiwix-sökning", + "search-options": "Sökalternativ", + "read-article": "Läs artikeln", + "read-text": "Läs markerad text", + "read-stop": "Sluta läsa", + "stop": "Stopp", + "voice": "Röst", + "select-read-voice": "Välj läsröst", + "select-read-language": "Välj lässpråk", + "save-or-open": "Spara eller Öppna fil", + "save-or-open-text": "Vad ska Kiwix göra med den här filen?" +} diff --git a/resources/i18n/sw.json b/resources/i18n/sw.json index 8baaa25b0..bccd53837 100644 --- a/resources/i18n/sw.json +++ b/resources/i18n/sw.json @@ -1,147 +1,147 @@ -{ - "@metadata": { - "authors": [ - "Peggy", - "Stephane" - ] - }, - "name": "Kiswahili", - "error-title": "Hitilafu", - "error-downloader-window-title": "Haiwezi kuunda kipakuzi", - "error-downloader-launch-message": "Haiwezekani kuzindua upakuaji, Kiwix-desktop itaanza lakini vitendaji vyote vya upakuaji havitafanya kazi!", - "error-launch-server-message": "Hitilafu imetokea!", - "error-archive": "Haiwezi kupata kumbukumbu", - "open-zim": "Fungua faili ZIM", - "local-kiwix-server": "Seva ya Kiwix ya ndani", - "random-article": "Ukurasa wa bahati", - "random-article-error": "Imeshindwa kufungua makala nasibu.", - "home-page": "Ukurasa wa Nyumbani", - "main-menu": "Menyu kuu", - "print": "Chapisha", - "print-page-error": "Hitilafu imetokea wakati wa uchapishaji.", - "new-tab": "Kichupo kipya", - "close-tab": "Funga kichupo", - "close": "Funga", - "reopen-closed-tab": "Fungua tena kichupo kilichofungwa", - "browse-library": "Vinjari maktaba", - "open-file": "Fungua faili", - "open-recent": "Fungua hivi karibuni", - "search-article": "Tafuta makala", - "search-in-library": "Tafuta katika maktaba", - "find-in-page": "Tafuta katika ukurasa", - "set-fullscreen": "Weka skrini nzima", - "quit-fullscreen": "Acha Skrini nzima", - "table-of-content": "Jedwali la maudhui", - "reading-list": "Orodha ya kusoma", - "zoom-in": "Vuta karibu", - "zoom-out": "Kuza nje", - "zoom-reset": "Kuza upya", - "help": "Msaada", - "feedback": "Mrejezo", - "report-a-bug": "Ripoti hitilafu", - "request-a-feature": "Omba kipengele", - "about-kiwix": "Kuhusu Kiwix", - "donate-to-support-kiwix": "Changia kusaidia Kiwix", - "exit": "Toka", - "save-file-as-window-title": "Hifadhi Faili kama", - "download-finished-message": "Hati imepakuliwa.", - "file": "Faili", - "edit": "Hariri", - "view": "Tazama", - "tools": "Zana", - "window-title": "Maktaba - Kiwix", - "search": "Tafuta", - "browse-directory": "Vinjari Saraka", - "settings": "Mipangilio", - "back": "Nyuma", - "forward": "Mbele", - "search-files": "Tafuta mafaili", - "title": "Kichwa", - "size": "Ukubwa", - "date": "Tarehe", - "content-type": "Makala", - "reset-sort": "panga upya", - "open": "Fungua", - "delete": "Futa", - "download": "Pakua", - "resume": "rejea", - "pause": "Imesitishwa", - "cancel": "Ghairi", - "apply": "Tumia", - "port-for-local-kiwix-server-setting": "Bandari ya seva ya Kiwix ya ndani", - "zoom-level-setting": "Kiwango chaguomsingi cha kukuza", - "download-directory-setting": "Pakua saraka", - "monitor-directory-setting": "Kufuatilia saraka", - "reset": "Panga upya", - "clear": "Futa", - "browse": "Vinjari", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix hukuruhusu kuwa na Wikipedia nzima popote unapoenda! Kwenye mashua, katikati ya mahali, au jela, Kiwix inakupa ufikiaji wa maarifa yote ya wanadamu. Huna haja ya mtandao, kila kitu kinahifadhiwa kwenye kompyuta yako.", - "about-learn-more-about-kiwix": "Pata maelezo zaidi kuhusu Kiwix", - "about-release-title": "Release", - "about-source-code": "Programu hii inatolewa chini ya masharti ya toleo la 3 la Leseni ya Umma ya GNU. Tazama msimbo wa chanzo kwenye Github .", - "about-version": "Toleo: {{VERSION}}", - "about-reporting-problem-title": "Ripoti tatizo", - "about-report-issue": "Ili kufungua suala, tembelea kifuatiliaji cha toleo la Kiwix-desktop na ufungue suala linaloelezea tatizo lako.", - "about-report-issue-2": "Tafadhali taja toleo hilo katika toleo hili.", - "about-libraries-title": "Maktaba", - "kiwix-server-running-message": "Seva ya Kiwix inafanya kazi na inaweza kupatikana katika mtandao wa ndani kwa:", - "kiwix-server-description": "Kuanzisha seva huruhusu kompyuta zingine kwenye mtandao wa karibu kufikia maktaba yako ya Kiwix na kivinjari cha kawaida cha wavuti.", - "fullscreen-notification": "Sasa uko katika hali ya skrini nzima. Bonyeza ESC ili kuacha!", - "online-files": "Faili za Mtandaoni", - "local-files": "Faili za Karibu", - "category": "Kundi", - "language": "Lugha", - "hide": "Ficha", - "open-in-browser": "Fungua katika kivinjari", - "start-kiwix-server": "Anzisha Seva ya Kiwix", - "stop-kiwix-server": "Acha Seva ya Kiwix", - "all": "Zote", - "fulltext-search": "Utafutaji wa maandishi kamili", - "pictures": "Picha", - "videos": "Video", - "ftindex": "Faharasa ya maandishi kamili", - "details": "Makala kamili", - "yes": "ndiyo", - "no": "Hapana", - "ok": "sawa", - "no-filter": "hakuna kichujio", - "open-link-in-web-browser": "Fungua kiungo kwenye kivinjari", - "download-dir-dialog-title": "Je, una uhakika unataka kubadilisha saraka ya upakuaji?", - "download-dir-dialog-msg": "Njia mpya ya saraka ya upakuaji itakuwa:\n{{DIRECTORY}}", - "invalid-port": "Mlango batili", - "zim-open-fail-title": "Jina batili", - "zim-open-fail-text": "Faili ya ZIM {{ZIM}} haiwezi kufunguliwa ipasavyo. Itaondolewa kwenye maktaba yako.", - "monitor-dir-dialog-title": "Je, una uhakika unataka kubadilisha saraka ya upakuaji?", - "monitor-dir-dialog-msg": "Njia mpya ya saraka ya upakuaji itakuwa:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Je, una uhakika unataka kubadilisha saraka ya upakuaji?", - "monitor-clear-dir-dialog-msg": "Hii itaacha kuangalia saraka ya kufuatilia kwa faili mpya za ZIM.", - "monitor-directory-tooltip": "Faili zote za ZIM katika saraka hii zitaongezwa kiotomatiki kwenye maktaba.", - "next-tab": "Nenda kwenye kichupo kinachofuata", - "previous-tab": "Nenda kwenye kichupo kilichotangulia", - "cancel-download": "Ghairi upakuaji", - "cancel-download-text": "Je, una uhakika unataka kughairi upakuaji wa {{ZIM}} ?", - "delete-book": "Futa kitabu", - "delete-book-text": "Je, una uhakika unataka kufuta {{ZIM}} ?", - "download-storage-error": "Hitilafu ya Hifadhi", - "download-storage-error-text": "Mfumo hauna hifadhi ya kutosha.", - "download-unavailable": "Upakuaji Haupatikani", - "download-unavailable-text": "Upakuaji huu haupatikani.", - "open-book": "Fungua kitabu", - "download-book": "Pakua vitabu", - "pause-download": "Sitisha upakuaji", - "resume-download": "Endelea kupakua", - "open-folder": "Fungua folda", - "couldnt-open-location": "Haikuweza kufungua eneo", - "couldnt-open-location-text": "Kiwix haiwezi kufungua folda {{FOLDER}}", - "move-files-to-trash": "Hamisha faili zilizofutwa hadi kwenye tupio", - "move-files-to-trash-text": "Kitendo hiki kitahamisha faili hadi kwenye tupio.", - "perma-delete-files-text": "Kitendo hiki kitafuta faili kabisa.", - "clear-filter": "Futa vichujio vilivyowekwa sasa", - "language-searcher-placeholder": "Lugha ya tovuti", - "category-searcher-placeholder": "Kichujio cha kategoria", - "content-type-searcher-placeholder": "Aina ya maudhui ya kichujio", - "no-details": "Utangulizi pekee", - "no-pictures": "Hakuna picha", - "no-videos": "Hakuna video" -} +{ + "@metadata": { + "authors": [ + "Peggy", + "Stephane" + ] + }, + "name": "Kiswahili", + "error-title": "Hitilafu", + "error-downloader-window-title": "Haiwezi kuunda kipakuzi", + "error-downloader-launch-message": "Haiwezekani kuzindua upakuaji, Kiwix-desktop itaanza lakini vitendaji vyote vya upakuaji havitafanya kazi!", + "error-launch-server-message": "Hitilafu imetokea!", + "error-archive": "Haiwezi kupata kumbukumbu", + "open-zim": "Fungua faili ZIM", + "local-kiwix-server": "Seva ya Kiwix ya ndani", + "random-article": "Ukurasa wa bahati", + "random-article-error": "Imeshindwa kufungua makala nasibu.", + "home-page": "Ukurasa wa Nyumbani", + "main-menu": "Menyu kuu", + "print": "Chapisha", + "print-page-error": "Hitilafu imetokea wakati wa uchapishaji.", + "new-tab": "Kichupo kipya", + "close-tab": "Funga kichupo", + "close": "Funga", + "reopen-closed-tab": "Fungua tena kichupo kilichofungwa", + "browse-library": "Vinjari maktaba", + "open-file": "Fungua faili", + "open-recent": "Fungua hivi karibuni", + "search-article": "Tafuta makala", + "search-in-library": "Tafuta katika maktaba", + "find-in-page": "Tafuta katika ukurasa", + "set-fullscreen": "Weka skrini nzima", + "quit-fullscreen": "Acha Skrini nzima", + "table-of-content": "Jedwali la maudhui", + "reading-list": "Orodha ya kusoma", + "zoom-in": "Vuta karibu", + "zoom-out": "Kuza nje", + "zoom-reset": "Kuza upya", + "help": "Msaada", + "feedback": "Mrejezo", + "report-a-bug": "Ripoti hitilafu", + "request-a-feature": "Omba kipengele", + "about-kiwix": "Kuhusu Kiwix", + "donate-to-support-kiwix": "Changia kusaidia Kiwix", + "exit": "Toka", + "save-file-as-window-title": "Hifadhi Faili kama", + "download-finished-message": "Hati imepakuliwa.", + "file": "Faili", + "edit": "Hariri", + "view": "Tazama", + "tools": "Zana", + "window-title": "Maktaba - Kiwix", + "search": "Tafuta", + "browse-directory": "Vinjari Saraka", + "settings": "Mipangilio", + "back": "Nyuma", + "forward": "Mbele", + "search-files": "Tafuta mafaili", + "title": "Kichwa", + "size": "Ukubwa", + "date": "Tarehe", + "content-type": "Makala", + "reset-sort": "panga upya", + "open": "Fungua", + "delete": "Futa", + "download": "Pakua", + "resume": "rejea", + "pause": "Imesitishwa", + "cancel": "Ghairi", + "apply": "Tumia", + "port-for-local-kiwix-server-setting": "Bandari ya seva ya Kiwix ya ndani", + "zoom-level-setting": "Kiwango chaguomsingi cha kukuza", + "download-directory-setting": "Pakua saraka", + "monitor-directory-setting": "Kufuatilia saraka", + "reset": "Panga upya", + "clear": "Futa", + "browse": "Vinjari", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix hukuruhusu kuwa na Wikipedia nzima popote unapoenda! Kwenye mashua, katikati ya mahali, au jela, Kiwix inakupa ufikiaji wa maarifa yote ya wanadamu. Huna haja ya mtandao, kila kitu kinahifadhiwa kwenye kompyuta yako.", + "about-learn-more-about-kiwix": "Pata maelezo zaidi kuhusu Kiwix", + "about-release-title": "Release", + "about-source-code": "Programu hii inatolewa chini ya masharti ya toleo la 3 la Leseni ya Umma ya GNU. Tazama msimbo wa chanzo kwenye Github .", + "about-version": "Toleo: {{VERSION}}", + "about-reporting-problem-title": "Ripoti tatizo", + "about-report-issue": "Ili kufungua suala, tembelea kifuatiliaji cha toleo la Kiwix-desktop na ufungue suala linaloelezea tatizo lako.", + "about-report-issue-2": "Tafadhali taja toleo hilo katika toleo hili.", + "about-libraries-title": "Maktaba", + "kiwix-server-running-message": "Seva ya Kiwix inafanya kazi na inaweza kupatikana katika mtandao wa ndani kwa:", + "kiwix-server-description": "Kuanzisha seva huruhusu kompyuta zingine kwenye mtandao wa karibu kufikia maktaba yako ya Kiwix na kivinjari cha kawaida cha wavuti.", + "fullscreen-notification": "Sasa uko katika hali ya skrini nzima. Bonyeza ESC ili kuacha!", + "online-files": "Faili za Mtandaoni", + "local-files": "Faili za Karibu", + "category": "Kundi", + "language": "Lugha", + "hide": "Ficha", + "open-in-browser": "Fungua katika kivinjari", + "start-kiwix-server": "Anzisha Seva ya Kiwix", + "stop-kiwix-server": "Acha Seva ya Kiwix", + "all": "Zote", + "fulltext-search": "Utafutaji wa maandishi kamili", + "pictures": "Picha", + "videos": "Video", + "ftindex": "Faharasa ya maandishi kamili", + "details": "Makala kamili", + "yes": "ndiyo", + "no": "Hapana", + "ok": "sawa", + "no-filter": "hakuna kichujio", + "open-link-in-web-browser": "Fungua kiungo kwenye kivinjari", + "download-dir-dialog-title": "Je, una uhakika unataka kubadilisha saraka ya upakuaji?", + "download-dir-dialog-msg": "Njia mpya ya saraka ya upakuaji itakuwa:\n{{DIRECTORY}}", + "invalid-port": "Mlango batili", + "zim-open-fail-title": "Jina batili", + "zim-open-fail-text": "Faili ya ZIM {{ZIM}} haiwezi kufunguliwa ipasavyo. Itaondolewa kwenye maktaba yako.", + "monitor-dir-dialog-title": "Je, una uhakika unataka kubadilisha saraka ya upakuaji?", + "monitor-dir-dialog-msg": "Njia mpya ya saraka ya upakuaji itakuwa:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Je, una uhakika unataka kubadilisha saraka ya upakuaji?", + "monitor-clear-dir-dialog-msg": "Hii itaacha kuangalia saraka ya kufuatilia kwa faili mpya za ZIM.", + "monitor-directory-tooltip": "Faili zote za ZIM katika saraka hii zitaongezwa kiotomatiki kwenye maktaba.", + "next-tab": "Nenda kwenye kichupo kinachofuata", + "previous-tab": "Nenda kwenye kichupo kilichotangulia", + "cancel-download": "Ghairi upakuaji", + "cancel-download-text": "Je, una uhakika unataka kughairi upakuaji wa {{ZIM}} ?", + "delete-book": "Futa kitabu", + "delete-book-text": "Je, una uhakika unataka kufuta {{ZIM}} ?", + "download-storage-error": "Hitilafu ya Hifadhi", + "download-storage-error-text": "Mfumo hauna hifadhi ya kutosha.", + "download-unavailable": "Upakuaji Haupatikani", + "download-unavailable-text": "Upakuaji huu haupatikani.", + "open-book": "Fungua kitabu", + "download-book": "Pakua vitabu", + "pause-download": "Sitisha upakuaji", + "resume-download": "Endelea kupakua", + "open-folder": "Fungua folda", + "couldnt-open-location": "Haikuweza kufungua eneo", + "couldnt-open-location-text": "Kiwix haiwezi kufungua folda {{FOLDER}}", + "move-files-to-trash": "Hamisha faili zilizofutwa hadi kwenye tupio", + "move-files-to-trash-text": "Kitendo hiki kitahamisha faili hadi kwenye tupio.", + "perma-delete-files-text": "Kitendo hiki kitafuta faili kabisa.", + "clear-filter": "Futa vichujio vilivyowekwa sasa", + "language-searcher-placeholder": "Lugha ya tovuti", + "category-searcher-placeholder": "Kichujio cha kategoria", + "content-type-searcher-placeholder": "Aina ya maudhui ya kichujio", + "no-details": "Utangulizi pekee", + "no-pictures": "Hakuna picha", + "no-videos": "Hakuna video" +} diff --git a/resources/i18n/ta.json b/resources/i18n/ta.json index c8ef54660..b12b3fd8c 100644 --- a/resources/i18n/ta.json +++ b/resources/i18n/ta.json @@ -1,109 +1,109 @@ -{ - "@metadata": { - "authors": [ - "Fahimrazick", - "Gurulenin", - "McDutchie", - "Siddhan" - ] - }, - "name": "தமிழ்", - "error-title": "பிழை", - "error-downloader-window-title": "பதிவிறக்கியை உருவாக்க முடியவில்லை", - "error-downloader-launch-message": "பதிவிறக்கியைத் தொடங்க சாத்தியமில்லை, கிவிக்ஸ்-டெஸ்க்டாப் தொடங்கும், ஆனால் அனைத்து பதிவிறக்க செயல்பாடுகளும் இயங்காது!", - "error-launch-server-message": "ஓர் பிழை ஏற்பட்டுள்ளது!", - "error-archive": "இந்த காப்பகத்தை பெற இயலவில்லை", - "open-zim": "Zim கோப்பினைத் திற", - "local-kiwix-server": "உள்ளிட கிவிக்ஸ் சேவையகம்", - "random-article": "ஏதாவதொரு கட்டுரை", - "random-article-error": "ஏதாவது ஒரு கட்டுரையைத் திறக்க முடியவில்லை.", - "home-page": "முகப்பு பக்கம்", - "main-menu": "முதன்மை பட்டியல்", - "print": "அச்சிடுக", - "print-page-error": "அச்சிடும்போது பிழை ஏற்பட்டது.", - "new-tab": "புதிய தத்தல்", - "close-tab": "தத்தலை மூடு", - "close": "மூடு", - "reopen-closed-tab": "மூடிய தாவலை மீண்டும் திறக்கவும்", - "browse-library": "நூலகத்தை உலாவுக", - "open-file": "கோப்பைத் திறக்கவும்", - "open-recent": "சமீபத்திய கோப்பினை திற", - "search-article": "கட்டுரையைத் தேடு", - "search-in-library": "நூலகத்தில் தேடு", - "find-in-page": "பக்கத்திற்குள் கண்டுபிடி", - "set-fullscreen": "முழுத்திரை அமைக்கவும்", - "quit-fullscreen": "முழுத்திரையிலிருந்து வெளியேறு", - "table-of-content": "உள்ளடக்க அட்டவணை", - "reading-list": "வாசிப்பு பட்டியல்", - "zoom-in": "பெரிதாக்க", - "zoom-out": "சிறிதாக்கவும்", - "zoom-reset": "பெரிதாக்கு மீட்டமை", - "help": "உதவி", - "feedback": "பின்னூட்டம்", - "report-a-bug": "வழுக்களை அறிக்கையிடுக", - "request-a-feature": "ஒரு அம்சத்தைக் கோரு", - "about-kiwix": "கிவிக்ஸ் பற்றி", - "donate-to-support-kiwix": "கிவிக்ஸை ஆதரிக்க நன்கொடை", - "exit": "வெளியேறு", - "save-file-as-window-title": "கோப்பை இவ்வாறு சேமி", - "download-finished-message": "ஆவணம் பதிவிறக்கம் செய்யப்பட்டுள்ளது.", - "file": "கோப்பு", - "edit": "தொகு", - "view": "பார்வையிடு", - "tools": "கருவிகள்", - "window-title": "நூலகம் - கிவிக்ஸ்", - "search": "தேடு", - "browse-directory": "கோப்பகத்தை உலாவுக", - "settings": "அமைப்புகள்", - "back": "பின்னே", - "forward": "முன் செல்", - "search-files": "கோப்புகளைத் தேடு", - "title": "தலைப்பு", - "size": "அளவு", - "date": "நாள்", - "content-type": "உள்ளடக்க வகை", - "reset-sort": "மீட்டமைத்தல்", - "open": "திற", - "delete": "நீக்கு", - "download": "பதிவிறக்கு", - "resume": "தொடர்க", - "pause": "இடைநிறுத்து", - "cancel": "கைவிடு", - "apply": "பயன்படுத்து", - "port-for-local-kiwix-server-setting": "உள்ளக கிவிக்ஸ் வழங்கிக்கான புறை", - "zoom-level-setting": "இயல்பான பெரிதாக்கு நிலை", - "download-directory-setting": "கோப்பகத்தைப் பதிவிறக்கவும்", - "reset": "மீட்டமை", - "browse": "உலவு", - "about-kiwix-desktop-title": "கிவிக்ஸ் டெஸ்க்டாப்", - "about-kiwix-desktop-description": "நீங்கள் எங்கு சென்றாலும் முழு விக்கிபீடியாவையும் கிவிக்ஸ் வைத்திருக்க உதவுகிறது! ஒரு படகில், எங்கும் நடுவில் அல்லது சிறையில், கிவிக்ஸ் முழு மனித அறிவையும் அணுகுவதற்கு உங்களுக்கு உதவி செய்கிறது. உங்களுக்கு இணையம் தேவையில்லை, அனைத்தும் உங்கள் கணினியில் சேமிக்கப்படுகிறது.", - "about-learn-more-about-kiwix": "கிவிக்ஸ் பற்றி மேலும் அறிக", - "about-release-title": "வெளியீடு", - "about-source-code": "இந்த மென்பொருள் குனு பொது உரிம பதிப்பு 3 இன் விதிமுறைகளின் கீழ் வெளியிடப்படுகிறது. Github மூலக் குறியீட்டைப் பார்.", - "about-version": "பதிப்பு: {{VERSION}}", - "about-reporting-problem-title": "சிக்கலை புகாரளி", - "about-report-issue": "சிக்கலைத் தெரிவிக்க, Kiwix-desktop issue tracker பார்வையிட்டு, உங்கள் சிக்கலை விவரிக்கவும்", - "about-report-issue-2": "தயவுசெய்து எந்தப் பதிப்பில் சிக்கல் இருக்கிறது என்பதைக் குறிப்பிடவும்.", - "about-libraries-title": "நூலகங்கள்", - "kiwix-server-running-message": "கிவிக்ஸ் சேவையகம் இயங்குகிறது மற்றும் உள்ளிடப் பிணையத்தில் அணுகலாம்:", - "kiwix-server-description": "ஒரு சேவையகத்தைத் தொடங்குவது உள்ளக பிணையத்தில் உள்ள பிற கணினிகளை உங்கள் கிவிக்ஸ் நூலகத்தை ஒரு நிலையான இணைய உலாவியுடன் அணுக அனுமதிக்கிறது.", - "fullscreen-notification": "நீங்கள் இப்போது முழுத்திரை பயன்முறையில் இருக்கிறீர்கள். வெளியேற ESC ஐ அழுத்தவும்!", - "local-files": "உள்ளிடக் கோப்புகள்", - "hide": "மறை", - "open-in-browser": "உலாவியில் திறக்க", - "start-kiwix-server": "கிவிக்ஸ் சேவையகத்தைத் தொடங்கவும்", - "stop-kiwix-server": "கிவிக்ஸ் சேவையகத்தை நிறுத்து", - "all": "அனைத்தும்", - "fulltext-search": "முழு உரை தேடல்", - "pictures": "படங்கள்", - "videos": "காணொளிகள்", - "ftindex": "முழு உரை அட்டவணை", - "details": "முழு கட்டுரை", - "yes": "ஆம்", - "no": "இல்லை", - "no-filter": "வடிப்பான் இல்லை", - "open-link-in-web-browser": "இணைய உலாவியில் இணைப்பைத் திறக்கவும்", - "download-dir-dialog-title": "பதிவிறக்க கோப்பகத்தை மாற்ற விரும்புகிறீர்களா?", - "download-dir-dialog-msg": "புதிய பதிவிறக்க அடைவு பாதை :{{DIRECTORY}}", - "invalid-port": "செல்லத்தகாத புறை" -} +{ + "@metadata": { + "authors": [ + "Fahimrazick", + "Gurulenin", + "McDutchie", + "Siddhan" + ] + }, + "name": "தமிழ்", + "error-title": "பிழை", + "error-downloader-window-title": "பதிவிறக்கியை உருவாக்க முடியவில்லை", + "error-downloader-launch-message": "பதிவிறக்கியைத் தொடங்க சாத்தியமில்லை, கிவிக்ஸ்-டெஸ்க்டாப் தொடங்கும், ஆனால் அனைத்து பதிவிறக்க செயல்பாடுகளும் இயங்காது!", + "error-launch-server-message": "ஓர் பிழை ஏற்பட்டுள்ளது!", + "error-archive": "இந்த காப்பகத்தை பெற இயலவில்லை", + "open-zim": "Zim கோப்பினைத் திற", + "local-kiwix-server": "உள்ளிட கிவிக்ஸ் சேவையகம்", + "random-article": "ஏதாவதொரு கட்டுரை", + "random-article-error": "ஏதாவது ஒரு கட்டுரையைத் திறக்க முடியவில்லை.", + "home-page": "முகப்பு பக்கம்", + "main-menu": "முதன்மை பட்டியல்", + "print": "அச்சிடுக", + "print-page-error": "அச்சிடும்போது பிழை ஏற்பட்டது.", + "new-tab": "புதிய தத்தல்", + "close-tab": "தத்தலை மூடு", + "close": "மூடு", + "reopen-closed-tab": "மூடிய தாவலை மீண்டும் திறக்கவும்", + "browse-library": "நூலகத்தை உலாவுக", + "open-file": "கோப்பைத் திறக்கவும்", + "open-recent": "சமீபத்திய கோப்பினை திற", + "search-article": "கட்டுரையைத் தேடு", + "search-in-library": "நூலகத்தில் தேடு", + "find-in-page": "பக்கத்திற்குள் கண்டுபிடி", + "set-fullscreen": "முழுத்திரை அமைக்கவும்", + "quit-fullscreen": "முழுத்திரையிலிருந்து வெளியேறு", + "table-of-content": "உள்ளடக்க அட்டவணை", + "reading-list": "வாசிப்பு பட்டியல்", + "zoom-in": "பெரிதாக்க", + "zoom-out": "சிறிதாக்கவும்", + "zoom-reset": "பெரிதாக்கு மீட்டமை", + "help": "உதவி", + "feedback": "பின்னூட்டம்", + "report-a-bug": "வழுக்களை அறிக்கையிடுக", + "request-a-feature": "ஒரு அம்சத்தைக் கோரு", + "about-kiwix": "கிவிக்ஸ் பற்றி", + "donate-to-support-kiwix": "கிவிக்ஸை ஆதரிக்க நன்கொடை", + "exit": "வெளியேறு", + "save-file-as-window-title": "கோப்பை இவ்வாறு சேமி", + "download-finished-message": "ஆவணம் பதிவிறக்கம் செய்யப்பட்டுள்ளது.", + "file": "கோப்பு", + "edit": "தொகு", + "view": "பார்வையிடு", + "tools": "கருவிகள்", + "window-title": "நூலகம் - கிவிக்ஸ்", + "search": "தேடு", + "browse-directory": "கோப்பகத்தை உலாவுக", + "settings": "அமைப்புகள்", + "back": "பின்னே", + "forward": "முன் செல்", + "search-files": "கோப்புகளைத் தேடு", + "title": "தலைப்பு", + "size": "அளவு", + "date": "நாள்", + "content-type": "உள்ளடக்க வகை", + "reset-sort": "மீட்டமைத்தல்", + "open": "திற", + "delete": "நீக்கு", + "download": "பதிவிறக்கு", + "resume": "தொடர்க", + "pause": "இடைநிறுத்து", + "cancel": "கைவிடு", + "apply": "பயன்படுத்து", + "port-for-local-kiwix-server-setting": "உள்ளக கிவிக்ஸ் வழங்கிக்கான புறை", + "zoom-level-setting": "இயல்பான பெரிதாக்கு நிலை", + "download-directory-setting": "கோப்பகத்தைப் பதிவிறக்கவும்", + "reset": "மீட்டமை", + "browse": "உலவு", + "about-kiwix-desktop-title": "கிவிக்ஸ் டெஸ்க்டாப்", + "about-kiwix-desktop-description": "நீங்கள் எங்கு சென்றாலும் முழு விக்கிபீடியாவையும் கிவிக்ஸ் வைத்திருக்க உதவுகிறது! ஒரு படகில், எங்கும் நடுவில் அல்லது சிறையில், கிவிக்ஸ் முழு மனித அறிவையும் அணுகுவதற்கு உங்களுக்கு உதவி செய்கிறது. உங்களுக்கு இணையம் தேவையில்லை, அனைத்தும் உங்கள் கணினியில் சேமிக்கப்படுகிறது.", + "about-learn-more-about-kiwix": "கிவிக்ஸ் பற்றி மேலும் அறிக", + "about-release-title": "வெளியீடு", + "about-source-code": "இந்த மென்பொருள் குனு பொது உரிம பதிப்பு 3 இன் விதிமுறைகளின் கீழ் வெளியிடப்படுகிறது. Github மூலக் குறியீட்டைப் பார்.", + "about-version": "பதிப்பு: {{VERSION}}", + "about-reporting-problem-title": "சிக்கலை புகாரளி", + "about-report-issue": "சிக்கலைத் தெரிவிக்க, Kiwix-desktop issue tracker பார்வையிட்டு, உங்கள் சிக்கலை விவரிக்கவும்", + "about-report-issue-2": "தயவுசெய்து எந்தப் பதிப்பில் சிக்கல் இருக்கிறது என்பதைக் குறிப்பிடவும்.", + "about-libraries-title": "நூலகங்கள்", + "kiwix-server-running-message": "கிவிக்ஸ் சேவையகம் இயங்குகிறது மற்றும் உள்ளிடப் பிணையத்தில் அணுகலாம்:", + "kiwix-server-description": "ஒரு சேவையகத்தைத் தொடங்குவது உள்ளக பிணையத்தில் உள்ள பிற கணினிகளை உங்கள் கிவிக்ஸ் நூலகத்தை ஒரு நிலையான இணைய உலாவியுடன் அணுக அனுமதிக்கிறது.", + "fullscreen-notification": "நீங்கள் இப்போது முழுத்திரை பயன்முறையில் இருக்கிறீர்கள். வெளியேற ESC ஐ அழுத்தவும்!", + "local-files": "உள்ளிடக் கோப்புகள்", + "hide": "மறை", + "open-in-browser": "உலாவியில் திறக்க", + "start-kiwix-server": "கிவிக்ஸ் சேவையகத்தைத் தொடங்கவும்", + "stop-kiwix-server": "கிவிக்ஸ் சேவையகத்தை நிறுத்து", + "all": "அனைத்தும்", + "fulltext-search": "முழு உரை தேடல்", + "pictures": "படங்கள்", + "videos": "காணொளிகள்", + "ftindex": "முழு உரை அட்டவணை", + "details": "முழு கட்டுரை", + "yes": "ஆம்", + "no": "இல்லை", + "no-filter": "வடிப்பான் இல்லை", + "open-link-in-web-browser": "இணைய உலாவியில் இணைப்பைத் திறக்கவும்", + "download-dir-dialog-title": "பதிவிறக்க கோப்பகத்தை மாற்ற விரும்புகிறீர்களா?", + "download-dir-dialog-msg": "புதிய பதிவிறக்க அடைவு பாதை :{{DIRECTORY}}", + "invalid-port": "செல்லத்தகாத புறை" +} diff --git a/resources/i18n/te.json b/resources/i18n/te.json index bc26b1d50..78ae74018 100644 --- a/resources/i18n/te.json +++ b/resources/i18n/te.json @@ -1,147 +1,147 @@ -{ - "@metadata": { - "authors": [ - "Rishitha 1238", - "Veeven" - ] - }, - "name": "తెలుగు", - "error-title": "లోపం", - "error-downloader-window-title": "డౌన్‌లోడ్‌ని సృష్టించడం సాధ్యం కాదు", - "error-downloader-launch-message": "డౌన్‌లోడ్‌ను ప్రారంభించడం అసాధ్యం, కివిక్స్-డెస్క్‌టాప్ ప్రారంభమవుతుంది కానీ అన్ని డౌన్‌లోడ్ ఫంక్షన్‌లు పని చేయవు!", - "error-launch-server-message": "ఒక లోపం సంభవించింది!", - "error-archive": "ఆర్కైవ్‌ని పొందడం సాధ్యం కాదు", - "open-zim": "ZIM ఫైల్‌ని తెరవండి", - "local-kiwix-server": "స్థానిక Kiwix సర్వర్", - "random-article": "యాదృచ్ఛిక వ్యాసం", - "random-article-error": "యాదృచ్ఛిక కథనాన్ని తెరవడంలో విఫలమైంది.", - "home-page": "ముంగిలి పేజీ", - "main-menu": "ప్రధాన మెనూ", - "print": "ముద్రించు", - "print-page-error": "ప్రింట్ చేస్తున్నప్పుడు లోపం ఏర్పడింది.", - "new-tab": "కొత్త ట్యాబ్", - "close-tab": "ట్యాబ్‌ను మూసివేయండి", - "close": "మూసివేయండి", - "reopen-closed-tab": "మూసివేసిన ట్యాబ్‌ను మళ్లీ తెరవండి", - "browse-library": "లైబ్రరీని బ్రౌజ్ చేయండి", - "open-file": "ఫైలును తెరవండి", - "open-recent": "ఇటీవల తెరువు", - "search-article": "శోధన కథనం", - "search-in-library": "లైబ్రరీలో శోధించండి", - "find-in-page": "పేజీలో కనుగొనండి", - "set-fullscreen": "పూర్తి స్క్రీన్‌ను సెట్ చేయండి", - "quit-fullscreen": "పూర్తి స్క్రీన్ నుండి నిష్క్రమించండి", - "table-of-content": "విషయ పట్టిక", - "reading-list": "పఠన జాబితా", - "zoom-in": "పెద్దదిగా చూపు", - "zoom-out": "చిన్నది చేయి", - "zoom-reset": "జూమ్ రీసెట్", - "help": "సహాయం", - "feedback": "ప్రతిస్పందన", - "report-a-bug": "ఒక సమస్యను నివేదించండి", - "request-a-feature": "లక్షణాన్ని అభ్యర్థించండి", - "about-kiwix": "కివిక్స్ గురించి", - "donate-to-support-kiwix": "Kiwixకు మద్దతు ఇవ్వడానికి విరాళం ఇవ్వండి", - "exit": "బయటకి దారి", - "save-file-as-window-title": "ఫైల్‌ని ఇలా సేవ్ చేయండి", - "download-finished-message": "పత్రం డౌన్‌లోడ్ చేయబడింది.", - "file": "ఫైల్", - "edit": "సవరించు", - "view": "చూడండి", - "tools": "పనిముట్లు", - "window-title": "లైబ్రరీ - కివిక్స్", - "search": "వెతుకు", - "browse-directory": "డైరెక్టరీని బ్రౌజ్ చేయండి", - "settings": "అమరికలు", - "back": "వెనుకకు", - "forward": "ముందుకు", - "search-files": "ఫైళ్ళను వెతకండి", - "title": "శీర్షిక", - "size": "పరిమాణం", - "date": "తేదీ", - "content-type": "విషయము రకం", - "reset-sort": "క్రమాన్ని రీసెట్ చేయండి", - "open": "తెరవండి", - "delete": "తొలగించు", - "download": "డౌన్‌లోడ్ చేయండి", - "resume": "కొనసాగించు", - "pause": "పాజ్ చేయబడింది", - "cancel": "రద్దుచేయి", - "apply": "దరఖాస్తు చేసుకోండి", - "port-for-local-kiwix-server-setting": "స్థానిక Kiwix సర్వర్ కోసం పోర్ట్", - "zoom-level-setting": "డిఫాల్ట్ జూమ్ స్థాయి", - "download-directory-setting": "డైరెక్టరీని డౌన్‌లోడ్ చేయండి", - "monitor-directory-setting": "మానిటర్ డైరెక్టరీ", - "reset": "రీసెట్ చేయండి", - "clear": "క్లియర్", - "browse": "విహరించు", - "about-kiwix-desktop-title": "కివిక్స్ డెస్క్‌టాప్", - "about-kiwix-desktop-description": "మీరు ఎక్కడికి వెళ్లినా మొత్తం వికీపీడియాను కలిగి ఉండటానికి Kiwix మిమ్మల్ని అనుమతిస్తుంది! పడవలో, ఎక్కడా మధ్యలో లేదా జైలులో, Kiwix మీకు మానవ విజ్ఞానం మొత్తాన్ని యాక్సెస్ చేస్తుంది. మీకు ఇంటర్నెట్ అవసరం లేదు, ప్రతిదీ మీ కంప్యూటర్‌లో నిల్వ చేయబడుతుంది.", - "about-learn-more-about-kiwix": "Kiwix గురించి మరింత తెలుసుకోండి", - "about-release-title": "విడుదల", - "about-source-code": "ఈ సాఫ్ట్‌వేర్ GNU జనరల్ పబ్లిక్ లైసెన్స్ వెర్షన్ 3 నిబంధనల ప్రకారం విడుదల చేయబడింది. Github లో సోర్స్ కోడ్‌ను వీక్షించండి.", - "about-version": "వెర్షన్: {{VERSION}}", - "about-reporting-problem-title": "సమస్యను నివేదించడం", - "about-report-issue": "సమస్యను తెరవడానికి, Kiwix-desktop ఇష్యూ ట్రాకర్‌ని సందర్శించి, మీ సమస్యను వివరించే సమస్యను తెరవండి.", - "about-report-issue-2": "దయచేసి సంచికలో సంస్కరణను పేర్కొనండి.", - "about-libraries-title": "గ్రంథాలయాలు", - "kiwix-server-running-message": "Kiwix సర్వర్ అమలవుతోంది మరియు స్థానిక నెట్‌వర్క్‌లో ఇక్కడ యాక్సెస్ చేయవచ్చు:", - "kiwix-server-description": "సర్వర్‌ను ప్రారంభించడం వలన స్థానిక నెట్‌వర్క్‌లోని ఇతర కంప్యూటర్‌లు మీ కివిక్స్ లైబ్రరీని ప్రామాణిక వెబ్ బ్రౌజర్‌తో యాక్సెస్ చేయడానికి అనుమతిస్తుంది.", - "fullscreen-notification": "మీరు ఇప్పుడు పూర్తి స్క్రీన్ మోడ్‌లో ఉన్నారు. నిష్క్రమించడానికి ESC నొక్కండి!", - "online-files": "ఆన్‌లైన్ ఫైల్‌లు", - "local-files": "స్థానిక ఫైల్‌లు", - "category": "వర్గం", - "language": "భాష", - "hide": "దాచు", - "open-in-browser": "బ్రౌజర్‌లో తెరవండి", - "start-kiwix-server": "Kiwix సర్వర్‌ని ప్రారంభించండి", - "stop-kiwix-server": "Kiwix సర్వర్‌ని ఆపు", - "all": "అన్నీ", - "fulltext-search": "పూర్తి వచన శోధన", - "pictures": "బొమ్మలు", - "videos": "వీడియోలు", - "ftindex": "పూర్తి వచన సూచిక", - "details": "పూర్తి వ్యాసం", - "yes": "అవును", - "no": "లేదు", - "ok": "అలాగే", - "no-filter": "సంఖ్య వడపోత", - "open-link-in-web-browser": "వెబ్ బ్రౌజర్‌లో లింక్‌ని తెరవండి", - "download-dir-dialog-title": "మీరు డౌన్‌లోడ్ డైరెక్టరీని ఖచ్చితంగా మార్చాలనుకుంటున్నారా?", - "download-dir-dialog-msg": "కొత్త డౌన్‌లోడ్ డైరెక్టరీ మార్గం:\n{{DIRECTORY}}", - "invalid-port": "చెల్లని పోర్ట్", - "zim-open-fail-title": "చెల్లని ఫైల్", - "zim-open-fail-text": "ZIM ఫైల్ {{ZIM}} సరిగ్గా తెరవబడదు. ఇది మీ లైబ్రరీ నుండి తీసివేయబడుతుంది.", - "monitor-dir-dialog-title": "మీరు ఖచ్చితంగా మానిటర్ డైరెక్టరీని మార్చాలనుకుంటున్నారా?", - "monitor-dir-dialog-msg": "కొత్త మానిటర్ డైరెక్టరీ మార్గం:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "మీరు మానిటర్ డైరెక్టరీని ఖచ్చితంగా క్లియర్ చేయాలనుకుంటున్నారా?", - "monitor-clear-dir-dialog-msg": "ఇది కొత్త ZIM ఫైల్‌ల కోసం మానిటర్ డైరెక్టరీని తనిఖీ చేయడం ఆపివేస్తుంది.", - "monitor-directory-tooltip": "ఈ డైరెక్టరీలోని అన్ని ZIM ఫైల్‌లు స్వయంచాలకంగా లైబ్రరీకి జోడించబడతాయి.", - "next-tab": "తదుపరి ట్యాబ్‌కు తరలించండి", - "previous-tab": "మునుపటి ట్యాబ్‌కు తరలించండి", - "cancel-download": "డౌన్‌లోడ్ రద్దు చేయండి", - "cancel-download-text": "మీరు ఖచ్చితంగా {{ZIM}} డౌన్‌లోడ్‌ని రద్దు చేయాలనుకుంటున్నారా?", - "delete-book": "పుస్తకాన్ని తొలగించండి", - "delete-book-text": "మీరు ఖచ్చితంగా {{ZIM}}ని తొలగించాలనుకుంటున్నారా?", - "download-storage-error": "నిల్వ లోపం", - "download-storage-error-text": "సిస్టమ్‌లో తగినంత నిల్వ అందుబాటులో లేదు.", - "download-unavailable": "డౌన్‌లోడ్ అందుబాటులో లేదు", - "download-unavailable-text": "ఈ డౌన్‌లోడ్ అందుబాటులో లేదు.", - "open-book": "పుస్తకం తెరవండి", - "download-book": "పుస్తకాన్ని డౌన్‌లోడ్ చేయండి", - "pause-download": "డౌన్‌లోడ్‌ను పాజ్ చేయండి", - "resume-download": "డౌన్‌లోడ్ పునఃప్రారంభించండి", - "open-folder": "ఫోల్డర్ను తెరువు", - "couldnt-open-location": "స్థానాన్ని తెరవడం సాధ్యపడలేదు", - "couldnt-open-location-text": "Kiwix {{FOLDER}} ఫోల్డర్‌ని తెరవలేకపోయింది", - "move-files-to-trash": "తొలగించిన ఫైల్‌లను ట్రాష్‌కి తరలించండి", - "move-files-to-trash-text": "ఈ చర్య ఫైల్‌ను ట్రాష్‌కి తరలిస్తుంది.", - "perma-delete-files-text": "ఈ చర్య ఫైల్‌ని శాశ్వతంగా తొలగిస్తుంది.", - "clear-filter": "ప్రస్తుతం సెట్ చేసిన ఫిల్టర్‌లను క్లియర్ చేయండి", - "language-searcher-placeholder": "ఫిల్టర్ భాష", - "category-searcher-placeholder": "ఫిల్టర్ వర్గం", - "content-type-searcher-placeholder": "ఫిల్టర్ కంటెంట్ రకం", - "no-details": "పరిచయం మాత్రమే", - "no-pictures": "చిత్రాలు లేవు", - "no-videos": "వీడియోలు లేవు" -} +{ + "@metadata": { + "authors": [ + "Rishitha 1238", + "Veeven" + ] + }, + "name": "తెలుగు", + "error-title": "లోపం", + "error-downloader-window-title": "డౌన్‌లోడ్‌ని సృష్టించడం సాధ్యం కాదు", + "error-downloader-launch-message": "డౌన్‌లోడ్‌ను ప్రారంభించడం అసాధ్యం, కివిక్స్-డెస్క్‌టాప్ ప్రారంభమవుతుంది కానీ అన్ని డౌన్‌లోడ్ ఫంక్షన్‌లు పని చేయవు!", + "error-launch-server-message": "ఒక లోపం సంభవించింది!", + "error-archive": "ఆర్కైవ్‌ని పొందడం సాధ్యం కాదు", + "open-zim": "ZIM ఫైల్‌ని తెరవండి", + "local-kiwix-server": "స్థానిక Kiwix సర్వర్", + "random-article": "యాదృచ్ఛిక వ్యాసం", + "random-article-error": "యాదృచ్ఛిక కథనాన్ని తెరవడంలో విఫలమైంది.", + "home-page": "ముంగిలి పేజీ", + "main-menu": "ప్రధాన మెనూ", + "print": "ముద్రించు", + "print-page-error": "ప్రింట్ చేస్తున్నప్పుడు లోపం ఏర్పడింది.", + "new-tab": "కొత్త ట్యాబ్", + "close-tab": "ట్యాబ్‌ను మూసివేయండి", + "close": "మూసివేయండి", + "reopen-closed-tab": "మూసివేసిన ట్యాబ్‌ను మళ్లీ తెరవండి", + "browse-library": "లైబ్రరీని బ్రౌజ్ చేయండి", + "open-file": "ఫైలును తెరవండి", + "open-recent": "ఇటీవల తెరువు", + "search-article": "శోధన కథనం", + "search-in-library": "లైబ్రరీలో శోధించండి", + "find-in-page": "పేజీలో కనుగొనండి", + "set-fullscreen": "పూర్తి స్క్రీన్‌ను సెట్ చేయండి", + "quit-fullscreen": "పూర్తి స్క్రీన్ నుండి నిష్క్రమించండి", + "table-of-content": "విషయ పట్టిక", + "reading-list": "పఠన జాబితా", + "zoom-in": "పెద్దదిగా చూపు", + "zoom-out": "చిన్నది చేయి", + "zoom-reset": "జూమ్ రీసెట్", + "help": "సహాయం", + "feedback": "ప్రతిస్పందన", + "report-a-bug": "ఒక సమస్యను నివేదించండి", + "request-a-feature": "లక్షణాన్ని అభ్యర్థించండి", + "about-kiwix": "కివిక్స్ గురించి", + "donate-to-support-kiwix": "Kiwixకు మద్దతు ఇవ్వడానికి విరాళం ఇవ్వండి", + "exit": "బయటకి దారి", + "save-file-as-window-title": "ఫైల్‌ని ఇలా సేవ్ చేయండి", + "download-finished-message": "పత్రం డౌన్‌లోడ్ చేయబడింది.", + "file": "ఫైల్", + "edit": "సవరించు", + "view": "చూడండి", + "tools": "పనిముట్లు", + "window-title": "లైబ్రరీ - కివిక్స్", + "search": "వెతుకు", + "browse-directory": "డైరెక్టరీని బ్రౌజ్ చేయండి", + "settings": "అమరికలు", + "back": "వెనుకకు", + "forward": "ముందుకు", + "search-files": "ఫైళ్ళను వెతకండి", + "title": "శీర్షిక", + "size": "పరిమాణం", + "date": "తేదీ", + "content-type": "విషయము రకం", + "reset-sort": "క్రమాన్ని రీసెట్ చేయండి", + "open": "తెరవండి", + "delete": "తొలగించు", + "download": "డౌన్‌లోడ్ చేయండి", + "resume": "కొనసాగించు", + "pause": "పాజ్ చేయబడింది", + "cancel": "రద్దుచేయి", + "apply": "దరఖాస్తు చేసుకోండి", + "port-for-local-kiwix-server-setting": "స్థానిక Kiwix సర్వర్ కోసం పోర్ట్", + "zoom-level-setting": "డిఫాల్ట్ జూమ్ స్థాయి", + "download-directory-setting": "డైరెక్టరీని డౌన్‌లోడ్ చేయండి", + "monitor-directory-setting": "మానిటర్ డైరెక్టరీ", + "reset": "రీసెట్ చేయండి", + "clear": "క్లియర్", + "browse": "విహరించు", + "about-kiwix-desktop-title": "కివిక్స్ డెస్క్‌టాప్", + "about-kiwix-desktop-description": "మీరు ఎక్కడికి వెళ్లినా మొత్తం వికీపీడియాను కలిగి ఉండటానికి Kiwix మిమ్మల్ని అనుమతిస్తుంది! పడవలో, ఎక్కడా మధ్యలో లేదా జైలులో, Kiwix మీకు మానవ విజ్ఞానం మొత్తాన్ని యాక్సెస్ చేస్తుంది. మీకు ఇంటర్నెట్ అవసరం లేదు, ప్రతిదీ మీ కంప్యూటర్‌లో నిల్వ చేయబడుతుంది.", + "about-learn-more-about-kiwix": "Kiwix గురించి మరింత తెలుసుకోండి", + "about-release-title": "విడుదల", + "about-source-code": "ఈ సాఫ్ట్‌వేర్ GNU జనరల్ పబ్లిక్ లైసెన్స్ వెర్షన్ 3 నిబంధనల ప్రకారం విడుదల చేయబడింది. Github లో సోర్స్ కోడ్‌ను వీక్షించండి.", + "about-version": "వెర్షన్: {{VERSION}}", + "about-reporting-problem-title": "సమస్యను నివేదించడం", + "about-report-issue": "సమస్యను తెరవడానికి, Kiwix-desktop ఇష్యూ ట్రాకర్‌ని సందర్శించి, మీ సమస్యను వివరించే సమస్యను తెరవండి.", + "about-report-issue-2": "దయచేసి సంచికలో సంస్కరణను పేర్కొనండి.", + "about-libraries-title": "గ్రంథాలయాలు", + "kiwix-server-running-message": "Kiwix సర్వర్ అమలవుతోంది మరియు స్థానిక నెట్‌వర్క్‌లో ఇక్కడ యాక్సెస్ చేయవచ్చు:", + "kiwix-server-description": "సర్వర్‌ను ప్రారంభించడం వలన స్థానిక నెట్‌వర్క్‌లోని ఇతర కంప్యూటర్‌లు మీ కివిక్స్ లైబ్రరీని ప్రామాణిక వెబ్ బ్రౌజర్‌తో యాక్సెస్ చేయడానికి అనుమతిస్తుంది.", + "fullscreen-notification": "మీరు ఇప్పుడు పూర్తి స్క్రీన్ మోడ్‌లో ఉన్నారు. నిష్క్రమించడానికి ESC నొక్కండి!", + "online-files": "ఆన్‌లైన్ ఫైల్‌లు", + "local-files": "స్థానిక ఫైల్‌లు", + "category": "వర్గం", + "language": "భాష", + "hide": "దాచు", + "open-in-browser": "బ్రౌజర్‌లో తెరవండి", + "start-kiwix-server": "Kiwix సర్వర్‌ని ప్రారంభించండి", + "stop-kiwix-server": "Kiwix సర్వర్‌ని ఆపు", + "all": "అన్నీ", + "fulltext-search": "పూర్తి వచన శోధన", + "pictures": "బొమ్మలు", + "videos": "వీడియోలు", + "ftindex": "పూర్తి వచన సూచిక", + "details": "పూర్తి వ్యాసం", + "yes": "అవును", + "no": "లేదు", + "ok": "అలాగే", + "no-filter": "సంఖ్య వడపోత", + "open-link-in-web-browser": "వెబ్ బ్రౌజర్‌లో లింక్‌ని తెరవండి", + "download-dir-dialog-title": "మీరు డౌన్‌లోడ్ డైరెక్టరీని ఖచ్చితంగా మార్చాలనుకుంటున్నారా?", + "download-dir-dialog-msg": "కొత్త డౌన్‌లోడ్ డైరెక్టరీ మార్గం:\n{{DIRECTORY}}", + "invalid-port": "చెల్లని పోర్ట్", + "zim-open-fail-title": "చెల్లని ఫైల్", + "zim-open-fail-text": "ZIM ఫైల్ {{ZIM}} సరిగ్గా తెరవబడదు. ఇది మీ లైబ్రరీ నుండి తీసివేయబడుతుంది.", + "monitor-dir-dialog-title": "మీరు ఖచ్చితంగా మానిటర్ డైరెక్టరీని మార్చాలనుకుంటున్నారా?", + "monitor-dir-dialog-msg": "కొత్త మానిటర్ డైరెక్టరీ మార్గం:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "మీరు మానిటర్ డైరెక్టరీని ఖచ్చితంగా క్లియర్ చేయాలనుకుంటున్నారా?", + "monitor-clear-dir-dialog-msg": "ఇది కొత్త ZIM ఫైల్‌ల కోసం మానిటర్ డైరెక్టరీని తనిఖీ చేయడం ఆపివేస్తుంది.", + "monitor-directory-tooltip": "ఈ డైరెక్టరీలోని అన్ని ZIM ఫైల్‌లు స్వయంచాలకంగా లైబ్రరీకి జోడించబడతాయి.", + "next-tab": "తదుపరి ట్యాబ్‌కు తరలించండి", + "previous-tab": "మునుపటి ట్యాబ్‌కు తరలించండి", + "cancel-download": "డౌన్‌లోడ్ రద్దు చేయండి", + "cancel-download-text": "మీరు ఖచ్చితంగా {{ZIM}} డౌన్‌లోడ్‌ని రద్దు చేయాలనుకుంటున్నారా?", + "delete-book": "పుస్తకాన్ని తొలగించండి", + "delete-book-text": "మీరు ఖచ్చితంగా {{ZIM}}ని తొలగించాలనుకుంటున్నారా?", + "download-storage-error": "నిల్వ లోపం", + "download-storage-error-text": "సిస్టమ్‌లో తగినంత నిల్వ అందుబాటులో లేదు.", + "download-unavailable": "డౌన్‌లోడ్ అందుబాటులో లేదు", + "download-unavailable-text": "ఈ డౌన్‌లోడ్ అందుబాటులో లేదు.", + "open-book": "పుస్తకం తెరవండి", + "download-book": "పుస్తకాన్ని డౌన్‌లోడ్ చేయండి", + "pause-download": "డౌన్‌లోడ్‌ను పాజ్ చేయండి", + "resume-download": "డౌన్‌లోడ్ పునఃప్రారంభించండి", + "open-folder": "ఫోల్డర్ను తెరువు", + "couldnt-open-location": "స్థానాన్ని తెరవడం సాధ్యపడలేదు", + "couldnt-open-location-text": "Kiwix {{FOLDER}} ఫోల్డర్‌ని తెరవలేకపోయింది", + "move-files-to-trash": "తొలగించిన ఫైల్‌లను ట్రాష్‌కి తరలించండి", + "move-files-to-trash-text": "ఈ చర్య ఫైల్‌ను ట్రాష్‌కి తరలిస్తుంది.", + "perma-delete-files-text": "ఈ చర్య ఫైల్‌ని శాశ్వతంగా తొలగిస్తుంది.", + "clear-filter": "ప్రస్తుతం సెట్ చేసిన ఫిల్టర్‌లను క్లియర్ చేయండి", + "language-searcher-placeholder": "ఫిల్టర్ భాష", + "category-searcher-placeholder": "ఫిల్టర్ వర్గం", + "content-type-searcher-placeholder": "ఫిల్టర్ కంటెంట్ రకం", + "no-details": "పరిచయం మాత్రమే", + "no-pictures": "చిత్రాలు లేవు", + "no-videos": "వీడియోలు లేవు" +} diff --git a/resources/i18n/th.json b/resources/i18n/th.json index dd5e9f338..66103fefd 100644 --- a/resources/i18n/th.json +++ b/resources/i18n/th.json @@ -1,63 +1,63 @@ -{ - "@metadata": { - "authors": [ - "McDutchie", - "Prame Tan" - ] - }, - "name": "ภาษาไทย", - "open-zim": "เปิด Zim", - "random-article": "สุ่มบทความ", - "home-page": "หน้าหลัก", - "main-menu": "เมนูหลัก", - "print": "พิมพ์", - "new-tab": "เปิดแท็บใหม่", - "close-tab": "ปิดแท็บ", - "close": "ปิด", - "reopen-closed-tab": "เปิดแท็บที่ปิดไปแล้ว", - "open-file": "เปิดไฟล์", - "search-article": "ค้นหาบทความ", - "search-in-library": "ค้นหาในไลบรารี่", - "find-in-page": "ค้นหาในหน้า", - "set-fullscreen": "มุมมองเต็มหน้าจอ", - "quit-fullscreen": "ออกจากมุมมองเต็มหน้าจอ", - "table-of-content": "สารบัญ", - "reading-list": "รายการอ่าน", - "zoom-in": "ซูมเข้า", - "zoom-out": "ซูมออก", - "zoom-reset": "ตั้งค่าซูมใหม่", - "help": "ช่วยเหลือ", - "feedback": "คำติชม", - "report-a-bug": "รายงานข้อผิดพลาด", - "about-kiwix": "เกี่ยวกับ Kiwix", - "donate-to-support-kiwix": "บริจาคเพื่อสนับสนุน Kiwix", - "exit": "ออก", - "save-file-as-window-title": "บันทึกไฟล์เป็น", - "download-finished-message": "ดาวน์โหลดบทความนี้แล้ว", - "file": "ไฟล์", - "edit": "แก้ไข", - "view": "ดูหน้า", - "tools": "เครื่องมือ", - "search": "ค้นหา", - "settings": "การตั้งค่า", - "back": "ย้อนกลับ", - "date": "วันที่", - "open": "เปิด", - "delete": "ลบ", - "download": "ดาวน์โหลด", - "resume": "ทำต่อ", - "cancel": "ยกเลิก", - "apply": "ใช้งาน", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-version": "เวอร์ชัน: {{VERSION}}", - "about-reporting-problem-title": "รายงานปัญหา", - "about-report-issue-2": "กรุณาระบุเวอร์ชันในรายงานปัญหาด้วย", - "hide": "ซ่อน", - "open-in-browser": "เปิดในเบราว์เซอร์", - "all": "ทั้งหมด", - "pictures": "รูปภาพ", - "videos": "วิดีโอ", - "yes": "ใช่", - "no": "ไม่", - "open-link-in-web-browser": "เปิดลิงก์ในเบราว์เซอร์" -} +{ + "@metadata": { + "authors": [ + "McDutchie", + "Prame Tan" + ] + }, + "name": "ภาษาไทย", + "open-zim": "เปิด Zim", + "random-article": "สุ่มบทความ", + "home-page": "หน้าหลัก", + "main-menu": "เมนูหลัก", + "print": "พิมพ์", + "new-tab": "เปิดแท็บใหม่", + "close-tab": "ปิดแท็บ", + "close": "ปิด", + "reopen-closed-tab": "เปิดแท็บที่ปิดไปแล้ว", + "open-file": "เปิดไฟล์", + "search-article": "ค้นหาบทความ", + "search-in-library": "ค้นหาในไลบรารี่", + "find-in-page": "ค้นหาในหน้า", + "set-fullscreen": "มุมมองเต็มหน้าจอ", + "quit-fullscreen": "ออกจากมุมมองเต็มหน้าจอ", + "table-of-content": "สารบัญ", + "reading-list": "รายการอ่าน", + "zoom-in": "ซูมเข้า", + "zoom-out": "ซูมออก", + "zoom-reset": "ตั้งค่าซูมใหม่", + "help": "ช่วยเหลือ", + "feedback": "คำติชม", + "report-a-bug": "รายงานข้อผิดพลาด", + "about-kiwix": "เกี่ยวกับ Kiwix", + "donate-to-support-kiwix": "บริจาคเพื่อสนับสนุน Kiwix", + "exit": "ออก", + "save-file-as-window-title": "บันทึกไฟล์เป็น", + "download-finished-message": "ดาวน์โหลดบทความนี้แล้ว", + "file": "ไฟล์", + "edit": "แก้ไข", + "view": "ดูหน้า", + "tools": "เครื่องมือ", + "search": "ค้นหา", + "settings": "การตั้งค่า", + "back": "ย้อนกลับ", + "date": "วันที่", + "open": "เปิด", + "delete": "ลบ", + "download": "ดาวน์โหลด", + "resume": "ทำต่อ", + "cancel": "ยกเลิก", + "apply": "ใช้งาน", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-version": "เวอร์ชัน: {{VERSION}}", + "about-reporting-problem-title": "รายงานปัญหา", + "about-report-issue-2": "กรุณาระบุเวอร์ชันในรายงานปัญหาด้วย", + "hide": "ซ่อน", + "open-in-browser": "เปิดในเบราว์เซอร์", + "all": "ทั้งหมด", + "pictures": "รูปภาพ", + "videos": "วิดีโอ", + "yes": "ใช่", + "no": "ไม่", + "open-link-in-web-browser": "เปิดลิงก์ในเบราว์เซอร์" +} diff --git a/resources/i18n/tn.json b/resources/i18n/tn.json index 60d13eeb2..38f915406 100644 --- a/resources/i18n/tn.json +++ b/resources/i18n/tn.json @@ -1,146 +1,146 @@ -{ - "@metadata": { - "authors": [ - "Sekhomba" - ] - }, - "name": "Setswana", - "error-title": "Phoso", - "error-downloader-window-title": "Ga o ka ke wa dira download", - "error-downloader-launch-message": "E re ka go sa kgonege go simolola go gatisa, Kiwix-desktop e tla simolola mme ditiro tsotlhe tsa go gatisa ga di kitla di bereka!", - "error-launch-server-message": "Go dirilwe phoso!", - "error-archive": "Ga o kgone go bona direkoto", - "open-zim": "Bula faele ya ZIM", - "local-kiwix-server": "Motlamedi wa Kiwix wa mo gae", - "random-article": "Padi ye e abelelwang", - "random-article-error": "Go bula padi e e abelelwang go retetse.", - "home-page": "Tsebe ya gae", - "main-menu": "Dintlha tsa konokono", - "print": "Gatisa", - "print-page-error": "Go dirilwe phoso fa go gatisiwa.", - "new-tab": "Tab e ntšha", - "close-tab": "Tswala tab", - "close": "Tswala", - "reopen-closed-tab": "Bula tab gape", - "browse-library": "Tsamayatsamaya le motlhobo", - "open-file": "Bula faele", - "open-recent": "Bula ya bosheng", - "search-article": "Batla setlhogo", - "search-in-library": "Batla mo motlobong", - "find-in-page": "Batla mo tsebeng", - "set-fullscreen": "Setshwantsho se se tletseng", - "quit-fullscreen": "Tlogela FullScreen", - "table-of-content": "Tafole ya ditêng", - "reading-list": "Lenaane la go bala", - "zoom-in": "Atolosetsa mo teng", - "zoom-out": "Atolosetsa kwa ntle", - "zoom-reset": "baya katoloso sesha", - "help": "Thuso", - "feedback": "Phetolo", - "report-a-bug": "Bega sekganedi", - "request-a-feature": "Kopa karolo", - "about-kiwix": "Ka ga Kiwix", - "donate-to-support-kiwix": "Ntsha moneelo go rotloetsa Kiwix", - "exit": "Tswa", - "save-file-as-window-title": "Boloka File jaaka", - "download-finished-message": "Tokumente e tserwe", - "file": "Faele", - "edit": "Fetola", - "view": "Bona", - "tools": "Dithulusu", - "window-title": "Motlobo - Kiwix", - "search": "Batla", - "browse-directory": "Tsamayatsamaya le patlisisi", - "settings": "Dipaakanyo", - "back": "Ko Morago", - "forward": "Ko pele", - "search-files": "Batla difaele", - "title": "Setlhogo", - "size": "Selekanyo", - "date": "Letsatsi", - "content-type": "Mofuta wa diteng", - "reset-sort": "go busetsa dilo kwa morago", - "open": "Bula", - "delete": "Phimola", - "download": "Baya", - "resume": "Tsweledisa", - "pause": "Kgama", - "cancel": "Sutlha", - "apply": "Dirisa", - "port-for-local-kiwix-server-setting": "Port ya server ya Kiwix ya selegae", - "zoom-level-setting": "Selekanyo sa zoom sa konokono", - "download-directory-setting": "Go gatisa direkoto", - "monitor-directory-setting": "Ela tlhoko rekoto", - "reset": "Tsosolosa", - "clear": "Sutlha", - "browse": "Tsamayatsamaya", - "about-kiwix-desktop-title": "Kiwix Desktop", - "about-kiwix-desktop-description": "Kiwix e go letlelela go nna le Wikipedia yotlhe gongwe le gongwe kwa o yang teng! Mo mokorong, mo gare ga lefelo le le se nang sepe, kgotsa mo kgolegelong, Kiwix e go naya kitso yotlhe ya setho. Ga o tlhoke Internet, sengwe le sengwe se bolokilwe mo khomputareng ya gago.", - "about-learn-more-about-kiwix": "Ithute go le gontsi ka Kiwix", - "about-release-title": "Golola", - "about-source-code": "Software ye e golotswe ka tlase ga ditsetlana tsa GNU General Public Liscence karolo ya boraro. Bona ko e tsang teng mo Github.", - "about-version": "Setlha: {{VERSION}}", - "about-reporting-problem-title": "Bothata bo a begwa", - "about-report-issue": "Go bula kgwetlho, etela Kiwix-desktop issue tracker o bo o bula kgwetlho e e tlhalosang bothata jwa gago.", - "about-report-issue-2": "Tsweetswee umaka setlha se mo kgwetlhong.", - "about-libraries-title": "Metlobo", - "kiwix-server-running-message": "Kiwix Server e a bereka mme e ka fitlhelelwa mo mafaratlhatlheng a selegae kwa:", - "kiwix-server-description": "Go simolola server go letlelela dikhomputara tse dingwe mo mafaratlhatlheng a selegae go tsena mo motlobong wa gago wa Kiwix ka browser ya web e e tlwaelegileng.", - "fullscreen-notification": "Jaanong o mo seemong se se tletseng sa sekirini. tobetsa ESC gore e tlogele!", - "online-files": "Difaele tsa Internet", - "local-files": "Difaele tsa mo gae", - "category": "Karolo", - "language": "Teme", - "hide": "Fitlha", - "open-in-browser": "Bula mo browser", - "start-kiwix-server": "Simolola Kiwix Server", - "stop-kiwix-server": "Emisa Kiwix Server", - "all": "Tsotlhe", - "fulltext-search": "Batlisisa ka mafoko a a feletseng", - "pictures": "Ditshwantsho", - "videos": "Dtshwantsho tsa motshikhinyego", - "ftindex": "Tlhatlhobo ya mafoko a a feletseng", - "details": "Padi e e feletseng", - "yes": "ee", - "no": "nnyaa", - "ok": "ee", - "no-filter": "Ga gona sefarologanyi", - "open-link-in-web-browser": "Bula kgolaganyi ye mo web browser", - "download-dir-dialog-title": "A o tlhomamisegile gore o batla go fetola lenaane la di-download?", - "download-dir-dialog-msg": "Tsetlana e ntšhs ya tsedi ya go itseela e tla nna:\n{{DIRECTORY}}", - "invalid-port": "Port ye e sa letlelesegeng", - "zim-open-fail-title": "Faele ye e sa letlelesegeng", - "zim-open-fail-text": "Faele ya IM ya {{ZIM}} ga e bulêgê sentlê. E tlaa ntshiwa mo motlôbông.", - "monitor-dir-dialog-title": "A o tlhomamisegile gore o batla go fetola lenaane la batlhatlhobi?", - "monitor-dir-dialog-msg": "Tselana e ntšha ya go itseela e tla nna:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "A o tlhomamisegile gore o batla go phepafatsa lenaane la batlhatlhobi?", - "monitor-clear-dir-dialog-msg": "Seno se tla emisa go tlhatlhoba direkoto tsa difaele tse disha tsa ZIM.", - "monitor-directory-tooltip": "Difaele tsotlhe tsa ZIM tse di mo lenaaneng leno di tla tsenngwa ka botsone mo motlôbông.", - "next-tab": "Fetela ko tab e e latêlang", - "previous-tab": "Ya ko tab e e fetilêng", - "cancel-download": "Khansele go itseela", - "cancel-download-text": "A o tlhomamisitswe gore o batla go khansela downloaf ya {{ZIM}}?", - "delete-book": "Phimola buka", - "delete-book-text": "A o tlhomamisitse gore o batla go phimola {{ZIM}}?", - "download-storage-error": "Phoso ya mabolokelo", - "download-storage-error-text": "Tsamaiso ga e na mabolokelo a a lekaneng.", - "download-unavailable": "Downloaf ga eyo", - "download-unavailable-text": "Download e ga eyo", - "open-book": "Bula buka", - "download-book": "Download buka", - "pause-download": "Kgama go download", - "resume-download": "Tsweledisa go download", - "open-folder": "Bula bobeelo", - "couldnt-open-location": "O ne o ka se kgone go bula lefelo", - "couldnt-open-location-text": "Kiwix ga e kgone go bula bobeelo {{FOLDER}}", - "move-files-to-trash": "Fusolosetsa difaele tse di phimotsweng mo matlakaleng", - "move-files-to-trash-text": "Kgato eno e tla dira gore faele e nne matlakala.", - "perma-delete-files-text": "Kgato eno e tla phimola faele go ya go ile.", - "clear-filter": "Phimola di-filter tse di beilweng gone jaanong", - "language-searcher-placeholder": "Farologanya teme", - "category-searcher-placeholder": "Farologanya dikarolo", - "content-type-searcher-placeholder": "Farologanya mofuta wa tshêdimosôtsô", - "no-details": "Matseno fela", - "no-pictures": "Ga gona Ditshwantsho", - "no-videos": "Ga gona ditshwantsho tsa motshikhinyego" -} +{ + "@metadata": { + "authors": [ + "Sekhomba" + ] + }, + "name": "Setswana", + "error-title": "Phoso", + "error-downloader-window-title": "Ga o ka ke wa dira download", + "error-downloader-launch-message": "E re ka go sa kgonege go simolola go gatisa, Kiwix-desktop e tla simolola mme ditiro tsotlhe tsa go gatisa ga di kitla di bereka!", + "error-launch-server-message": "Go dirilwe phoso!", + "error-archive": "Ga o kgone go bona direkoto", + "open-zim": "Bula faele ya ZIM", + "local-kiwix-server": "Motlamedi wa Kiwix wa mo gae", + "random-article": "Padi ye e abelelwang", + "random-article-error": "Go bula padi e e abelelwang go retetse.", + "home-page": "Tsebe ya gae", + "main-menu": "Dintlha tsa konokono", + "print": "Gatisa", + "print-page-error": "Go dirilwe phoso fa go gatisiwa.", + "new-tab": "Tab e ntšha", + "close-tab": "Tswala tab", + "close": "Tswala", + "reopen-closed-tab": "Bula tab gape", + "browse-library": "Tsamayatsamaya le motlhobo", + "open-file": "Bula faele", + "open-recent": "Bula ya bosheng", + "search-article": "Batla setlhogo", + "search-in-library": "Batla mo motlobong", + "find-in-page": "Batla mo tsebeng", + "set-fullscreen": "Setshwantsho se se tletseng", + "quit-fullscreen": "Tlogela FullScreen", + "table-of-content": "Tafole ya ditêng", + "reading-list": "Lenaane la go bala", + "zoom-in": "Atolosetsa mo teng", + "zoom-out": "Atolosetsa kwa ntle", + "zoom-reset": "baya katoloso sesha", + "help": "Thuso", + "feedback": "Phetolo", + "report-a-bug": "Bega sekganedi", + "request-a-feature": "Kopa karolo", + "about-kiwix": "Ka ga Kiwix", + "donate-to-support-kiwix": "Ntsha moneelo go rotloetsa Kiwix", + "exit": "Tswa", + "save-file-as-window-title": "Boloka File jaaka", + "download-finished-message": "Tokumente e tserwe", + "file": "Faele", + "edit": "Fetola", + "view": "Bona", + "tools": "Dithulusu", + "window-title": "Motlobo - Kiwix", + "search": "Batla", + "browse-directory": "Tsamayatsamaya le patlisisi", + "settings": "Dipaakanyo", + "back": "Ko Morago", + "forward": "Ko pele", + "search-files": "Batla difaele", + "title": "Setlhogo", + "size": "Selekanyo", + "date": "Letsatsi", + "content-type": "Mofuta wa diteng", + "reset-sort": "go busetsa dilo kwa morago", + "open": "Bula", + "delete": "Phimola", + "download": "Baya", + "resume": "Tsweledisa", + "pause": "Kgama", + "cancel": "Sutlha", + "apply": "Dirisa", + "port-for-local-kiwix-server-setting": "Port ya server ya Kiwix ya selegae", + "zoom-level-setting": "Selekanyo sa zoom sa konokono", + "download-directory-setting": "Go gatisa direkoto", + "monitor-directory-setting": "Ela tlhoko rekoto", + "reset": "Tsosolosa", + "clear": "Sutlha", + "browse": "Tsamayatsamaya", + "about-kiwix-desktop-title": "Kiwix Desktop", + "about-kiwix-desktop-description": "Kiwix e go letlelela go nna le Wikipedia yotlhe gongwe le gongwe kwa o yang teng! Mo mokorong, mo gare ga lefelo le le se nang sepe, kgotsa mo kgolegelong, Kiwix e go naya kitso yotlhe ya setho. Ga o tlhoke Internet, sengwe le sengwe se bolokilwe mo khomputareng ya gago.", + "about-learn-more-about-kiwix": "Ithute go le gontsi ka Kiwix", + "about-release-title": "Golola", + "about-source-code": "Software ye e golotswe ka tlase ga ditsetlana tsa GNU General Public Liscence karolo ya boraro. Bona ko e tsang teng mo Github.", + "about-version": "Setlha: {{VERSION}}", + "about-reporting-problem-title": "Bothata bo a begwa", + "about-report-issue": "Go bula kgwetlho, etela Kiwix-desktop issue tracker o bo o bula kgwetlho e e tlhalosang bothata jwa gago.", + "about-report-issue-2": "Tsweetswee umaka setlha se mo kgwetlhong.", + "about-libraries-title": "Metlobo", + "kiwix-server-running-message": "Kiwix Server e a bereka mme e ka fitlhelelwa mo mafaratlhatlheng a selegae kwa:", + "kiwix-server-description": "Go simolola server go letlelela dikhomputara tse dingwe mo mafaratlhatlheng a selegae go tsena mo motlobong wa gago wa Kiwix ka browser ya web e e tlwaelegileng.", + "fullscreen-notification": "Jaanong o mo seemong se se tletseng sa sekirini. tobetsa ESC gore e tlogele!", + "online-files": "Difaele tsa Internet", + "local-files": "Difaele tsa mo gae", + "category": "Karolo", + "language": "Teme", + "hide": "Fitlha", + "open-in-browser": "Bula mo browser", + "start-kiwix-server": "Simolola Kiwix Server", + "stop-kiwix-server": "Emisa Kiwix Server", + "all": "Tsotlhe", + "fulltext-search": "Batlisisa ka mafoko a a feletseng", + "pictures": "Ditshwantsho", + "videos": "Dtshwantsho tsa motshikhinyego", + "ftindex": "Tlhatlhobo ya mafoko a a feletseng", + "details": "Padi e e feletseng", + "yes": "ee", + "no": "nnyaa", + "ok": "ee", + "no-filter": "Ga gona sefarologanyi", + "open-link-in-web-browser": "Bula kgolaganyi ye mo web browser", + "download-dir-dialog-title": "A o tlhomamisegile gore o batla go fetola lenaane la di-download?", + "download-dir-dialog-msg": "Tsetlana e ntšhs ya tsedi ya go itseela e tla nna:\n{{DIRECTORY}}", + "invalid-port": "Port ye e sa letlelesegeng", + "zim-open-fail-title": "Faele ye e sa letlelesegeng", + "zim-open-fail-text": "Faele ya IM ya {{ZIM}} ga e bulêgê sentlê. E tlaa ntshiwa mo motlôbông.", + "monitor-dir-dialog-title": "A o tlhomamisegile gore o batla go fetola lenaane la batlhatlhobi?", + "monitor-dir-dialog-msg": "Tselana e ntšha ya go itseela e tla nna:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "A o tlhomamisegile gore o batla go phepafatsa lenaane la batlhatlhobi?", + "monitor-clear-dir-dialog-msg": "Seno se tla emisa go tlhatlhoba direkoto tsa difaele tse disha tsa ZIM.", + "monitor-directory-tooltip": "Difaele tsotlhe tsa ZIM tse di mo lenaaneng leno di tla tsenngwa ka botsone mo motlôbông.", + "next-tab": "Fetela ko tab e e latêlang", + "previous-tab": "Ya ko tab e e fetilêng", + "cancel-download": "Khansele go itseela", + "cancel-download-text": "A o tlhomamisitswe gore o batla go khansela downloaf ya {{ZIM}}?", + "delete-book": "Phimola buka", + "delete-book-text": "A o tlhomamisitse gore o batla go phimola {{ZIM}}?", + "download-storage-error": "Phoso ya mabolokelo", + "download-storage-error-text": "Tsamaiso ga e na mabolokelo a a lekaneng.", + "download-unavailable": "Downloaf ga eyo", + "download-unavailable-text": "Download e ga eyo", + "open-book": "Bula buka", + "download-book": "Download buka", + "pause-download": "Kgama go download", + "resume-download": "Tsweledisa go download", + "open-folder": "Bula bobeelo", + "couldnt-open-location": "O ne o ka se kgone go bula lefelo", + "couldnt-open-location-text": "Kiwix ga e kgone go bula bobeelo {{FOLDER}}", + "move-files-to-trash": "Fusolosetsa difaele tse di phimotsweng mo matlakaleng", + "move-files-to-trash-text": "Kgato eno e tla dira gore faele e nne matlakala.", + "perma-delete-files-text": "Kgato eno e tla phimola faele go ya go ile.", + "clear-filter": "Phimola di-filter tse di beilweng gone jaanong", + "language-searcher-placeholder": "Farologanya teme", + "category-searcher-placeholder": "Farologanya dikarolo", + "content-type-searcher-placeholder": "Farologanya mofuta wa tshêdimosôtsô", + "no-details": "Matseno fela", + "no-pictures": "Ga gona Ditshwantsho", + "no-videos": "Ga gona ditshwantsho tsa motshikhinyego" +} diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 1a84abbf1..5c363e1e1 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -1,154 +1,154 @@ -{ - "@metadata": { - "authors": [ - "1917 Ekim Devrimi", - "BaRaN6161 TURK", - "Can", - "DDPAT", - "Hedda", - "Ice bulldog", - "Joseph", - "Rofiatmustapha12", - "SaldırganSincap" - ] - }, - "name": "Türkçe", - "error-title": "Hata", - "error-downloader-window-title": "İndirici oluşturulamıyor", - "error-downloader-launch-message": "İndiriciyi başlatmak imkansız, Kiwix-masaüstü başlayacak, ancak tüm indirme işlevleri çalışmaz!", - "error-launch-server-message": "Bir hata oluştu!", - "error-archive": "Arşiv alınamıyor", - "open-zim": "ZIM Dosyasını Aç", - "local-kiwix-server": "Yerel Kiwix Sunucusu", - "random-article": "Rastgele Madde", - "random-article-error": "Rastgele madde açılamadı.", - "home-page": "Anasayfa", - "main-menu": "Ana Menü", - "print": "Yazdır", - "print-page-error": "Yazım sırasında bir hata meydana geldi.", - "new-tab": "Yeni sekme", - "close-tab": "Sekmeyi kapat", - "close": "Kapat", - "reopen-closed-tab": "Kapatılan sekmeyi yeniden aç", - "browse-library": "Kütüphaneye göz at", - "open-file": "Dosyayı aç", - "open-recent": "Son aç", - "search-article": "Madde ara", - "search-in-library": "Kütüphanede ara", - "find-in-page": "Sayfada bul", - "set-fullscreen": "Tam ekranı ayarla", - "quit-fullscreen": "Tam Ekrandan Çık", - "table-of-content": "İçerik tablosu", - "reading-list": "Okuma listesi", - "zoom-in": "Yakınlaştır", - "zoom-out": "Uzaklaştır", - "zoom-reset": "Zoom sıfırla", - "help": "Yardım", - "feedback": "Geri bildirim", - "report-a-bug": "Hata bildir", - "request-a-feature": "Bir özellik isteyin", - "about-kiwix": "Kiwix hakkında", - "donate-to-support-kiwix": "Kiwix'i desteklemek için bağış yapın", - "exit": "Çıkış", - "save-file-as-window-title": "Farklı Kaydet", - "download-finished-message": "Belge indirildi.", - "file": "Dosya", - "edit": "Düzenle", - "view": "Görüntüle", - "tools": "Araçlar", - "window-title": "Kütüphane - Kiwix", - "search": "Ara", - "browse-directory": "Dizine Göz At", - "settings": "Ayarlar", - "back": "Geri", - "forward": "Yönlendir", - "search-files": "Dosya ara", - "title": "Başlık", - "size": "Boyut", - "date": "Tarih", - "content-type": "İçerik türü", - "reset-sort": "sıralamayı sıfırla", - "open": "Aç", - "delete": "Sil", - "download": "İndir", - "resume": "Devam et", - "pause": "Duraklat", - "cancel": "İptal", - "apply": "Uygula", - "port-for-local-kiwix-server-setting": "Yerel Kiwix sunucusu için bağlantı noktası", - "zoom-level-setting": "Varsayılan yakınlaştırma düzeyi", - "download-directory-setting": "İndirme dizini", - "monitor-directory-setting": "Dizini izle", - "reset": "Sıfırla", - "clear": "Temizle", - "browse": "Göz at", - "about-kiwix-desktop-title": "Kiwix Masaüstü", - "about-kiwix-desktop-description": "Kiwix, gittiğiniz her yerde Wikipedia'nın tamamının elinizin altında olmasını sağlar! Bir teknede, hiçliğin ortasında veya hapishanede, Kiwix size insanlığın tüm bilgisine erişme olanağı sağlar. İnternete ihtiyacınız yok, her şey bilgisayarınızda saklanıyor.", - "about-learn-more-about-kiwix": "Kiwix hakkında daha fazla bilgi edinin", - "about-release-title": "Sürüm", - "about-source-code": "Bu yazılım GNU Genel Kamu Lisansı sürüm 3 koşulları altında yayımlanmıştır. Kaynak kodunu Github'da görün.", - "about-version": "Sürüm: {{VERSION}}", - "about-reporting-problem-title": "Sorun bildirme", - "about-report-issue": "Bir sorun açmak için Kiwix masaüstü sorun izleyiciyiyi ziyaret edin ve sorununuzu açıklayan bir sorun açın.", - "about-report-issue-2": "Lütfen sorundaki sürümü belirtin.", - "about-libraries-title": "Kütüphaneler", - "kiwix-server-running-message": "Kiwix Sunucusu çalışıyor ve yerel ağdan şu adresten erişilebilir:", - "kiwix-server-description": "Sunucu başlatmak, yerel ağdaki diğer bilgisayarların standart bir web tarayıcısıyla Kiwix kitaplığınıza erişmesine izin verir.", - "fullscreen-notification": "Şimdi tam ekran modundasınız. Çıkmak için ESC tuşuna basın!", - "online-files": "Çevrimiçi Dosyalar", - "local-files": "Yerel Dosyalar", - "category": "Kategori", - "language": "Dil", - "hide": "Gizle", - "open-in-browser": "Tarayıcıda aç", - "start-kiwix-server": "Kiwix Sunucusunu Başlat", - "stop-kiwix-server": "Kiwix Sunucusunu Durdur", - "all": "Hepsi", - "fulltext-search": "Tam metin arama", - "pictures": "Resimler", - "videos": "Videolar", - "ftindex": "Tam metin dizini", - "details": "Tam madde", - "yes": "evet", - "no": "hayır", - "ok": "tamam", - "no-filter": "filtre yok", - "open-link-in-web-browser": "Bağlantıyı web tarayıcısında aç", - "download-dir-dialog-title": "İndirme dizinini değiştirmek istediğinizden emin misiniz?", - "download-dir-dialog-msg": "Yeni indirme dizini yolu şunu olacaktır:\n {{DIRECTORY}}", - "invalid-port": "Geçersiz bağlantı noktası", - "zim-open-fail-title": "Geçersiz dosya", - "zim-open-fail-text": "ZIM dosyası {{ZIM}} düzgün açılamadı. Kütüphanenizden kaldırılacaktır.", - "monitor-dir-dialog-title": "Monitör dizinini değiştirmek istediğinizden emin misiniz?", - "monitor-dir-dialog-msg": "Yeni monitör dizini yolu şöyle olacaktır:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Monitör dizinini temizlemek istediğinizden emin misiniz?", - "monitor-clear-dir-dialog-msg": "Bu, yeni ZIM dosyaları için monitör dizinini kontrol etmeyi durduracaktır.", - "monitor-directory-tooltip": "Bu dizindeki tüm ZIM dosyaları otomatik olarak kitaplığa eklenecektir.", - "next-tab": "Sonraki sekmeye git", - "previous-tab": "Önceki sekmeye git", - "cancel-download": "İndirmeyi iptal et", - "cancel-download-text": "{{ZIM}} indirmesini iptal etmek istediğinizden emin misiniz?", - "delete-book": "Kitabı sil", - "delete-book-text": "{{ZIM}}? silmek istediğinizden emin misiniz?", - "download-storage-error": "Depolama Hatası", - "download-storage-error-text": "Sistemde yeterli depolama alanı yok.", - "download-unavailable": "İndirilemiyor", - "download-unavailable-text": "Bu anket mevcut değil", - "open-book": "Sahte Aç", - "download-book": "Kitapları indir", - "pause-download": "İndirmeyi duraklat", - "resume-download": "İndirmeye devam et", - "open-folder": "Açık dosya", - "couldnt-open-location": "Konum açılamadı", - "couldnt-open-location-text": "Kiwix, {{FOLDER}} klasörünü açamıyor", - "move-files-to-trash": "Silinen dosyaları çöp kutusuna taşıyın", - "move-files-to-trash-text": "Bu işlem dosyayı çöp kutusuna taşıyacaktır.", - "perma-delete-files-text": "Bu işlem dosyayı kalıcı olarak silecektir.", - "clear-filter": "Halihazırda ayarlanmış olan filtreleri temizle", - "language-searcher-placeholder": "Dile göre filtrele", - "category-searcher-placeholder": "Kategorilerine göre filtrele", - "content-type-searcher-placeholder": "İçerik türünü filtrele", - "no-details": "Yalnızca giriş", - "no-pictures": "Resim Yok", - "no-videos": "Video Yok" -} +{ + "@metadata": { + "authors": [ + "1917 Ekim Devrimi", + "BaRaN6161 TURK", + "Can", + "DDPAT", + "Hedda", + "Ice bulldog", + "Joseph", + "Rofiatmustapha12", + "SaldırganSincap" + ] + }, + "name": "Türkçe", + "error-title": "Hata", + "error-downloader-window-title": "İndirici oluşturulamıyor", + "error-downloader-launch-message": "İndiriciyi başlatmak imkansız, Kiwix-masaüstü başlayacak, ancak tüm indirme işlevleri çalışmaz!", + "error-launch-server-message": "Bir hata oluştu!", + "error-archive": "Arşiv alınamıyor", + "open-zim": "ZIM Dosyasını Aç", + "local-kiwix-server": "Yerel Kiwix Sunucusu", + "random-article": "Rastgele Madde", + "random-article-error": "Rastgele madde açılamadı.", + "home-page": "Anasayfa", + "main-menu": "Ana Menü", + "print": "Yazdır", + "print-page-error": "Yazım sırasında bir hata meydana geldi.", + "new-tab": "Yeni sekme", + "close-tab": "Sekmeyi kapat", + "close": "Kapat", + "reopen-closed-tab": "Kapatılan sekmeyi yeniden aç", + "browse-library": "Kütüphaneye göz at", + "open-file": "Dosyayı aç", + "open-recent": "Son aç", + "search-article": "Madde ara", + "search-in-library": "Kütüphanede ara", + "find-in-page": "Sayfada bul", + "set-fullscreen": "Tam ekranı ayarla", + "quit-fullscreen": "Tam Ekrandan Çık", + "table-of-content": "İçerik tablosu", + "reading-list": "Okuma listesi", + "zoom-in": "Yakınlaştır", + "zoom-out": "Uzaklaştır", + "zoom-reset": "Zoom sıfırla", + "help": "Yardım", + "feedback": "Geri bildirim", + "report-a-bug": "Hata bildir", + "request-a-feature": "Bir özellik isteyin", + "about-kiwix": "Kiwix hakkında", + "donate-to-support-kiwix": "Kiwix'i desteklemek için bağış yapın", + "exit": "Çıkış", + "save-file-as-window-title": "Farklı Kaydet", + "download-finished-message": "Belge indirildi.", + "file": "Dosya", + "edit": "Düzenle", + "view": "Görüntüle", + "tools": "Araçlar", + "window-title": "Kütüphane - Kiwix", + "search": "Ara", + "browse-directory": "Dizine Göz At", + "settings": "Ayarlar", + "back": "Geri", + "forward": "Yönlendir", + "search-files": "Dosya ara", + "title": "Başlık", + "size": "Boyut", + "date": "Tarih", + "content-type": "İçerik türü", + "reset-sort": "sıralamayı sıfırla", + "open": "Aç", + "delete": "Sil", + "download": "İndir", + "resume": "Devam et", + "pause": "Duraklat", + "cancel": "İptal", + "apply": "Uygula", + "port-for-local-kiwix-server-setting": "Yerel Kiwix sunucusu için bağlantı noktası", + "zoom-level-setting": "Varsayılan yakınlaştırma düzeyi", + "download-directory-setting": "İndirme dizini", + "monitor-directory-setting": "Dizini izle", + "reset": "Sıfırla", + "clear": "Temizle", + "browse": "Göz at", + "about-kiwix-desktop-title": "Kiwix Masaüstü", + "about-kiwix-desktop-description": "Kiwix, gittiğiniz her yerde Wikipedia'nın tamamının elinizin altında olmasını sağlar! Bir teknede, hiçliğin ortasında veya hapishanede, Kiwix size insanlığın tüm bilgisine erişme olanağı sağlar. İnternete ihtiyacınız yok, her şey bilgisayarınızda saklanıyor.", + "about-learn-more-about-kiwix": "Kiwix hakkında daha fazla bilgi edinin", + "about-release-title": "Sürüm", + "about-source-code": "Bu yazılım GNU Genel Kamu Lisansı sürüm 3 koşulları altında yayımlanmıştır. Kaynak kodunu Github'da görün.", + "about-version": "Sürüm: {{VERSION}}", + "about-reporting-problem-title": "Sorun bildirme", + "about-report-issue": "Bir sorun açmak için Kiwix masaüstü sorun izleyiciyiyi ziyaret edin ve sorununuzu açıklayan bir sorun açın.", + "about-report-issue-2": "Lütfen sorundaki sürümü belirtin.", + "about-libraries-title": "Kütüphaneler", + "kiwix-server-running-message": "Kiwix Sunucusu çalışıyor ve yerel ağdan şu adresten erişilebilir:", + "kiwix-server-description": "Sunucu başlatmak, yerel ağdaki diğer bilgisayarların standart bir web tarayıcısıyla Kiwix kitaplığınıza erişmesine izin verir.", + "fullscreen-notification": "Şimdi tam ekran modundasınız. Çıkmak için ESC tuşuna basın!", + "online-files": "Çevrimiçi Dosyalar", + "local-files": "Yerel Dosyalar", + "category": "Kategori", + "language": "Dil", + "hide": "Gizle", + "open-in-browser": "Tarayıcıda aç", + "start-kiwix-server": "Kiwix Sunucusunu Başlat", + "stop-kiwix-server": "Kiwix Sunucusunu Durdur", + "all": "Hepsi", + "fulltext-search": "Tam metin arama", + "pictures": "Resimler", + "videos": "Videolar", + "ftindex": "Tam metin dizini", + "details": "Tam madde", + "yes": "evet", + "no": "hayır", + "ok": "tamam", + "no-filter": "filtre yok", + "open-link-in-web-browser": "Bağlantıyı web tarayıcısında aç", + "download-dir-dialog-title": "İndirme dizinini değiştirmek istediğinizden emin misiniz?", + "download-dir-dialog-msg": "Yeni indirme dizini yolu şunu olacaktır:\n {{DIRECTORY}}", + "invalid-port": "Geçersiz bağlantı noktası", + "zim-open-fail-title": "Geçersiz dosya", + "zim-open-fail-text": "ZIM dosyası {{ZIM}} düzgün açılamadı. Kütüphanenizden kaldırılacaktır.", + "monitor-dir-dialog-title": "Monitör dizinini değiştirmek istediğinizden emin misiniz?", + "monitor-dir-dialog-msg": "Yeni monitör dizini yolu şöyle olacaktır:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Monitör dizinini temizlemek istediğinizden emin misiniz?", + "monitor-clear-dir-dialog-msg": "Bu, yeni ZIM dosyaları için monitör dizinini kontrol etmeyi durduracaktır.", + "monitor-directory-tooltip": "Bu dizindeki tüm ZIM dosyaları otomatik olarak kitaplığa eklenecektir.", + "next-tab": "Sonraki sekmeye git", + "previous-tab": "Önceki sekmeye git", + "cancel-download": "İndirmeyi iptal et", + "cancel-download-text": "{{ZIM}} indirmesini iptal etmek istediğinizden emin misiniz?", + "delete-book": "Kitabı sil", + "delete-book-text": "{{ZIM}}? silmek istediğinizden emin misiniz?", + "download-storage-error": "Depolama Hatası", + "download-storage-error-text": "Sistemde yeterli depolama alanı yok.", + "download-unavailable": "İndirilemiyor", + "download-unavailable-text": "Bu anket mevcut değil", + "open-book": "Sahte Aç", + "download-book": "Kitapları indir", + "pause-download": "İndirmeyi duraklat", + "resume-download": "İndirmeye devam et", + "open-folder": "Açık dosya", + "couldnt-open-location": "Konum açılamadı", + "couldnt-open-location-text": "Kiwix, {{FOLDER}} klasörünü açamıyor", + "move-files-to-trash": "Silinen dosyaları çöp kutusuna taşıyın", + "move-files-to-trash-text": "Bu işlem dosyayı çöp kutusuna taşıyacaktır.", + "perma-delete-files-text": "Bu işlem dosyayı kalıcı olarak silecektir.", + "clear-filter": "Halihazırda ayarlanmış olan filtreleri temizle", + "language-searcher-placeholder": "Dile göre filtrele", + "category-searcher-placeholder": "Kategorilerine göre filtrele", + "content-type-searcher-placeholder": "İçerik türünü filtrele", + "no-details": "Yalnızca giriş", + "no-pictures": "Resim Yok", + "no-videos": "Video Yok" +} diff --git a/resources/i18n/uk.json b/resources/i18n/uk.json index 2d82f1720..e67fa0ed3 100644 --- a/resources/i18n/uk.json +++ b/resources/i18n/uk.json @@ -1,132 +1,132 @@ -{ - "@metadata": { - "authors": [ - "Asokolov", - "DDPAT", - "Daries1", - "Ice bulldog", - "Ignatgg", - "McDutchie", - "Renamerr", - "Renvoy", - "Ата", - "Пан Хаунд" - ] - }, - "name": "Українська", - "error-title": "Помилка", - "error-downloader-window-title": "Неможливо створити завантажника", - "error-downloader-launch-message": "Неможливо запустити завантажувач. Kiwik-десктоп буде все-одно запущено, але всі завантажувальні функції не будуть працювати!", - "error-launch-server-message": "Сталася помилка!", - "error-archive": "Не можу отримати архів", - "open-zim": "Відкрийте файл ZIM", - "local-kiwix-server": "Локальний сервер Kiwik", - "random-article": "Випадкова стаття", - "random-article-error": "Не вдалося відкрити випадкову статтю.", - "home-page": "Домашня сторінка", - "main-menu": "Головне меню", - "print": "Друк", - "print-page-error": "Під час друку сталася помилка.", - "new-tab": "Нова вкладка", - "close-tab": "Закрити вкладку", - "close": "Закрити", - "reopen-closed-tab": "Повторно відкрити закриту вкладку", - "browse-library": "Перегляд бібліотеки", - "open-file": "Відкрити файл", - "open-recent": "Відкрити нещодавні", - "search-article": "Знайти статтю", - "search-in-library": "Пошук у бібліотеці", - "find-in-page": "Знайти на сторінці", - "set-fullscreen": "Перейти до повноекранного режиму", - "quit-fullscreen": "Вийти з повноекранного режиму", - "table-of-content": "Зміст", - "add-bookmark": "Додати до списку для читання", - "remove-bookmark": "Вилучити з списку читання", - "reading-list": "Читальний список", - "zoom-in": "Збільшити", - "zoom-out": "Зменшити", - "zoom-reset": "Скинути масштаб", - "help": "Допомога", - "feedback": "Зворотний зв'язок", - "report-a-bug": "Повідомити про помилку", - "request-a-feature": "Запропонувати нову функцію", - "about-kiwix": "Про Kiwix", - "donate-to-support-kiwix": "Допомогти Kiwix пожертвою", - "exit": "Вихід", - "save-file-as-window-title": "Зберегти файл як", - "download-finished-message": "Документ був завантажлений.", - "file": "Файл", - "edit": "Редагувати", - "view": "Переглянути", - "tools": "Інструменти", - "window-title": "Бібліотека — Kiwix", - "search": "Пошук", - "browse-directory": "Переглянути каталог", - "settings": "Налаштування", - "back": "Назад", - "forward": "Вперед", - "search-files": "Пошук файлів", - "title": "Назва", - "size": "Розмір", - "date": "Дата", - "content-type": "Тип вмісту", - "reset-sort": "скинути сортування", - "open": "Відкрити", - "delete": "Видалити", - "download": "Завантажити", - "resume": "Продовжити", - "pause": "Призупинити", - "cancel": "Скасувати", - "apply": "Застосувати", - "port-for-local-kiwix-server-setting": "Порт для локального серверу Kiwik", - "zoom-level-setting": "Рівень масштабування за замовчуванням", - "download-directory-setting": "Каталог для завантажень", - "monitor-directory-setting": "Каталог монітора", - "reset": "Скинути", - "clear": "Очистити", - "browse": "Переглянути", - "about-kiwix-desktop-title": "Kiwik-десктоп", - "about-kiwix-desktop-description": "З Kiwix уся Вікіпедія буде у вас під рукою, куди б ви не запроторилися! Будь-де, на човні чи хоч у в'язниці, з Kiwix вам будуть доступні всі знання людства. Для доступу вам не потрібний Інтернет, оскільки все буде збережене на вашому комп'ютері.", - "about-learn-more-about-kiwix": "Дізнайтеся більше про Kiwik", - "about-release-title": "Випуск", - "about-source-code": "Це програмне забезпечення випущене згідно умов GNU General Public License версії 3. Вихідний код доступний за адресою Github.", - "about-version": "Версія: {{VERSION}}", - "about-reporting-problem-title": "Повідомити про проблему", - "about-report-issue": "Аби повідомити про проблему, відвідайте відслідковувач проблем із Kiwik-десктоп та додайте опис вашої проблеми.", - "about-report-issue-2": "Будь ласка, повідомте номер версії у описі проблеми.", - "about-libraries-title": "Бібліотеки", - "kiwix-server-running-message": "Сервер Kiwix запущено і він може бути доступний у локальній мережі на:", - "kiwix-server-description": "Запуск серверу дозволяє іншим комп'ютерам у локальні мережі мати доступ до вашої Kiwik-бібліотеки за допомогою звичайного браузера.", - "fullscreen-notification": "Ви зараз у повноекранному режимі. Натисніть ESC для виходу!", - "online-files": "Онлайн файли", - "local-files": "Локальні файли", - "category": "Категорія", - "language": "Мова", - "hide": "Приховати", - "open-in-browser": "Відкрити у браузері", - "start-kiwix-server": "Запустити сервер Kiwik", - "stop-kiwix-server": "Зупинити сервер Kiwix", - "all": "Усе", - "fulltext-search": "Повнотекстовий пошук", - "pictures": "Зображення", - "videos": "Відео", - "ftindex": "Повнотекстовий пошук", - "details": "Повна стаття", - "yes": "так", - "no": "ні", - "no-filter": "Без фільтру", - "open-link-in-web-browser": "Відкрити посилання у навігаторі", - "open-link-new-tab": "Відкрити посилання в новій вкладці", - "download-dir-dialog-title": "Ви впевнені, що хочете змінити каталог для завантажень?", - "download-dir-dialog-msg": "Новий каталог для завантажень тепер:\n{{DIRECTORY}}", - "invalid-port": "Недійсний порт", - "zim-open-fail-title": "Недійсний файл", - "zim-open-fail-text": "Файл ZIM {{ZIM}} неможливо відкрити належним чином. Його буде видалено з вашої бібліотеки.", - "monitor-dir-dialog-title": "Ви впевнені, що хочете змінити каталог монітора?", - "monitor-dir-dialog-msg": "Новий шлях до каталогу монітора буде таким:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Ви впевнені, що хочете очистити каталог монітора?", - "monitor-clear-dir-dialog-msg": "Це припинить перевірку каталогу монітора на наявність нових файлів ZIM.", - "monitor-directory-tooltip": "Усі ZIM-файли в цьому каталозі будуть автоматично додані до бібліотеки.", - "cancel-download": "Скасувати завантаження", - "cancel-download-text": "Ви впевнені, що бажаєте скасувати завантаження {{ZIM}}?" -} +{ + "@metadata": { + "authors": [ + "Asokolov", + "DDPAT", + "Daries1", + "Ice bulldog", + "Ignatgg", + "McDutchie", + "Renamerr", + "Renvoy", + "Ата", + "Пан Хаунд" + ] + }, + "name": "Українська", + "error-title": "Помилка", + "error-downloader-window-title": "Неможливо створити завантажника", + "error-downloader-launch-message": "Неможливо запустити завантажувач. Kiwik-десктоп буде все-одно запущено, але всі завантажувальні функції не будуть працювати!", + "error-launch-server-message": "Сталася помилка!", + "error-archive": "Не можу отримати архів", + "open-zim": "Відкрийте файл ZIM", + "local-kiwix-server": "Локальний сервер Kiwik", + "random-article": "Випадкова стаття", + "random-article-error": "Не вдалося відкрити випадкову статтю.", + "home-page": "Домашня сторінка", + "main-menu": "Головне меню", + "print": "Друк", + "print-page-error": "Під час друку сталася помилка.", + "new-tab": "Нова вкладка", + "close-tab": "Закрити вкладку", + "close": "Закрити", + "reopen-closed-tab": "Повторно відкрити закриту вкладку", + "browse-library": "Перегляд бібліотеки", + "open-file": "Відкрити файл", + "open-recent": "Відкрити нещодавні", + "search-article": "Знайти статтю", + "search-in-library": "Пошук у бібліотеці", + "find-in-page": "Знайти на сторінці", + "set-fullscreen": "Перейти до повноекранного режиму", + "quit-fullscreen": "Вийти з повноекранного режиму", + "table-of-content": "Зміст", + "add-bookmark": "Додати до списку для читання", + "remove-bookmark": "Вилучити з списку читання", + "reading-list": "Читальний список", + "zoom-in": "Збільшити", + "zoom-out": "Зменшити", + "zoom-reset": "Скинути масштаб", + "help": "Допомога", + "feedback": "Зворотний зв'язок", + "report-a-bug": "Повідомити про помилку", + "request-a-feature": "Запропонувати нову функцію", + "about-kiwix": "Про Kiwix", + "donate-to-support-kiwix": "Допомогти Kiwix пожертвою", + "exit": "Вихід", + "save-file-as-window-title": "Зберегти файл як", + "download-finished-message": "Документ був завантажлений.", + "file": "Файл", + "edit": "Редагувати", + "view": "Переглянути", + "tools": "Інструменти", + "window-title": "Бібліотека — Kiwix", + "search": "Пошук", + "browse-directory": "Переглянути каталог", + "settings": "Налаштування", + "back": "Назад", + "forward": "Вперед", + "search-files": "Пошук файлів", + "title": "Назва", + "size": "Розмір", + "date": "Дата", + "content-type": "Тип вмісту", + "reset-sort": "скинути сортування", + "open": "Відкрити", + "delete": "Видалити", + "download": "Завантажити", + "resume": "Продовжити", + "pause": "Призупинити", + "cancel": "Скасувати", + "apply": "Застосувати", + "port-for-local-kiwix-server-setting": "Порт для локального серверу Kiwik", + "zoom-level-setting": "Рівень масштабування за замовчуванням", + "download-directory-setting": "Каталог для завантажень", + "monitor-directory-setting": "Каталог монітора", + "reset": "Скинути", + "clear": "Очистити", + "browse": "Переглянути", + "about-kiwix-desktop-title": "Kiwik-десктоп", + "about-kiwix-desktop-description": "З Kiwix уся Вікіпедія буде у вас під рукою, куди б ви не запроторилися! Будь-де, на човні чи хоч у в'язниці, з Kiwix вам будуть доступні всі знання людства. Для доступу вам не потрібний Інтернет, оскільки все буде збережене на вашому комп'ютері.", + "about-learn-more-about-kiwix": "Дізнайтеся більше про Kiwik", + "about-release-title": "Випуск", + "about-source-code": "Це програмне забезпечення випущене згідно умов GNU General Public License версії 3. Вихідний код доступний за адресою Github.", + "about-version": "Версія: {{VERSION}}", + "about-reporting-problem-title": "Повідомити про проблему", + "about-report-issue": "Аби повідомити про проблему, відвідайте відслідковувач проблем із Kiwik-десктоп та додайте опис вашої проблеми.", + "about-report-issue-2": "Будь ласка, повідомте номер версії у описі проблеми.", + "about-libraries-title": "Бібліотеки", + "kiwix-server-running-message": "Сервер Kiwix запущено і він може бути доступний у локальній мережі на:", + "kiwix-server-description": "Запуск серверу дозволяє іншим комп'ютерам у локальні мережі мати доступ до вашої Kiwik-бібліотеки за допомогою звичайного браузера.", + "fullscreen-notification": "Ви зараз у повноекранному режимі. Натисніть ESC для виходу!", + "online-files": "Онлайн файли", + "local-files": "Локальні файли", + "category": "Категорія", + "language": "Мова", + "hide": "Приховати", + "open-in-browser": "Відкрити у браузері", + "start-kiwix-server": "Запустити сервер Kiwik", + "stop-kiwix-server": "Зупинити сервер Kiwix", + "all": "Усе", + "fulltext-search": "Повнотекстовий пошук", + "pictures": "Зображення", + "videos": "Відео", + "ftindex": "Повнотекстовий пошук", + "details": "Повна стаття", + "yes": "так", + "no": "ні", + "no-filter": "Без фільтру", + "open-link-in-web-browser": "Відкрити посилання у навігаторі", + "open-link-new-tab": "Відкрити посилання в новій вкладці", + "download-dir-dialog-title": "Ви впевнені, що хочете змінити каталог для завантажень?", + "download-dir-dialog-msg": "Новий каталог для завантажень тепер:\n{{DIRECTORY}}", + "invalid-port": "Недійсний порт", + "zim-open-fail-title": "Недійсний файл", + "zim-open-fail-text": "Файл ZIM {{ZIM}} неможливо відкрити належним чином. Його буде видалено з вашої бібліотеки.", + "monitor-dir-dialog-title": "Ви впевнені, що хочете змінити каталог монітора?", + "monitor-dir-dialog-msg": "Новий шлях до каталогу монітора буде таким:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Ви впевнені, що хочете очистити каталог монітора?", + "monitor-clear-dir-dialog-msg": "Це припинить перевірку каталогу монітора на наявність нових файлів ZIM.", + "monitor-directory-tooltip": "Усі ZIM-файли в цьому каталозі будуть автоматично додані до бібліотеки.", + "cancel-download": "Скасувати завантаження", + "cancel-download-text": "Ви впевнені, що бажаєте скасувати завантаження {{ZIM}}?" +} diff --git a/resources/i18n/yo.json b/resources/i18n/yo.json index 51790c096..afa053877 100644 --- a/resources/i18n/yo.json +++ b/resources/i18n/yo.json @@ -1,136 +1,136 @@ -{ - "@metadata": { - "authors": [ - "Rofiatmustapha12", - "Sheriff" - ] - }, - "name": "English", - "error-title": "Àsìṣe", - "error-downloader-window-title": "Ko le ṣẹda olugbasilẹ", - "error-downloader-launch-message": "Ko ṣee ṣe lati ṣe ifilọlẹ igbasilẹ, Kiwix-desktop yoo bẹrẹ ṣugbọn gbogbo awọn iṣẹ igbasilẹ kii yoo ṣiṣẹ!", - "error-launch-server-message": "Aṣiṣe kan ti ṣẹlẹ!", - "error-archive": "Ko le gba ile-ipamọ naa", - "open-zim": "Ṣii faili ZIM", - "local-kiwix-server": "Agbegbe Kiwix Server", - "random-article": "Àyọkà àrìnnàkò", - "random-article-error": "Kuna lati ṣii nkan laileto.", - "home-page": "Oju-iwe Ile", - "main-menu": "Àkọlé àwòrán", - "print": "Titẹ sita", - "print-page-error": "Aṣiṣe ti waye lakoko titẹ.", - "new-tab": "Titun taabu", - "close-tab": "Pa taabu", - "close": "Padé", - "reopen-closed-tab": "Tun taabu pipade", - "browse-library": "Ṣawakiri ile-ikawe", - "open-file": "Ṣii faili", - "open-recent": "Ṣii laipe", - "search-article": "Wa nkan", - "search-in-library": "Wa ninu ile-ikawe", - "find-in-page": "Wárí nínú ojúewé", - "set-fullscreen": "Ṣeto iboju kikun", - "quit-fullscreen": "Jade ni kikun Iboju", - "table-of-content": "Àtẹ̀sílẹ̀ àwọn ohun tó wà nínú rẹ̀", - "reading-list": "Akojọ kika", - "zoom-in": "Sun-un sinu", - "zoom-out": "Sun-un jade", - "zoom-reset": "Atunto sun-un", - "help": "Ìrànlọ́wọ́", - "feedback": "Esi", - "report-a-bug": "ṣe àfisùn àsìṣe", - "request-a-feature": "Beere ẹya kan", - "about-kiwix": "Nipa Kiwix", - "donate-to-support-kiwix": "Ṣetọrẹ lati ṣe atilẹyin Kiwix", - "exit": "Jade", - "save-file-as-window-title": "Fi faili pamọ bi", - "download-finished-message": "Iwe aṣẹ ti gba lati ayelujara.", - "file": "Àkọsílẹ̀", - "edit": "Satunko", - "view": "Yẹ̀ ẹ́ wò", - "tools": "Àwọn irinṣẹ́", - "window-title": "Library - Kiwix", - "search": "Wa", - "browse-directory": "Ṣawakiri Itọsọna", - "settings": "ètò", - "back": "Pada", - "forward": "Tesiwaju", - "search-files": "Iwari iwe", - "title": "Akole", - "size": "Iwon", - "date": "Ojo", - "content-type": "Orisirisi nkan inu", - "reset-sort": "Iyipada tito", - "open": "Sii le", - "delete": "Paare", - "download": "Gba lati ayelujara", - "resume": "Atun bere", - "pause": "Dawoduro", - "cancel": "Fa igi le", - "apply": "Lilo", - "port-for-local-kiwix-server-setting": "Ibudo fun kiwix tibile", - "zoom-level-setting": "Ifeloju to baa wa", - "download-directory-setting": "Ona ati gba lati ayelujara", - "monitor-directory-setting": "Sakiesi ona", - "reset": "Atunto", - "clear": "Pare", - "browse": "Sawa kiri", - "about-kiwix-desktop-title": "Kiwix Ojú-iṣẹ", - "about-kiwix-desktop-description": "Kiwix gba ọ laaye lati ni gbogbo Wikipedia ni ọwọ nibikibi ti o lọ! Lori ọkọ oju omi, ni aarin ti ko si, tabi ni ẹwọn, Kiwix fun ọ ni iwọle si gbogbo imoye eniyan. O ko nilo Intanẹẹti, ohun gbogbo wa ni ipamọ lori kọnputa rẹ.", - "about-learn-more-about-kiwix": "Ikekosi nipa kiwix", - "about-release-title": "Itu sile", - "about-source-code": "Sọfitiwia yii jẹ idasilẹ labẹ awọn ofin ti ẹya GNU Gbogbogbo ẹya Iwe-aṣẹ Awujọ. Wo koodu orisun lori Github .", - "about-version": "Ẹya: {{VERSION}}", - "about-reporting-problem-title": "Ifejo sun abuku", - "about-report-issue": "Lati ṣii ọrọ kan, ṣabẹwo si Kiwix-desktop olutọpa ọrọ ati ṣii ọrọ kan ti n ṣalaye iṣoro rẹ.", - "about-report-issue-2": "Jọwọ darukọ awọn ti ikede ninu atejade.", - "about-libraries-title": "Ile kawe", - "kiwix-server-running-message": "Olupin Kiwix naa nṣiṣẹ ati pe o le wọle si ni nẹtiwọọki agbegbe ni:", - "kiwix-server-description": "Bibẹrẹ olupin ngbanilaaye awọn kọnputa miiran ni nẹtiwọọki agbegbe lati wọle si ile-ikawe Kiwix rẹ pẹlu aṣawakiri wẹẹbu boṣewa kan.", - "fullscreen-notification": "Oti Wani oju ero kunkun. Te ESC lati dawoduro", - "online-files": "Iwe eto ayelujara", - "local-files": "Iwe eto tibile", - "category": "Eka", - "language": "Ede", - "hide": "Fipamo", - "open-in-browser": "Si ni awakiri", - "start-kiwix-server": "Ṣiṣẹ Kiwix Server", - "stop-kiwix-server": "Duro Kiwix Server", - "all": "Gbogbo re", - "fulltext-search": "Wiwa kikokunkun", - "pictures": "Awon Aworan", - "videos": "Wiwo", - "ftindex": "Atọka kikun ọrọ", - "details": "Apileko kunkun", - "yes": "Beni", - "no": "Beeko", - "ok": "Otida", - "no-filter": "ko si àlẹmọ", - "open-link-in-web-browser": "Si ona ni ero awakiri", - "download-dir-dialog-title": "So da o loju pe ofe yi ona ati gba lati ayelujara pada", - "download-dir-dialog-msg": "Ona ati gba lati ayelujara tuntun ma je {{DIRECTORY}}", - "invalid-port": "Ibudo tio sise", - "zim-open-fail-title": "Iwe kole tio wulo", - "zim-open-fail-text": "Faili ZIM {{ZIM}} ko le ṣii daradara. Yoo yọkuro kuro ni ile-ikawe rẹ.", - "monitor-dir-dialog-title": "Ṣe o da ọ loju pe o fẹ yi ilana igbasilẹ naa pada?", - "monitor-dir-dialog-msg": "Ona ilana igbasilẹ tuntun yoo jẹ:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "Ṣe o da ọ loju pe o fẹ yi ilana igbasilẹ naa pada?", - "monitor-clear-dir-dialog-msg": "Eyi yoo dẹkun ṣiṣe ayẹwo ilana atẹle fun awọn faili ZIM tuntun.", - "monitor-directory-tooltip": "Gbogbo awọn faili ZIM ti o wa ninu itọsọna yii yoo jẹ afikun laifọwọyi si ile-ikawe.", - "next-tab": "Lọ si taabu atẹle", - "previous-tab": "Yí lọ sí àtẹ́lẹ̀ ìsàlẹ̀", - "cancel-download": "Fagilee gbigba lati ayelujara", - "cancel-download-text": "Ṣe o da ọ loju pe o fẹ fagilee igbasilẹ ti {{ZIM}} ?", - "delete-book": "Paa Bulọọku rẹ", - "delete-book-text": "Ṣe o da ọ loju pe o fẹ paarẹ {{ZIM}} ?", - "download-storage-error": "Aṣiṣe Ibi ipamọ", - "download-storage-error-text": "Eto naa ko ni ibi ipamọ to wa.", - "download-unavailable": "Gbigba lati ayelujara Ko si", - "open-book": "Si iwe", - "download-book": "Iwe gbigbalati ayelujara", - "pause-download": "Da gbigba lati ayelujara duro", - "resume-download": "Tunbere gbigbalati ayelujara", - "open-folder": "Si akpo iwe", - "couldnt-open-location": "Ipo ose si", - "couldnt-open-location-text": "Kiwix ole si apo iwe {{FOLDER}}" -} +{ + "@metadata": { + "authors": [ + "Rofiatmustapha12", + "Sheriff" + ] + }, + "name": "English", + "error-title": "Àsìṣe", + "error-downloader-window-title": "Ko le ṣẹda olugbasilẹ", + "error-downloader-launch-message": "Ko ṣee ṣe lati ṣe ifilọlẹ igbasilẹ, Kiwix-desktop yoo bẹrẹ ṣugbọn gbogbo awọn iṣẹ igbasilẹ kii yoo ṣiṣẹ!", + "error-launch-server-message": "Aṣiṣe kan ti ṣẹlẹ!", + "error-archive": "Ko le gba ile-ipamọ naa", + "open-zim": "Ṣii faili ZIM", + "local-kiwix-server": "Agbegbe Kiwix Server", + "random-article": "Àyọkà àrìnnàkò", + "random-article-error": "Kuna lati ṣii nkan laileto.", + "home-page": "Oju-iwe Ile", + "main-menu": "Àkọlé àwòrán", + "print": "Titẹ sita", + "print-page-error": "Aṣiṣe ti waye lakoko titẹ.", + "new-tab": "Titun taabu", + "close-tab": "Pa taabu", + "close": "Padé", + "reopen-closed-tab": "Tun taabu pipade", + "browse-library": "Ṣawakiri ile-ikawe", + "open-file": "Ṣii faili", + "open-recent": "Ṣii laipe", + "search-article": "Wa nkan", + "search-in-library": "Wa ninu ile-ikawe", + "find-in-page": "Wárí nínú ojúewé", + "set-fullscreen": "Ṣeto iboju kikun", + "quit-fullscreen": "Jade ni kikun Iboju", + "table-of-content": "Àtẹ̀sílẹ̀ àwọn ohun tó wà nínú rẹ̀", + "reading-list": "Akojọ kika", + "zoom-in": "Sun-un sinu", + "zoom-out": "Sun-un jade", + "zoom-reset": "Atunto sun-un", + "help": "Ìrànlọ́wọ́", + "feedback": "Esi", + "report-a-bug": "ṣe àfisùn àsìṣe", + "request-a-feature": "Beere ẹya kan", + "about-kiwix": "Nipa Kiwix", + "donate-to-support-kiwix": "Ṣetọrẹ lati ṣe atilẹyin Kiwix", + "exit": "Jade", + "save-file-as-window-title": "Fi faili pamọ bi", + "download-finished-message": "Iwe aṣẹ ti gba lati ayelujara.", + "file": "Àkọsílẹ̀", + "edit": "Satunko", + "view": "Yẹ̀ ẹ́ wò", + "tools": "Àwọn irinṣẹ́", + "window-title": "Library - Kiwix", + "search": "Wa", + "browse-directory": "Ṣawakiri Itọsọna", + "settings": "ètò", + "back": "Pada", + "forward": "Tesiwaju", + "search-files": "Iwari iwe", + "title": "Akole", + "size": "Iwon", + "date": "Ojo", + "content-type": "Orisirisi nkan inu", + "reset-sort": "Iyipada tito", + "open": "Sii le", + "delete": "Paare", + "download": "Gba lati ayelujara", + "resume": "Atun bere", + "pause": "Dawoduro", + "cancel": "Fa igi le", + "apply": "Lilo", + "port-for-local-kiwix-server-setting": "Ibudo fun kiwix tibile", + "zoom-level-setting": "Ifeloju to baa wa", + "download-directory-setting": "Ona ati gba lati ayelujara", + "monitor-directory-setting": "Sakiesi ona", + "reset": "Atunto", + "clear": "Pare", + "browse": "Sawa kiri", + "about-kiwix-desktop-title": "Kiwix Ojú-iṣẹ", + "about-kiwix-desktop-description": "Kiwix gba ọ laaye lati ni gbogbo Wikipedia ni ọwọ nibikibi ti o lọ! Lori ọkọ oju omi, ni aarin ti ko si, tabi ni ẹwọn, Kiwix fun ọ ni iwọle si gbogbo imoye eniyan. O ko nilo Intanẹẹti, ohun gbogbo wa ni ipamọ lori kọnputa rẹ.", + "about-learn-more-about-kiwix": "Ikekosi nipa kiwix", + "about-release-title": "Itu sile", + "about-source-code": "Sọfitiwia yii jẹ idasilẹ labẹ awọn ofin ti ẹya GNU Gbogbogbo ẹya Iwe-aṣẹ Awujọ. Wo koodu orisun lori Github .", + "about-version": "Ẹya: {{VERSION}}", + "about-reporting-problem-title": "Ifejo sun abuku", + "about-report-issue": "Lati ṣii ọrọ kan, ṣabẹwo si Kiwix-desktop olutọpa ọrọ ati ṣii ọrọ kan ti n ṣalaye iṣoro rẹ.", + "about-report-issue-2": "Jọwọ darukọ awọn ti ikede ninu atejade.", + "about-libraries-title": "Ile kawe", + "kiwix-server-running-message": "Olupin Kiwix naa nṣiṣẹ ati pe o le wọle si ni nẹtiwọọki agbegbe ni:", + "kiwix-server-description": "Bibẹrẹ olupin ngbanilaaye awọn kọnputa miiran ni nẹtiwọọki agbegbe lati wọle si ile-ikawe Kiwix rẹ pẹlu aṣawakiri wẹẹbu boṣewa kan.", + "fullscreen-notification": "Oti Wani oju ero kunkun. Te ESC lati dawoduro", + "online-files": "Iwe eto ayelujara", + "local-files": "Iwe eto tibile", + "category": "Eka", + "language": "Ede", + "hide": "Fipamo", + "open-in-browser": "Si ni awakiri", + "start-kiwix-server": "Ṣiṣẹ Kiwix Server", + "stop-kiwix-server": "Duro Kiwix Server", + "all": "Gbogbo re", + "fulltext-search": "Wiwa kikokunkun", + "pictures": "Awon Aworan", + "videos": "Wiwo", + "ftindex": "Atọka kikun ọrọ", + "details": "Apileko kunkun", + "yes": "Beni", + "no": "Beeko", + "ok": "Otida", + "no-filter": "ko si àlẹmọ", + "open-link-in-web-browser": "Si ona ni ero awakiri", + "download-dir-dialog-title": "So da o loju pe ofe yi ona ati gba lati ayelujara pada", + "download-dir-dialog-msg": "Ona ati gba lati ayelujara tuntun ma je {{DIRECTORY}}", + "invalid-port": "Ibudo tio sise", + "zim-open-fail-title": "Iwe kole tio wulo", + "zim-open-fail-text": "Faili ZIM {{ZIM}} ko le ṣii daradara. Yoo yọkuro kuro ni ile-ikawe rẹ.", + "monitor-dir-dialog-title": "Ṣe o da ọ loju pe o fẹ yi ilana igbasilẹ naa pada?", + "monitor-dir-dialog-msg": "Ona ilana igbasilẹ tuntun yoo jẹ:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "Ṣe o da ọ loju pe o fẹ yi ilana igbasilẹ naa pada?", + "monitor-clear-dir-dialog-msg": "Eyi yoo dẹkun ṣiṣe ayẹwo ilana atẹle fun awọn faili ZIM tuntun.", + "monitor-directory-tooltip": "Gbogbo awọn faili ZIM ti o wa ninu itọsọna yii yoo jẹ afikun laifọwọyi si ile-ikawe.", + "next-tab": "Lọ si taabu atẹle", + "previous-tab": "Yí lọ sí àtẹ́lẹ̀ ìsàlẹ̀", + "cancel-download": "Fagilee gbigba lati ayelujara", + "cancel-download-text": "Ṣe o da ọ loju pe o fẹ fagilee igbasilẹ ti {{ZIM}} ?", + "delete-book": "Paa Bulọọku rẹ", + "delete-book-text": "Ṣe o da ọ loju pe o fẹ paarẹ {{ZIM}} ?", + "download-storage-error": "Aṣiṣe Ibi ipamọ", + "download-storage-error-text": "Eto naa ko ni ibi ipamọ to wa.", + "download-unavailable": "Gbigba lati ayelujara Ko si", + "open-book": "Si iwe", + "download-book": "Iwe gbigbalati ayelujara", + "pause-download": "Da gbigba lati ayelujara duro", + "resume-download": "Tunbere gbigbalati ayelujara", + "open-folder": "Si akpo iwe", + "couldnt-open-location": "Ipo ose si", + "couldnt-open-location-text": "Kiwix ole si apo iwe {{FOLDER}}" +} diff --git a/resources/i18n/zgh.json b/resources/i18n/zgh.json index 0722c157f..b1aadd958 100644 --- a/resources/i18n/zgh.json +++ b/resources/i18n/zgh.json @@ -1,38 +1,38 @@ -{ - "@metadata": { - "authors": [ - "Hakim1bal", - "McDutchie" - ] - }, - "name": "ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ", - "new-tab": "ⴰⵙⴽⵙⵍ ⴰⵎⴰⵢⵏⵓ", - "close-tab": "ⵔⴳⵍ ⴰⵙⴽⵙⵍ", - "open-file": "ⵕⵥⵎ ⴰⴼⴰⵢⵍⵓ", - "find-in-page": "ⴰⴼ ⴳ ⵜⴰⵙⵏⴰ", - "help": "ⵜⵉⵡⵉⵙⵉ", - "about-kiwix": "ⵖⴼ ⴽⵉⵡⵉⴽⵙ", - "save-file-as-window-title": "ⵃⴹⵓ ⴰⴼⴰⵢⵍⵓ ⴰⵎ", - "file": "ⴰⴼⴰⵢⵍⵓ", - "edit": "ⵙⵏⴼⵍ", - "tools": "ⵉⵎⴰⵙⵙⵏ", - "window-title": "ⵜⴰⵙⴷⵍⵉⵙⵜ - ⴽⵉⵡⵉⴽⵙ", - "search": "ⵔⵣⵓ", - "settings": "ⵜⵉⵙⵖⴰⵍ", - "title": "ⴰⵣⵡⵍ", - "date": "ⴰⵙⴰⴽⵓⴷ", - "open": "ⵕⵥⵎ", - "delete": "ⴽⴽⵙ", - "download": "ⴰⴳⵎ", - "cancel": "ⵙⵔ", - "about-learn-more-about-kiwix": "ⵙⵙⵏ ⵓⴳⴳⴰⵔ ⵖⵔ ⴽⵉⵡⵉⴽⵙ", - "about-libraries-title": "ⵜⵉⵙⴷⵍⵉⵙⵉⵏ", - "local-files": "ⵉⴼⵓⵢⵍⴰ ⵉⴷⵖⵔⴰⵏⵏ", - "hide": "ⵙⵙⵏⵜⵍ", - "all": "ⴰⴽⴽⵯ", - "pictures": "ⵜⵉⵡⵍⴰⴼⵉⵏ", - "videos": "ⵉⴼⵉⴷⵢⵜⵏ", - "yes": "ⵢⴰⵀ", - "no": "ⵓⵀⵓ", - "no-filter": "ⵡⴰⵍⵓ ⵉⵙⵜⴰⵢⵏ" -} +{ + "@metadata": { + "authors": [ + "Hakim1bal", + "McDutchie" + ] + }, + "name": "ⵜⴰⵎⴰⵣⵉⵖⵜ ⵜⴰⵏⴰⵡⴰⵢⵜ", + "new-tab": "ⴰⵙⴽⵙⵍ ⴰⵎⴰⵢⵏⵓ", + "close-tab": "ⵔⴳⵍ ⴰⵙⴽⵙⵍ", + "open-file": "ⵕⵥⵎ ⴰⴼⴰⵢⵍⵓ", + "find-in-page": "ⴰⴼ ⴳ ⵜⴰⵙⵏⴰ", + "help": "ⵜⵉⵡⵉⵙⵉ", + "about-kiwix": "ⵖⴼ ⴽⵉⵡⵉⴽⵙ", + "save-file-as-window-title": "ⵃⴹⵓ ⴰⴼⴰⵢⵍⵓ ⴰⵎ", + "file": "ⴰⴼⴰⵢⵍⵓ", + "edit": "ⵙⵏⴼⵍ", + "tools": "ⵉⵎⴰⵙⵙⵏ", + "window-title": "ⵜⴰⵙⴷⵍⵉⵙⵜ - ⴽⵉⵡⵉⴽⵙ", + "search": "ⵔⵣⵓ", + "settings": "ⵜⵉⵙⵖⴰⵍ", + "title": "ⴰⵣⵡⵍ", + "date": "ⴰⵙⴰⴽⵓⴷ", + "open": "ⵕⵥⵎ", + "delete": "ⴽⴽⵙ", + "download": "ⴰⴳⵎ", + "cancel": "ⵙⵔ", + "about-learn-more-about-kiwix": "ⵙⵙⵏ ⵓⴳⴳⴰⵔ ⵖⵔ ⴽⵉⵡⵉⴽⵙ", + "about-libraries-title": "ⵜⵉⵙⴷⵍⵉⵙⵉⵏ", + "local-files": "ⵉⴼⵓⵢⵍⴰ ⵉⴷⵖⵔⴰⵏⵏ", + "hide": "ⵙⵙⵏⵜⵍ", + "all": "ⴰⴽⴽⵯ", + "pictures": "ⵜⵉⵡⵍⴰⴼⵉⵏ", + "videos": "ⵉⴼⵉⴷⵢⵜⵏ", + "yes": "ⵢⴰⵀ", + "no": "ⵓⵀⵓ", + "no-filter": "ⵡⴰⵍⵓ ⵉⵙⵜⴰⵢⵏ" +} diff --git a/resources/i18n/zh-hans.json b/resources/i18n/zh-hans.json index cf75f7497..406886bb1 100644 --- a/resources/i18n/zh-hans.json +++ b/resources/i18n/zh-hans.json @@ -1,202 +1,202 @@ -{ - "@metadata": { - "authors": [ - "Anterdc99", - "GuoPC", - "Huajing", - "IceButBin", - "Josephine W.", - "Key0121", - "Kichin", - "Lynzrand", - "McDutchie", - "Nadine Siak", - "Shuiwater", - "SomeyaMako", - "Stang", - "StarrySky", - "Sunai", - "TFX202X", - "XtexChooser", - "Zhang8569", - "沈澄心" - ] - }, - "name": "简体中文", - "error-title": "错误", - "error-downloader-window-title": "无法创建下载器", - "error-downloader-launch-message": "发送一个下载器是不可能的,Kiwix-desktop将开始但是下载功能将不会工作!", - "error-launch-server-message": "发生错误!", - "error-archive": "无法获取存档", - "open-zim": "打开ZIM文件", - "local-kiwix-server": "本地 Kiwix 服务器", - "random-article": "随机条目", - "random-article-error": "无法打开随机文章。", - "home-page": "主页", - "main-menu": "主菜单", - "print": "打印", - "print-page-error": "打印时发生错误。", - "new-tab": "新建标签页", - "close-tab": "关闭标签页", - "close": "关闭", - "reopen-closed-tab": "重新打开关闭的标签页", - "browse-library": "浏览图书馆", - "open-file": "打开文件", - "open-recent": "打开最近的文件", - "search-article": "搜索条目", - "search-in-library": "在图书馆中查找", - "find-in-page": "在页面中查找", - "set-fullscreen": "设置全屏", - "quit-fullscreen": "退出全屏", - "table-of-content": "目录", - "add-bookmark": "添加至阅读列表", - "remove-bookmark": "从阅读列表中移除", - "reading-list": "阅读列表", - "zoom-in": "放大", - "zoom-out": "缩小", - "zoom-reset": "重置缩放", - "help": "帮助", - "feedback": "反馈", - "report-a-bug": "报告程序缺陷", - "request-a-feature": "请求一个特性", - "about-kiwix": "关于Kiwix", - "donate-to-support-kiwix": "捐款支持 Kiwix", - "exit": "退出", - "save-file-as-window-title": "将文件另存为", - "download-finished-message": "此文档已经下载完毕。", - "file": "文件", - "edit": "编辑", - "view": "查看", - "tools": "工具", - "window-title": "图书馆 - Kiwix", - "search": "搜索", - "browse-directory": "浏览文件夹", - "settings": "设置", - "back": "返回", - "forward": "前进", - "search-files": "搜索文件", - "title": "标题", - "size": "大小", - "date": "日期", - "content-type": "内容类型", - "reset-sort": "重置排序", - "open": "打开", - "delete": "删除", - "download": "下载", - "resume": "继续", - "pause": "暂停", - "cancel": "取消", - "apply": "应用", - "port-for-local-kiwix-server-setting": "本地Kiwix服务器端口", - "zoom-level-setting": "默认缩放级别", - "download-directory-setting": "下载文件夹", - "monitor-directory-setting": "监视目录", - "reset": "重置", - "clear": "清除", - "browse": "浏览", - "about-kiwix-desktop-title": "Kiwix 桌面版", - "about-kiwix-desktop-description": "Kiwix 能让您随时随地拥有整个维基百科!不论您是在船上、荒郊野外、甚至是待在监狱,Kiwix 都可让您获取全人类的知识。您不必需要互联网,所有内容都能存储在您的计算机中。", - "about-learn-more-about-kiwix": "了解有关 Kiwix 的更多信息", - "about-release-title": "发布", - "about-source-code": "该软件根据 GNU 通用公共许可协议第 3 版本发布。您可在Github上查看该软件的源代码。", - "about-version": "版本:{{VERSION}}", - "about-reporting-problem-title": "报告问题", - "about-report-issue": "要提出问题{{TRACKER_URL}},请访问Kiwix电脑版问题追踪器来提出一个问题,然后描述您遇到的状况。", - "about-report-issue-2": "请在问题中提及版本。", - "about-libraries-title": "图书馆", - "kiwix-server-running-message": "Kiwix服务器已在运行。您可以在局域网中访问。链接是:", - "kiwix-server-description": "开启服务器可以允许局域网中的其他电脑使用普通的网页浏览器来访问你的 Kiwix 图书馆", - "fullscreen-notification": "您现已进入全屏模式。点击Esc键以退出全屏!", - "online-files": "在线文件", - "local-files": "本地文件", - "category": "分类", - "language": "语言", - "hide": "隐藏", - "open-in-browser": "在浏览器中打开", - "start-kiwix-server": "启动 Kiwix 服务器", - "stop-kiwix-server": "停止 Kiwix 服务器", - "all": "全部", - "all_ips": "全部 - 双栈模式", - "ipv4": "仅 IPv4 模式", - "ipv6": "仅 IPv6 模式", - "fulltext-search": "全文搜索", - "pictures": "图片", - "videos": "视频", - "ftindex": "全文索引", - "details": "完整条目", - "yes": "是", - "no": "否", - "ok": "好的", - "no-filter": "无过滤", - "open-link-in-web-browser": "在网页浏览器中打开链接", - "open-link-new-tab": "在新标签页打开链接", - "download-dir-dialog-title": "您确定您要更改下载路径吗?", - "download-dir-dialog-msg": "新的下载路径为:\n{{DIRECTORY}}", - "invalid-port": "无效的接口", - "zim-open-fail-title": "无效文件", - "zim-open-fail-text": "ZIM 文件 {{ZIM}} 打开失败。我们将把它从你的图书馆中移除。", - "monitor-dir-dialog-title": "您确定您要更改下载路径吗?", - "monitor-dir-dialog-msg": "新的下载路径为:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "您确定您要更改下载路径吗?", - "path-was-copied": "路径已复制", - "monitor-clear-dir-dialog-msg": "这将停止检查新 ZIM 文件的监视器目录。", - "monitor-directory-tooltip": "这个文件夹下的所有 ZIM 文件都将加入你的图书馆。", - "next-tab": "移至下一个标签页", - "previous-tab": "移至上一个上一标签页", - "cancel-download": "取消下载", - "cancel-download-text": "您确定要取消下载{{ZIM}}吗?", - "delete-book": "删除图书", - "delete-book-text": "您确定要删除{{ZIM}}吗?", - "download-storage-error": "储存错误", - "download-storage-error-text": "存储空间不足。", - "download-exceeds-max-file-size": "下载大小超出了目标文件系统支持的最大文件大小。", - "download-dir-missing": "下载目录不存在。", - "download-dir-not-writable": "下载目录不可写。", - "download-unavailable": "下载不可用", - "download-unavailable-text": "此次下载不可用", - "open-book": "打开图书", - "download-book": "下载图书", - "pause-download": "暂停下载", - "resume-download": "继续下载", - "open-folder": "打开文件夹", - "couldnt-open-location": "无法打开位置", - "couldnt-open-location-text": "Kiwix 无法打开文件夹 {{FOLDER}}", - "move-files-to-trash": "将已删除的文件移至回收站", - "move-files-to-trash-text": "这将使文件被移至回收站。", - "perma-delete-files-text": "这将使文件被永久删除。", - "clear-filter": "清除当前的过滤设置", - "language-searcher-placeholder": "按语言过滤", - "category-searcher-placeholder": "按分类过滤", - "content-type-searcher-placeholder": "按内容类型过滤", - "no-details": "仅介绍", - "no-pictures": "无图片", - "no-videos": "无视频", - "open-previous-tabs-at-startup": "启动时打开先前的标签页", - "preview-book-in-web-browser": "在网页浏览器中预览书籍", - "file-not-found-title": "找不到ZIM文件", - "file-not-found-text": "ZIM文件不存在或无法读取", - "zim-id": "ZIM ID", - "zim-name": "ZIM名称", - "zim-path": "ZIM文件路径", - "bad-zim-file-error-page-title": "无效的 ZIM 文件", - "bad-zim-file-error-page-text": "ZIM 文件无效或已损坏。", - "zim-entry-path": "ZIM 入口路径", - "export-reading-list": "导出阅读列表", - "export-reading-list-error": "导出阅读列表时发生了错误。", - "import-reading-list": "导入阅读列表", - "import-reading-list-error": "导入阅读列表时发生了错误。", - "disable-sandbox": "Kiwix是从网络驱动器启动的。这会导致已知的由浏览器沙盒的兼容性问题。因此沙盒将被禁用,您想要继续吗?", - "save-page-as": "另存为...", - "portable-disabled-tooltip": "便携模式下此功能被禁用", - "scroll-next-tab": "滚动到下一个标签页", - "scroll-previous-tab": "滚动到上一个标签页", - "kiwix-search": "Kiwix 搜索", - "search-options": "搜索选项", - "read-article": "阅读条目", - "read-text": "阅读选定的文本", - "read-stop": "停止阅读", - "stop": "停止", - "voice": "语音", - "select-read-voice": "选择朗读声音", - "select-read-language": "选择阅读语言" -} +{ + "@metadata": { + "authors": [ + "Anterdc99", + "GuoPC", + "Huajing", + "IceButBin", + "Josephine W.", + "Key0121", + "Kichin", + "Lynzrand", + "McDutchie", + "Nadine Siak", + "Shuiwater", + "SomeyaMako", + "Stang", + "StarrySky", + "Sunai", + "TFX202X", + "XtexChooser", + "Zhang8569", + "沈澄心" + ] + }, + "name": "简体中文", + "error-title": "错误", + "error-downloader-window-title": "无法创建下载器", + "error-downloader-launch-message": "发送一个下载器是不可能的,Kiwix-desktop将开始但是下载功能将不会工作!", + "error-launch-server-message": "发生错误!", + "error-archive": "无法获取存档", + "open-zim": "打开ZIM文件", + "local-kiwix-server": "本地 Kiwix 服务器", + "random-article": "随机条目", + "random-article-error": "无法打开随机文章。", + "home-page": "主页", + "main-menu": "主菜单", + "print": "打印", + "print-page-error": "打印时发生错误。", + "new-tab": "新建标签页", + "close-tab": "关闭标签页", + "close": "关闭", + "reopen-closed-tab": "重新打开关闭的标签页", + "browse-library": "浏览图书馆", + "open-file": "打开文件", + "open-recent": "打开最近的文件", + "search-article": "搜索条目", + "search-in-library": "在图书馆中查找", + "find-in-page": "在页面中查找", + "set-fullscreen": "设置全屏", + "quit-fullscreen": "退出全屏", + "table-of-content": "目录", + "add-bookmark": "添加至阅读列表", + "remove-bookmark": "从阅读列表中移除", + "reading-list": "阅读列表", + "zoom-in": "放大", + "zoom-out": "缩小", + "zoom-reset": "重置缩放", + "help": "帮助", + "feedback": "反馈", + "report-a-bug": "报告程序缺陷", + "request-a-feature": "请求一个特性", + "about-kiwix": "关于Kiwix", + "donate-to-support-kiwix": "捐款支持 Kiwix", + "exit": "退出", + "save-file-as-window-title": "将文件另存为", + "download-finished-message": "此文档已经下载完毕。", + "file": "文件", + "edit": "编辑", + "view": "查看", + "tools": "工具", + "window-title": "图书馆 - Kiwix", + "search": "搜索", + "browse-directory": "浏览文件夹", + "settings": "设置", + "back": "返回", + "forward": "前进", + "search-files": "搜索文件", + "title": "标题", + "size": "大小", + "date": "日期", + "content-type": "内容类型", + "reset-sort": "重置排序", + "open": "打开", + "delete": "删除", + "download": "下载", + "resume": "继续", + "pause": "暂停", + "cancel": "取消", + "apply": "应用", + "port-for-local-kiwix-server-setting": "本地Kiwix服务器端口", + "zoom-level-setting": "默认缩放级别", + "download-directory-setting": "下载文件夹", + "monitor-directory-setting": "监视目录", + "reset": "重置", + "clear": "清除", + "browse": "浏览", + "about-kiwix-desktop-title": "Kiwix 桌面版", + "about-kiwix-desktop-description": "Kiwix 能让您随时随地拥有整个维基百科!不论您是在船上、荒郊野外、甚至是待在监狱,Kiwix 都可让您获取全人类的知识。您不必需要互联网,所有内容都能存储在您的计算机中。", + "about-learn-more-about-kiwix": "了解有关 Kiwix 的更多信息", + "about-release-title": "发布", + "about-source-code": "该软件根据 GNU 通用公共许可协议第 3 版本发布。您可在Github上查看该软件的源代码。", + "about-version": "版本:{{VERSION}}", + "about-reporting-problem-title": "报告问题", + "about-report-issue": "要提出问题{{TRACKER_URL}},请访问Kiwix电脑版问题追踪器来提出一个问题,然后描述您遇到的状况。", + "about-report-issue-2": "请在问题中提及版本。", + "about-libraries-title": "图书馆", + "kiwix-server-running-message": "Kiwix服务器已在运行。您可以在局域网中访问。链接是:", + "kiwix-server-description": "开启服务器可以允许局域网中的其他电脑使用普通的网页浏览器来访问你的 Kiwix 图书馆", + "fullscreen-notification": "您现已进入全屏模式。点击Esc键以退出全屏!", + "online-files": "在线文件", + "local-files": "本地文件", + "category": "分类", + "language": "语言", + "hide": "隐藏", + "open-in-browser": "在浏览器中打开", + "start-kiwix-server": "启动 Kiwix 服务器", + "stop-kiwix-server": "停止 Kiwix 服务器", + "all": "全部", + "all_ips": "全部 - 双栈模式", + "ipv4": "仅 IPv4 模式", + "ipv6": "仅 IPv6 模式", + "fulltext-search": "全文搜索", + "pictures": "图片", + "videos": "视频", + "ftindex": "全文索引", + "details": "完整条目", + "yes": "是", + "no": "否", + "ok": "好的", + "no-filter": "无过滤", + "open-link-in-web-browser": "在网页浏览器中打开链接", + "open-link-new-tab": "在新标签页打开链接", + "download-dir-dialog-title": "您确定您要更改下载路径吗?", + "download-dir-dialog-msg": "新的下载路径为:\n{{DIRECTORY}}", + "invalid-port": "无效的接口", + "zim-open-fail-title": "无效文件", + "zim-open-fail-text": "ZIM 文件 {{ZIM}} 打开失败。我们将把它从你的图书馆中移除。", + "monitor-dir-dialog-title": "您确定您要更改下载路径吗?", + "monitor-dir-dialog-msg": "新的下载路径为:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "您确定您要更改下载路径吗?", + "path-was-copied": "路径已复制", + "monitor-clear-dir-dialog-msg": "这将停止检查新 ZIM 文件的监视器目录。", + "monitor-directory-tooltip": "这个文件夹下的所有 ZIM 文件都将加入你的图书馆。", + "next-tab": "移至下一个标签页", + "previous-tab": "移至上一个上一标签页", + "cancel-download": "取消下载", + "cancel-download-text": "您确定要取消下载{{ZIM}}吗?", + "delete-book": "删除图书", + "delete-book-text": "您确定要删除{{ZIM}}吗?", + "download-storage-error": "储存错误", + "download-storage-error-text": "存储空间不足。", + "download-exceeds-max-file-size": "下载大小超出了目标文件系统支持的最大文件大小。", + "download-dir-missing": "下载目录不存在。", + "download-dir-not-writable": "下载目录不可写。", + "download-unavailable": "下载不可用", + "download-unavailable-text": "此次下载不可用", + "open-book": "打开图书", + "download-book": "下载图书", + "pause-download": "暂停下载", + "resume-download": "继续下载", + "open-folder": "打开文件夹", + "couldnt-open-location": "无法打开位置", + "couldnt-open-location-text": "Kiwix 无法打开文件夹 {{FOLDER}}", + "move-files-to-trash": "将已删除的文件移至回收站", + "move-files-to-trash-text": "这将使文件被移至回收站。", + "perma-delete-files-text": "这将使文件被永久删除。", + "clear-filter": "清除当前的过滤设置", + "language-searcher-placeholder": "按语言过滤", + "category-searcher-placeholder": "按分类过滤", + "content-type-searcher-placeholder": "按内容类型过滤", + "no-details": "仅介绍", + "no-pictures": "无图片", + "no-videos": "无视频", + "open-previous-tabs-at-startup": "启动时打开先前的标签页", + "preview-book-in-web-browser": "在网页浏览器中预览书籍", + "file-not-found-title": "找不到ZIM文件", + "file-not-found-text": "ZIM文件不存在或无法读取", + "zim-id": "ZIM ID", + "zim-name": "ZIM名称", + "zim-path": "ZIM文件路径", + "bad-zim-file-error-page-title": "无效的 ZIM 文件", + "bad-zim-file-error-page-text": "ZIM 文件无效或已损坏。", + "zim-entry-path": "ZIM 入口路径", + "export-reading-list": "导出阅读列表", + "export-reading-list-error": "导出阅读列表时发生了错误。", + "import-reading-list": "导入阅读列表", + "import-reading-list-error": "导入阅读列表时发生了错误。", + "disable-sandbox": "Kiwix是从网络驱动器启动的。这会导致已知的由浏览器沙盒的兼容性问题。因此沙盒将被禁用,您想要继续吗?", + "save-page-as": "另存为...", + "portable-disabled-tooltip": "便携模式下此功能被禁用", + "scroll-next-tab": "滚动到下一个标签页", + "scroll-previous-tab": "滚动到上一个标签页", + "kiwix-search": "Kiwix 搜索", + "search-options": "搜索选项", + "read-article": "阅读条目", + "read-text": "阅读选定的文本", + "read-stop": "停止阅读", + "stop": "停止", + "voice": "语音", + "select-read-voice": "选择朗读声音", + "select-read-language": "选择阅读语言" +} diff --git a/resources/i18n/zh-hant.json b/resources/i18n/zh-hant.json index fe8b2d4d3..5de28b947 100644 --- a/resources/i18n/zh-hant.json +++ b/resources/i18n/zh-hant.json @@ -1,178 +1,178 @@ -{ - "@metadata": { - "authors": [ - "Kly", - "Weather Top Wizard", - "Winston Sung" - ] - }, - "name": "繁體中文", - "error-title": "錯誤", - "error-downloader-window-title": "無法建立下載程序", - "error-downloader-launch-message": "無法啟用下載程序,Kiwix-桌機版將啟動;但所有的下載功能將無法運作!", - "error-launch-server-message": "發生錯誤!", - "error-archive": "無法取得存檔", - "open-zim": "開啟 Zim 檔案", - "local-kiwix-server": "本地 Kiwix 伺服器", - "random-article": "隨機條目", - "random-article-error": "開啟隨機條目失敗。", - "home-page": "首頁", - "main-menu": "主選單", - "print": "列印", - "print-page-error": "當列印時發生錯誤。", - "new-tab": "新分頁", - "close-tab": "關閉分頁", - "close": "關閉", - "reopen-closed-tab": "重新開啟已關閉的分頁", - "browse-library": "瀏覽圖書館", - "open-file": "開啟檔案", - "open-recent": "最近開啟", - "search-article": "搜尋條目", - "search-in-library": "在圖書館裡搜尋", - "find-in-page": "在頁面裡尋找", - "set-fullscreen": "設定全螢幕", - "quit-fullscreen": "結束全螢幕", - "table-of-content": "目次", - "add-bookmark": "新增至閱讀清單", - "remove-bookmark": "從閱讀清單移除", - "reading-list": "閱讀清單", - "zoom-in": "放大", - "zoom-out": "縮小", - "zoom-reset": "重設縮放", - "help": "說明", - "feedback": "意見回饋", - "report-a-bug": "回報程式錯誤", - "request-a-feature": "請求新功能", - "about-kiwix": "關於 Kiwix", - "donate-to-support-kiwix": "捐款支持 Kiwix", - "exit": "離開", - "save-file-as-window-title": "檔案另存為", - "download-finished-message": "文件已下載。", - "file": "檔案", - "edit": "編輯", - "view": "檢視", - "tools": "工具", - "window-title": "圖書館 - Kiwix", - "search": "搜尋", - "browse-directory": "瀏覽目錄", - "settings": "設定", - "back": "返回", - "forward": "向前", - "search-files": "搜尋檔案", - "title": "標題", - "size": "大小", - "date": "日期", - "content-type": "內容類型", - "reset-sort": "重設排序", - "open": "開啟", - "delete": "刪除", - "download": "下載", - "resume": "繼續", - "pause": "暫停", - "cancel": "取消", - "apply": "套用", - "port-for-local-kiwix-server-setting": "本地 Kiwix 伺服器通訊埠", - "zoom-level-setting": "縮放層級", - "download-directory-setting": "下載目錄", - "monitor-directory-setting": "監視目錄", - "reset": "重新設定", - "clear": "清除", - "browse": "瀏覽", - "about-kiwix-desktop-title": "Kiwix 桌機版", - "about-kiwix-desktop-description": "Kiwix 能讓您隨時隨地擁有整個維基百科!不論是在船上、荒郊野外、甚至是待在監獄,Kiwix 都可讓您取用全人類的知識。您不必需要網際網路,因為所有一切內容都可以存放在您的電腦裡。", - "about-learn-more-about-kiwix": "了解更多有關 Kiwix", - "about-release-title": "釋出", - "about-source-code": "此軟體是基於 GNU 通用公眾授權條款第 3 版本釋出。您可以在 Github 查看該軟體的程式原始碼。", - "about-version": "版本:{{VERSION}}", - "about-reporting-problem-title": "回報問題", - "about-report-issue": "要開啟問題,請參訪 Kiwix-桌機版問題追蹤,並開啟一個問題來描述您遇到的情況。", - "about-report-issue-2": "請在問題中提及版本。", - "about-libraries-title": "圖書館", - "kiwix-server-running-message": "Kiwix 伺服器正運作中,要在本地網路存取請在:", - "kiwix-server-description": "啟動伺服器能允許在本地網路上的其它台電腦以標準網頁瀏覽器來存取您的 Kiwix 圖書館。", - "fullscreen-notification": "您現在是處在全螢幕模式。若要離開請按下 ESC 鍵!", - "online-files": "線上檔案", - "local-files": "本地檔案", - "category": "分類", - "language": "語言", - "hide": "隱藏", - "open-in-browser": "在瀏覽器中開啟", - "start-kiwix-server": "啟動 Kiwix 伺服器", - "stop-kiwix-server": "停用 Kiwix 伺服器", - "all": "全部", - "all_ips": "全部 - 雙堆疊模式", - "ipv4": "僅 IPv4 模式", - "ipv6": "僅 IPv6 模式", - "fulltext-search": "全文搜尋", - "pictures": "圖片", - "videos": "影片", - "ftindex": "全文字索引", - "details": "整個條目", - "yes": "是", - "no": "否", - "ok": "好", - "no-filter": "無篩選", - "open-link-in-web-browser": "在網路瀏覽器開啟連結", - "open-link-new-tab": "在新分頁開啟連結", - "download-dir-dialog-title": "您確定您要更改下載目錄嗎?", - "download-dir-dialog-msg": "新目錄路徑會是:\n{{DIRECTORY}}", - "invalid-port": "無效通訊埠", - "zim-open-fail-title": "無效檔案", - "zim-open-fail-text": "ZIM 檔案 {{ZIM}} 無法正常開啓。這將會從您的圖書館裡移除。", - "monitor-dir-dialog-title": "您確定您要更改監視目錄嗎?", - "monitor-dir-dialog-msg": "新監視目錄路徑會是:\n{{DIRECTORY}}", - "monitor-clear-dir-dialog-title": "您確定您要更改監視目錄嗎?", - "path-was-copied": "已複製路徑", - "monitor-clear-dir-dialog-msg": "這將會停止檢查用於新 ZIM 檔案的監視目錄。", - "monitor-directory-tooltip": "所有在此目錄的 ZIM 檔案將會自動添加到圖書館裡。", - "next-tab": "移動至下一分頁", - "previous-tab": "移動至上一分頁", - "cancel-download": "取消下載", - "cancel-download-text": "您確定要取消下載{{ZIM}}嗎?", - "delete-book": "刪除書籍", - "delete-book-text": "您確定要刪除{{ZIM}}嗎?", - "download-storage-error": "儲存錯誤", - "download-storage-error-text": "系統沒有足夠的可用儲存空間。", - "download-exceeds-max-file-size": "下載大小超過目標檔案系統所支援的最大檔案大小。", - "download-dir-missing": "下載目錄不存在。", - "download-dir-not-writable": "下載目錄不可寫入。", - "download-unavailable": "無法下載", - "download-unavailable-text": "此下載不可用。", - "open-book": "開啟書籍", - "download-book": "下載書籍", - "pause-download": "暫停下載", - "resume-download": "繼續下載", - "open-folder": "開啟資料夾", - "couldnt-open-location": "無法開啟位置", - "couldnt-open-location-text": "Kiwix 開啟資料夾{{FOLDER}}", - "move-files-to-trash": "將已刪除的檔案移至垃圾桶", - "move-files-to-trash-text": "此操作將會把檔案移至垃圾桶。", - "perma-delete-files-text": "此操作將會永久刪除檔案。", - "clear-filter": "清除目前的篩選設定", - "language-searcher-placeholder": "依語言篩選", - "category-searcher-placeholder": "依分類篩選", - "content-type-searcher-placeholder": "依內容類型篩選", - "no-details": "僅介紹", - "no-pictures": "沒有圖片", - "no-videos": "沒有影片", - "open-previous-tabs-at-startup": "啟動時打開先前的分頁", - "preview-book-in-web-browser": "在網頁瀏覽器預覽書籍", - "file-not-found-title": "找不到 ZIM 檔案", - "file-not-found-text": "ZIM 檔案不存在或無法讀取", - "zim-id": "ZIM ID", - "zim-name": "ZIM 名稱", - "zim-path": "ZIM 檔案路徑", - "bad-zim-file-error-page-title": "無效的 ZIM 檔案", - "bad-zim-file-error-page-text": "ZIM 檔案無效或已損毀。", - "zim-entry-path": "ZIM 入口路徑", - "export-reading-list": "匯出閱讀清單", - "export-reading-list-error": "匯出閱讀清單時發生錯誤。", - "import-reading-list": "匯入閱讀清單", - "import-reading-list-error": "匯入閱讀清單時發生錯誤。", - "disable-sandbox": "Kiwix 已從網路磁碟機啟動。目前已知這會導致在瀏覽沙盒時發生相容性問題,因此已將沙盒功能關閉。您要繼續嗎?", - "save-page-as": "另存為…", - "portable-disabled-tooltip": "功能在可攜式模式下停用", - "scroll-next-tab": "捲動至下一個分頁", - "scroll-previous-tab": "捲動至上一個分頁", - "kiwix-search": "Kiwix 搜尋" -} +{ + "@metadata": { + "authors": [ + "Kly", + "Weather Top Wizard", + "Winston Sung" + ] + }, + "name": "繁體中文", + "error-title": "錯誤", + "error-downloader-window-title": "無法建立下載程序", + "error-downloader-launch-message": "無法啟用下載程序,Kiwix-桌機版將啟動;但所有的下載功能將無法運作!", + "error-launch-server-message": "發生錯誤!", + "error-archive": "無法取得存檔", + "open-zim": "開啟 Zim 檔案", + "local-kiwix-server": "本地 Kiwix 伺服器", + "random-article": "隨機條目", + "random-article-error": "開啟隨機條目失敗。", + "home-page": "首頁", + "main-menu": "主選單", + "print": "列印", + "print-page-error": "當列印時發生錯誤。", + "new-tab": "新分頁", + "close-tab": "關閉分頁", + "close": "關閉", + "reopen-closed-tab": "重新開啟已關閉的分頁", + "browse-library": "瀏覽圖書館", + "open-file": "開啟檔案", + "open-recent": "最近開啟", + "search-article": "搜尋條目", + "search-in-library": "在圖書館裡搜尋", + "find-in-page": "在頁面裡尋找", + "set-fullscreen": "設定全螢幕", + "quit-fullscreen": "結束全螢幕", + "table-of-content": "目次", + "add-bookmark": "新增至閱讀清單", + "remove-bookmark": "從閱讀清單移除", + "reading-list": "閱讀清單", + "zoom-in": "放大", + "zoom-out": "縮小", + "zoom-reset": "重設縮放", + "help": "說明", + "feedback": "意見回饋", + "report-a-bug": "回報程式錯誤", + "request-a-feature": "請求新功能", + "about-kiwix": "關於 Kiwix", + "donate-to-support-kiwix": "捐款支持 Kiwix", + "exit": "離開", + "save-file-as-window-title": "檔案另存為", + "download-finished-message": "文件已下載。", + "file": "檔案", + "edit": "編輯", + "view": "檢視", + "tools": "工具", + "window-title": "圖書館 - Kiwix", + "search": "搜尋", + "browse-directory": "瀏覽目錄", + "settings": "設定", + "back": "返回", + "forward": "向前", + "search-files": "搜尋檔案", + "title": "標題", + "size": "大小", + "date": "日期", + "content-type": "內容類型", + "reset-sort": "重設排序", + "open": "開啟", + "delete": "刪除", + "download": "下載", + "resume": "繼續", + "pause": "暫停", + "cancel": "取消", + "apply": "套用", + "port-for-local-kiwix-server-setting": "本地 Kiwix 伺服器通訊埠", + "zoom-level-setting": "縮放層級", + "download-directory-setting": "下載目錄", + "monitor-directory-setting": "監視目錄", + "reset": "重新設定", + "clear": "清除", + "browse": "瀏覽", + "about-kiwix-desktop-title": "Kiwix 桌機版", + "about-kiwix-desktop-description": "Kiwix 能讓您隨時隨地擁有整個維基百科!不論是在船上、荒郊野外、甚至是待在監獄,Kiwix 都可讓您取用全人類的知識。您不必需要網際網路,因為所有一切內容都可以存放在您的電腦裡。", + "about-learn-more-about-kiwix": "了解更多有關 Kiwix", + "about-release-title": "釋出", + "about-source-code": "此軟體是基於 GNU 通用公眾授權條款第 3 版本釋出。您可以在 Github 查看該軟體的程式原始碼。", + "about-version": "版本:{{VERSION}}", + "about-reporting-problem-title": "回報問題", + "about-report-issue": "要開啟問題,請參訪 Kiwix-桌機版問題追蹤,並開啟一個問題來描述您遇到的情況。", + "about-report-issue-2": "請在問題中提及版本。", + "about-libraries-title": "圖書館", + "kiwix-server-running-message": "Kiwix 伺服器正運作中,要在本地網路存取請在:", + "kiwix-server-description": "啟動伺服器能允許在本地網路上的其它台電腦以標準網頁瀏覽器來存取您的 Kiwix 圖書館。", + "fullscreen-notification": "您現在是處在全螢幕模式。若要離開請按下 ESC 鍵!", + "online-files": "線上檔案", + "local-files": "本地檔案", + "category": "分類", + "language": "語言", + "hide": "隱藏", + "open-in-browser": "在瀏覽器中開啟", + "start-kiwix-server": "啟動 Kiwix 伺服器", + "stop-kiwix-server": "停用 Kiwix 伺服器", + "all": "全部", + "all_ips": "全部 - 雙堆疊模式", + "ipv4": "僅 IPv4 模式", + "ipv6": "僅 IPv6 模式", + "fulltext-search": "全文搜尋", + "pictures": "圖片", + "videos": "影片", + "ftindex": "全文字索引", + "details": "整個條目", + "yes": "是", + "no": "否", + "ok": "好", + "no-filter": "無篩選", + "open-link-in-web-browser": "在網路瀏覽器開啟連結", + "open-link-new-tab": "在新分頁開啟連結", + "download-dir-dialog-title": "您確定您要更改下載目錄嗎?", + "download-dir-dialog-msg": "新目錄路徑會是:\n{{DIRECTORY}}", + "invalid-port": "無效通訊埠", + "zim-open-fail-title": "無效檔案", + "zim-open-fail-text": "ZIM 檔案 {{ZIM}} 無法正常開啓。這將會從您的圖書館裡移除。", + "monitor-dir-dialog-title": "您確定您要更改監視目錄嗎?", + "monitor-dir-dialog-msg": "新監視目錄路徑會是:\n{{DIRECTORY}}", + "monitor-clear-dir-dialog-title": "您確定您要更改監視目錄嗎?", + "path-was-copied": "已複製路徑", + "monitor-clear-dir-dialog-msg": "這將會停止檢查用於新 ZIM 檔案的監視目錄。", + "monitor-directory-tooltip": "所有在此目錄的 ZIM 檔案將會自動添加到圖書館裡。", + "next-tab": "移動至下一分頁", + "previous-tab": "移動至上一分頁", + "cancel-download": "取消下載", + "cancel-download-text": "您確定要取消下載{{ZIM}}嗎?", + "delete-book": "刪除書籍", + "delete-book-text": "您確定要刪除{{ZIM}}嗎?", + "download-storage-error": "儲存錯誤", + "download-storage-error-text": "系統沒有足夠的可用儲存空間。", + "download-exceeds-max-file-size": "下載大小超過目標檔案系統所支援的最大檔案大小。", + "download-dir-missing": "下載目錄不存在。", + "download-dir-not-writable": "下載目錄不可寫入。", + "download-unavailable": "無法下載", + "download-unavailable-text": "此下載不可用。", + "open-book": "開啟書籍", + "download-book": "下載書籍", + "pause-download": "暫停下載", + "resume-download": "繼續下載", + "open-folder": "開啟資料夾", + "couldnt-open-location": "無法開啟位置", + "couldnt-open-location-text": "Kiwix 開啟資料夾{{FOLDER}}", + "move-files-to-trash": "將已刪除的檔案移至垃圾桶", + "move-files-to-trash-text": "此操作將會把檔案移至垃圾桶。", + "perma-delete-files-text": "此操作將會永久刪除檔案。", + "clear-filter": "清除目前的篩選設定", + "language-searcher-placeholder": "依語言篩選", + "category-searcher-placeholder": "依分類篩選", + "content-type-searcher-placeholder": "依內容類型篩選", + "no-details": "僅介紹", + "no-pictures": "沒有圖片", + "no-videos": "沒有影片", + "open-previous-tabs-at-startup": "啟動時打開先前的分頁", + "preview-book-in-web-browser": "在網頁瀏覽器預覽書籍", + "file-not-found-title": "找不到 ZIM 檔案", + "file-not-found-text": "ZIM 檔案不存在或無法讀取", + "zim-id": "ZIM ID", + "zim-name": "ZIM 名稱", + "zim-path": "ZIM 檔案路徑", + "bad-zim-file-error-page-title": "無效的 ZIM 檔案", + "bad-zim-file-error-page-text": "ZIM 檔案無效或已損毀。", + "zim-entry-path": "ZIM 入口路徑", + "export-reading-list": "匯出閱讀清單", + "export-reading-list-error": "匯出閱讀清單時發生錯誤。", + "import-reading-list": "匯入閱讀清單", + "import-reading-list-error": "匯入閱讀清單時發生錯誤。", + "disable-sandbox": "Kiwix 已從網路磁碟機啟動。目前已知這會導致在瀏覽沙盒時發生相容性問題,因此已將沙盒功能關閉。您要繼續嗎?", + "save-page-as": "另存為…", + "portable-disabled-tooltip": "功能在可攜式模式下停用", + "scroll-next-tab": "捲動至下一個分頁", + "scroll-previous-tab": "捲動至上一個分頁", + "kiwix-search": "Kiwix 搜尋" +} diff --git a/resources/icons/copy.svg b/resources/icons/copy.svg index e1d35291d..a16d8bd1f 100644 --- a/resources/icons/copy.svg +++ b/resources/icons/copy.svg @@ -1,7 +1,7 @@ - - - - - - - + + + + + + + diff --git a/resources/icons/history-left.svg b/resources/icons/history-left.svg index 516b38574..da7aa03bf 100755 --- a/resources/icons/history-left.svg +++ b/resources/icons/history-left.svg @@ -1,57 +1,57 @@ - - - - - - image/svg+xml - - - - - - - icon / back - - + + + + + + image/svg+xml + + + + + + + icon / back + + diff --git a/resources/icons/history-right.svg b/resources/icons/history-right.svg index e3510ec74..a4efb4af4 100755 --- a/resources/icons/history-right.svg +++ b/resources/icons/history-right.svg @@ -1,57 +1,57 @@ - - - - - - image/svg+xml - - - - - - - icon / forward - - + + + + + + image/svg+xml + + + + + + + icon / forward + + diff --git a/resources/icons/home-button.svg b/resources/icons/home-button.svg index 6423d41ec..40e4792d5 100644 --- a/resources/icons/home-button.svg +++ b/resources/icons/home-button.svg @@ -1,17 +1,17 @@ - - -icon / home-button - - - - + + +icon / home-button + + + + diff --git a/resources/icons/kiwix-app-icons-square.svg b/resources/icons/kiwix-app-icons-square.svg index 2ab6edcd2..1456c201e 100644 --- a/resources/icons/kiwix-app-icons-square.svg +++ b/resources/icons/kiwix-app-icons-square.svg @@ -1,75 +1,75 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/kiwix-logo.svg b/resources/icons/kiwix-logo.svg index 566b1064b..6ccf74fc6 100644 --- a/resources/icons/kiwix-logo.svg +++ b/resources/icons/kiwix-logo.svg @@ -1,20 +1,20 @@ - - - - - - + + + + + + diff --git a/resources/icons/kiwix/app_icon_source.svg b/resources/icons/kiwix/app_icon_source.svg index 2ab6edcd2..1456c201e 100644 --- a/resources/icons/kiwix/app_icon_source.svg +++ b/resources/icons/kiwix/app_icon_source.svg @@ -1,75 +1,75 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/kiwix/scalable/kiwix-desktop.svg b/resources/icons/kiwix/scalable/kiwix-desktop.svg index fd30d8209..4c91caa97 100644 --- a/resources/icons/kiwix/scalable/kiwix-desktop.svg +++ b/resources/icons/kiwix/scalable/kiwix-desktop.svg @@ -1,21 +1,21 @@ - - - - - - - + + + + + + + diff --git a/resources/icons/open-file.svg b/resources/icons/open-file.svg index a62336d8b..59807c494 100644 --- a/resources/icons/open-file.svg +++ b/resources/icons/open-file.svg @@ -1,23 +1,23 @@ - - - - -Created by potrace 1.15, written by Peter Selinger 2001-2017 - - - - - + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + diff --git a/resources/icons/random.svg b/resources/icons/random.svg index 93c71d1ae..4a4cd7ef1 100755 --- a/resources/icons/random.svg +++ b/resources/icons/random.svg @@ -1,9 +1,9 @@ - - icon / random - - - - - + + icon / random + + + + + \ No newline at end of file diff --git a/resources/icons/search.svg b/resources/icons/search.svg index c9ad2463d..7af1451d0 100755 --- a/resources/icons/search.svg +++ b/resources/icons/search.svg @@ -1,77 +1,77 @@ - - - - - - image/svg+xml - - icon / search - - - - - - - - - icon / search - - + + + + + + image/svg+xml + + icon / search + + + + + + + + + icon / search + + diff --git a/resources/icons/search_backward.svg b/resources/icons/search_backward.svg index f9df3b2ab..e13ba971f 100644 --- a/resources/icons/search_backward.svg +++ b/resources/icons/search_backward.svg @@ -1,58 +1,58 @@ - - - - - - image/svg+xml - - icon / back - - - - - - icon / back - - + + + + + + image/svg+xml + + icon / back + + + + + + icon / back + + diff --git a/resources/icons/search_forward.svg b/resources/icons/search_forward.svg index 1063a29e8..eb6023183 100644 --- a/resources/icons/search_forward.svg +++ b/resources/icons/search_forward.svg @@ -1,58 +1,58 @@ - - - - - - image/svg+xml - - icon / back - - - - - - icon / back - - + + + + + + image/svg+xml + + icon / back + + + + + + icon / back + + diff --git a/resources/icons/xmark-solid.svg b/resources/icons/xmark-solid.svg index c693bd00a..af7a0944b 100644 --- a/resources/icons/xmark-solid.svg +++ b/resources/icons/xmark-solid.svg @@ -1 +1 @@ - + diff --git a/resources/js.qrc b/resources/js.qrc index 839123950..fe92d065b 100644 --- a/resources/js.qrc +++ b/resources/js.qrc @@ -1,5 +1,5 @@ - - - js/headerAnchor.js - - + + + js/headerAnchor.js + + diff --git a/resources/js/headerAnchor.js b/resources/js/headerAnchor.js index 044d9cc56..eaf7db01f 100644 --- a/resources/js/headerAnchor.js +++ b/resources/js/headerAnchor.js @@ -1,53 +1,156 @@ -function isHeaderElement(elem) -{ - return elem.nodeName.match(/^H\d+$/) && elem.textContent; -} - -function getDOMElementsPreorderDFS(elem, pred) -{ - var result = []; - if (pred(elem)) - result.push(elem); - - for ( const child of elem.children) - result = result.concat(getDOMElementsPreorderDFS(child, pred)); - return result; -} - -function anchorHeaderElements(headers) -{ - return Array.from(headers, function(elem, i) - { - const text = elem.textContent.trim().replace(/"/g, '\\"'); - const level = parseInt(elem.nodeName.substr(1)); - const anchor = `kiwix-toc-${i}`; - - const anchorElem = document.createElement("a"); - anchorElem.id = anchor; - - /* Mark header content with something we can reference. */ - elem.insertAdjacentElement("afterbegin", anchorElem); - return { text, level, anchor }; - }); -} - -function getHeadersJSONStr() -{ - const headerInfo = { url: window.location.href.replace(location.hash,""), headers: [] }; - - if (document.body !== undefined) - { - const headers = getDOMElementsPreorderDFS(document.body, isHeaderElement); - headerInfo.headers = anchorHeaderElements(headers); - } - return JSON.stringify(headerInfo); -} - -new QWebChannel(qt.webChannelTransport, function(channel) { - var kiwixObj = channel.objects.kiwixChannelObj; - kiwixObj.sendHeadersJSONStr(getHeadersJSONStr()); - kiwixObj.navigationRequested.connect(function(url, anchor) { - if (window.location.href.replace(location.hash,"") == url) - document.getElementById(anchor).scrollIntoView(); - }); -}); +function isHeaderElement(elem) +{ + return elem.nodeName.match(/^H\d+$/) && elem.textContent; +} + +function getDOMElementsPreorderDFS(elem, pred) +{ + var result = []; + if (pred(elem)) + result.push(elem); + + for ( const child of elem.children) + result = result.concat(getDOMElementsPreorderDFS(child, pred)); + return result; +} + +function anchorHeaderElements(headers) +{ + return Array.from(headers, function(elem, i) + { + const text = elem.textContent.trim().replace(/"/g, '\\"'); + const level = parseInt(elem.nodeName.substr(1)); + const anchor = `kiwix-toc-${i}`; + + const anchorElem = document.createElement("a"); + anchorElem.id = anchor; + + /* Mark header content with something we can reference. */ + elem.insertAdjacentElement("afterbegin", anchorElem); + return { text, level, anchor }; + }); +} + +function getHeadersJSONStr() +{ + const headerInfo = { url: window.location.href.replace(location.hash,""), headers: [] }; + + if (document.body !== undefined) + { + const headers = getDOMElementsPreorderDFS(document.body, isHeaderElement); + headerInfo.headers = anchorHeaderElements(headers); + } + return JSON.stringify(headerInfo); +} + +// Track the current anchor for history state +let currentAnchor = null; +let isNavigating = false; + +// Function to scroll to an anchor with animation +function scrollToAnchor(anchor, updateHistory = false) { + if (!anchor || typeof anchor !== 'string') { + console.error("Invalid anchor:", anchor); + return false; + } + + if (isNavigating || anchor === currentAnchor) { + console.log("Already navigating to or at anchor: " + anchor); + return true; + } + + try { + isNavigating = true; + const element = document.getElementById(anchor); + if (element) { + setTimeout(() => { + element.scrollIntoView({behavior: 'smooth'}); + currentAnchor = anchor; + + // Update the URL in history if requested + if (updateHistory && window.history && window.history.pushState) { + try { + const baseUrl = window.location.href.replace(location.hash, ""); + window.history.pushState({ anchor: anchor }, "", baseUrl + "#" + anchor); + } catch (historyError) { + console.error("Error updating history:", historyError); + } + } + + // Reset navigation flag after a short delay + setTimeout(() => { + isNavigating = false; + }, 100); + }, 10); + + return true; + } + console.error("Anchor not found: " + anchor); + isNavigating = false; + return false; + } catch (error) { + console.error("Error scrolling to anchor:", error); + isNavigating = false; + return false; + } +} + +function initializeWebChannel() { + try { + if (typeof qt === 'undefined' || typeof qt.webChannelTransport === 'undefined') { + console.error("Qt WebChannel not available"); + return; + } + + new QWebChannel(qt.webChannelTransport, function(channel) { + if (!channel || !channel.objects || !channel.objects.kiwixChannelObj) { + console.error("Kiwix channel object not available"); + return; + } + + var kiwixObj = channel.objects.kiwixChannelObj; + + try { + kiwixObj.sendHeadersJSONStr(getHeadersJSONStr()); + } catch (e) { + console.error("Error sending headers:", e); + } + + kiwixObj.navigationRequested.connect(function(url, anchor) { + if (isNavigating || anchor === currentAnchor) { + return; + } + + if (window.location.href.replace(location.hash, "") == url) { + scrollToAnchor(anchor, false); + } + }); + + window.addEventListener('popstate', function(event) { + if (isNavigating) { + return; + } + + // Handle navigation from history + if (location.hash) { + const anchor = location.hash.substring(1); + if (anchor !== currentAnchor) { + scrollToAnchor(anchor, false); + } + } else if (event.state && event.state.anchor) { + if (event.state.anchor !== currentAnchor) { + scrollToAnchor(event.state.anchor, false); + } + } + }); + }); + } catch (error) { + console.error("Error initializing web channel:", error); + } +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeWebChannel); +} else { + initializeWebChannel(); +} diff --git a/resources/kiwix.qrc b/resources/kiwix.qrc index ddb7f3fc7..c71582853 100644 --- a/resources/kiwix.qrc +++ b/resources/kiwix.qrc @@ -1,72 +1,72 @@ - - - icons/input-number.svg - icons/minimize.svg - icons/download.svg - icons/checkbox.svg - icons/history-left.svg - icons/history-right.svg - icons/help.svg - icons/tick.svg - icons/menu.svg - icons/more.svg - icons/add.svg - icons/exit.svg - icons/donate.svg - icons/star.svg - icons/search.svg - icons/close.svg - icons/copy.svg - icons/star-active.svg - icons/search-inactive.svg - icons/checkbox-indeterminate.svg - icons/add-to-reading-list.svg - icons/checkbox-active.svg - icons/pictures.svg - icons/search-filter.svg - icons/pause-download.svg - icons/folder.svg - icons/sidebar.svg - icons/share.svg - icons/blank.svg - icons/print.svg - icons/random.svg - icons/full-screen-exit.svg - icons/full-screen-enter.svg - icons/search-toc.svg - icons/downloading.svg - icons/toc.svg - icons/settings.svg - icons/filter.svg - icons/reading-list.svg - icons/reading-list-active.svg - icons/kiwix/scalable/kiwix-desktop.svg - fonts/Selawik/LICENSE.txt - fonts/Selawik/selawkb.ttf - fonts/Selawik/selawkl.ttf - fonts/Selawik/selawksb.ttf - fonts/Selawik/selawksl.ttf - fonts/Selawik/selawk.ttf - texts/about.html - icons/search_backward.svg - icons/search_forward.svg - icons/kiwix-app-icons-square.svg - icons/play-button.png - icons/pause-button.png - icons/cancel-button.png - icons/new-tab-icon.svg - icons/open-file.svg - icons/placeholder-icon.png - icons/caret-down-solid.svg - icons/caret-right-solid.svg - icons/caret-up-solid.svg - icons/kiwix-logo.svg - icons/check-solid.svg - icons/xmark-solid.svg - icons/home-button.svg - icons/more-vertical.svg - icons/caret-left-solid.svg - icons/stop-circle.svg - icons/drop-down.svg - - + + + icons/input-number.svg + icons/minimize.svg + icons/download.svg + icons/checkbox.svg + icons/history-left.svg + icons/history-right.svg + icons/help.svg + icons/tick.svg + icons/menu.svg + icons/more.svg + icons/add.svg + icons/exit.svg + icons/donate.svg + icons/star.svg + icons/search.svg + icons/close.svg + icons/copy.svg + icons/star-active.svg + icons/search-inactive.svg + icons/checkbox-indeterminate.svg + icons/add-to-reading-list.svg + icons/checkbox-active.svg + icons/pictures.svg + icons/search-filter.svg + icons/pause-download.svg + icons/folder.svg + icons/sidebar.svg + icons/share.svg + icons/blank.svg + icons/print.svg + icons/random.svg + icons/full-screen-exit.svg + icons/full-screen-enter.svg + icons/search-toc.svg + icons/downloading.svg + icons/toc.svg + icons/settings.svg + icons/filter.svg + icons/reading-list.svg + icons/reading-list-active.svg + icons/kiwix/scalable/kiwix-desktop.svg + fonts/Selawik/LICENSE.txt + fonts/Selawik/selawkb.ttf + fonts/Selawik/selawkl.ttf + fonts/Selawik/selawksb.ttf + fonts/Selawik/selawksl.ttf + fonts/Selawik/selawk.ttf + texts/about.html + icons/search_backward.svg + icons/search_forward.svg + icons/kiwix-app-icons-square.svg + icons/play-button.png + icons/pause-button.png + icons/cancel-button.png + icons/new-tab-icon.svg + icons/open-file.svg + icons/placeholder-icon.png + icons/caret-down-solid.svg + icons/caret-right-solid.svg + icons/caret-up-solid.svg + icons/kiwix-logo.svg + icons/check-solid.svg + icons/xmark-solid.svg + icons/home-button.svg + icons/more-vertical.svg + icons/caret-left-solid.svg + icons/stop-circle.svg + icons/drop-down.svg + + diff --git a/resources/org.kiwix.desktop-mime.xml b/resources/org.kiwix.desktop-mime.xml index b8f110135..ccd4a522d 100644 --- a/resources/org.kiwix.desktop-mime.xml +++ b/resources/org.kiwix.desktop-mime.xml @@ -1,13 +1,13 @@ - - - - ZIM archive - - - - - - - - - + + + + ZIM archive + + + + + + + + + diff --git a/resources/org.kiwix.desktop.appdata.xml b/resources/org.kiwix.desktop.appdata.xml index a4d8e18be..4d67afa44 100644 --- a/resources/org.kiwix.desktop.appdata.xml +++ b/resources/org.kiwix.desktop.appdata.xml @@ -1,67 +1,67 @@ - - - org.kiwix.desktop - Kiwix - View offline content - The Kiwix Foundation - -

- Kiwix is dedicated to providing offline access to educational content. -

- Kiwix makes knowledge available to people with no or limited Internet access. -

- Kiwix is an offline reader for content like Wikipedia, Project Gutenberg, TED Talks, IFixit... and thousands of others. -

- Kiwix deals with highly compressed snapshots of Websites that each fit into a single (.zim) file. ZIM files are small enough that they can be stored on users’ mobile phones, computers or small, inexpensive WiFi Hotspot. -

- Kiwix then acts like a regular Web browser, except that it reads these local copies. People with no or limited internet access can enjoy the same browsing experience as being online. -

- The name “Kiwix” is a play on the word “Wiki” as it represented our initial goal of making Wikipedia accessible offline. -

- Kiwix is free and open-source. -

- - - - - Most important improvements: -
    -
  • Revamping of the internal Library
  • -
  • Robuster download manager
  • -
  • Far better portable version
  • -
  • New table of content
  • -
  • Revamped search box
  • -
- ... here is the complete changelog -
-
- - org.kiwix.desktop.desktop - CC0-1.0 - GPL-3.0+ - /~https://github.com/kiwix/kiwix-desktop - https://www.kiwix.org/support/ - https://www.kiwix.org/faq/ - https://www.kiwix.org/ - contact_AT_kiwix.org - - - https://upload.wikimedia.org/wikipedia/commons/b/b4/Kiwix-desktop-2.4.1-article-with-toc.png - - - https://upload.wikimedia.org/wikipedia/commons/6/6c/Kiwix-desktop-2.4.1-multitab-gutenberg.png - - - https://upload.wikimedia.org/wikipedia/commons/8/81/Kiwix-desktop-2.4.1-library-med.png - - - https://upload.wikimedia.org/wikipedia/commons/a/ac/Kiwix-desktop-2.4.1-ted-bookmarks.png - - - https://upload.wikimedia.org/wikipedia/commons/2/2e/Kiwix-desktop-2.4.1-wiktionary-search.png - - - https://upload.wikimedia.org/wikipedia/commons/3/39/Kiwix-desktop-2.4.1-kiwix-server.png - - - + + + org.kiwix.desktop + Kiwix + View offline content + The Kiwix Foundation + +

+ Kiwix is dedicated to providing offline access to educational content. +

+ Kiwix makes knowledge available to people with no or limited Internet access. +

+ Kiwix is an offline reader for content like Wikipedia, Project Gutenberg, TED Talks, IFixit... and thousands of others. +

+ Kiwix deals with highly compressed snapshots of Websites that each fit into a single (.zim) file. ZIM files are small enough that they can be stored on users’ mobile phones, computers or small, inexpensive WiFi Hotspot. +

+ Kiwix then acts like a regular Web browser, except that it reads these local copies. People with no or limited internet access can enjoy the same browsing experience as being online. +

+ The name “Kiwix” is a play on the word “Wiki” as it represented our initial goal of making Wikipedia accessible offline. +

+ Kiwix is free and open-source. +

+
+ + + + Most important improvements: +
    +
  • Revamping of the internal Library
  • +
  • Robuster download manager
  • +
  • Far better portable version
  • +
  • New table of content
  • +
  • Revamped search box
  • +
+ ... here is the complete changelog +
+
+ + org.kiwix.desktop.desktop + CC0-1.0 + GPL-3.0+ + /~https://github.com/kiwix/kiwix-desktop + https://www.kiwix.org/support/ + https://www.kiwix.org/faq/ + https://www.kiwix.org/ + contact_AT_kiwix.org + + + https://upload.wikimedia.org/wikipedia/commons/b/b4/Kiwix-desktop-2.4.1-article-with-toc.png + + + https://upload.wikimedia.org/wikipedia/commons/6/6c/Kiwix-desktop-2.4.1-multitab-gutenberg.png + + + https://upload.wikimedia.org/wikipedia/commons/8/81/Kiwix-desktop-2.4.1-library-med.png + + + https://upload.wikimedia.org/wikipedia/commons/a/ac/Kiwix-desktop-2.4.1-ted-bookmarks.png + + + https://upload.wikimedia.org/wikipedia/commons/2/2e/Kiwix-desktop-2.4.1-wiktionary-search.png + + + https://upload.wikimedia.org/wikipedia/commons/3/39/Kiwix-desktop-2.4.1-kiwix-server.png + + +
diff --git a/resources/org.kiwix.desktop.desktop b/resources/org.kiwix.desktop.desktop index c636da3fa..3c5f0961b 100644 --- a/resources/org.kiwix.desktop.desktop +++ b/resources/org.kiwix.desktop.desktop @@ -1,10 +1,10 @@ -[Desktop Entry] -Type=Application -Name=Kiwix -Comment=View offline content -Icon=kiwix-desktop -Exec=kiwix-desktop %F -Terminal=false -MimeType=application/org.kiwix.desktop.x-zim; -Keywords=zim; -Categories=Education; +[Desktop Entry] +Type=Application +Name=Kiwix +Comment=View offline content +Icon=kiwix-desktop +Exec=kiwix-desktop %F +Terminal=false +MimeType=application/org.kiwix.desktop.x-zim; +Keywords=zim; +Categories=Education; diff --git a/resources/settingsmanager.qrc b/resources/settingsmanager.qrc index 92722e95a..d6fc0d6ab 100644 --- a/resources/settingsmanager.qrc +++ b/resources/settingsmanager.qrc @@ -1,5 +1,5 @@ - - - css/_settingsManager.css - - + + + css/_settingsManager.css + + diff --git a/resources/style.qrc b/resources/style.qrc index 864bb7fba..c8ac0ce75 100644 --- a/resources/style.qrc +++ b/resources/style.qrc @@ -1,10 +1,10 @@ - - - css/style.css - css/popup.css - css/localServer.css - css/messageBox.css - css/contentmanagerside.css - css/choiceBox.css - - + + + css/style.css + css/popup.css + css/localServer.css + css/messageBox.css + css/contentmanagerside.css + css/choiceBox.css + + diff --git a/resources/texts/about.html b/resources/texts/about.html index 3d71424b0..58d66fee0 100644 --- a/resources/texts/about.html +++ b/resources/texts/about.html @@ -1,29 +1,29 @@ - - - - -

{{KIWIX_DESKTOP_TITLE}}

-

{{KIWIX_DESKTOP_DESCRIPTION}}

-

{{LEARN_MORE_ABOUT_KIWIX}}

-

{{PRIVACY_POLICY}}

-

https://kiwix.org/privacy-policy/

-

{{RELEASE}}

-

{{SOURCE_CODE}}

-

{{VERSION_TXT}}

-

{{REPORTING_PROBLEM}}

-

{{REPORT_ISSUE}} -

{{REPORT_ISSUE_2}}

-

{{LIBRARIES}}

- - + + + + +

{{KIWIX_DESKTOP_TITLE}}

+

{{KIWIX_DESKTOP_DESCRIPTION}}

+

{{LEARN_MORE_ABOUT_KIWIX}}

+

{{PRIVACY_POLICY}}

+

https://kiwix.org/privacy-policy/

+

{{RELEASE}}

+

{{SOURCE_CODE}}

+

{{VERSION_TXT}}

+

{{REPORTING_PROBLEM}}

+

{{REPORT_ISSUE}} +

{{REPORT_ISSUE_2}}

+

{{LIBRARIES}}

+ + diff --git a/resources/translations.qrc b/resources/translations.qrc index f879482ba..f0576bb12 100644 --- a/resources/translations.qrc +++ b/resources/translations.qrc @@ -1,67 +1,67 @@ - - - - i18n/ar.json - i18n/bcl.json - i18n/bn.json - i18n/br.json - i18n/ca.json - i18n/cs.json - i18n/da.json - i18n/de.json - i18n/diq.json - i18n/en.json - i18n/eo.json - i18n/es.json - i18n/fa.json - i18n/fi.json - i18n/fr.json - i18n/gsw.json - i18n/he.json - i18n/hi.json - i18n/hy.json - i18n/ia.json - i18n/id.json - i18n/ie.json - i18n/io.json - i18n/it.json - i18n/ja.json - i18n/ko.json - i18n/ku-latn.json - i18n/lb.json - i18n/mk.json - i18n/ms.json - i18n/my.json - i18n/nb.json - i18n/nl.json - i18n/nqo.json - i18n/or.json - i18n/pl.json - i18n/pt-br.json - i18n/pt.json - i18n/qqq.json - i18n/ro.json - i18n/roa-tara.json - i18n/ru.json - i18n/sc.json - i18n/scn.json - i18n/sdc.json - i18n/se.json - i18n/sk.json - i18n/skr-arab.json - i18n/sl.json - i18n/sms.json - i18n/sq.json - i18n/sr-ec.json - i18n/sro.json - i18n/sv.json - i18n/ta.json - i18n/te.json - i18n/th.json - i18n/tr.json - i18n/uk.json - i18n/zgh.json - i18n/zh-hans.json - i18n/zh-hant.json - + + + + i18n/ar.json + i18n/bcl.json + i18n/bn.json + i18n/br.json + i18n/ca.json + i18n/cs.json + i18n/da.json + i18n/de.json + i18n/diq.json + i18n/en.json + i18n/eo.json + i18n/es.json + i18n/fa.json + i18n/fi.json + i18n/fr.json + i18n/gsw.json + i18n/he.json + i18n/hi.json + i18n/hy.json + i18n/ia.json + i18n/id.json + i18n/ie.json + i18n/io.json + i18n/it.json + i18n/ja.json + i18n/ko.json + i18n/ku-latn.json + i18n/lb.json + i18n/mk.json + i18n/ms.json + i18n/my.json + i18n/nb.json + i18n/nl.json + i18n/nqo.json + i18n/or.json + i18n/pl.json + i18n/pt-br.json + i18n/pt.json + i18n/qqq.json + i18n/ro.json + i18n/roa-tara.json + i18n/ru.json + i18n/sc.json + i18n/scn.json + i18n/sdc.json + i18n/se.json + i18n/sk.json + i18n/skr-arab.json + i18n/sl.json + i18n/sms.json + i18n/sq.json + i18n/sr-ec.json + i18n/sro.json + i18n/sv.json + i18n/ta.json + i18n/te.json + i18n/th.json + i18n/tr.json + i18n/uk.json + i18n/zgh.json + i18n/zh-hans.json + i18n/zh-hant.json + \ No newline at end of file diff --git a/scripts/pkg-config-wrapper.py b/scripts/pkg-config-wrapper.py index cb89b859a..976f1384c 100644 --- a/scripts/pkg-config-wrapper.py +++ b/scripts/pkg-config-wrapper.py @@ -1,59 +1,59 @@ -#!/usr/bin/env python - -# On Windows, static libraries are using "foo.lib" naming. -# On Unix, they are in the form of "libfoo.a" -# On Windows, "foo.lib" can also the definition of symbols in a foo.dll. -# So you can link to "foo.lib", whatever you are doing static or dynamic linking and you are good. -# However, meson is always creating static library as "libfoo.a" -# 'to avoid a potential name clash with shared libraries which also generate import libraries with a lib suffix.' [1] -# On top of that, qmake is replacing all `-lfoo` in LIBS by `foo.lib` (on Windows). -# So at the end, we try to link with `foo.lib` but we have `libfoo.a` -# Solution could be : -# - Rename `libfoo.a` to `foo.lib`, but it would mean modify deps libraries on the FS -# - Don't use LIBS and directly set QMAKE_LFLAGS but we would have to handle different command line option format -# between g++/clang and msvc -# - Update meson build system of each projet to explicitly set the library naming. -# - Replace `-lfoo` with absolute path to static library. This is what meson is doing internally and what -# we are doing here -# -# Any `-lfoo` is replace with absolute path to static library (`libfoo.a`) if we found one. -# Else, it is keep unchanged. -# -# [1] https://mesonbuild.com/Reference-manual_functions.html#library_name_suffix - -import sys, subprocess -from pathlib import Path - - -def forward_to_pkg_config(): - completeProcess = subprocess.run( - ["pkg-config", *sys.argv[1:]], capture_output=True, check=True, text=True - ) - return completeProcess.stdout - - -def search_static_lib(lib_name, search_paths): - for path in search_paths: - lib_path = path / f"lib{lib_name}.a" - if lib_path.exists(): - return str(lib_path) - return None - - -def replace_static_lib(pkg_output): - search_paths = [] - for option in pkg_output.split(): - if option.startswith("-L"): - search_paths.append(Path(option[2:])) - yield option - if option.startswith("-l"): - static_lib = search_static_lib(option[2:], search_paths) - if static_lib: - yield static_lib - else: - yield option - - -if __name__ == "__main__": - pkg_output = forward_to_pkg_config() - print(" ".join(replace_static_lib(pkg_output))) +#!/usr/bin/env python + +# On Windows, static libraries are using "foo.lib" naming. +# On Unix, they are in the form of "libfoo.a" +# On Windows, "foo.lib" can also the definition of symbols in a foo.dll. +# So you can link to "foo.lib", whatever you are doing static or dynamic linking and you are good. +# However, meson is always creating static library as "libfoo.a" +# 'to avoid a potential name clash with shared libraries which also generate import libraries with a lib suffix.' [1] +# On top of that, qmake is replacing all `-lfoo` in LIBS by `foo.lib` (on Windows). +# So at the end, we try to link with `foo.lib` but we have `libfoo.a` +# Solution could be : +# - Rename `libfoo.a` to `foo.lib`, but it would mean modify deps libraries on the FS +# - Don't use LIBS and directly set QMAKE_LFLAGS but we would have to handle different command line option format +# between g++/clang and msvc +# - Update meson build system of each projet to explicitly set the library naming. +# - Replace `-lfoo` with absolute path to static library. This is what meson is doing internally and what +# we are doing here +# +# Any `-lfoo` is replace with absolute path to static library (`libfoo.a`) if we found one. +# Else, it is keep unchanged. +# +# [1] https://mesonbuild.com/Reference-manual_functions.html#library_name_suffix + +import sys, subprocess +from pathlib import Path + + +def forward_to_pkg_config(): + completeProcess = subprocess.run( + ["pkg-config", *sys.argv[1:]], capture_output=True, check=True, text=True + ) + return completeProcess.stdout + + +def search_static_lib(lib_name, search_paths): + for path in search_paths: + lib_path = path / f"lib{lib_name}.a" + if lib_path.exists(): + return str(lib_path) + return None + + +def replace_static_lib(pkg_output): + search_paths = [] + for option in pkg_output.split(): + if option.startswith("-L"): + search_paths.append(Path(option[2:])) + yield option + if option.startswith("-l"): + static_lib = search_static_lib(option[2:], search_paths) + if static_lib: + yield static_lib + else: + yield option + + +if __name__ == "__main__": + pkg_output = forward_to_pkg_config() + print(" ".join(replace_static_lib(pkg_output))) diff --git a/src/about.cpp b/src/about.cpp index 0bee73f1f..9b0317365 100644 --- a/src/about.cpp +++ b/src/about.cpp @@ -1,32 +1,32 @@ -#include "about.h" -#include "ui_about.h" -#include "kiwixapp.h" - -About::About(QWidget *parent) : - QDialog(parent), - ui(new Ui::AboutDialog) -{ - ui->setupUi(this); - auto htmlText = ui->aboutText->toHtml(); - htmlText = htmlText.replace("{{KIWIX_DESKTOP_TITLE}}", gt("about-kiwix-desktop-title")); - htmlText = htmlText.replace("{{KIWIX_DESKTOP_DESCRIPTION}}", gt("about-kiwix-desktop-description")); - htmlText = htmlText.replace("{{LEARN_MORE_ABOUT_KIWIX}}", gt("about-learn-more-about-kiwix")); - htmlText = htmlText.replace("{{PRIVACY_POLICY}}", gt("about-privacy-policy")); - htmlText = htmlText.replace("{{RELEASE}}", gt("about-release-title")); - htmlText = htmlText.replace("{{SOURCE_CODE}}", gt("about-source-code")); - htmlText = htmlText.replace("{{VERSION_TXT}}", gt("about-version")); - htmlText = htmlText.replace("{{REPORTING_PROBLEM}}", gt("about-reporting-problem-title")); - htmlText = htmlText.replace("{{REPORT_ISSUE}}", gt("about-report-issue")); - htmlText = htmlText.replace("{{REPORT_ISSUE_2}}", gt("about-report-issue-2")); - htmlText = htmlText.replace("{{LIBRARIES}}", gt("about-libraries-title")); - - htmlText = htmlText.replace("{{GITHUB_URL}}", "/~https://github.com/kiwix/kiwix-desktop"); - htmlText = htmlText.replace("{{VERSION}}", version); - htmlText = htmlText.replace("{{TRACKER_URL}}", "/~https://github.com/kiwix/kiwix-desktop/issues"); - ui->aboutText->setHtml(htmlText); -} - -About::~About() -{ - delete ui; -} +#include "about.h" +#include "ui_about.h" +#include "kiwixapp.h" + +About::About(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutDialog) +{ + ui->setupUi(this); + auto htmlText = ui->aboutText->toHtml(); + htmlText = htmlText.replace("{{KIWIX_DESKTOP_TITLE}}", gt("about-kiwix-desktop-title")); + htmlText = htmlText.replace("{{KIWIX_DESKTOP_DESCRIPTION}}", gt("about-kiwix-desktop-description")); + htmlText = htmlText.replace("{{LEARN_MORE_ABOUT_KIWIX}}", gt("about-learn-more-about-kiwix")); + htmlText = htmlText.replace("{{PRIVACY_POLICY}}", gt("about-privacy-policy")); + htmlText = htmlText.replace("{{RELEASE}}", gt("about-release-title")); + htmlText = htmlText.replace("{{SOURCE_CODE}}", gt("about-source-code")); + htmlText = htmlText.replace("{{VERSION_TXT}}", gt("about-version")); + htmlText = htmlText.replace("{{REPORTING_PROBLEM}}", gt("about-reporting-problem-title")); + htmlText = htmlText.replace("{{REPORT_ISSUE}}", gt("about-report-issue")); + htmlText = htmlText.replace("{{REPORT_ISSUE_2}}", gt("about-report-issue-2")); + htmlText = htmlText.replace("{{LIBRARIES}}", gt("about-libraries-title")); + + htmlText = htmlText.replace("{{GITHUB_URL}}", "/~https://github.com/kiwix/kiwix-desktop"); + htmlText = htmlText.replace("{{VERSION}}", version); + htmlText = htmlText.replace("{{TRACKER_URL}}", "/~https://github.com/kiwix/kiwix-desktop/issues"); + ui->aboutText->setHtml(htmlText); +} + +About::~About() +{ + delete ui; +} diff --git a/src/about.h b/src/about.h index 433b1ef4a..faf898dd3 100644 --- a/src/about.h +++ b/src/about.h @@ -1,22 +1,22 @@ -#ifndef ABOUT_H -#define ABOUT_H - -#include - -namespace Ui { -class AboutDialog; -} - -class About : public QDialog -{ - Q_OBJECT - -public: - explicit About(QWidget *parent = 0); - ~About(); - -private: - Ui::AboutDialog *ui; -}; - -#endif // ABOUT_H +#ifndef ABOUT_H +#define ABOUT_H + +#include + +namespace Ui { +class AboutDialog; +} + +class About : public QDialog +{ + Q_OBJECT + +public: + explicit About(QWidget *parent = 0); + ~About(); + +private: + Ui::AboutDialog *ui; +}; + +#endif // ABOUT_H diff --git a/src/blobbuffer.cpp b/src/blobbuffer.cpp index e11e59b15..78350878c 100644 --- a/src/blobbuffer.cpp +++ b/src/blobbuffer.cpp @@ -1,7 +1,7 @@ -#include "blobbuffer.h" - -BlobBuffer::BlobBuffer(zim::Blob blob) - : m_blob(blob) -{ - setData(blob.data(), blob.size()); -} +#include "blobbuffer.h" + +BlobBuffer::BlobBuffer(zim::Blob blob) + : m_blob(blob) +{ + setData(blob.data(), blob.size()); +} diff --git a/src/blobbuffer.h b/src/blobbuffer.h index 72c8fe9d3..6fa5ed2c6 100644 --- a/src/blobbuffer.h +++ b/src/blobbuffer.h @@ -1,18 +1,18 @@ -#ifndef BLOBBUFFER_H -#define BLOBBUFFER_H - -#include -#include - -class BlobBuffer : public QBuffer -{ - Q_OBJECT -public: - BlobBuffer(zim::Blob m_blob); - virtual ~BlobBuffer() = default; - -private: - zim::Blob m_blob; -}; - -#endif // BLOBBUFFER_H +#ifndef BLOBBUFFER_H +#define BLOBBUFFER_H + +#include +#include + +class BlobBuffer : public QBuffer +{ + Q_OBJECT +public: + BlobBuffer(zim::Blob m_blob); + virtual ~BlobBuffer() = default; + +private: + zim::Blob m_blob; +}; + +#endif // BLOBBUFFER_H diff --git a/src/choiceitem.cpp b/src/choiceitem.cpp index e63464580..6291fbec5 100644 --- a/src/choiceitem.cpp +++ b/src/choiceitem.cpp @@ -1,32 +1,32 @@ -#include "choiceitem.h" -#include "ui_choiceitem.h" -#include -#include -#include "kiwixapp.h" - -ChoiceItem::ChoiceItem(QString key, QString value, QWidget *parent) : - QWidget(parent), - ui(new Ui::ChoiceItem), - m_key(key), - m_value(value) -{ - ui->setupUi(this); - this->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/choiceBox.css")); - ui->itemLabel->setText(key); - ui->itemLabel->setToolTip(key); - connect(ui->closeButton, &QPushButton::clicked, [=](){ - emit(closeButtonClicked(ui->itemLabel->text())); - }); - ui->closeButton->setCursor(Qt::PointingHandCursor); -} - -ChoiceItem::~ChoiceItem() -{ - delete ui; -} - -void ChoiceItem::mousePressEvent(QMouseEvent *event) -{ - Q_UNUSED(event); - return; -} +#include "choiceitem.h" +#include "ui_choiceitem.h" +#include +#include +#include "kiwixapp.h" + +ChoiceItem::ChoiceItem(QString key, QString value, QWidget *parent) : + QWidget(parent), + ui(new Ui::ChoiceItem), + m_key(key), + m_value(value) +{ + ui->setupUi(this); + this->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/choiceBox.css")); + ui->itemLabel->setText(key); + ui->itemLabel->setToolTip(key); + connect(ui->closeButton, &QPushButton::clicked, [=](){ + emit(closeButtonClicked(ui->itemLabel->text())); + }); + ui->closeButton->setCursor(Qt::PointingHandCursor); +} + +ChoiceItem::~ChoiceItem() +{ + delete ui; +} + +void ChoiceItem::mousePressEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + return; +} diff --git a/src/choiceitem.h b/src/choiceitem.h index b03b11db6..c87c8ee48 100644 --- a/src/choiceitem.h +++ b/src/choiceitem.h @@ -1,32 +1,32 @@ -#ifndef CHOICEITEM_H -#define CHOICEITEM_H - -#include - -namespace Ui { -class ChoiceItem; -} - -class ChoiceItem : public QWidget -{ - Q_OBJECT - -public: - explicit ChoiceItem(QString key, QString value, QWidget *parent = nullptr); - ~ChoiceItem(); - QString getKey() { return m_key; } - QString getValue() { return m_value; } - -protected: - void mousePressEvent(QMouseEvent *event) override; - -private: - Ui::ChoiceItem *ui; - QString m_key; - QString m_value; - -signals: - void closeButtonClicked(QString); -}; - -#endif // CHOICEITEM_H +#ifndef CHOICEITEM_H +#define CHOICEITEM_H + +#include + +namespace Ui { +class ChoiceItem; +} + +class ChoiceItem : public QWidget +{ + Q_OBJECT + +public: + explicit ChoiceItem(QString key, QString value, QWidget *parent = nullptr); + ~ChoiceItem(); + QString getKey() { return m_key; } + QString getValue() { return m_value; } + +protected: + void mousePressEvent(QMouseEvent *event) override; + +private: + Ui::ChoiceItem *ui; + QString m_key; + QString m_value; + +signals: + void closeButtonClicked(QString); +}; + +#endif // CHOICEITEM_H diff --git a/src/choiceitem.ui b/src/choiceitem.ui index 19aae85ca..cd0f060fc 100644 --- a/src/choiceitem.ui +++ b/src/choiceitem.ui @@ -1,143 +1,143 @@ - - - ChoiceItem - - - - 0 - 0 - 119 - 35 - - - - - 0 - 0 - - - - - 0 - 30 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 3 - - - 3 - - - 3 - - - 3 - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - TextLabel - - - - - - - true - - - - 0 - 0 - - - - - 0 - 0 - - - - - 10 - 16777215 - - - - - - - - :/icons/xmark-solid.svg:/icons/xmark-solid.svg - - - - 10 - 20 - - - - true - - - - - - - - - - - - - + + + ChoiceItem + + + + 0 + 0 + 119 + 35 + + + + + 0 + 0 + + + + + 0 + 30 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + TextLabel + + + + + + + true + + + + 0 + 0 + + + + + 0 + 0 + + + + + 10 + 16777215 + + + + + + + + :/icons/xmark-solid.svg:/icons/xmark-solid.svg + + + + 10 + 20 + + + + true + + + + + + + + + + + + + diff --git a/src/contentmanager.cpp b/src/contentmanager.cpp index ff0604488..2f0f301c0 100644 --- a/src/contentmanager.cpp +++ b/src/contentmanager.cpp @@ -1,1075 +1,1075 @@ -#include "contentmanager.h" - -#include "kiwixapp.h" -#include -#include - -#include -#include -#include -#include -#include -#include "contentmanagermodel.h" -#include -#include -#include -#include "contentmanagerdelegate.h" -#include "node.h" -#include "rownode.h" -#include "descriptionnode.h" -#include "kiwixmessagebox.h" -#include -#include "contentmanagerheader.h" -#include - -#ifndef QT_NO_DEBUG -#define DBGOUT(X) qDebug().nospace() << "DBG: " << X -#else -#define DBGOUT(X) -#endif - -namespace -{ - - -SettingsManager* getSettingsManager() -{ - return KiwixApp::instance()->getSettingsManager(); -} - -// Opens the directory containing the input file path. -// parent is the widget serving as the parent for the error dialog in case of -// failure. -void openFileLocation(QString path, QWidget *parent = nullptr) -{ - QFileInfo fileInfo(path); - QDir dir = fileInfo.absoluteDir(); - bool dirOpen = dir.exists() && dir.isReadable() && QDesktopServices::openUrl(dir.absolutePath()); - if (!dirOpen) { - QString failedText = gt("couldnt-open-location-text"); - failedText = failedText.replace("{{FOLDER}}", "" + dir.absolutePath() + ""); - showInfoBox(gt("couldnt-open-location"), failedText, parent); - } -} - -} // unnamed namespace - -ContentManager::ContentManager(Library* library) - : DownloadManager(library), - mp_library(library), - mp_remoteLibrary(kiwix::Library::create()), - m_remoteLibraryManager() -{ - // mp_view will be passed to the tab who will take ownership, - // so, we don't need to delete it. - mp_view = new ContentManagerView(); - managerModel = new ContentManagerModel(this); - updateModel(); - auto treeView = mp_view->getView(); - treeView->setModel(managerModel); - treeView->show(); - - auto header = new ContentManagerHeader(Qt::Orientation::Horizontal, treeView); - treeView->setHeader(header); - header->setSectionResizeMode(0, QHeaderView::Fixed); - header->setSectionResizeMode(1, QHeaderView::Stretch); - header->setSectionResizeMode(2, QHeaderView::Fixed); - header->setSectionResizeMode(3, QHeaderView::Fixed); - header->setSectionResizeMode(4, QHeaderView::Fixed); - header->setDefaultAlignment(Qt::AlignLeft); - header->setStretchLastSection(false); - header->setSectionsClickable(true); - header->setHighlightSections(true); - treeView->setWordWrap(true); - treeView->resizeColumnToContents(4); - treeView->setColumnWidth(0, 70); - treeView->setColumnWidth(5, 120); - // TODO: set width for all columns based on viewport - treeView->setAllColumnsShowFocus(true); - - setCurrentLanguage(getSettingsManager()->getLanguageList()); - setCurrentCategoryFilter(getSettingsManager()->getCategoryList()); - setCurrentContentTypeFilter(getSettingsManager()->getContentType()); - connect(mp_library, &Library::booksChanged, this, [=]() {emit(this->booksChanged());}); - connect(this, &ContentManager::filterParamsChanged, this, &ContentManager::updateLibrary); - connect(this, &ContentManager::booksChanged, this, [=]() { - updateModel(); - setCategories(); - setLanguages(); - }); - connect(&m_remoteLibraryManager, &OpdsRequestManager::requestReceived, this, &ContentManager::updateRemoteLibrary); - connect(mp_view->getView(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onCustomContextMenu(const QPoint &))); - connect(this, &ContentManager::pendingRequest, mp_view, &ContentManagerView::showLoader); - connect(treeView, &QTreeView::doubleClicked, this, &ContentManager::openBookWithIndex); - connect(&m_remoteLibraryManager, &OpdsRequestManager::languagesReceived, this, &ContentManager::updateLanguages); - connect(&m_remoteLibraryManager, &OpdsRequestManager::categoriesReceived, this, &ContentManager::updateCategories); - setCategories(); - setLanguages(); - - connect(this, &DownloadManager::downloadUpdated, - this, &ContentManager::updateDownload); - - connect(this, &DownloadManager::downloadCancelled, - this, &ContentManager::downloadWasCancelled); - - connect(this, &DownloadManager::downloadDisappeared, - this, &ContentManager::downloadDisappeared); - - connect(this, &DownloadManager::error, this, &ContentManager::handleError); - - if ( DownloadManager::downloadingFunctionalityAvailable() ) { - startDownloadUpdaterThread(); - } - - connect(&m_watcher, &QFileSystemWatcher::directoryChanged, - this, &ContentManager::asyncUpdateLibraryFromDir); -} - -void ContentManager::updateModel() -{ - const auto bookIds = getBookIds(); - BookInfoList bookList; - QStringList keys = {"title", "tags", "date", "id", "size", "description", "favicon"}; - for (auto bookId : bookIds) { - auto mp = getBookInfos(bookId, keys); - bookList.append(mp); - } - - const DownloadManager& downloadMgr = *this; - managerModel->setBooksData(bookList, downloadMgr); -} - -void ContentManager::onCustomContextMenu(const QPoint &point) -{ - QModelIndex index = mp_view->getView()->indexAt(point); - if (!index.isValid()) - return; - QMenu contextMenu("optionsMenu", mp_view->getView()); - auto bookNode = static_cast(index.internalPointer()); - const auto id = bookNode->getBookId(); - - QAction menuDeleteBook(gt("delete-book"), this); - QAction menuOpenBook(gt("open-book"), this); - QAction menuDownloadBook(gt("download-book"), this); - QAction menuPauseBook(gt("pause-download"), this); - QAction menuResumeBook(gt("resume-download"), this); - QAction menuCancelBook(gt("cancel-download"), this); - QAction menuOpenFolder(gt("open-folder"), this); - QAction menuPreviewBook(gt("preview-book-in-web-browser"), this); - - const auto bookState = getBookState(id); - switch ( bookState ) { - case BookState::DOWNLOAD_PAUSED: - if ( getDownloadState(id)->getStatus() == DownloadState::PAUSED ) { - contextMenu.addAction(&menuResumeBook); - contextMenu.addAction(&menuCancelBook); - } - contextMenu.addAction(&menuPreviewBook); - break; - - case BookState::DOWNLOADING: - if ( getDownloadState(id)->getStatus() == DownloadState::DOWNLOADING ) { - contextMenu.addAction(&menuPauseBook); - contextMenu.addAction(&menuCancelBook); - } - contextMenu.addAction(&menuPreviewBook); - break; - - case BookState::AVAILABLE_LOCALLY_AND_HEALTHY: - case BookState::ERROR_MISSING_ZIM_FILE: - case BookState::ERROR_CORRUPTED_ZIM_FILE: - { - const auto book = mp_library->getBookById(id); - auto bookPath = QString::fromStdString(book.getPath()); - if ( bookState == BookState::AVAILABLE_LOCALLY_AND_HEALTHY ) { - contextMenu.addAction(&menuOpenBook); - } - contextMenu.addAction(&menuDeleteBook); - contextMenu.addAction(&menuOpenFolder); - connect(&menuOpenFolder, &QAction::triggered, [=]() { - openFileLocation(bookPath, mp_view); - }); - break; - } - - case BookState::AVAILABLE_ONLINE: - contextMenu.addAction(&menuDownloadBook); - contextMenu.addAction(&menuPreviewBook); - break; - - default: break; - } - - connect(&menuDeleteBook, &QAction::triggered, [=]() { - eraseBook(id); - }); - connect(&menuOpenBook, &QAction::triggered, [=]() { - openBook(id); - }); - connect(&menuDownloadBook, &QAction::triggered, [=]() { - downloadBook(id); - }); - connect(&menuPauseBook, &QAction::triggered, [=]() { - pauseBook(id, index); - }); - connect(&menuCancelBook, &QAction::triggered, [=]() { - cancelBook(id); - }); - connect(&menuResumeBook, &QAction::triggered, [=]() { - resumeBook(id, index); - }); - connect(&menuPreviewBook, &QAction::triggered, [=]() { - openBookPreview(id); - }); - - contextMenu.exec(mp_view->getView()->viewport()->mapToGlobal(point)); -} - -void ContentManager::setLocal(bool local) { - if (local == m_local) { - return; - } - m_local = local; - emit(filterParamsChanged()); - setCategories(); - setLanguages(); -} - -QStringList ContentManager::getTranslations(const QStringList &keys) -{ - QStringList translations; - - for(auto& key: keys) { - translations.append(KiwixApp::instance()->getText(key)); - } - return translations; -} - -void ContentManager::setCategories() -{ - QStringList categories; - if (m_local) { - auto categoryData = mp_library->getKiwixLibrary()->getBooksCategories(); - for (auto category : categoryData) { - auto categoryName = QString::fromStdString(category); - categories.push_back(categoryName); - } - m_categories = categories; - emit(categoriesLoaded(m_categories)); - return; - } - m_remoteLibraryManager.getCategoriesFromOpds(); -} - -void ContentManager::setLanguages() -{ - LanguageList languages; - if (m_local) { - auto languageData = mp_library->getKiwixLibrary()->getBooksLanguages(); - for (auto language : languageData) { - auto langCode = QString::fromStdString(language); - auto selfName = QString::fromStdString(kiwix::getLanguageSelfName(language)); - languages.push_back({selfName, langCode}); - } - m_languages = languages; - emit(languagesLoaded(m_languages)); - return; - } - m_remoteLibraryManager.getLanguagesFromOpds(); -} - -namespace -{ - -QString getBookTags(const kiwix::Book& b) -{ - QStringList tagList = QString::fromStdString(b.getTags()).split(';'); - QMap displayTagMap; - for(auto tag: tagList) { - if (tag[0] == '_') { - auto splitTag = tag.split(":"); - displayTagMap[splitTag[0]] = splitTag[1] == "yes" ? true:false; - } - } - QStringList displayTagList; - if (displayTagMap["_videos"]) displayTagList << QObject::tr("Videos"); - if (displayTagMap["_pictures"]) displayTagList << QObject::tr("Pictures"); - if (!displayTagMap["_details"]) displayTagList << QObject::tr("Introduction only"); - return displayTagList.join(", "); -} - -QString getFaviconUrl(const kiwix::Book& b) -{ - std::string url; - try { - auto item = b.getIllustration(48); - url = item->url; - } catch (...) { - } - return QString::fromStdString(url); -} - -QByteArray getFaviconData(const kiwix::Book& b) -{ - QByteArray qdata; - try { - // Try to obtain favicons only from local books (otherwise - // kiwix::Book::Illustration::getData() attempts to download the image - // on its own, whereas we want that operation to be performed - // asynchronously by ThumbnailDownloader). - if ( b.isPathValid() ) { - const auto illustration = b.getIllustration(48); - const std::string data = illustration->getData(); - - qdata = QByteArray::fromRawData(data.data(), data.size()); - qdata.detach(); // deep copy - } - } catch ( ... ) { - return QByteArray(); - } - - return qdata; -} - -QVariant getFaviconDataOrUrl(const kiwix::Book& b) -{ - const QByteArray data = getFaviconData(b); - return !data.isNull() ? QVariant(data) : QVariant(getFaviconUrl(b)); -} - -QVariant getBookAttribute(const kiwix::Book& b, const QString& a) -{ - if ( a == "id" ) return QString::fromStdString(b.getId()); - if ( a == "path" ) return QString::fromStdString(b.getPath()); - if ( a == "title" ) return QString::fromStdString(b.getTitle()); - if ( a == "description" ) return QString::fromStdString(b.getDescription()); - if ( a == "date" ) return QString::fromStdString(b.getDate()); - if ( a == "url" ) return QString::fromStdString(b.getUrl()); - if ( a == "name" ) return QString::fromStdString(b.getName()); - if ( a == "favicon") return getFaviconDataOrUrl(b); - if ( a == "size" ) return QString::number(b.getSize()); - if ( a == "tags" ) return getBookTags(b); - - return QVariant(); -} - -ContentManager::BookState getStateOfLocalBook(const kiwix::Book& book) -{ - if ( !book.isPathValid() ) { - return ContentManager::BookState::ERROR_MISSING_ZIM_FILE; - } - - // XXX: When a book is detected to be corrupted, information about that - // XXX: has to be recorded somewhere so that we can return - // XXX: ERROR_CORRUPTED_ZIM_FILE here - - return ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY; -} - -} // unnamed namespace - -ContentManager::BookInfo ContentManager::getBookInfos(QString id, const QStringList &keys) -{ - const kiwix::Book* b = nullptr; - try { - b = &mp_library->getBookById(id); - if ( ! b->getDownloadId().empty() ) { - // The book is still being downloaded and has been entered into the - // local library for technical reasons only. Get the book info from - // the remote library. - b = nullptr; - } - } catch (...) {} - - if ( !b ) { - try { - QMutexLocker locker(&remoteLibraryLocker); - b = &mp_remoteLibrary->getBookById(id.toStdString()); - } catch(...) {} - } - - BookInfo values; - for(auto& key: keys){ - values.insert(key, b ? getBookAttribute(*b, key) : ""); - } - - return values; -} - -ContentManager::BookState ContentManager::getBookState(QString bookId) -{ - if ( const auto downloadState = DownloadManager::getDownloadState(bookId) ) { - return downloadState->getStatus() == DownloadState::PAUSED - ? BookState::DOWNLOAD_PAUSED - : BookState::DOWNLOADING; - // TODO: a download may be in error state - } - - try { - const kiwix::Book& b = mp_library->getBookById(bookId); - return b.getDownloadId().empty() - ? getStateOfLocalBook(b) - : BookState::DOWNLOADING; - } catch (...) {} - - try { - QMutexLocker locker(&remoteLibraryLocker); - const kiwix::Book& b = mp_remoteLibrary->getBookById(bookId.toStdString()); - return !b.getUrl().empty() - ? BookState::AVAILABLE_ONLINE - : BookState::METADATA_ONLY; - } catch (...) {} - - return BookState::INVALID; -} - -void ContentManager::openBookWithIndex(const QModelIndex &index) -{ - auto bookNode = static_cast(index.internalPointer()); - const QString bookId = bookNode->getBookId(); - if ( getBookState(bookId) == BookState::AVAILABLE_LOCALLY_AND_HEALTHY ) { - openBook(bookId); - } -} - -void ContentManager::openBook(const QString &id) -{ - QUrl url("zim://"+id+".zim/"); - try { - KiwixApp::instance()->openUrl(url, true); - } catch (const std::exception& e) { - auto tabBar = KiwixApp::instance()->getTabWidget(); - tabBar->closeTab(1); - auto text = gt("zim-open-fail-text"); - text = text.replace("{{ZIM}}", QString::fromStdString(mp_library->getBookById(id).getPath())); - auto title = gt("zim-open-fail-title"); - KiwixApp::instance()->showMessage(text, title, QMessageBox::Warning); - mp_library->removeBookFromLibraryById(id); - tabBar->setCurrentIndex(0); - emit(booksChanged()); - } -} - -void ContentManager::openBookPreview(const QString &id) -{ - try { - QMutexLocker locker(&remoteLibraryLocker); - const std::string &downloadUrl = - mp_remoteLibrary->getBookById(id.toStdString()).getUrl(); - locker.unlock(); - - /* Extract the Zim name from the book's download URL */ - const auto zimNameStartIndex = downloadUrl.find_last_of('/') + 1; - const std::string& zimName = downloadUrl.substr( - zimNameStartIndex, - downloadUrl.find(".zim", zimNameStartIndex) - zimNameStartIndex); - - const QUrl previewUrl = getRemoteLibraryUrl() + "/viewer#" + zimName.c_str(); - QDesktopServices::openUrl(previewUrl); - } catch (...) {} -} - -void ContentManager::removeDownload(QString bookId) -{ - DownloadManager::removeDownload(bookId); - managerModel->setDownloadState(bookId, nullptr); -} - -void ContentManager::downloadDisappeared(QString bookId) -{ - removeDownload(bookId); - kiwix::Book bCopy; - try { - bCopy = mp_library->getBookById(bookId); - } catch ( const std::out_of_range& ) { - // If the download has disappeared as a result of some - // obscure chain of events, the book may have disappeared too. - return; - } - - bCopy.setDownloadId(""); - mp_library->getKiwixLibrary()->addOrUpdateBook(bCopy); - mp_library->save(); - emit(mp_library->booksChanged()); -} - -void ContentManager::downloadCompleted(QString bookId, QString path) -{ - removeDownload(bookId); - kiwix::Book bCopy(mp_library->getBookById(bookId)); - bCopy.setPath(QDir::toNativeSeparators(path).toStdString()); - bCopy.setDownloadId(""); - bCopy.setPathValid(true); - // removing book url so that download link in kiwix-serve is not displayed. - bCopy.setUrl(""); - mp_library->getKiwixLibrary()->addOrUpdateBook(bCopy); - mp_library->save(); - mp_library->bookmarksChanged(); - if (!m_local) { - emit(oneBookChanged(bookId)); - } else { - emit(mp_library->booksChanged()); - } -} - -void ContentManager::updateDownload(QString bookId, const DownloadInfo& downloadInfo) -{ - const auto downloadState = DownloadManager::getDownloadState(bookId); - if ( downloadState ) { - const auto downloadPath = downloadInfo["path"].toString(); - if ( downloadInfo["status"].toString() == "completed" ) { - downloadCompleted(bookId, downloadPath); - } else { - mp_library->updateBookBeingDownloaded(bookId, downloadPath); - downloadState->update(downloadInfo); - managerModel->updateDownload(bookId); - } - } -} - -void ContentManager::handleError(QString errSummary, QString errDetails) -{ - showErrorBox(KiwixAppError(errSummary, errDetails), mp_view); -} - -void ContentManager::downloadBook(const QString &id) -{ - kiwix::Book book = getRemoteOrLocalBook(id); - const auto downloadPath = getSettingsManager()->getDownloadDir(); - - try { - DownloadManager::checkThatBookCanBeDownloaded(book, downloadPath); - } catch ( const KiwixAppError& err ) { - showErrorBox(err, mp_view); - return; - } - - mp_library->addBookBeingDownloaded(book, downloadPath); - mp_library->save(); - - DownloadManager::addRequest(DownloadState::START, id); - const auto downloadState = DownloadManager::getDownloadState(id); - managerModel->setDownloadState(id, downloadState); -} - -// This function is called asynchronously in a worker thread processing all -// download operations. The call is initiated in downloadBook(). -void ContentManager::startDownload(QString id) -{ - kiwix::Book book = getRemoteOrLocalBook(id); - const auto downloadPath = getSettingsManager()->getDownloadDir(); - // downloadPath may be different from the value used in - // downloadBook(). This may happen in the following scenario: - // - // 1. aria2c is stuck because of having to save to - // slow storage (and the fact that it is a single-threaded - // application). This may result in startDownload() being - // called with significant delay after downloadBook(). - // - // 2. The user changes the download directory after starting - // a download. - // - // That's why the checkThatBookCanBeDownloaded() check is repeated here. - - std::string downloadId; - try { - DownloadManager::checkThatBookCanBeDownloaded(book, downloadPath); - downloadId = DownloadManager::startDownload(book, downloadPath); - } catch ( const KiwixAppError& err ) { - emit error(err.summary(), err.details()); - return; - } - - book.setDownloadId(downloadId); - mp_library->addBookBeingDownloaded(book, downloadPath); - mp_library->save(); - emit(oneBookChanged(id)); -} - -const kiwix::Book& ContentManager::getRemoteOrLocalBook(const QString &id) -{ - try { - QMutexLocker locker(&remoteLibraryLocker); - return mp_remoteLibrary->getBookById(id.toStdString()); - } catch (...) { - return mp_library->getBookById(id); - } -} - -QString ContentManager::getRemoteLibraryUrl() const -{ - auto host = m_remoteLibraryManager.getCatalogHost(); - auto port = m_remoteLibraryManager.getCatalogPort(); - return port == 443 ? "https://" + host - : "http://" + host + ":" + QString::number(port); -} - -static const char MSG_FOR_PREVENTED_RMSTAR_OPERATION[] = R"( - BUG: Errare humanum est. - BUG: Kiwix developers are human, but we try to ensure that our mistakes - BUG: don't cause harm to our users. - BUG: If we didn't detect this situation we could have erased a lot of files - BUG: on your computer. -)"; - -void ContentManager::eraseBookFilesFromComputer(const std::string& bookPath, bool moveToTrash) -{ -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - Q_UNUSED(moveToTrash); -#endif - const std::string dirPath = kiwix::removeLastPathElement(bookPath); - const std::string fileGlob = kiwix::getLastPathElement(bookPath) + "*"; - - if ( fileGlob == "*" ) { - std::cerr << MSG_FOR_PREVENTED_RMSTAR_OPERATION << std::endl; - return; - } - - QDir dir(QString::fromStdString(dirPath), QString::fromStdString(fileGlob)); - for(const QString& file: dir.entryList()) { -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - if (moveToTrash) - QFile::moveToTrash(dir.filePath(file)); - else -#endif - dir.remove(file); // moveToTrash will always be false here, no check required. - } -} - -QString formatText(QString text) -{ - QString finalText = "

"; - finalText += text; - finalText += ""; - return finalText; -} - -void ContentManager::reallyEraseBook(const QString& id, bool moveToTrash) -{ - auto tabBar = KiwixApp::instance()->getTabWidget(); - tabBar->closeTabsByZimId(id); - eraseBookFilesFromComputer(mp_library->getBookFilePath(id), moveToTrash); - mp_library->removeBookFromLibraryById(id); - mp_library->save(); - emit mp_library->bookmarksChanged(); - if (m_local) { - emit(bookRemoved(id)); - } else { - emit(oneBookChanged(id)); - } - getSettingsManager()->deleteSettings(id); - emit booksChanged(); -} - -void ContentManager::eraseBook(const QString& id) -{ - auto text = gt("delete-book-text"); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) - const auto moveToTrash = getSettingsManager()->getMoveToTrash(); -#else - const auto moveToTrash = false; // we do not support move to trash functionality for qt versions below 5.15 -#endif - if (moveToTrash) { - text += formatText(gt("move-files-to-trash-text")); - } else { - text += formatText(gt("perma-delete-files-text")); - } - text = text.replace("{{ZIM}}", QString::fromStdString(mp_library->getBookById(id).getTitle())); - showConfirmBox(gt("delete-book"), text, mp_view, [=]() { - reallyEraseBook(id, moveToTrash); - }); -} - -void ContentManager::pauseBook(const QString& id, QModelIndex index) -{ - DownloadManager::addRequest(DownloadState::PAUSE, id); - managerModel->triggerDataUpdateAt(index); -} - -void ContentManager::resumeBook(const QString& id, QModelIndex index) -{ - DownloadManager::addRequest(DownloadState::RESUME, id); - managerModel->triggerDataUpdateAt(index); -} - -void ContentManager::cancelBook(const QString& id) -{ - auto text = gt("cancel-download-text"); - text = text.replace("{{ZIM}}", QString::fromStdString(mp_library->getBookById(id).getTitle())); - showConfirmBox(gt("cancel-download"), text, mp_view, [=]() { - DownloadManager::addRequest(DownloadState::CANCEL, id); - }); -} - -void ContentManager::downloadWasCancelled(const QString& id) -{ - removeDownload(id); - - // incompleted downloaded file should be perma deleted - eraseBookFilesFromComputer(mp_library->getBookFilePath(id), false); - mp_library->removeBookFromLibraryById(id); - mp_library->save(); - emit(oneBookChanged(id)); -} - -void ContentManager::setCurrentLanguage(FilterList langPairList) -{ - QStringList languageList; - for (auto &langPair : langPairList) { - languageList.append(langPair.second); - } - languageList.sort(); - for (auto &language : languageList) { - if (language.length() == 2) { - try { - language = QString::fromStdString( - kiwix::converta2toa3(language.toStdString())); - } catch (std::out_of_range&) {} - } - } - auto newLanguage = languageList.join(","); - if (m_currentLanguage == newLanguage) - return; - m_currentLanguage = newLanguage; - getSettingsManager()->setLanguage(langPairList); - emit(currentLangChanged()); - emit(filterParamsChanged()); -} - -void ContentManager::setCurrentCategoryFilter(QStringList categoryList) -{ - categoryList.sort(); - if (m_categoryFilter == categoryList.join(",")) - return; - m_categoryFilter = categoryList.join(","); - getSettingsManager()->setCategory(categoryList); - emit(filterParamsChanged()); -} - -void ContentManager::setCurrentContentTypeFilter(FilterList contentTypeFiltersPairList) -{ - QStringList contentTypeFilters; - for (auto &ctfPair : contentTypeFiltersPairList) { - contentTypeFilters.append(ctfPair.second); - } - m_contentTypeFilters = contentTypeFilters; - getSettingsManager()->setContentType(contentTypeFiltersPairList); - emit(filterParamsChanged()); -} - -void ContentManager::updateLibrary() { - if (m_local) { - emit(pendingRequest(false)); - emit(booksChanged()); - return; - } - try { - emit(pendingRequest(true)); - m_remoteLibraryManager.doUpdate(m_currentLanguage, m_categoryFilter); - } catch (std::runtime_error&) {} -} - -void ContentManager::updateRemoteLibrary(const QString& content) { - (void) QtConcurrent::run([=]() { - QMutexLocker locker(&remoteLibraryLocker); - mp_remoteLibrary = kiwix::Library::create(); - kiwix::Manager manager(mp_remoteLibrary); - manager.readOpds(content.toStdString(), getRemoteLibraryUrl().toStdString()); - emit(this->booksChanged()); - emit(this->pendingRequest(false)); - }); -} - -void ContentManager::updateLanguages(const QString& content) { - auto languages = kiwix::readLanguagesFromFeed(content.toStdString()); - LanguageList tempLanguages; - for (auto language : languages) { - auto code = QString::fromStdString(language.first); - auto title = QString::fromStdString(language.second); - tempLanguages.push_back({title, code}); - } - m_languages = tempLanguages; - emit(languagesLoaded(m_languages)); -} - -void ContentManager::updateCategories(const QString& content) {; - auto categories = kiwix::readCategoriesFromFeed(content.toStdString()); - QStringList tempCategories; - for (auto catg : categories) { - tempCategories.push_back(QString::fromStdString(catg)); - } - m_categories = tempCategories; - emit(categoriesLoaded(m_categories)); -} - -void ContentManager::setSearch(const QString &search) -{ - m_searchQuery = search; - emit(booksChanged()); -} - -QStringList ContentManager::getBookIds() -{ - kiwix::Filter filter; - std::vector acceptTags, rejectTags; - - for (auto &contentTypeFilter : m_contentTypeFilters) { - acceptTags.push_back(contentTypeFilter.toStdString()); - } - - filter.acceptTags(acceptTags); - filter.rejectTags(rejectTags); - filter.query(m_searchQuery.toStdString()); - if (m_currentLanguage != "") - filter.lang(m_currentLanguage.toStdString()); - if (m_categoryFilter != "") - filter.category(m_categoryFilter.toStdString()); - - if (m_local) { - filter.local(true); - filter.valid(true); - return mp_library->listBookIds(filter, m_sortBy, m_sortOrderAsc); - } else { - filter.remote(true); - QMutexLocker locker(&remoteLibraryLocker); - auto bookIds = mp_remoteLibrary->filter(filter); - mp_remoteLibrary->sort(bookIds, m_sortBy, m_sortOrderAsc); - QStringList list; - for(auto& bookId:bookIds) { - list.append(QString::fromStdString(bookId)); - } - return list; - } -} - -void ContentManager::setSortBy(const QString& sortBy, const bool sortOrderAsc) -{ - if (sortBy == "unsorted") { - m_sortBy = kiwix::UNSORTED; - } else if (sortBy == "title") { - m_sortBy = kiwix::TITLE; - } else if (sortBy == "size") { - m_sortBy = kiwix::SIZE; - } else if (sortBy == "date") { - m_sortBy = kiwix::DATE; - } - m_sortOrderAsc = sortOrderAsc; - emit(booksChanged()); -} - -//////////////////////////////////////////////////////////////////////////////// -// Directory monitoring stuff -//////////////////////////////////////////////////////////////////////////////// - -void ContentManager::setMonitoredDirectories(QStringSet dirList) -{ - for (auto path : m_watcher.directories()) { - m_watcher.removePath(path); - } - m_knownZimsInDir.clear(); - MonitoredZimFileInfo libraryZimFileInfo; - libraryZimFileInfo.status = MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY; - for (auto dir : dirList) { - if (dir != "") { - auto& zimsInDir = m_knownZimsInDir[dir]; - for ( const auto& fname : mp_library->getLibraryZimsFromDir(dir) ) { - zimsInDir.insert(fname, libraryZimFileInfo); - } - m_watcher.addPath(dir); - asyncUpdateLibraryFromDir(dir); - } - } -} - -void ContentManager::asyncUpdateLibraryFromDir(QString dir) -{ - (void) QtConcurrent::run([=]() { - updateLibraryFromDir(dir); - }); -} - -void ContentManager::handleDisappearedZimFiles(const QString& dirPath, const QStringSet& fileNames) -{ - const auto kiwixLib = mp_library->getKiwixLibrary(); - auto& zimsInDir = m_knownZimsInDir[dirPath]; - for (const auto& file : fileNames) { - const auto bookPath = QDir::toNativeSeparators(dirPath + "/" + file); - try { - DBGOUT("directory monitoring: file disappeared: " << bookPath); - const auto book = kiwixLib->getBookByPath(bookPath.toStdString()); - if ( handleDisappearedBook(QString::fromStdString(book.getId())) ) { - zimsInDir.remove(file); - } - } catch (const std::exception& err) { - DBGOUT("directory monitoring: " - "error while dropping the disappeared book: " << err.what()); - // the book was removed by the user via the UI - zimsInDir.remove(file); - } - } -} - -size_t ContentManager::handleNewZimFiles(const QString& dirPath, const QStringSet& fileNames) -{ - size_t countOfSuccessfullyAddedZims = 0; - for (const auto& file : fileNames) { - const bool addedToLib = handleZimFileInMonitoredDirLogged(dirPath, file); - countOfSuccessfullyAddedZims += addedToLib; - } - return countOfSuccessfullyAddedZims; -} - -namespace -{ - -#ifndef QT_NO_DEBUG - -// indexed by MonitoredZimFileInfo::ZimFileStatus enum -const char* monitoredDirZimFileHandlingMsgs[] = { - "", - "it is being downloaded by us, ignoring...", - "the file was added to the library", - "the file could not be added to the library", - "it is an unchanged known bad zim file", - "deferring the check of an updated bad zim file", - "bad zim file was updated but a deferred request to check it is pending" -}; - -#endif - -} // unnamed namespace - -bool ContentManager::handleZimFileInMonitoredDirLogged(QString dir, QString fileName) -{ - DBGOUT("ContentManager::handleZimFileInMonitoredDir(" << dir << ", " << fileName << ")"); - const int status = handleZimFileInMonitoredDir(dir, fileName); - DBGOUT("\t" << monitoredDirZimFileHandlingMsgs[status]); - return status == MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY; -} - -bool ContentManager::MonitoredZimFileInfo::fileKeepsBeingModified() const -{ - // A file is considered stable if it has stayed unchanged for at least - // this long. - const qint64 FILE_STABILITY_DURATION_MS = 1000; - - const QDateTime now = QDateTime::currentDateTime(); - return this->lastModified > now.addMSecs(-FILE_STABILITY_DURATION_MS); -} - -void ContentManager::MonitoredZimFileInfo::updateStatus(const MonitoredZimFileInfo& prevInfo) -{ - Q_ASSERT(prevInfo.status != ADDED_TO_THE_LIBRARY); - - if ( this->lastModified == prevInfo.lastModified ) { - this->status = UNCHANGED_KNOWN_BAD_ZIM_FILE; - } else if ( prevInfo.status == PROCESS_LATER ) { - this->status = DEFERRED_PROCESSING_ALREADY_PENDING; - } else if ( this->fileKeepsBeingModified() ) { - this->status = PROCESS_LATER; - } else { - this->status = PROCESS_NOW; - } -} - -ContentManager::MonitoredZimFileInfo ContentManager::getMonitoredZimFileInfo(QString dir, QString fileName) const -{ - const auto bookPath = QDir::toNativeSeparators(dir + "/" + fileName); - - MonitoredZimFileInfo zimFileInfo; - - zimFileInfo.lastModified = QFileInfo(bookPath).lastModified(); - - const auto& zimsInDir = m_knownZimsInDir[dir]; - const auto fileInfoEntry = zimsInDir.constFind(fileName); - if ( fileInfoEntry != zimsInDir.constEnd() ) { - zimFileInfo.updateStatus(fileInfoEntry.value()); - } - - return zimFileInfo; -} - -int ContentManager::handleZimFileInMonitoredDir(QString dir, QString fileName) -{ - const auto bookPath = QDir::toNativeSeparators(dir + "/" + fileName); - - if ( mp_library->isBeingDownloadedByUs(bookPath) ) { - return MonitoredZimFileInfo::BEING_DOWNLOADED_BY_US; - } - - MonitoredZimFileInfo zfi = getMonitoredZimFileInfo(dir, fileName); - if ( zfi.status == MonitoredZimFileInfo::PROCESS_LATER ) { - deferHandlingOfZimFileInMonitoredDir(dir, fileName); - } else if ( zfi.status == MonitoredZimFileInfo::PROCESS_NOW ) { - kiwix::Manager manager(mp_library->getKiwixLibrary()); - const bool addedToLib = manager.addBookFromPath(bookPath.toStdString()); - zfi.status = addedToLib - ? MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY - : MonitoredZimFileInfo::COULD_NOT_BE_ADDED_TO_THE_LIBRARY; - m_knownZimsInDir[dir].insert(fileName, zfi); - } - return zfi.status; -} - -ContentManager::QStringSet ContentManager::getLibraryZims(QString dirPath) const -{ - QStringSet zimFileNames; - const auto& zimsInDir = m_knownZimsInDir[dirPath]; - for ( auto it = zimsInDir.begin(); it != zimsInDir.end(); ++it ) { - if ( it.value().status == MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY ) - zimFileNames.insert(it.key()); - } - return zimFileNames; -} - -void ContentManager::updateLibraryFromDir(QString dirPath) -{ - QMutexLocker locker(&m_updateFromDirMutex); - const QDir dir(dirPath); - const QStringSet zimsPresentInLib = getLibraryZims(dirPath); - - QStringSet zimsInDir; - for (const auto &file : dir.entryList({"*.zim"})) { - zimsInDir.insert(file); - } - - const QStringSet zimsNotInLib = zimsInDir - zimsPresentInLib; - const QStringSet removedZims = zimsPresentInLib - zimsInDir; - handleDisappearedZimFiles(dirPath, removedZims); - const auto countOfAddedZims = handleNewZimFiles(dirPath, zimsNotInLib); - if (!removedZims.empty() || countOfAddedZims != 0) { - mp_library->save(); - emit(booksChanged()); - } -} - -void ContentManager::handleZimFileInMonitoredDirDeferred(QString dir, QString fileName) -{ - QMutexLocker locker(&m_updateFromDirMutex); - DBGOUT("ContentManager::handleZimFileInMonitoredDirDeferred(" << dir << ", " << fileName << ")"); - m_knownZimsInDir[dir][fileName].status = MonitoredZimFileInfo::PROCESS_NOW; - if ( handleZimFileInMonitoredDirLogged(dir, fileName) ) { - mp_library->save(); - emit(booksChanged()); - } -} - -void ContentManager::deferHandlingOfZimFileInMonitoredDir(QString dir, QString fname) -{ - const qint64 DEBOUNCING_DELAY_MILLISECONDS = 1000; - - m_knownZimsInDir[dir][fname].status = MonitoredZimFileInfo::PROCESS_LATER; - - QTimer::singleShot(DEBOUNCING_DELAY_MILLISECONDS, this, [=]() { - handleZimFileInMonitoredDirDeferred(dir, fname); - }); -} - -bool ContentManager::handleDisappearedBook(QString bookId) -{ - if ( KiwixApp::instance()->getTabWidget()->getTabZimIds().contains(bookId) ) - return false; - - mp_library->removeBookFromLibraryById(bookId); - return true; -} +#include "contentmanager.h" + +#include "kiwixapp.h" +#include +#include + +#include +#include +#include +#include +#include +#include "contentmanagermodel.h" +#include +#include +#include +#include "contentmanagerdelegate.h" +#include "node.h" +#include "rownode.h" +#include "descriptionnode.h" +#include "kiwixmessagebox.h" +#include +#include "contentmanagerheader.h" +#include + +#ifndef QT_NO_DEBUG +#define DBGOUT(X) qDebug().nospace() << "DBG: " << X +#else +#define DBGOUT(X) +#endif + +namespace +{ + + +SettingsManager* getSettingsManager() +{ + return KiwixApp::instance()->getSettingsManager(); +} + +// Opens the directory containing the input file path. +// parent is the widget serving as the parent for the error dialog in case of +// failure. +void openFileLocation(QString path, QWidget *parent = nullptr) +{ + QFileInfo fileInfo(path); + QDir dir = fileInfo.absoluteDir(); + bool dirOpen = dir.exists() && dir.isReadable() && QDesktopServices::openUrl(dir.absolutePath()); + if (!dirOpen) { + QString failedText = gt("couldnt-open-location-text"); + failedText = failedText.replace("{{FOLDER}}", "" + dir.absolutePath() + ""); + showInfoBox(gt("couldnt-open-location"), failedText, parent); + } +} + +} // unnamed namespace + +ContentManager::ContentManager(Library* library) + : DownloadManager(library), + mp_library(library), + mp_remoteLibrary(kiwix::Library::create()), + m_remoteLibraryManager() +{ + // mp_view will be passed to the tab who will take ownership, + // so, we don't need to delete it. + mp_view = new ContentManagerView(); + managerModel = new ContentManagerModel(this); + updateModel(); + auto treeView = mp_view->getView(); + treeView->setModel(managerModel); + treeView->show(); + + auto header = new ContentManagerHeader(Qt::Orientation::Horizontal, treeView); + treeView->setHeader(header); + header->setSectionResizeMode(0, QHeaderView::Fixed); + header->setSectionResizeMode(1, QHeaderView::Stretch); + header->setSectionResizeMode(2, QHeaderView::Fixed); + header->setSectionResizeMode(3, QHeaderView::Fixed); + header->setSectionResizeMode(4, QHeaderView::Fixed); + header->setDefaultAlignment(Qt::AlignLeft); + header->setStretchLastSection(false); + header->setSectionsClickable(true); + header->setHighlightSections(true); + treeView->setWordWrap(true); + treeView->resizeColumnToContents(4); + treeView->setColumnWidth(0, 70); + treeView->setColumnWidth(5, 120); + // TODO: set width for all columns based on viewport + treeView->setAllColumnsShowFocus(true); + + setCurrentLanguage(getSettingsManager()->getLanguageList()); + setCurrentCategoryFilter(getSettingsManager()->getCategoryList()); + setCurrentContentTypeFilter(getSettingsManager()->getContentType()); + connect(mp_library, &Library::booksChanged, this, [=]() {emit(this->booksChanged());}); + connect(this, &ContentManager::filterParamsChanged, this, &ContentManager::updateLibrary); + connect(this, &ContentManager::booksChanged, this, [=]() { + updateModel(); + setCategories(); + setLanguages(); + }); + connect(&m_remoteLibraryManager, &OpdsRequestManager::requestReceived, this, &ContentManager::updateRemoteLibrary); + connect(mp_view->getView(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onCustomContextMenu(const QPoint &))); + connect(this, &ContentManager::pendingRequest, mp_view, &ContentManagerView::showLoader); + connect(treeView, &QTreeView::doubleClicked, this, &ContentManager::openBookWithIndex); + connect(&m_remoteLibraryManager, &OpdsRequestManager::languagesReceived, this, &ContentManager::updateLanguages); + connect(&m_remoteLibraryManager, &OpdsRequestManager::categoriesReceived, this, &ContentManager::updateCategories); + setCategories(); + setLanguages(); + + connect(this, &DownloadManager::downloadUpdated, + this, &ContentManager::updateDownload); + + connect(this, &DownloadManager::downloadCancelled, + this, &ContentManager::downloadWasCancelled); + + connect(this, &DownloadManager::downloadDisappeared, + this, &ContentManager::downloadDisappeared); + + connect(this, &DownloadManager::error, this, &ContentManager::handleError); + + if ( DownloadManager::downloadingFunctionalityAvailable() ) { + startDownloadUpdaterThread(); + } + + connect(&m_watcher, &QFileSystemWatcher::directoryChanged, + this, &ContentManager::asyncUpdateLibraryFromDir); +} + +void ContentManager::updateModel() +{ + const auto bookIds = getBookIds(); + BookInfoList bookList; + QStringList keys = {"title", "tags", "date", "id", "size", "description", "favicon"}; + for (auto bookId : bookIds) { + auto mp = getBookInfos(bookId, keys); + bookList.append(mp); + } + + const DownloadManager& downloadMgr = *this; + managerModel->setBooksData(bookList, downloadMgr); +} + +void ContentManager::onCustomContextMenu(const QPoint &point) +{ + QModelIndex index = mp_view->getView()->indexAt(point); + if (!index.isValid()) + return; + QMenu contextMenu("optionsMenu", mp_view->getView()); + auto bookNode = static_cast(index.internalPointer()); + const auto id = bookNode->getBookId(); + + QAction menuDeleteBook(gt("delete-book"), this); + QAction menuOpenBook(gt("open-book"), this); + QAction menuDownloadBook(gt("download-book"), this); + QAction menuPauseBook(gt("pause-download"), this); + QAction menuResumeBook(gt("resume-download"), this); + QAction menuCancelBook(gt("cancel-download"), this); + QAction menuOpenFolder(gt("open-folder"), this); + QAction menuPreviewBook(gt("preview-book-in-web-browser"), this); + + const auto bookState = getBookState(id); + switch ( bookState ) { + case BookState::DOWNLOAD_PAUSED: + if ( getDownloadState(id)->getStatus() == DownloadState::PAUSED ) { + contextMenu.addAction(&menuResumeBook); + contextMenu.addAction(&menuCancelBook); + } + contextMenu.addAction(&menuPreviewBook); + break; + + case BookState::DOWNLOADING: + if ( getDownloadState(id)->getStatus() == DownloadState::DOWNLOADING ) { + contextMenu.addAction(&menuPauseBook); + contextMenu.addAction(&menuCancelBook); + } + contextMenu.addAction(&menuPreviewBook); + break; + + case BookState::AVAILABLE_LOCALLY_AND_HEALTHY: + case BookState::ERROR_MISSING_ZIM_FILE: + case BookState::ERROR_CORRUPTED_ZIM_FILE: + { + const auto book = mp_library->getBookById(id); + auto bookPath = QString::fromStdString(book.getPath()); + if ( bookState == BookState::AVAILABLE_LOCALLY_AND_HEALTHY ) { + contextMenu.addAction(&menuOpenBook); + } + contextMenu.addAction(&menuDeleteBook); + contextMenu.addAction(&menuOpenFolder); + connect(&menuOpenFolder, &QAction::triggered, [=]() { + openFileLocation(bookPath, mp_view); + }); + break; + } + + case BookState::AVAILABLE_ONLINE: + contextMenu.addAction(&menuDownloadBook); + contextMenu.addAction(&menuPreviewBook); + break; + + default: break; + } + + connect(&menuDeleteBook, &QAction::triggered, [=]() { + eraseBook(id); + }); + connect(&menuOpenBook, &QAction::triggered, [=]() { + openBook(id); + }); + connect(&menuDownloadBook, &QAction::triggered, [=]() { + downloadBook(id); + }); + connect(&menuPauseBook, &QAction::triggered, [=]() { + pauseBook(id, index); + }); + connect(&menuCancelBook, &QAction::triggered, [=]() { + cancelBook(id); + }); + connect(&menuResumeBook, &QAction::triggered, [=]() { + resumeBook(id, index); + }); + connect(&menuPreviewBook, &QAction::triggered, [=]() { + openBookPreview(id); + }); + + contextMenu.exec(mp_view->getView()->viewport()->mapToGlobal(point)); +} + +void ContentManager::setLocal(bool local) { + if (local == m_local) { + return; + } + m_local = local; + emit(filterParamsChanged()); + setCategories(); + setLanguages(); +} + +QStringList ContentManager::getTranslations(const QStringList &keys) +{ + QStringList translations; + + for(auto& key: keys) { + translations.append(KiwixApp::instance()->getText(key)); + } + return translations; +} + +void ContentManager::setCategories() +{ + QStringList categories; + if (m_local) { + auto categoryData = mp_library->getKiwixLibrary()->getBooksCategories(); + for (auto category : categoryData) { + auto categoryName = QString::fromStdString(category); + categories.push_back(categoryName); + } + m_categories = categories; + emit(categoriesLoaded(m_categories)); + return; + } + m_remoteLibraryManager.getCategoriesFromOpds(); +} + +void ContentManager::setLanguages() +{ + LanguageList languages; + if (m_local) { + auto languageData = mp_library->getKiwixLibrary()->getBooksLanguages(); + for (auto language : languageData) { + auto langCode = QString::fromStdString(language); + auto selfName = QString::fromStdString(kiwix::getLanguageSelfName(language)); + languages.push_back({selfName, langCode}); + } + m_languages = languages; + emit(languagesLoaded(m_languages)); + return; + } + m_remoteLibraryManager.getLanguagesFromOpds(); +} + +namespace +{ + +QString getBookTags(const kiwix::Book& b) +{ + QStringList tagList = QString::fromStdString(b.getTags()).split(';'); + QMap displayTagMap; + for(auto tag: tagList) { + if (tag[0] == '_') { + auto splitTag = tag.split(":"); + displayTagMap[splitTag[0]] = splitTag[1] == "yes" ? true:false; + } + } + QStringList displayTagList; + if (displayTagMap["_videos"]) displayTagList << QObject::tr("Videos"); + if (displayTagMap["_pictures"]) displayTagList << QObject::tr("Pictures"); + if (!displayTagMap["_details"]) displayTagList << QObject::tr("Introduction only"); + return displayTagList.join(", "); +} + +QString getFaviconUrl(const kiwix::Book& b) +{ + std::string url; + try { + auto item = b.getIllustration(48); + url = item->url; + } catch (...) { + } + return QString::fromStdString(url); +} + +QByteArray getFaviconData(const kiwix::Book& b) +{ + QByteArray qdata; + try { + // Try to obtain favicons only from local books (otherwise + // kiwix::Book::Illustration::getData() attempts to download the image + // on its own, whereas we want that operation to be performed + // asynchronously by ThumbnailDownloader). + if ( b.isPathValid() ) { + const auto illustration = b.getIllustration(48); + const std::string data = illustration->getData(); + + qdata = QByteArray::fromRawData(data.data(), data.size()); + qdata.detach(); // deep copy + } + } catch ( ... ) { + return QByteArray(); + } + + return qdata; +} + +QVariant getFaviconDataOrUrl(const kiwix::Book& b) +{ + const QByteArray data = getFaviconData(b); + return !data.isNull() ? QVariant(data) : QVariant(getFaviconUrl(b)); +} + +QVariant getBookAttribute(const kiwix::Book& b, const QString& a) +{ + if ( a == "id" ) return QString::fromStdString(b.getId()); + if ( a == "path" ) return QString::fromStdString(b.getPath()); + if ( a == "title" ) return QString::fromStdString(b.getTitle()); + if ( a == "description" ) return QString::fromStdString(b.getDescription()); + if ( a == "date" ) return QString::fromStdString(b.getDate()); + if ( a == "url" ) return QString::fromStdString(b.getUrl()); + if ( a == "name" ) return QString::fromStdString(b.getName()); + if ( a == "favicon") return getFaviconDataOrUrl(b); + if ( a == "size" ) return QString::number(b.getSize()); + if ( a == "tags" ) return getBookTags(b); + + return QVariant(); +} + +ContentManager::BookState getStateOfLocalBook(const kiwix::Book& book) +{ + if ( !book.isPathValid() ) { + return ContentManager::BookState::ERROR_MISSING_ZIM_FILE; + } + + // XXX: When a book is detected to be corrupted, information about that + // XXX: has to be recorded somewhere so that we can return + // XXX: ERROR_CORRUPTED_ZIM_FILE here + + return ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY; +} + +} // unnamed namespace + +ContentManager::BookInfo ContentManager::getBookInfos(QString id, const QStringList &keys) +{ + const kiwix::Book* b = nullptr; + try { + b = &mp_library->getBookById(id); + if ( ! b->getDownloadId().empty() ) { + // The book is still being downloaded and has been entered into the + // local library for technical reasons only. Get the book info from + // the remote library. + b = nullptr; + } + } catch (...) {} + + if ( !b ) { + try { + QMutexLocker locker(&remoteLibraryLocker); + b = &mp_remoteLibrary->getBookById(id.toStdString()); + } catch(...) {} + } + + BookInfo values; + for(auto& key: keys){ + values.insert(key, b ? getBookAttribute(*b, key) : ""); + } + + return values; +} + +ContentManager::BookState ContentManager::getBookState(QString bookId) +{ + if ( const auto downloadState = DownloadManager::getDownloadState(bookId) ) { + return downloadState->getStatus() == DownloadState::PAUSED + ? BookState::DOWNLOAD_PAUSED + : BookState::DOWNLOADING; + // TODO: a download may be in error state + } + + try { + const kiwix::Book& b = mp_library->getBookById(bookId); + return b.getDownloadId().empty() + ? getStateOfLocalBook(b) + : BookState::DOWNLOADING; + } catch (...) {} + + try { + QMutexLocker locker(&remoteLibraryLocker); + const kiwix::Book& b = mp_remoteLibrary->getBookById(bookId.toStdString()); + return !b.getUrl().empty() + ? BookState::AVAILABLE_ONLINE + : BookState::METADATA_ONLY; + } catch (...) {} + + return BookState::INVALID; +} + +void ContentManager::openBookWithIndex(const QModelIndex &index) +{ + auto bookNode = static_cast(index.internalPointer()); + const QString bookId = bookNode->getBookId(); + if ( getBookState(bookId) == BookState::AVAILABLE_LOCALLY_AND_HEALTHY ) { + openBook(bookId); + } +} + +void ContentManager::openBook(const QString &id) +{ + QUrl url("zim://"+id+".zim/"); + try { + KiwixApp::instance()->openUrl(url, true); + } catch (const std::exception& e) { + auto tabBar = KiwixApp::instance()->getTabWidget(); + tabBar->closeTab(1); + auto text = gt("zim-open-fail-text"); + text = text.replace("{{ZIM}}", QString::fromStdString(mp_library->getBookById(id).getPath())); + auto title = gt("zim-open-fail-title"); + KiwixApp::instance()->showMessage(text, title, QMessageBox::Warning); + mp_library->removeBookFromLibraryById(id); + tabBar->setCurrentIndex(0); + emit(booksChanged()); + } +} + +void ContentManager::openBookPreview(const QString &id) +{ + try { + QMutexLocker locker(&remoteLibraryLocker); + const std::string &downloadUrl = + mp_remoteLibrary->getBookById(id.toStdString()).getUrl(); + locker.unlock(); + + /* Extract the Zim name from the book's download URL */ + const auto zimNameStartIndex = downloadUrl.find_last_of('/') + 1; + const std::string& zimName = downloadUrl.substr( + zimNameStartIndex, + downloadUrl.find(".zim", zimNameStartIndex) - zimNameStartIndex); + + const QUrl previewUrl = getRemoteLibraryUrl() + "/viewer#" + zimName.c_str(); + QDesktopServices::openUrl(previewUrl); + } catch (...) {} +} + +void ContentManager::removeDownload(QString bookId) +{ + DownloadManager::removeDownload(bookId); + managerModel->setDownloadState(bookId, nullptr); +} + +void ContentManager::downloadDisappeared(QString bookId) +{ + removeDownload(bookId); + kiwix::Book bCopy; + try { + bCopy = mp_library->getBookById(bookId); + } catch ( const std::out_of_range& ) { + // If the download has disappeared as a result of some + // obscure chain of events, the book may have disappeared too. + return; + } + + bCopy.setDownloadId(""); + mp_library->getKiwixLibrary()->addOrUpdateBook(bCopy); + mp_library->save(); + emit(mp_library->booksChanged()); +} + +void ContentManager::downloadCompleted(QString bookId, QString path) +{ + removeDownload(bookId); + kiwix::Book bCopy(mp_library->getBookById(bookId)); + bCopy.setPath(QDir::toNativeSeparators(path).toStdString()); + bCopy.setDownloadId(""); + bCopy.setPathValid(true); + // removing book url so that download link in kiwix-serve is not displayed. + bCopy.setUrl(""); + mp_library->getKiwixLibrary()->addOrUpdateBook(bCopy); + mp_library->save(); + mp_library->bookmarksChanged(); + if (!m_local) { + emit(oneBookChanged(bookId)); + } else { + emit(mp_library->booksChanged()); + } +} + +void ContentManager::updateDownload(QString bookId, const DownloadInfo& downloadInfo) +{ + const auto downloadState = DownloadManager::getDownloadState(bookId); + if ( downloadState ) { + const auto downloadPath = downloadInfo["path"].toString(); + if ( downloadInfo["status"].toString() == "completed" ) { + downloadCompleted(bookId, downloadPath); + } else { + mp_library->updateBookBeingDownloaded(bookId, downloadPath); + downloadState->update(downloadInfo); + managerModel->updateDownload(bookId); + } + } +} + +void ContentManager::handleError(QString errSummary, QString errDetails) +{ + showErrorBox(KiwixAppError(errSummary, errDetails), mp_view); +} + +void ContentManager::downloadBook(const QString &id) +{ + kiwix::Book book = getRemoteOrLocalBook(id); + const auto downloadPath = getSettingsManager()->getDownloadDir(); + + try { + DownloadManager::checkThatBookCanBeDownloaded(book, downloadPath); + } catch ( const KiwixAppError& err ) { + showErrorBox(err, mp_view); + return; + } + + mp_library->addBookBeingDownloaded(book, downloadPath); + mp_library->save(); + + DownloadManager::addRequest(DownloadState::START, id); + const auto downloadState = DownloadManager::getDownloadState(id); + managerModel->setDownloadState(id, downloadState); +} + +// This function is called asynchronously in a worker thread processing all +// download operations. The call is initiated in downloadBook(). +void ContentManager::startDownload(QString id) +{ + kiwix::Book book = getRemoteOrLocalBook(id); + const auto downloadPath = getSettingsManager()->getDownloadDir(); + // downloadPath may be different from the value used in + // downloadBook(). This may happen in the following scenario: + // + // 1. aria2c is stuck because of having to save to + // slow storage (and the fact that it is a single-threaded + // application). This may result in startDownload() being + // called with significant delay after downloadBook(). + // + // 2. The user changes the download directory after starting + // a download. + // + // That's why the checkThatBookCanBeDownloaded() check is repeated here. + + std::string downloadId; + try { + DownloadManager::checkThatBookCanBeDownloaded(book, downloadPath); + downloadId = DownloadManager::startDownload(book, downloadPath); + } catch ( const KiwixAppError& err ) { + emit error(err.summary(), err.details()); + return; + } + + book.setDownloadId(downloadId); + mp_library->addBookBeingDownloaded(book, downloadPath); + mp_library->save(); + emit(oneBookChanged(id)); +} + +const kiwix::Book& ContentManager::getRemoteOrLocalBook(const QString &id) +{ + try { + QMutexLocker locker(&remoteLibraryLocker); + return mp_remoteLibrary->getBookById(id.toStdString()); + } catch (...) { + return mp_library->getBookById(id); + } +} + +QString ContentManager::getRemoteLibraryUrl() const +{ + auto host = m_remoteLibraryManager.getCatalogHost(); + auto port = m_remoteLibraryManager.getCatalogPort(); + return port == 443 ? "https://" + host + : "http://" + host + ":" + QString::number(port); +} + +static const char MSG_FOR_PREVENTED_RMSTAR_OPERATION[] = R"( + BUG: Errare humanum est. + BUG: Kiwix developers are human, but we try to ensure that our mistakes + BUG: don't cause harm to our users. + BUG: If we didn't detect this situation we could have erased a lot of files + BUG: on your computer. +)"; + +void ContentManager::eraseBookFilesFromComputer(const std::string& bookPath, bool moveToTrash) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + Q_UNUSED(moveToTrash); +#endif + const std::string dirPath = kiwix::removeLastPathElement(bookPath); + const std::string fileGlob = kiwix::getLastPathElement(bookPath) + "*"; + + if ( fileGlob == "*" ) { + std::cerr << MSG_FOR_PREVENTED_RMSTAR_OPERATION << std::endl; + return; + } + + QDir dir(QString::fromStdString(dirPath), QString::fromStdString(fileGlob)); + for(const QString& file: dir.entryList()) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + if (moveToTrash) + QFile::moveToTrash(dir.filePath(file)); + else +#endif + dir.remove(file); // moveToTrash will always be false here, no check required. + } +} + +QString formatText(QString text) +{ + QString finalText = "

"; + finalText += text; + finalText += ""; + return finalText; +} + +void ContentManager::reallyEraseBook(const QString& id, bool moveToTrash) +{ + auto tabBar = KiwixApp::instance()->getTabWidget(); + tabBar->closeTabsByZimId(id); + eraseBookFilesFromComputer(mp_library->getBookFilePath(id), moveToTrash); + mp_library->removeBookFromLibraryById(id); + mp_library->save(); + emit mp_library->bookmarksChanged(); + if (m_local) { + emit(bookRemoved(id)); + } else { + emit(oneBookChanged(id)); + } + getSettingsManager()->deleteSettings(id); + emit booksChanged(); +} + +void ContentManager::eraseBook(const QString& id) +{ + auto text = gt("delete-book-text"); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const auto moveToTrash = getSettingsManager()->getMoveToTrash(); +#else + const auto moveToTrash = false; // we do not support move to trash functionality for qt versions below 5.15 +#endif + if (moveToTrash) { + text += formatText(gt("move-files-to-trash-text")); + } else { + text += formatText(gt("perma-delete-files-text")); + } + text = text.replace("{{ZIM}}", QString::fromStdString(mp_library->getBookById(id).getTitle())); + showConfirmBox(gt("delete-book"), text, mp_view, [=]() { + reallyEraseBook(id, moveToTrash); + }); +} + +void ContentManager::pauseBook(const QString& id, QModelIndex index) +{ + DownloadManager::addRequest(DownloadState::PAUSE, id); + managerModel->triggerDataUpdateAt(index); +} + +void ContentManager::resumeBook(const QString& id, QModelIndex index) +{ + DownloadManager::addRequest(DownloadState::RESUME, id); + managerModel->triggerDataUpdateAt(index); +} + +void ContentManager::cancelBook(const QString& id) +{ + auto text = gt("cancel-download-text"); + text = text.replace("{{ZIM}}", QString::fromStdString(mp_library->getBookById(id).getTitle())); + showConfirmBox(gt("cancel-download"), text, mp_view, [=]() { + DownloadManager::addRequest(DownloadState::CANCEL, id); + }); +} + +void ContentManager::downloadWasCancelled(const QString& id) +{ + removeDownload(id); + + // incompleted downloaded file should be perma deleted + eraseBookFilesFromComputer(mp_library->getBookFilePath(id), false); + mp_library->removeBookFromLibraryById(id); + mp_library->save(); + emit(oneBookChanged(id)); +} + +void ContentManager::setCurrentLanguage(FilterList langPairList) +{ + QStringList languageList; + for (auto &langPair : langPairList) { + languageList.append(langPair.second); + } + languageList.sort(); + for (auto &language : languageList) { + if (language.length() == 2) { + try { + language = QString::fromStdString( + kiwix::converta2toa3(language.toStdString())); + } catch (std::out_of_range&) {} + } + } + auto newLanguage = languageList.join(","); + if (m_currentLanguage == newLanguage) + return; + m_currentLanguage = newLanguage; + getSettingsManager()->setLanguage(langPairList); + emit(currentLangChanged()); + emit(filterParamsChanged()); +} + +void ContentManager::setCurrentCategoryFilter(QStringList categoryList) +{ + categoryList.sort(); + if (m_categoryFilter == categoryList.join(",")) + return; + m_categoryFilter = categoryList.join(","); + getSettingsManager()->setCategory(categoryList); + emit(filterParamsChanged()); +} + +void ContentManager::setCurrentContentTypeFilter(FilterList contentTypeFiltersPairList) +{ + QStringList contentTypeFilters; + for (auto &ctfPair : contentTypeFiltersPairList) { + contentTypeFilters.append(ctfPair.second); + } + m_contentTypeFilters = contentTypeFilters; + getSettingsManager()->setContentType(contentTypeFiltersPairList); + emit(filterParamsChanged()); +} + +void ContentManager::updateLibrary() { + if (m_local) { + emit(pendingRequest(false)); + emit(booksChanged()); + return; + } + try { + emit(pendingRequest(true)); + m_remoteLibraryManager.doUpdate(m_currentLanguage, m_categoryFilter); + } catch (std::runtime_error&) {} +} + +void ContentManager::updateRemoteLibrary(const QString& content) { + (void) QtConcurrent::run([=]() { + QMutexLocker locker(&remoteLibraryLocker); + mp_remoteLibrary = kiwix::Library::create(); + kiwix::Manager manager(mp_remoteLibrary); + manager.readOpds(content.toStdString(), getRemoteLibraryUrl().toStdString()); + emit(this->booksChanged()); + emit(this->pendingRequest(false)); + }); +} + +void ContentManager::updateLanguages(const QString& content) { + auto languages = kiwix::readLanguagesFromFeed(content.toStdString()); + LanguageList tempLanguages; + for (auto language : languages) { + auto code = QString::fromStdString(language.first); + auto title = QString::fromStdString(language.second); + tempLanguages.push_back({title, code}); + } + m_languages = tempLanguages; + emit(languagesLoaded(m_languages)); +} + +void ContentManager::updateCategories(const QString& content) {; + auto categories = kiwix::readCategoriesFromFeed(content.toStdString()); + QStringList tempCategories; + for (auto catg : categories) { + tempCategories.push_back(QString::fromStdString(catg)); + } + m_categories = tempCategories; + emit(categoriesLoaded(m_categories)); +} + +void ContentManager::setSearch(const QString &search) +{ + m_searchQuery = search; + emit(booksChanged()); +} + +QStringList ContentManager::getBookIds() +{ + kiwix::Filter filter; + std::vector acceptTags, rejectTags; + + for (auto &contentTypeFilter : m_contentTypeFilters) { + acceptTags.push_back(contentTypeFilter.toStdString()); + } + + filter.acceptTags(acceptTags); + filter.rejectTags(rejectTags); + filter.query(m_searchQuery.toStdString()); + if (m_currentLanguage != "") + filter.lang(m_currentLanguage.toStdString()); + if (m_categoryFilter != "") + filter.category(m_categoryFilter.toStdString()); + + if (m_local) { + filter.local(true); + filter.valid(true); + return mp_library->listBookIds(filter, m_sortBy, m_sortOrderAsc); + } else { + filter.remote(true); + QMutexLocker locker(&remoteLibraryLocker); + auto bookIds = mp_remoteLibrary->filter(filter); + mp_remoteLibrary->sort(bookIds, m_sortBy, m_sortOrderAsc); + QStringList list; + for(auto& bookId:bookIds) { + list.append(QString::fromStdString(bookId)); + } + return list; + } +} + +void ContentManager::setSortBy(const QString& sortBy, const bool sortOrderAsc) +{ + if (sortBy == "unsorted") { + m_sortBy = kiwix::UNSORTED; + } else if (sortBy == "title") { + m_sortBy = kiwix::TITLE; + } else if (sortBy == "size") { + m_sortBy = kiwix::SIZE; + } else if (sortBy == "date") { + m_sortBy = kiwix::DATE; + } + m_sortOrderAsc = sortOrderAsc; + emit(booksChanged()); +} + +//////////////////////////////////////////////////////////////////////////////// +// Directory monitoring stuff +//////////////////////////////////////////////////////////////////////////////// + +void ContentManager::setMonitoredDirectories(QStringSet dirList) +{ + for (auto path : m_watcher.directories()) { + m_watcher.removePath(path); + } + m_knownZimsInDir.clear(); + MonitoredZimFileInfo libraryZimFileInfo; + libraryZimFileInfo.status = MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY; + for (auto dir : dirList) { + if (dir != "") { + auto& zimsInDir = m_knownZimsInDir[dir]; + for ( const auto& fname : mp_library->getLibraryZimsFromDir(dir) ) { + zimsInDir.insert(fname, libraryZimFileInfo); + } + m_watcher.addPath(dir); + asyncUpdateLibraryFromDir(dir); + } + } +} + +void ContentManager::asyncUpdateLibraryFromDir(QString dir) +{ + (void) QtConcurrent::run([=]() { + updateLibraryFromDir(dir); + }); +} + +void ContentManager::handleDisappearedZimFiles(const QString& dirPath, const QStringSet& fileNames) +{ + const auto kiwixLib = mp_library->getKiwixLibrary(); + auto& zimsInDir = m_knownZimsInDir[dirPath]; + for (const auto& file : fileNames) { + const auto bookPath = QDir::toNativeSeparators(dirPath + "/" + file); + try { + DBGOUT("directory monitoring: file disappeared: " << bookPath); + const auto book = kiwixLib->getBookByPath(bookPath.toStdString()); + if ( handleDisappearedBook(QString::fromStdString(book.getId())) ) { + zimsInDir.remove(file); + } + } catch (const std::exception& err) { + DBGOUT("directory monitoring: " + "error while dropping the disappeared book: " << err.what()); + // the book was removed by the user via the UI + zimsInDir.remove(file); + } + } +} + +size_t ContentManager::handleNewZimFiles(const QString& dirPath, const QStringSet& fileNames) +{ + size_t countOfSuccessfullyAddedZims = 0; + for (const auto& file : fileNames) { + const bool addedToLib = handleZimFileInMonitoredDirLogged(dirPath, file); + countOfSuccessfullyAddedZims += addedToLib; + } + return countOfSuccessfullyAddedZims; +} + +namespace +{ + +#ifndef QT_NO_DEBUG + +// indexed by MonitoredZimFileInfo::ZimFileStatus enum +const char* monitoredDirZimFileHandlingMsgs[] = { + "", + "it is being downloaded by us, ignoring...", + "the file was added to the library", + "the file could not be added to the library", + "it is an unchanged known bad zim file", + "deferring the check of an updated bad zim file", + "bad zim file was updated but a deferred request to check it is pending" +}; + +#endif + +} // unnamed namespace + +bool ContentManager::handleZimFileInMonitoredDirLogged(QString dir, QString fileName) +{ + DBGOUT("ContentManager::handleZimFileInMonitoredDir(" << dir << ", " << fileName << ")"); + const int status = handleZimFileInMonitoredDir(dir, fileName); + DBGOUT("\t" << monitoredDirZimFileHandlingMsgs[status]); + return status == MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY; +} + +bool ContentManager::MonitoredZimFileInfo::fileKeepsBeingModified() const +{ + // A file is considered stable if it has stayed unchanged for at least + // this long. + const qint64 FILE_STABILITY_DURATION_MS = 1000; + + const QDateTime now = QDateTime::currentDateTime(); + return this->lastModified > now.addMSecs(-FILE_STABILITY_DURATION_MS); +} + +void ContentManager::MonitoredZimFileInfo::updateStatus(const MonitoredZimFileInfo& prevInfo) +{ + Q_ASSERT(prevInfo.status != ADDED_TO_THE_LIBRARY); + + if ( this->lastModified == prevInfo.lastModified ) { + this->status = UNCHANGED_KNOWN_BAD_ZIM_FILE; + } else if ( prevInfo.status == PROCESS_LATER ) { + this->status = DEFERRED_PROCESSING_ALREADY_PENDING; + } else if ( this->fileKeepsBeingModified() ) { + this->status = PROCESS_LATER; + } else { + this->status = PROCESS_NOW; + } +} + +ContentManager::MonitoredZimFileInfo ContentManager::getMonitoredZimFileInfo(QString dir, QString fileName) const +{ + const auto bookPath = QDir::toNativeSeparators(dir + "/" + fileName); + + MonitoredZimFileInfo zimFileInfo; + + zimFileInfo.lastModified = QFileInfo(bookPath).lastModified(); + + const auto& zimsInDir = m_knownZimsInDir[dir]; + const auto fileInfoEntry = zimsInDir.constFind(fileName); + if ( fileInfoEntry != zimsInDir.constEnd() ) { + zimFileInfo.updateStatus(fileInfoEntry.value()); + } + + return zimFileInfo; +} + +int ContentManager::handleZimFileInMonitoredDir(QString dir, QString fileName) +{ + const auto bookPath = QDir::toNativeSeparators(dir + "/" + fileName); + + if ( mp_library->isBeingDownloadedByUs(bookPath) ) { + return MonitoredZimFileInfo::BEING_DOWNLOADED_BY_US; + } + + MonitoredZimFileInfo zfi = getMonitoredZimFileInfo(dir, fileName); + if ( zfi.status == MonitoredZimFileInfo::PROCESS_LATER ) { + deferHandlingOfZimFileInMonitoredDir(dir, fileName); + } else if ( zfi.status == MonitoredZimFileInfo::PROCESS_NOW ) { + kiwix::Manager manager(mp_library->getKiwixLibrary()); + const bool addedToLib = manager.addBookFromPath(bookPath.toStdString()); + zfi.status = addedToLib + ? MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY + : MonitoredZimFileInfo::COULD_NOT_BE_ADDED_TO_THE_LIBRARY; + m_knownZimsInDir[dir].insert(fileName, zfi); + } + return zfi.status; +} + +ContentManager::QStringSet ContentManager::getLibraryZims(QString dirPath) const +{ + QStringSet zimFileNames; + const auto& zimsInDir = m_knownZimsInDir[dirPath]; + for ( auto it = zimsInDir.begin(); it != zimsInDir.end(); ++it ) { + if ( it.value().status == MonitoredZimFileInfo::ADDED_TO_THE_LIBRARY ) + zimFileNames.insert(it.key()); + } + return zimFileNames; +} + +void ContentManager::updateLibraryFromDir(QString dirPath) +{ + QMutexLocker locker(&m_updateFromDirMutex); + const QDir dir(dirPath); + const QStringSet zimsPresentInLib = getLibraryZims(dirPath); + + QStringSet zimsInDir; + for (const auto &file : dir.entryList({"*.zim"})) { + zimsInDir.insert(file); + } + + const QStringSet zimsNotInLib = zimsInDir - zimsPresentInLib; + const QStringSet removedZims = zimsPresentInLib - zimsInDir; + handleDisappearedZimFiles(dirPath, removedZims); + const auto countOfAddedZims = handleNewZimFiles(dirPath, zimsNotInLib); + if (!removedZims.empty() || countOfAddedZims != 0) { + mp_library->save(); + emit(booksChanged()); + } +} + +void ContentManager::handleZimFileInMonitoredDirDeferred(QString dir, QString fileName) +{ + QMutexLocker locker(&m_updateFromDirMutex); + DBGOUT("ContentManager::handleZimFileInMonitoredDirDeferred(" << dir << ", " << fileName << ")"); + m_knownZimsInDir[dir][fileName].status = MonitoredZimFileInfo::PROCESS_NOW; + if ( handleZimFileInMonitoredDirLogged(dir, fileName) ) { + mp_library->save(); + emit(booksChanged()); + } +} + +void ContentManager::deferHandlingOfZimFileInMonitoredDir(QString dir, QString fname) +{ + const qint64 DEBOUNCING_DELAY_MILLISECONDS = 1000; + + m_knownZimsInDir[dir][fname].status = MonitoredZimFileInfo::PROCESS_LATER; + + QTimer::singleShot(DEBOUNCING_DELAY_MILLISECONDS, this, [=]() { + handleZimFileInMonitoredDirDeferred(dir, fname); + }); +} + +bool ContentManager::handleDisappearedBook(QString bookId) +{ + if ( KiwixApp::instance()->getTabWidget()->getTabZimIds().contains(bookId) ) + return false; + + mp_library->removeBookFromLibraryById(bookId); + return true; +} diff --git a/src/contentmanager.h b/src/contentmanager.h index 650db9338..21199857d 100644 --- a/src/contentmanager.h +++ b/src/contentmanager.h @@ -1,201 +1,201 @@ -#ifndef CONTENTMANAGER_H -#define CONTENTMANAGER_H - -#include -#include "library.h" -#include "contentmanagerview.h" -#include "opdsrequestmanager.h" -#include "contenttypefilter.h" -#include "contentmanagermodel.h" -#include "downloadmanagement.h" - -class ContentManager : public DownloadManager -{ - Q_OBJECT - Q_PROPERTY(bool isLocal MEMBER m_local READ isLocal WRITE setLocal NOTIFY localChanged) - -public: // types - typedef QList> LanguageList; - typedef QList> FilterList; - typedef ContentManagerModel::BookInfo BookInfo; - typedef ContentManagerModel::BookInfoList BookInfoList; - typedef Library::QStringSet QStringSet; - - enum class BookState - { - // Nothing known about a book with that id - INVALID, - - // Only (some) metadata is available for the book, however neither a - // ZIM-file nor a URL is associated with it. - METADATA_ONLY, - - // No ZIM file is associated with the book but a URL is provided. - AVAILABLE_ONLINE, - - // The book is being downloaded. - DOWNLOADING, - - // The book started downloading, but the download is currently paused. - DOWNLOAD_PAUSED, - - // The book started downloading but the download was stopped due to - // errors. - DOWNLOAD_ERROR, - - // A valid ZIM file path is associated with the book and no evidence - // about any issues with that ZIM file has so far been obtained. - AVAILABLE_LOCALLY_AND_HEALTHY, - - // A ZIM file path is associated with the book but no such file seems - // to exist (may be caused by missing read permissions for the directory - // containing the ZIM file). - ERROR_MISSING_ZIM_FILE, - - // A ZIM file is associated with the book but it cannot be opened - // due to issues with its content. - ERROR_CORRUPTED_ZIM_FILE - }; - - -public: // functions - ContentManager(Library* library); - - ContentManagerView* getView() { return mp_view; } - void setLocal(bool local); - void setCurrentLanguage(FilterList languageList); - void setCurrentCategoryFilter(QStringList categoryList); - void setCurrentContentTypeFilter(FilterList contentTypeFilter); - bool isLocal() const { return m_local; } - QStringList getCategories() const { return m_categories; } - LanguageList getLanguages() const { return m_languages; } - - void setMonitoredDirectories(QStringSet dirList); - -signals: - void filterParamsChanged(); - void booksChanged(); - void oneBookChanged(const QString&); - void bookRemoved(const QString&); - void downloadsChanged(); - void currentLangChanged(); - void pendingRequest(const bool); - void categoriesLoaded(QStringList); - void languagesLoaded(LanguageList); - void localChanged(const bool); - -public slots: - QStringList getTranslations(const QStringList &keys); - BookInfo getBookInfos(QString id, const QStringList &keys); - BookState getBookState(QString id); - void openBook(const QString& id); - void openBookPreview(const QString& id); - void downloadBook(const QString& id); - void updateLibrary(); - void setSearch(const QString& search); - void setSortBy(const QString& sortBy, const bool sortOrderAsc); - // eraseBook() asks for confirmation (reallyEraseBook() doesn't) - void eraseBook(const QString& id); - void updateRemoteLibrary(const QString& content); - void updateLanguages(const QString& content); - void updateCategories(const QString& content); - void pauseBook(const QString& id, QModelIndex index); - void resumeBook(const QString& id, QModelIndex index); - void cancelBook(const QString& id); - void onCustomContextMenu(const QPoint &point); - void openBookWithIndex(const QModelIndex& index); - void updateDownload(QString bookId, const DownloadInfo& downloadInfo); - void downloadWasCancelled(const QString& id); - void handleError(QString errSummary, QString errDetails); - -private: // types - struct MonitoredZimFileInfo - { - enum ZimFileStatus - { - // try to add this file to the library right away - PROCESS_NOW, - - // the file is known to be downloaded by our own download manager - BEING_DOWNLOADED_BY_US, - - // the file was added to the library successfully - ADDED_TO_THE_LIBRARY, - - // the attempt to add the file to the library failed - COULD_NOT_BE_ADDED_TO_THE_LIBRARY, - - // the file couldn't be added to the library earlier and hasn't - // changed since then - UNCHANGED_KNOWN_BAD_ZIM_FILE, - - // try to add this file to the library at a later time - PROCESS_LATER, - - // this file is known to be enqueued for later processing - DEFERRED_PROCESSING_ALREADY_PENDING - }; - - bool fileKeepsBeingModified() const; - void updateStatus(const MonitoredZimFileInfo& prevInfo); - - ZimFileStatus status = PROCESS_NOW; - QDateTime lastModified; - }; - - typedef QMap ZimFileName2InfoMap; - -private: // functions - QStringList getBookIds(); - // reallyEraseBook() doesn't ask for confirmation (unlike eraseBook()) - void reallyEraseBook(const QString& id, bool moveToTrash); - void eraseBookFilesFromComputer(const std::string& bookPath, bool moveToTrash); - void updateModel(); - void setCategories(); - void setLanguages(); - QStringSet getLibraryZims(QString dirPath) const; - void asyncUpdateLibraryFromDir(QString dir); - void updateLibraryFromDir(QString dir); - void handleDisappearedZimFiles(const QString& dirPath, const QStringSet& fileNames); - size_t handleNewZimFiles(const QString& dirPath, const QStringSet& fileNames); - bool handleZimFileInMonitoredDirLogged(QString dirPath, QString fileName); - int handleZimFileInMonitoredDir(QString dirPath, QString fileName); - MonitoredZimFileInfo getMonitoredZimFileInfo(QString dir, QString fileName) const; - void deferHandlingOfZimFileInMonitoredDir(QString dir, QString fileName); - void handleZimFileInMonitoredDirDeferred(QString dirPath, QString fileName); - bool handleDisappearedBook(QString bookId); - - // Get the book with the specified id from - // the remote or local library (in that order). - const kiwix::Book& getRemoteOrLocalBook(const QString &id); - QString getRemoteLibraryUrl() const; - - void startDownload(QString bookId) override; - void removeDownload(QString bookId); - void downloadDisappeared(QString bookId); - void downloadCompleted(QString bookId, QString path); - -private: // data - Library* mp_library; - kiwix::LibraryPtr mp_remoteLibrary; - OpdsRequestManager m_remoteLibraryManager; - ContentManagerView* mp_view; - bool m_local = true; - QString m_currentLanguage; - QString m_searchQuery; - QString m_categoryFilter = "all"; - QStringList m_contentTypeFilters; - kiwix::supportedListSortBy m_sortBy = kiwix::UNSORTED; - bool m_sortOrderAsc = true; - LanguageList m_languages; - QStringList m_categories; - - ContentManagerModel *managerModel; - QMutex remoteLibraryLocker; - - QFileSystemWatcher m_watcher; - QMutex m_updateFromDirMutex; - QMap m_knownZimsInDir; -}; - -#endif // CONTENTMANAGER_H +#ifndef CONTENTMANAGER_H +#define CONTENTMANAGER_H + +#include +#include "library.h" +#include "contentmanagerview.h" +#include "opdsrequestmanager.h" +#include "contenttypefilter.h" +#include "contentmanagermodel.h" +#include "downloadmanagement.h" + +class ContentManager : public DownloadManager +{ + Q_OBJECT + Q_PROPERTY(bool isLocal MEMBER m_local READ isLocal WRITE setLocal NOTIFY localChanged) + +public: // types + typedef QList> LanguageList; + typedef QList> FilterList; + typedef ContentManagerModel::BookInfo BookInfo; + typedef ContentManagerModel::BookInfoList BookInfoList; + typedef Library::QStringSet QStringSet; + + enum class BookState + { + // Nothing known about a book with that id + INVALID, + + // Only (some) metadata is available for the book, however neither a + // ZIM-file nor a URL is associated with it. + METADATA_ONLY, + + // No ZIM file is associated with the book but a URL is provided. + AVAILABLE_ONLINE, + + // The book is being downloaded. + DOWNLOADING, + + // The book started downloading, but the download is currently paused. + DOWNLOAD_PAUSED, + + // The book started downloading but the download was stopped due to + // errors. + DOWNLOAD_ERROR, + + // A valid ZIM file path is associated with the book and no evidence + // about any issues with that ZIM file has so far been obtained. + AVAILABLE_LOCALLY_AND_HEALTHY, + + // A ZIM file path is associated with the book but no such file seems + // to exist (may be caused by missing read permissions for the directory + // containing the ZIM file). + ERROR_MISSING_ZIM_FILE, + + // A ZIM file is associated with the book but it cannot be opened + // due to issues with its content. + ERROR_CORRUPTED_ZIM_FILE + }; + + +public: // functions + ContentManager(Library* library); + + ContentManagerView* getView() { return mp_view; } + void setLocal(bool local); + void setCurrentLanguage(FilterList languageList); + void setCurrentCategoryFilter(QStringList categoryList); + void setCurrentContentTypeFilter(FilterList contentTypeFilter); + bool isLocal() const { return m_local; } + QStringList getCategories() const { return m_categories; } + LanguageList getLanguages() const { return m_languages; } + + void setMonitoredDirectories(QStringSet dirList); + +signals: + void filterParamsChanged(); + void booksChanged(); + void oneBookChanged(const QString&); + void bookRemoved(const QString&); + void downloadsChanged(); + void currentLangChanged(); + void pendingRequest(const bool); + void categoriesLoaded(QStringList); + void languagesLoaded(LanguageList); + void localChanged(const bool); + +public slots: + QStringList getTranslations(const QStringList &keys); + BookInfo getBookInfos(QString id, const QStringList &keys); + BookState getBookState(QString id); + void openBook(const QString& id); + void openBookPreview(const QString& id); + void downloadBook(const QString& id); + void updateLibrary(); + void setSearch(const QString& search); + void setSortBy(const QString& sortBy, const bool sortOrderAsc); + // eraseBook() asks for confirmation (reallyEraseBook() doesn't) + void eraseBook(const QString& id); + void updateRemoteLibrary(const QString& content); + void updateLanguages(const QString& content); + void updateCategories(const QString& content); + void pauseBook(const QString& id, QModelIndex index); + void resumeBook(const QString& id, QModelIndex index); + void cancelBook(const QString& id); + void onCustomContextMenu(const QPoint &point); + void openBookWithIndex(const QModelIndex& index); + void updateDownload(QString bookId, const DownloadInfo& downloadInfo); + void downloadWasCancelled(const QString& id); + void handleError(QString errSummary, QString errDetails); + +private: // types + struct MonitoredZimFileInfo + { + enum ZimFileStatus + { + // try to add this file to the library right away + PROCESS_NOW, + + // the file is known to be downloaded by our own download manager + BEING_DOWNLOADED_BY_US, + + // the file was added to the library successfully + ADDED_TO_THE_LIBRARY, + + // the attempt to add the file to the library failed + COULD_NOT_BE_ADDED_TO_THE_LIBRARY, + + // the file couldn't be added to the library earlier and hasn't + // changed since then + UNCHANGED_KNOWN_BAD_ZIM_FILE, + + // try to add this file to the library at a later time + PROCESS_LATER, + + // this file is known to be enqueued for later processing + DEFERRED_PROCESSING_ALREADY_PENDING + }; + + bool fileKeepsBeingModified() const; + void updateStatus(const MonitoredZimFileInfo& prevInfo); + + ZimFileStatus status = PROCESS_NOW; + QDateTime lastModified; + }; + + typedef QMap ZimFileName2InfoMap; + +private: // functions + QStringList getBookIds(); + // reallyEraseBook() doesn't ask for confirmation (unlike eraseBook()) + void reallyEraseBook(const QString& id, bool moveToTrash); + void eraseBookFilesFromComputer(const std::string& bookPath, bool moveToTrash); + void updateModel(); + void setCategories(); + void setLanguages(); + QStringSet getLibraryZims(QString dirPath) const; + void asyncUpdateLibraryFromDir(QString dir); + void updateLibraryFromDir(QString dir); + void handleDisappearedZimFiles(const QString& dirPath, const QStringSet& fileNames); + size_t handleNewZimFiles(const QString& dirPath, const QStringSet& fileNames); + bool handleZimFileInMonitoredDirLogged(QString dirPath, QString fileName); + int handleZimFileInMonitoredDir(QString dirPath, QString fileName); + MonitoredZimFileInfo getMonitoredZimFileInfo(QString dir, QString fileName) const; + void deferHandlingOfZimFileInMonitoredDir(QString dir, QString fileName); + void handleZimFileInMonitoredDirDeferred(QString dirPath, QString fileName); + bool handleDisappearedBook(QString bookId); + + // Get the book with the specified id from + // the remote or local library (in that order). + const kiwix::Book& getRemoteOrLocalBook(const QString &id); + QString getRemoteLibraryUrl() const; + + void startDownload(QString bookId) override; + void removeDownload(QString bookId); + void downloadDisappeared(QString bookId); + void downloadCompleted(QString bookId, QString path); + +private: // data + Library* mp_library; + kiwix::LibraryPtr mp_remoteLibrary; + OpdsRequestManager m_remoteLibraryManager; + ContentManagerView* mp_view; + bool m_local = true; + QString m_currentLanguage; + QString m_searchQuery; + QString m_categoryFilter = "all"; + QStringList m_contentTypeFilters; + kiwix::supportedListSortBy m_sortBy = kiwix::UNSORTED; + bool m_sortOrderAsc = true; + LanguageList m_languages; + QStringList m_categories; + + ContentManagerModel *managerModel; + QMutex remoteLibraryLocker; + + QFileSystemWatcher m_watcher; + QMutex m_updateFromDirMutex; + QMap m_knownZimsInDir; +}; + +#endif // CONTENTMANAGER_H diff --git a/src/contentmanagerdelegate.cpp b/src/contentmanagerdelegate.cpp index 60a55e4de..7c04a10f9 100644 --- a/src/contentmanagerdelegate.cpp +++ b/src/contentmanagerdelegate.cpp @@ -1,300 +1,300 @@ -#include "contentmanagerdelegate.h" -#include -#include -#include -#include -#include "kiwixapp.h" -#include -#include "rownode.h" -#include "descriptionnode.h" -#include "portutils.h" -#include "css_constants.h" - -ContentManagerDelegate::ContentManagerDelegate(QObject *parent) - : QStyledItemDelegate(parent), baseButton(new QPushButton) -{ - baseButton->setStyleSheet("background-color: #00000000;" - "border: 0;" - "font-weight: bold;" - "font-family: Selawik;" - "color: blue;" - "margin: 4px;"); -} - -namespace -{ - -void createPauseSymbol(QPainter *painter, const QRect& buttonRect) -{ - QPen pen; - pen.setWidth(3); - QPainterPath path; - const int x = buttonRect.left() + 12.5; - const int y = buttonRect.top() + 10; - pen.setColor("#3366cc"); - path.moveTo(x, y); - path.lineTo(x, y + 10); - painter->strokePath(path, pen); - path.moveTo(x + 5, y); - path.lineTo(x + 5, y + 10); - painter->strokePath(path, pen); -} - -void createResumeSymbol(QPainter *painter, const QRect& buttonRect) -{ - QPen pen; - pen.setWidth(3); - QPainterPath path; - const int x = buttonRect.left() + 12.5; - const int y = buttonRect.top() + 8; - pen.setColor("#3366cc"); - path.moveTo(x, y); - path.lineTo(x, y + 15); - path.lineTo(x + 10, y + 8); - path.lineTo(x, y); - painter->setRenderHint(QPainter::Antialiasing); - painter->strokePath(path, pen); -} - -void createArc(QPainter *painter, int startAngle, int spanAngle, QRect rectangle, QPen pen) -{ - painter->setRenderHint(QPainter::Antialiasing); - int arcX = rectangle.x(); - int arcY = rectangle.y(); - int arcW = rectangle.width(); - int arcH = rectangle.height(); - QPainterPath path; - path.moveTo(arcX + arcW, arcY + arcH/2); - path.arcTo(rectangle, startAngle, spanAngle); - painter->strokePath(path, pen); -} - -void createCancelButton(QPainter *painter, const QRect& r) -{ - QPen p; - p.setWidth(3); - p.setColor("#dd3333"); - createArc(painter, 0, 360, r, p); - painter->setPen(p); - auto oldFont = painter->font(); - auto bFont = oldFont; - bFont.setBold(true); - painter->setFont(bFont); - painter->drawText(r, Qt::AlignCenter | Qt::AlignJustify, "X"); - painter->setFont(oldFont); -} - -void createDownloadStats(QPainter *painter, QRect box, QString downloadSpeed, QString completedLength) -{ - QPen pen; - int x = box.x(); - int y = box.y(); - int w = box.width(); - int h = box.height(); - pen.setColor("#666666"); - painter->setPen(pen); - auto oldFont = painter->font(); - painter->setFont(QFont("Selawik", 8)); - QRect nRect(x - 20, y - 10, w, h); - painter->drawText(nRect,Qt::AlignCenter | Qt::AlignJustify, downloadSpeed); - QRect fRect(x - 20, y + 10, w, h); - painter->drawText(fRect,Qt::AlignCenter | Qt::AlignJustify, completedLength); - painter->setFont(oldFont); -} - -struct DownloadControlLayout -{ - QRect pauseResumeButtonRect; - QRect cancelButtonRect; -}; - -DownloadControlLayout getDownloadControlLayout(QRect box) -{ - const int x = box.left(); - const int y = box.top(); - const int w = box.width(); - const int h = box.height(); - - const int buttonW = w - 90; - const int buttonH = h - 40; - - DownloadControlLayout dcl; - dcl.pauseResumeButtonRect = QRect(x + w/2 + 10, y + 20, buttonW, buttonH); - dcl.cancelButtonRect = QRect(x + w/2 - 40, y + 20, buttonW, buttonH); - return dcl; -} - -void showDownloadProgress(QPainter *painter, QRect box, const DownloadState& downloadInfo) -{ - const DownloadControlLayout dcl = getDownloadControlLayout(box); - double progress = (double) (downloadInfo.progress) / 100; - progress = -progress; - auto completedLength = downloadInfo.completedLength; - auto downloadSpeed = downloadInfo.getDownloadSpeed(); - - if (downloadInfo.getStatus() == DownloadState::PAUSED) { - createResumeSymbol(painter, dcl.pauseResumeButtonRect); - createCancelButton(painter, dcl.cancelButtonRect); - } else if (downloadInfo.getStatus() == DownloadState::DOWNLOADING) { - createPauseSymbol(painter, dcl.pauseResumeButtonRect); - createDownloadStats(painter, box, downloadSpeed, completedLength); - } - - QPen pen; - pen.setWidth(3); - painter->setPen(pen); - painter->setRenderHint(QPainter::Antialiasing); - - pen.setColor("#dadce0"); - createArc(painter, 0, 360, dcl.pauseResumeButtonRect, pen); - - int startAngle = 0; - int spanAngle = progress * 360; - pen.setColor("#3366cc"); - createArc(painter, startAngle, spanAngle, dcl.pauseResumeButtonRect, pen); -} - -} // unnamed namespace - -void ContentManagerDelegate::paintButton(QPainter *p, const QRect &r, QString t) const -{ - QStyleOptionButton button; - button.rect = r; - button.state = QStyle::State_Enabled; - button.text = t; - baseButton->style()->drawControl( QStyle::CE_PushButton, &button, p, baseButton.data()); -} - -void ContentManagerDelegate::paintBookState(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const -{ - const QRect r = opt.rect; - if (opt.state & QStyle::State_MouseOver) { - // don't paint over the line separator - const auto cellInternalArea = r.adjusted(0, 0, 0, -1); - p->fillRect(cellInternalArea, QBrush("#eaecf0")); - } - const auto node = static_cast(index.internalPointer()); - const auto id = node->getBookId(); - switch ( KiwixApp::instance()->getContentManager()->getBookState(id) ) { - case ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY: - return paintButton(p, r, gt("open")); - - case ContentManager::BookState::AVAILABLE_ONLINE: - return paintButton(p, r, gt("download")); - - case ContentManager::BookState::DOWNLOADING: - case ContentManager::BookState::DOWNLOAD_PAUSED: - case ContentManager::BookState::DOWNLOAD_ERROR: - return showDownloadProgress(p, r, *node->getDownloadState()); - - default: - return; - } -} - -void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (isDescriptionIndex(index)) - return QStyledItemDelegate::paint(painter, option, index); - - QStyleOptionViewItem eOpt = option; - if (index.column() == 1) { - auto bFont = painter->font(); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - bFont.setWeight(60); -#else - bFont.setWeight(QFont::DemiBold); -#endif - eOpt.font = bFont; - } - - QStyledItemDelegate::paint(painter, eOpt, index); - - if (index.column() == 5) { - paintBookState(painter, eOpt, index); - } -} - -bool ContentManagerDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) -{ - Q_UNUSED(model); - - if(event->type() == QEvent::MouseButtonRelease ) - { - QMouseEvent * e = (QMouseEvent *)event; - if (e->button() == Qt::MiddleButton && index.column() != 5) { - KiwixApp::instance()->getContentManager()->openBookWithIndex(index); - return true; - } - - const QPoint clickPoint(portutils::getX(*e), portutils::getY(*e)); - - if ( index.column() == 5 && option.rect.contains(clickPoint, true) ) - handleLastColumnClicked(index, e, option); - } - - return true; -} - -void ContentManagerDelegate::handleLastColumnClicked(const QModelIndex& index, QMouseEvent *mouseEvent, const QStyleOptionViewItem &option) -{ - const auto node = static_cast(index.internalPointer()); - const auto id = node->getBookId(); - const auto downloadState = node->getDownloadState(); - - const int clickX = portutils::getX(*mouseEvent); - const int clickY = portutils::getY(*mouseEvent); - const QPoint clickPoint(clickX, clickY); - const DownloadControlLayout dcl = getDownloadControlLayout(option.rect); - - ContentManager& contentMgr = *KiwixApp::instance()->getContentManager(); - switch ( contentMgr.getBookState(id) ) { - case ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY: - return contentMgr.openBook(id); - - case ContentManager::BookState::AVAILABLE_ONLINE: - return contentMgr.downloadBook(id); - - case ContentManager::BookState::DOWNLOADING: - if ( downloadState->getStatus() == DownloadState::DOWNLOADING ) { - if ( dcl.pauseResumeButtonRect.contains(clickPoint) ) { - contentMgr.pauseBook(id, index); - } - } - return; - - case ContentManager::BookState::DOWNLOAD_PAUSED: - if ( downloadState->getStatus() == DownloadState::PAUSED ) { - if ( dcl.cancelButtonRect.contains(clickPoint) ) { - contentMgr.cancelBook(id); - } else if ( dcl.pauseResumeButtonRect.contains(clickPoint) ) { - contentMgr.resumeBook(id, index); - } - } - return; - - default: - return; - } -} - -QSize ContentManagerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const -{ - if (isDescriptionIndex(index)) - { - const auto treeView = KiwixApp::instance()->getContentManager()->getView()->getView(); - - const int width = treeView->header()->length() - 2*treeView->indentation(); - const int verticalPadding = CSS::ContentManagerCSS::QTreeView::padding; - const int horizontalPadding = CSS::ContentManagerCSS::QTreeView::padding; - QRect descRect(0, 0, width - 2 * horizontalPadding, 0); - - /* Based on the rectangle and text, find the best fitting size. */ - QFontMetrics fm(option.font); - const QString text = index.data().toString(); - const auto format = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap; - const int textHeight = fm.boundingRect(descRect, format, text).height(); - return QSize(width, std::max(textHeight + verticalPadding, 70)); - } - return QSize(50, 70); -} +#include "contentmanagerdelegate.h" +#include +#include +#include +#include +#include "kiwixapp.h" +#include +#include "rownode.h" +#include "descriptionnode.h" +#include "portutils.h" +#include "css_constants.h" + +ContentManagerDelegate::ContentManagerDelegate(QObject *parent) + : QStyledItemDelegate(parent), baseButton(new QPushButton) +{ + baseButton->setStyleSheet("background-color: #00000000;" + "border: 0;" + "font-weight: bold;" + "font-family: Selawik;" + "color: blue;" + "margin: 4px;"); +} + +namespace +{ + +void createPauseSymbol(QPainter *painter, const QRect& buttonRect) +{ + QPen pen; + pen.setWidth(3); + QPainterPath path; + const int x = buttonRect.left() + 12.5; + const int y = buttonRect.top() + 10; + pen.setColor("#3366cc"); + path.moveTo(x, y); + path.lineTo(x, y + 10); + painter->strokePath(path, pen); + path.moveTo(x + 5, y); + path.lineTo(x + 5, y + 10); + painter->strokePath(path, pen); +} + +void createResumeSymbol(QPainter *painter, const QRect& buttonRect) +{ + QPen pen; + pen.setWidth(3); + QPainterPath path; + const int x = buttonRect.left() + 12.5; + const int y = buttonRect.top() + 8; + pen.setColor("#3366cc"); + path.moveTo(x, y); + path.lineTo(x, y + 15); + path.lineTo(x + 10, y + 8); + path.lineTo(x, y); + painter->setRenderHint(QPainter::Antialiasing); + painter->strokePath(path, pen); +} + +void createArc(QPainter *painter, int startAngle, int spanAngle, QRect rectangle, QPen pen) +{ + painter->setRenderHint(QPainter::Antialiasing); + int arcX = rectangle.x(); + int arcY = rectangle.y(); + int arcW = rectangle.width(); + int arcH = rectangle.height(); + QPainterPath path; + path.moveTo(arcX + arcW, arcY + arcH/2); + path.arcTo(rectangle, startAngle, spanAngle); + painter->strokePath(path, pen); +} + +void createCancelButton(QPainter *painter, const QRect& r) +{ + QPen p; + p.setWidth(3); + p.setColor("#dd3333"); + createArc(painter, 0, 360, r, p); + painter->setPen(p); + auto oldFont = painter->font(); + auto bFont = oldFont; + bFont.setBold(true); + painter->setFont(bFont); + painter->drawText(r, Qt::AlignCenter | Qt::AlignJustify, "X"); + painter->setFont(oldFont); +} + +void createDownloadStats(QPainter *painter, QRect box, QString downloadSpeed, QString completedLength) +{ + QPen pen; + int x = box.x(); + int y = box.y(); + int w = box.width(); + int h = box.height(); + pen.setColor("#666666"); + painter->setPen(pen); + auto oldFont = painter->font(); + painter->setFont(QFont("Selawik", 8)); + QRect nRect(x - 20, y - 10, w, h); + painter->drawText(nRect,Qt::AlignCenter | Qt::AlignJustify, downloadSpeed); + QRect fRect(x - 20, y + 10, w, h); + painter->drawText(fRect,Qt::AlignCenter | Qt::AlignJustify, completedLength); + painter->setFont(oldFont); +} + +struct DownloadControlLayout +{ + QRect pauseResumeButtonRect; + QRect cancelButtonRect; +}; + +DownloadControlLayout getDownloadControlLayout(QRect box) +{ + const int x = box.left(); + const int y = box.top(); + const int w = box.width(); + const int h = box.height(); + + const int buttonW = w - 90; + const int buttonH = h - 40; + + DownloadControlLayout dcl; + dcl.pauseResumeButtonRect = QRect(x + w/2 + 10, y + 20, buttonW, buttonH); + dcl.cancelButtonRect = QRect(x + w/2 - 40, y + 20, buttonW, buttonH); + return dcl; +} + +void showDownloadProgress(QPainter *painter, QRect box, const DownloadState& downloadInfo) +{ + const DownloadControlLayout dcl = getDownloadControlLayout(box); + double progress = (double) (downloadInfo.progress) / 100; + progress = -progress; + auto completedLength = downloadInfo.completedLength; + auto downloadSpeed = downloadInfo.getDownloadSpeed(); + + if (downloadInfo.getStatus() == DownloadState::PAUSED) { + createResumeSymbol(painter, dcl.pauseResumeButtonRect); + createCancelButton(painter, dcl.cancelButtonRect); + } else if (downloadInfo.getStatus() == DownloadState::DOWNLOADING) { + createPauseSymbol(painter, dcl.pauseResumeButtonRect); + createDownloadStats(painter, box, downloadSpeed, completedLength); + } + + QPen pen; + pen.setWidth(3); + painter->setPen(pen); + painter->setRenderHint(QPainter::Antialiasing); + + pen.setColor("#dadce0"); + createArc(painter, 0, 360, dcl.pauseResumeButtonRect, pen); + + int startAngle = 0; + int spanAngle = progress * 360; + pen.setColor("#3366cc"); + createArc(painter, startAngle, spanAngle, dcl.pauseResumeButtonRect, pen); +} + +} // unnamed namespace + +void ContentManagerDelegate::paintButton(QPainter *p, const QRect &r, QString t) const +{ + QStyleOptionButton button; + button.rect = r; + button.state = QStyle::State_Enabled; + button.text = t; + baseButton->style()->drawControl( QStyle::CE_PushButton, &button, p, baseButton.data()); +} + +void ContentManagerDelegate::paintBookState(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + const QRect r = opt.rect; + if (opt.state & QStyle::State_MouseOver) { + // don't paint over the line separator + const auto cellInternalArea = r.adjusted(0, 0, 0, -1); + p->fillRect(cellInternalArea, QBrush("#eaecf0")); + } + const auto node = static_cast(index.internalPointer()); + const auto id = node->getBookId(); + switch ( KiwixApp::instance()->getContentManager()->getBookState(id) ) { + case ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY: + return paintButton(p, r, gt("open")); + + case ContentManager::BookState::AVAILABLE_ONLINE: + return paintButton(p, r, gt("download")); + + case ContentManager::BookState::DOWNLOADING: + case ContentManager::BookState::DOWNLOAD_PAUSED: + case ContentManager::BookState::DOWNLOAD_ERROR: + return showDownloadProgress(p, r, *node->getDownloadState()); + + default: + return; + } +} + +void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (isDescriptionIndex(index)) + return QStyledItemDelegate::paint(painter, option, index); + + QStyleOptionViewItem eOpt = option; + if (index.column() == 1) { + auto bFont = painter->font(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + bFont.setWeight(60); +#else + bFont.setWeight(QFont::DemiBold); +#endif + eOpt.font = bFont; + } + + QStyledItemDelegate::paint(painter, eOpt, index); + + if (index.column() == 5) { + paintBookState(painter, eOpt, index); + } +} + +bool ContentManagerDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) +{ + Q_UNUSED(model); + + if(event->type() == QEvent::MouseButtonRelease ) + { + QMouseEvent * e = (QMouseEvent *)event; + if (e->button() == Qt::MiddleButton && index.column() != 5) { + KiwixApp::instance()->getContentManager()->openBookWithIndex(index); + return true; + } + + const QPoint clickPoint(portutils::getX(*e), portutils::getY(*e)); + + if ( index.column() == 5 && option.rect.contains(clickPoint, true) ) + handleLastColumnClicked(index, e, option); + } + + return true; +} + +void ContentManagerDelegate::handleLastColumnClicked(const QModelIndex& index, QMouseEvent *mouseEvent, const QStyleOptionViewItem &option) +{ + const auto node = static_cast(index.internalPointer()); + const auto id = node->getBookId(); + const auto downloadState = node->getDownloadState(); + + const int clickX = portutils::getX(*mouseEvent); + const int clickY = portutils::getY(*mouseEvent); + const QPoint clickPoint(clickX, clickY); + const DownloadControlLayout dcl = getDownloadControlLayout(option.rect); + + ContentManager& contentMgr = *KiwixApp::instance()->getContentManager(); + switch ( contentMgr.getBookState(id) ) { + case ContentManager::BookState::AVAILABLE_LOCALLY_AND_HEALTHY: + return contentMgr.openBook(id); + + case ContentManager::BookState::AVAILABLE_ONLINE: + return contentMgr.downloadBook(id); + + case ContentManager::BookState::DOWNLOADING: + if ( downloadState->getStatus() == DownloadState::DOWNLOADING ) { + if ( dcl.pauseResumeButtonRect.contains(clickPoint) ) { + contentMgr.pauseBook(id, index); + } + } + return; + + case ContentManager::BookState::DOWNLOAD_PAUSED: + if ( downloadState->getStatus() == DownloadState::PAUSED ) { + if ( dcl.cancelButtonRect.contains(clickPoint) ) { + contentMgr.cancelBook(id); + } else if ( dcl.pauseResumeButtonRect.contains(clickPoint) ) { + contentMgr.resumeBook(id, index); + } + } + return; + + default: + return; + } +} + +QSize ContentManagerDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + if (isDescriptionIndex(index)) + { + const auto treeView = KiwixApp::instance()->getContentManager()->getView()->getView(); + + const int width = treeView->header()->length() - 2*treeView->indentation(); + const int verticalPadding = CSS::ContentManagerCSS::QTreeView::padding; + const int horizontalPadding = CSS::ContentManagerCSS::QTreeView::padding; + QRect descRect(0, 0, width - 2 * horizontalPadding, 0); + + /* Based on the rectangle and text, find the best fitting size. */ + QFontMetrics fm(option.font); + const QString text = index.data().toString(); + const auto format = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap; + const int textHeight = fm.boundingRect(descRect, format, text).height(); + return QSize(width, std::max(textHeight + verticalPadding, 70)); + } + return QSize(50, 70); +} diff --git a/src/contentmanagerdelegate.h b/src/contentmanagerdelegate.h index 48c5cef1a..5d613e1b5 100644 --- a/src/contentmanagerdelegate.h +++ b/src/contentmanagerdelegate.h @@ -1,27 +1,27 @@ -#ifndef CONTENTMANAGERDELEGATE_H -#define CONTENTMANAGERDELEGATE_H - -#include -#include -#include - -class ContentManagerDelegate : public QStyledItemDelegate -{ - Q_OBJECT -public: - - ContentManagerDelegate(QObject *parent=0); - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - -private: // functions - void paintBookState(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const; - void paintButton(QPainter *p, const QRect &r, QString t) const; - -private: - QScopedPointer baseButton; - void handleLastColumnClicked(const QModelIndex& index, QMouseEvent *event, const QStyleOptionViewItem &option); -}; - -#endif // CONTENTMANAGERDELEGATE_H +#ifndef CONTENTMANAGERDELEGATE_H +#define CONTENTMANAGERDELEGATE_H + +#include +#include +#include + +class ContentManagerDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + + ContentManagerDelegate(QObject *parent=0); + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: // functions + void paintBookState(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const; + void paintButton(QPainter *p, const QRect &r, QString t) const; + +private: + QScopedPointer baseButton; + void handleLastColumnClicked(const QModelIndex& index, QMouseEvent *event, const QStyleOptionViewItem &option); +}; + +#endif // CONTENTMANAGERDELEGATE_H diff --git a/src/contentmanagerheader.cpp b/src/contentmanagerheader.cpp index de5fc3ce5..a4b757d4f 100644 --- a/src/contentmanagerheader.cpp +++ b/src/contentmanagerheader.cpp @@ -1,20 +1,20 @@ -#include "contentmanagerheader.h" -#include - -ContentManagerHeader::ContentManagerHeader(Qt::Orientation orientation, QWidget *parent) - : QHeaderView(orientation, parent) -{} - -ContentManagerHeader::~ContentManagerHeader() -{} - -void ContentManagerHeader::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const -{ - // This is required so that the sort indicator icon is not shown in first column - if (logicalIndex == 0) - return; - painter->save(); - QHeaderView::paintSection(painter, rect, logicalIndex); - painter->restore(); -} - +#include "contentmanagerheader.h" +#include + +ContentManagerHeader::ContentManagerHeader(Qt::Orientation orientation, QWidget *parent) + : QHeaderView(orientation, parent) +{} + +ContentManagerHeader::~ContentManagerHeader() +{} + +void ContentManagerHeader::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const +{ + // This is required so that the sort indicator icon is not shown in first column + if (logicalIndex == 0) + return; + painter->save(); + QHeaderView::paintSection(painter, rect, logicalIndex); + painter->restore(); +} + diff --git a/src/contentmanagerheader.h b/src/contentmanagerheader.h index 6eea7e176..bbba8f13c 100644 --- a/src/contentmanagerheader.h +++ b/src/contentmanagerheader.h @@ -1,18 +1,18 @@ -#ifndef CONTENTMANAGERHEADER_H -#define CONTENTMANAGERHEADER_H - -#include - -class ContentManagerHeader : public QHeaderView -{ - Q_OBJECT -public: - explicit ContentManagerHeader(Qt::Orientation orientation, QWidget *parent = nullptr); - ~ContentManagerHeader(); - -protected: - void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override; - -}; - -#endif // CONTENTMANAGERHEADER_H +#ifndef CONTENTMANAGERHEADER_H +#define CONTENTMANAGERHEADER_H + +#include + +class ContentManagerHeader : public QHeaderView +{ + Q_OBJECT +public: + explicit ContentManagerHeader(Qt::Orientation orientation, QWidget *parent = nullptr); + ~ContentManagerHeader(); + +protected: + void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override; + +}; + +#endif // CONTENTMANAGERHEADER_H diff --git a/src/contentmanagermodel.cpp b/src/contentmanagermodel.cpp index e79565bf3..e1ad06b0f 100644 --- a/src/contentmanagermodel.cpp +++ b/src/contentmanagermodel.cpp @@ -1,278 +1,278 @@ -#include "contentmanagermodel.h" -#include "node.h" -#include "rownode.h" -#include "descriptionnode.h" -#include -#include -#include "kiwixapp.h" -#include - -ContentManagerModel::ContentManagerModel(ContentManager *contentMgr) - : QAbstractItemModel(contentMgr) - , m_contentMgr(*contentMgr) -{ - connect(&td, &ThumbnailDownloader::oneThumbnailDownloaded, this, &ContentManagerModel::updateImage); -} - -ContentManagerModel::~ContentManagerModel() -{ -} - -int ContentManagerModel::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return static_cast(parent.internalPointer())->columnCount(); - return rootNode->columnCount(); -} - -namespace -{ - -QIcon makeIcon(const QByteArray& iconData) -{ - QPixmap pix; - pix.loadFromData(iconData); - return QIcon(pix); -} - -QIcon createPlaceholderIcon() -{ - QImage placeholderIconFile(":/icons/placeholder-icon.png"); - QByteArray iconData; - QBuffer buffer(&iconData); - buffer.open(QIODevice::WriteOnly); - placeholderIconFile.save(&buffer, "png"); - return makeIcon(iconData); -} - -QIcon getIcon(const QByteArray& iconData) -{ - static QIcon placeholderIcon = createPlaceholderIcon(); - - return iconData.isNull() ? placeholderIcon : makeIcon(iconData); -} - -} // unnamed namespace - -QVariant ContentManagerModel::data(const QModelIndex& index, int role) const -{ - if ( ! index.isValid() ) - return QVariant(); - - const auto col = index.column(); - const bool isThumbnailRequest = - !isDescriptionIndex(index) && col == 0 && role == Qt::DecorationRole; - const bool isDescriptionRequest = - isDescriptionIndex(index) && col == 0 && role == Qt::DisplayRole; - const bool otherDataRequest = col != 0 && role == Qt::DisplayRole; - - if ( !isThumbnailRequest && !otherDataRequest && !isDescriptionRequest ) - return QVariant(); - - const auto item = static_cast(index.internalPointer()); - QVariant r = item->data(col); - if ( !isThumbnailRequest ) - return r; - - r = getThumbnail(r); - - if ( r.userType() == QMetaType::QByteArray ) - return getIcon(r.toByteArray()); - - const QString faviconUrl = r.toString(); - if ( !faviconUrl.isEmpty() ) - td.addDownload(faviconUrl, item->getBookId()); - - return QVariant(); -} - -Qt::ItemFlags ContentManagerModel::flags(const QModelIndex &index) const -{ - Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); - if (isDescriptionIndex(index)) { - return defaultFlags & ~Qt::ItemIsDropEnabled & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsSelectable & ~Qt::ItemIsEditable & ~Qt::ItemIsUserCheckable; - } - return defaultFlags; -} - -QModelIndex ContentManagerModel::index(int row, int column, const QModelIndex &parent) const -{ - if (!hasIndex(row, column, parent)) - return QModelIndex(); - - RowNode* parentItem; - - if (!parent.isValid()) { - parentItem = rootNode.get(); - } - else { - parentItem = static_cast(parent.internalPointer()); - } - auto childItem = parentItem->child(row); - if (childItem) - return createIndex(row, column, childItem.get()); - - return QModelIndex(); -} - -QModelIndex ContentManagerModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - auto childItem = static_cast(index.internalPointer()); - auto parentItem = childItem->parentItem(); - - if (!parentItem || parentItem == rootNode) - return QModelIndex(); - - return createIndex(parentItem->row(), 0, parentItem.get()); -} - -int ContentManagerModel::rowCount(const QModelIndex &parent) const -{ - const auto node = parent.isValid() - ? static_cast(parent.internalPointer()) - : rootNode.get(); - - return node->childCount(); -} - -QVariant ContentManagerModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole || orientation != Qt::Horizontal) - return QVariant(); - - switch (section) - { - case 1: return "Name"; - case 2: return "Date"; - case 3: return "Size"; - case 4: return "Content Type"; - default: return QVariant(); - } -} - -void ContentManagerModel::setBooksData(const BookInfoList& data, const DownloadManager& downloadMgr) -{ - rootNode = std::shared_ptr(new RowNode({tr("Icon"), tr("Name"), tr("Date"), tr("Size"), tr("Content Type"), tr("Download")}, "", std::weak_ptr())); - beginResetModel(); - bookIdToRowMap.clear(); - for (auto bookItem : data) { - const auto rowNode = createNode(bookItem); - - // Restore download state during model updates (filtering, etc) - rowNode->setDownloadState(downloadMgr.getDownloadState(rowNode->getBookId())); - - bookIdToRowMap[bookItem["id"].toString()] = rootNode->childCount(); - rootNode->appendChild(rowNode); - } - endResetModel(); - emit dataChanged(QModelIndex(), QModelIndex()); -} - -// Returns either data of the thumbnail (as a QByteArray) or a URL (as a -// QString) from where the actual data can be obtained. -QVariant ContentManagerModel::getThumbnail(const QVariant& faviconEntry) const -{ - if ( faviconEntry.userType() == QMetaType::QByteArray ) - return faviconEntry; - - const auto faviconUrl = faviconEntry.toString(); - return m_iconMap.contains(faviconUrl) - ? m_iconMap[faviconUrl] - : faviconEntry; -} - -std::shared_ptr ContentManagerModel::createNode(BookInfo bookItem) const -{ - QString id = bookItem["id"].toString(); - const QVariant bookIcon = getThumbnail(bookItem["favicon"]); - std::weak_ptr weakRoot = rootNode; - auto rowNodePtr = std::shared_ptr(new - RowNode({bookIcon, bookItem["title"], - bookItem["date"], - QString::fromStdString(kiwix::beautifyFileSize(bookItem["size"].toULongLong())), - bookItem["tags"] - }, id, weakRoot)); - std::weak_ptr weakRowNodePtr = rowNodePtr; - const auto descNodePtr = std::make_shared(DescriptionNode(bookItem["description"].toString(), weakRowNodePtr)); - - rowNodePtr->appendChild(descNodePtr); - return rowNodePtr; -} - -bool ContentManagerModel::hasChildren(const QModelIndex &parent) const -{ - auto item = static_cast(parent.internalPointer()); - if (item) - return item->childCount() > 0; - return true; -} - -void ContentManagerModel::sort(int column, Qt::SortOrder order) -{ - if (column == 0 || column == 4 || column == 5) - return; - - QString sortBy = ""; - switch(column) { - case 1: - sortBy = "title"; - break; - case 2: - sortBy = "date"; - break; - case 3: - sortBy = "size"; - break; - default: - sortBy = "unsorted"; - } - m_contentMgr.setSortBy(sortBy, order == Qt::AscendingOrder); -} - -RowNode* ContentManagerModel::getRowNode(size_t row) -{ - return static_cast(rootNode->child(row).get()); -} - -void ContentManagerModel::updateImage(QString bookId, QString url, QByteArray imageData) -{ - const auto it = bookIdToRowMap.constFind(bookId); - if ( it == bookIdToRowMap.constEnd() ) - return; - - const size_t row = it.value(); - const auto item = getRowNode(row); - item->setIconData(imageData); - m_iconMap[url] = imageData; - triggerDataUpdateAt( this->index(row, 0) ); -} - -void ContentManagerModel::updateDownload(QString bookId) -{ - const auto it = bookIdToRowMap.constFind(bookId); - - if ( it != bookIdToRowMap.constEnd() ) { - const size_t row = it.value(); - triggerDataUpdateAt( this->index(row, 5) ); - } -} - -void ContentManagerModel::triggerDataUpdateAt(QModelIndex index) -{ - emit dataChanged(index, index); -} - -void ContentManagerModel::setDownloadState(QString bookId, std::shared_ptr ds) -{ - const auto it = bookIdToRowMap.constFind(bookId); - if ( it == bookIdToRowMap.constEnd() ) - return; - - const size_t row = it.value(); - getRowNode(row)->setDownloadState(ds); - triggerDataUpdateAt( this->index(row, 5) ); -} - +#include "contentmanagermodel.h" +#include "node.h" +#include "rownode.h" +#include "descriptionnode.h" +#include +#include +#include "kiwixapp.h" +#include + +ContentManagerModel::ContentManagerModel(ContentManager *contentMgr) + : QAbstractItemModel(contentMgr) + , m_contentMgr(*contentMgr) +{ + connect(&td, &ThumbnailDownloader::oneThumbnailDownloaded, this, &ContentManagerModel::updateImage); +} + +ContentManagerModel::~ContentManagerModel() +{ +} + +int ContentManagerModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return static_cast(parent.internalPointer())->columnCount(); + return rootNode->columnCount(); +} + +namespace +{ + +QIcon makeIcon(const QByteArray& iconData) +{ + QPixmap pix; + pix.loadFromData(iconData); + return QIcon(pix); +} + +QIcon createPlaceholderIcon() +{ + QImage placeholderIconFile(":/icons/placeholder-icon.png"); + QByteArray iconData; + QBuffer buffer(&iconData); + buffer.open(QIODevice::WriteOnly); + placeholderIconFile.save(&buffer, "png"); + return makeIcon(iconData); +} + +QIcon getIcon(const QByteArray& iconData) +{ + static QIcon placeholderIcon = createPlaceholderIcon(); + + return iconData.isNull() ? placeholderIcon : makeIcon(iconData); +} + +} // unnamed namespace + +QVariant ContentManagerModel::data(const QModelIndex& index, int role) const +{ + if ( ! index.isValid() ) + return QVariant(); + + const auto col = index.column(); + const bool isThumbnailRequest = + !isDescriptionIndex(index) && col == 0 && role == Qt::DecorationRole; + const bool isDescriptionRequest = + isDescriptionIndex(index) && col == 0 && role == Qt::DisplayRole; + const bool otherDataRequest = col != 0 && role == Qt::DisplayRole; + + if ( !isThumbnailRequest && !otherDataRequest && !isDescriptionRequest ) + return QVariant(); + + const auto item = static_cast(index.internalPointer()); + QVariant r = item->data(col); + if ( !isThumbnailRequest ) + return r; + + r = getThumbnail(r); + + if ( r.userType() == QMetaType::QByteArray ) + return getIcon(r.toByteArray()); + + const QString faviconUrl = r.toString(); + if ( !faviconUrl.isEmpty() ) + td.addDownload(faviconUrl, item->getBookId()); + + return QVariant(); +} + +Qt::ItemFlags ContentManagerModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); + if (isDescriptionIndex(index)) { + return defaultFlags & ~Qt::ItemIsDropEnabled & ~Qt::ItemIsDragEnabled & ~Qt::ItemIsSelectable & ~Qt::ItemIsEditable & ~Qt::ItemIsUserCheckable; + } + return defaultFlags; +} + +QModelIndex ContentManagerModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + RowNode* parentItem; + + if (!parent.isValid()) { + parentItem = rootNode.get(); + } + else { + parentItem = static_cast(parent.internalPointer()); + } + auto childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem.get()); + + return QModelIndex(); +} + +QModelIndex ContentManagerModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + auto childItem = static_cast(index.internalPointer()); + auto parentItem = childItem->parentItem(); + + if (!parentItem || parentItem == rootNode) + return QModelIndex(); + + return createIndex(parentItem->row(), 0, parentItem.get()); +} + +int ContentManagerModel::rowCount(const QModelIndex &parent) const +{ + const auto node = parent.isValid() + ? static_cast(parent.internalPointer()) + : rootNode.get(); + + return node->childCount(); +} + +QVariant ContentManagerModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + + switch (section) + { + case 1: return "Name"; + case 2: return "Date"; + case 3: return "Size"; + case 4: return "Content Type"; + default: return QVariant(); + } +} + +void ContentManagerModel::setBooksData(const BookInfoList& data, const DownloadManager& downloadMgr) +{ + rootNode = std::shared_ptr(new RowNode({tr("Icon"), tr("Name"), tr("Date"), tr("Size"), tr("Content Type"), tr("Download")}, "", std::weak_ptr())); + beginResetModel(); + bookIdToRowMap.clear(); + for (auto bookItem : data) { + const auto rowNode = createNode(bookItem); + + // Restore download state during model updates (filtering, etc) + rowNode->setDownloadState(downloadMgr.getDownloadState(rowNode->getBookId())); + + bookIdToRowMap[bookItem["id"].toString()] = rootNode->childCount(); + rootNode->appendChild(rowNode); + } + endResetModel(); + emit dataChanged(QModelIndex(), QModelIndex()); +} + +// Returns either data of the thumbnail (as a QByteArray) or a URL (as a +// QString) from where the actual data can be obtained. +QVariant ContentManagerModel::getThumbnail(const QVariant& faviconEntry) const +{ + if ( faviconEntry.userType() == QMetaType::QByteArray ) + return faviconEntry; + + const auto faviconUrl = faviconEntry.toString(); + return m_iconMap.contains(faviconUrl) + ? m_iconMap[faviconUrl] + : faviconEntry; +} + +std::shared_ptr ContentManagerModel::createNode(BookInfo bookItem) const +{ + QString id = bookItem["id"].toString(); + const QVariant bookIcon = getThumbnail(bookItem["favicon"]); + std::weak_ptr weakRoot = rootNode; + auto rowNodePtr = std::shared_ptr(new + RowNode({bookIcon, bookItem["title"], + bookItem["date"], + QString::fromStdString(kiwix::beautifyFileSize(bookItem["size"].toULongLong())), + bookItem["tags"] + }, id, weakRoot)); + std::weak_ptr weakRowNodePtr = rowNodePtr; + const auto descNodePtr = std::make_shared(DescriptionNode(bookItem["description"].toString(), weakRowNodePtr)); + + rowNodePtr->appendChild(descNodePtr); + return rowNodePtr; +} + +bool ContentManagerModel::hasChildren(const QModelIndex &parent) const +{ + auto item = static_cast(parent.internalPointer()); + if (item) + return item->childCount() > 0; + return true; +} + +void ContentManagerModel::sort(int column, Qt::SortOrder order) +{ + if (column == 0 || column == 4 || column == 5) + return; + + QString sortBy = ""; + switch(column) { + case 1: + sortBy = "title"; + break; + case 2: + sortBy = "date"; + break; + case 3: + sortBy = "size"; + break; + default: + sortBy = "unsorted"; + } + m_contentMgr.setSortBy(sortBy, order == Qt::AscendingOrder); +} + +RowNode* ContentManagerModel::getRowNode(size_t row) +{ + return static_cast(rootNode->child(row).get()); +} + +void ContentManagerModel::updateImage(QString bookId, QString url, QByteArray imageData) +{ + const auto it = bookIdToRowMap.constFind(bookId); + if ( it == bookIdToRowMap.constEnd() ) + return; + + const size_t row = it.value(); + const auto item = getRowNode(row); + item->setIconData(imageData); + m_iconMap[url] = imageData; + triggerDataUpdateAt( this->index(row, 0) ); +} + +void ContentManagerModel::updateDownload(QString bookId) +{ + const auto it = bookIdToRowMap.constFind(bookId); + + if ( it != bookIdToRowMap.constEnd() ) { + const size_t row = it.value(); + triggerDataUpdateAt( this->index(row, 5) ); + } +} + +void ContentManagerModel::triggerDataUpdateAt(QModelIndex index) +{ + emit dataChanged(index, index); +} + +void ContentManagerModel::setDownloadState(QString bookId, std::shared_ptr ds) +{ + const auto it = bookIdToRowMap.constFind(bookId); + if ( it == bookIdToRowMap.constEnd() ) + return; + + const size_t row = it.value(); + getRowNode(row)->setDownloadState(ds); + triggerDataUpdateAt( this->index(row, 5) ); +} + diff --git a/src/contentmanagermodel.h b/src/contentmanagermodel.h index 7853e0495..d344ea62e 100644 --- a/src/contentmanagermodel.h +++ b/src/contentmanagermodel.h @@ -1,70 +1,70 @@ -#ifndef CONTENTMANAGERMODEL_H -#define CONTENTMANAGERMODEL_H - -#include -#include -#include -#include -#include "thumbnaildownloader.h" -#include "rownode.h" -#include "downloadmanagement.h" -#include - -class ContentManager; -class RowNode; -class Node; -class DescriptionNode; - -class ContentManagerModel : public QAbstractItemModel -{ - Q_OBJECT - -public: // types - typedef QMap BookInfo; - typedef QList BookInfoList; - -public: // functions - explicit ContentManagerModel(ContentManager* contentMgr); - ~ContentManagerModel(); - - QVariant data(const QModelIndex &index, int role) const override; - Qt::ItemFlags flags(const QModelIndex &index) const override; - QVariant headerData(int section, Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - QModelIndex index(int row, int column, - const QModelIndex &parent = QModelIndex()) const override; - QModelIndex parent(const QModelIndex &index) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - void setBooksData(const BookInfoList& data, const DownloadManager& downloadMgr); - bool hasChildren(const QModelIndex &parent) const override; - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; - - std::shared_ptr createNode(BookInfo bookItem) const; - -public slots: - void updateImage(QString bookId, QString url, QByteArray imageData); - void triggerDataUpdateAt(QModelIndex index); - void setDownloadState(QString bookId, std::shared_ptr ds); - void updateDownload(QString bookId); - -private: // functions - // Returns either data of the thumbnail (as a QByteArray) or a URL (as a - // QString) from where the actual data can be obtained. - QVariant getThumbnail(const QVariant& faviconEntry) const; - RowNode* getRowNode(size_t row); - -private: // data - ContentManager& m_contentMgr; - std::shared_ptr rootNode; - mutable ThumbnailDownloader td; - QMap bookIdToRowMap; - QMap m_iconMap; -}; - -inline bool isDescriptionIndex(const QModelIndex& index) -{ - return index.parent().isValid(); -} - -#endif // CONTENTMANAGERMODEL_H +#ifndef CONTENTMANAGERMODEL_H +#define CONTENTMANAGERMODEL_H + +#include +#include +#include +#include +#include "thumbnaildownloader.h" +#include "rownode.h" +#include "downloadmanagement.h" +#include + +class ContentManager; +class RowNode; +class Node; +class DescriptionNode; + +class ContentManagerModel : public QAbstractItemModel +{ + Q_OBJECT + +public: // types + typedef QMap BookInfo; + typedef QList BookInfoList; + +public: // functions + explicit ContentManagerModel(ContentManager* contentMgr); + ~ContentManagerModel(); + + QVariant data(const QModelIndex &index, int role) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + void setBooksData(const BookInfoList& data, const DownloadManager& downloadMgr); + bool hasChildren(const QModelIndex &parent) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + + std::shared_ptr createNode(BookInfo bookItem) const; + +public slots: + void updateImage(QString bookId, QString url, QByteArray imageData); + void triggerDataUpdateAt(QModelIndex index); + void setDownloadState(QString bookId, std::shared_ptr ds); + void updateDownload(QString bookId); + +private: // functions + // Returns either data of the thumbnail (as a QByteArray) or a URL (as a + // QString) from where the actual data can be obtained. + QVariant getThumbnail(const QVariant& faviconEntry) const; + RowNode* getRowNode(size_t row); + +private: // data + ContentManager& m_contentMgr; + std::shared_ptr rootNode; + mutable ThumbnailDownloader td; + QMap bookIdToRowMap; + QMap m_iconMap; +}; + +inline bool isDescriptionIndex(const QModelIndex& index) +{ + return index.parent().isValid(); +} + +#endif // CONTENTMANAGERMODEL_H diff --git a/src/contentmanagerside.cpp b/src/contentmanagerside.cpp index fa9a466ac..fd5959da3 100644 --- a/src/contentmanagerside.cpp +++ b/src/contentmanagerside.cpp @@ -1,124 +1,124 @@ -#include "contentmanagerside.h" -#include "ui_contentmanagerside.h" -#include "kiwixapp.h" -#include "kiwixchoicebox.h" -#include -#include - -#include "klistwidgetitem.h" -#include - -ContentManagerSide::ContentManagerSide(QWidget *parent) : - QWidget(parent), - mp_ui(new Ui::contentmanagerside) -{ - setFocusPolicy(Qt::FocusPolicy::StrongFocus); - mp_ui->setupUi(this); - this->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/contentmanagerside.css")); - - mp_ui->buttonGroup->setId(mp_ui->allFileButton, CatalogButtonId::ALL); - mp_ui->buttonGroup->setId(mp_ui->localFileButton, CatalogButtonId::LOCAL); - connect(mp_ui->buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) { - const auto id = mp_ui->buttonGroup->id(btn); - mp_contentManager->setLocal(id == CatalogButtonId::LOCAL); - }); - - connect(mp_ui->allFileButton, &QRadioButton::toggled, - this, [=](bool checked) { mp_ui->allFileButton->setStyleSheet( - checked ? "*{font-weight: bold}" : "");}); - connect(mp_ui->localFileButton, &QRadioButton::toggled, - this, [=](bool checked) { mp_ui->localFileButton->setStyleSheet( - checked ?"*{font-weight: bold}" : "");}); - - mp_ui->allFileButton->setText(gt("online-files")); - mp_ui->localFileButton ->setText(gt("local-files")); - - mp_categories = mp_ui->categories; - mp_categories->setType("category"); - - mp_languages = mp_ui->languages; - mp_languages->setType("language"); - - mp_contentType = mp_ui->contentType; - mp_contentType->setType("content-type"); - - auto searcher = mp_ui->searcher; - searcher->setPlaceholderText(gt("search-files")); - QIcon searchIcon = QIcon(":/icons/search.svg"); - - searcher->addAction(searchIcon, QLineEdit::LeadingPosition); - connect(searcher, &QLineEdit::textChanged, [searcher](){ - KiwixApp::instance()->getContentManager()->setSearch(searcher->text()); - }); - - FilterList contentTypeList = { - {gt("pictures"), "_pictures:yes"}, - {gt("no-pictures"), "_pictures:no"}, - {gt("videos"), "_videos:yes"}, - {gt("no-videos"), "_videos:no"}, - {gt("details"), "_details:yes"}, - {gt("no-details"), "_details:no"} - }; - - mp_contentType->setSelections(contentTypeList, KiwixApp::instance()->getSettingsManager()->getContentType()); - - setCategories(KiwixApp::instance()->getContentManager()->getCategories()); - setLanguages(KiwixApp::instance()->getContentManager()->getLanguages()); - connect(KiwixApp::instance()->getContentManager(), &ContentManager::categoriesLoaded, this, &ContentManagerSide::setCategories); - connect(KiwixApp::instance()->getContentManager(), &ContentManager::languagesLoaded, this, &ContentManagerSide::setLanguages); -} - -ContentManagerSide::~ContentManagerSide() -{ - delete mp_ui; -} - -void ContentManagerSide::setContentManager(ContentManager *contentManager) -{ - mp_contentManager = contentManager; - const auto isLocal = mp_contentManager->isLocal(); - const auto checkedButton = mp_ui->buttonGroup->button(isLocal == CatalogButtonId::LOCAL); - checkedButton->setChecked(true); - checkedButton->setStyleSheet("*{font-weight: bold}"); - connect(mp_languages, &KiwixChoiceBox::choiceUpdated, this, [=](FilterList values) { - mp_contentManager->setCurrentLanguage(values); - }); - connect(mp_categories, &KiwixChoiceBox::choiceUpdated, this, [=](FilterList values) { - QStringList categoryList; - for (const auto& pair : values) - categoryList.append(pair.second); - mp_contentManager->setCurrentCategoryFilter(categoryList); - }); - connect(mp_contentType, &KiwixChoiceBox::choiceUpdated, this, [=](FilterList values) { - mp_contentManager->setCurrentContentTypeFilter(values); - }); -} - -namespace -{ - -KiwixChoiceBox::SelectionList getCategorySelectionList(QStringList categoryList) -{ - const auto lang = QLocale().name().split('_')[0].toStdString(); - KiwixChoiceBox::SelectionList sList; - for (const auto &category : categoryList) { - const auto translation = QString::fromStdString(kiwix::translateBookCategory(lang, category.toStdString())); - sList.append({translation, category}); - } - return sList; -} - -} - -void ContentManagerSide::setCategories(QStringList categories) -{ - auto categorySelections = getCategorySelectionList(categories); - auto defaultSelections = getCategorySelectionList( - KiwixApp::instance()->getSettingsManager()->getCategoryList()); - mp_categories->setSelections(categorySelections, defaultSelections); -} - -void ContentManagerSide::setLanguages(ContentManager::LanguageList langList) -{ - mp_languages->setSelections(langList, KiwixApp::instance()->getSettingsManager()->getLanguageList()); -} +#include "contentmanagerside.h" +#include "ui_contentmanagerside.h" +#include "kiwixapp.h" +#include "kiwixchoicebox.h" +#include +#include + +#include "klistwidgetitem.h" +#include + +ContentManagerSide::ContentManagerSide(QWidget *parent) : + QWidget(parent), + mp_ui(new Ui::contentmanagerside) +{ + setFocusPolicy(Qt::FocusPolicy::StrongFocus); + mp_ui->setupUi(this); + this->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/contentmanagerside.css")); + + mp_ui->buttonGroup->setId(mp_ui->allFileButton, CatalogButtonId::ALL); + mp_ui->buttonGroup->setId(mp_ui->localFileButton, CatalogButtonId::LOCAL); + connect(mp_ui->buttonGroup, QOverload::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) { + const auto id = mp_ui->buttonGroup->id(btn); + mp_contentManager->setLocal(id == CatalogButtonId::LOCAL); + }); + + connect(mp_ui->allFileButton, &QRadioButton::toggled, + this, [=](bool checked) { mp_ui->allFileButton->setStyleSheet( + checked ? "*{font-weight: bold}" : "");}); + connect(mp_ui->localFileButton, &QRadioButton::toggled, + this, [=](bool checked) { mp_ui->localFileButton->setStyleSheet( + checked ?"*{font-weight: bold}" : "");}); + + mp_ui->allFileButton->setText(gt("online-files")); + mp_ui->localFileButton ->setText(gt("local-files")); + + mp_categories = mp_ui->categories; + mp_categories->setType("category"); + + mp_languages = mp_ui->languages; + mp_languages->setType("language"); + + mp_contentType = mp_ui->contentType; + mp_contentType->setType("content-type"); + + auto searcher = mp_ui->searcher; + searcher->setPlaceholderText(gt("search-files")); + QIcon searchIcon = QIcon(":/icons/search.svg"); + + searcher->addAction(searchIcon, QLineEdit::LeadingPosition); + connect(searcher, &QLineEdit::textChanged, [searcher](){ + KiwixApp::instance()->getContentManager()->setSearch(searcher->text()); + }); + + FilterList contentTypeList = { + {gt("pictures"), "_pictures:yes"}, + {gt("no-pictures"), "_pictures:no"}, + {gt("videos"), "_videos:yes"}, + {gt("no-videos"), "_videos:no"}, + {gt("details"), "_details:yes"}, + {gt("no-details"), "_details:no"} + }; + + mp_contentType->setSelections(contentTypeList, KiwixApp::instance()->getSettingsManager()->getContentType()); + + setCategories(KiwixApp::instance()->getContentManager()->getCategories()); + setLanguages(KiwixApp::instance()->getContentManager()->getLanguages()); + connect(KiwixApp::instance()->getContentManager(), &ContentManager::categoriesLoaded, this, &ContentManagerSide::setCategories); + connect(KiwixApp::instance()->getContentManager(), &ContentManager::languagesLoaded, this, &ContentManagerSide::setLanguages); +} + +ContentManagerSide::~ContentManagerSide() +{ + delete mp_ui; +} + +void ContentManagerSide::setContentManager(ContentManager *contentManager) +{ + mp_contentManager = contentManager; + const auto isLocal = mp_contentManager->isLocal(); + const auto checkedButton = mp_ui->buttonGroup->button(isLocal == CatalogButtonId::LOCAL); + checkedButton->setChecked(true); + checkedButton->setStyleSheet("*{font-weight: bold}"); + connect(mp_languages, &KiwixChoiceBox::choiceUpdated, this, [=](FilterList values) { + mp_contentManager->setCurrentLanguage(values); + }); + connect(mp_categories, &KiwixChoiceBox::choiceUpdated, this, [=](FilterList values) { + QStringList categoryList; + for (const auto& pair : values) + categoryList.append(pair.second); + mp_contentManager->setCurrentCategoryFilter(categoryList); + }); + connect(mp_contentType, &KiwixChoiceBox::choiceUpdated, this, [=](FilterList values) { + mp_contentManager->setCurrentContentTypeFilter(values); + }); +} + +namespace +{ + +KiwixChoiceBox::SelectionList getCategorySelectionList(QStringList categoryList) +{ + const auto lang = QLocale().name().split('_')[0].toStdString(); + KiwixChoiceBox::SelectionList sList; + for (const auto &category : categoryList) { + const auto translation = QString::fromStdString(kiwix::translateBookCategory(lang, category.toStdString())); + sList.append({translation, category}); + } + return sList; +} + +} + +void ContentManagerSide::setCategories(QStringList categories) +{ + auto categorySelections = getCategorySelectionList(categories); + auto defaultSelections = getCategorySelectionList( + KiwixApp::instance()->getSettingsManager()->getCategoryList()); + mp_categories->setSelections(categorySelections, defaultSelections); +} + +void ContentManagerSide::setLanguages(ContentManager::LanguageList langList) +{ + mp_languages->setSelections(langList, KiwixApp::instance()->getSettingsManager()->getLanguageList()); +} diff --git a/src/contentmanagerside.h b/src/contentmanagerside.h index a751f616a..25adbf90f 100644 --- a/src/contentmanagerside.h +++ b/src/contentmanagerside.h @@ -1,45 +1,45 @@ -#ifndef CONTENTMANAGERSIDE_H -#define CONTENTMANAGERSIDE_H - -#include -#include -#include -#include "contentmanager.h" -#include "contenttypefilter.h" - -namespace Ui { -class contentmanagerside; -} - -class KiwixChoiceBox; -using FilterList = ContentManager::FilterList; - -class ContentManagerSide : public QWidget -{ - Q_OBJECT - -public: - enum CatalogButtonId { - ALL = 0, - LOCAL = 1 - }; - explicit ContentManagerSide(QWidget *parent = 0); - ~ContentManagerSide(); - - void setContentManager(ContentManager* contentManager); - -private: - Ui::contentmanagerside *mp_ui; - ContentManager* mp_contentManager; - KiwixChoiceBox *mp_categories; - KiwixChoiceBox *mp_languages; - KiwixChoiceBox *mp_contentType; - QCheckBox* mp_contentTypeButton; - QStringList m_contentTypeFilters; - -public slots: - void setCategories(QStringList); - void setLanguages(ContentManager::LanguageList); -}; - -#endif // CONTENTMANAGERSIDE_H +#ifndef CONTENTMANAGERSIDE_H +#define CONTENTMANAGERSIDE_H + +#include +#include +#include +#include "contentmanager.h" +#include "contenttypefilter.h" + +namespace Ui { +class contentmanagerside; +} + +class KiwixChoiceBox; +using FilterList = ContentManager::FilterList; + +class ContentManagerSide : public QWidget +{ + Q_OBJECT + +public: + enum CatalogButtonId { + ALL = 0, + LOCAL = 1 + }; + explicit ContentManagerSide(QWidget *parent = 0); + ~ContentManagerSide(); + + void setContentManager(ContentManager* contentManager); + +private: + Ui::contentmanagerside *mp_ui; + ContentManager* mp_contentManager; + KiwixChoiceBox *mp_categories; + KiwixChoiceBox *mp_languages; + KiwixChoiceBox *mp_contentType; + QCheckBox* mp_contentTypeButton; + QStringList m_contentTypeFilters; + +public slots: + void setCategories(QStringList); + void setLanguages(ContentManager::LanguageList); +}; + +#endif // CONTENTMANAGERSIDE_H diff --git a/src/contentmanagerside.ui b/src/contentmanagerside.ui index b06e1595c..7bfdf1cd4 100644 --- a/src/contentmanagerside.ui +++ b/src/contentmanagerside.ui @@ -1,221 +1,221 @@ - - - contentmanagerside - - - - 0 - 0 - 300 - 386 - - - - - 0 - 0 - - - - - 250 - 0 - - - - Form - - - true - - - - QLayout::SetNoConstraint - - - 0 - - - 0 - - - 0 - - - 0 - - - - - true - - - true - - - - - 0 - 0 - 298 - 384 - - - - - 9 - - - - - - 0 - 0 - - - - PointingHandCursor - - - All Files - - - buttonGroup - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - PointingHandCursor - - - Local Files - - - true - - - buttonGroup - - - - - - - - - - - 0 - 0 - - - - true - - - - - - - - 0 - 0 - - - - true - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - PointingHandCursor - - - QFrame::NoFrame - - - QFrame::Sunken - - - 0 - - - - 0 - - - 0 - - - 0 - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 0 - - - - - - - - - - - - - KiwixChoiceBox - QWidget -
src/kiwixchoicebox.h
- 1 -
-
- - - - - -
+ + + contentmanagerside + + + + 0 + 0 + 300 + 386 + + + + + 0 + 0 + + + + + 250 + 0 + + + + Form + + + true + + + + QLayout::SetNoConstraint + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + true + + + + + 0 + 0 + 298 + 384 + + + + + 9 + + + + + + 0 + 0 + + + + PointingHandCursor + + + All Files + + + buttonGroup + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + PointingHandCursor + + + Local Files + + + true + + + buttonGroup + + + + + + + + + + + 0 + 0 + + + + true + + + + + + + + 0 + 0 + + + + true + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + PointingHandCursor + + + QFrame::NoFrame + + + QFrame::Sunken + + + 0 + + + + 0 + + + 0 + + + 0 + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 0 + + + + + + + + + + + + + KiwixChoiceBox + QWidget +
src/kiwixchoicebox.h
+ 1 +
+
+ + + + + +
diff --git a/src/contentmanagerview.cpp b/src/contentmanagerview.cpp index 6e29e18e5..758082fb9 100644 --- a/src/contentmanagerview.cpp +++ b/src/contentmanagerview.cpp @@ -1,83 +1,83 @@ -#include "contentmanagerview.h" -#include -#include "kiwixapp.h" -#include "contentmanagerdelegate.h" -#include -#include "ui_contentmanagerview.h" - -ContentManagerView::ContentManagerView(QWidget *parent) - : QWidget(parent), mp_ui(new Ui::contentmanagerview) -{ - mp_ui->setupUi(this); - mp_ui->m_view->setSortingEnabled(true); - mp_ui->m_view->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/_contentManager.css")); - mp_ui->m_view->setContextMenuPolicy(Qt::CustomContextMenu); - auto managerDelegate = new ContentManagerDelegate(); - mp_ui->m_view->setItemDelegate(managerDelegate); - mp_ui->m_view->setCursor(Qt::PointingHandCursor); - - loader = new KiwixLoader(mp_ui->loading); - mp_ui->stackedWidget->setCurrentIndex(0); - - connect(mp_ui->m_view, &QTreeView::clicked, this, &ContentManagerView::onClicked); - connect(mp_ui->m_view, &QTreeView::expanded, this, &ContentManagerView::onExpanded); - connect(this, &ContentManagerView::sizeHintChanged, managerDelegate, &QStyledItemDelegate::sizeHintChanged); - - // Needed to reveal the situation with downloads not being updated timely - // (due to aria2c becoming unresponsive when saving to slow storage) - QTimer *timer = new QTimer(this); - connect(timer, &QTimer::timeout, [this]() { this->update(); }); - timer->start(1000); -} - -ContentManagerView::~ContentManagerView() -{ - -} - -void ContentManagerView::showLoader(bool show) -{ - mp_ui->stackedWidget->setCurrentIndex(show); - if (show) { - loader->startAnimation(); - } else { - loader->stopAnimation(); - } -} - -void ContentManagerView::onClicked(QModelIndex index) -{ - if (index.column() == (mp_ui->m_view->model()->columnCount() - 1)) - return; - - auto zeroColIndex = index.siblingAtColumn(0); - if (mp_ui->m_view->isExpanded(zeroColIndex)) { - mp_ui->m_view->collapse(zeroColIndex); - } else { - mp_ui->m_view->expand(zeroColIndex); - } -} - -void ContentManagerView::onExpanded(QModelIndex index) -{ - if (!mp_ui->m_view->isFirstColumnSpanned(0, index)) - mp_ui->m_view->setFirstColumnSpanned(0, index, true); -} - -/** - * @brief Notify delegate to update size hint of the visible description rows. - */ -void ContentManagerView::updateSizeHint() -{ - auto view = this->getView(); - if (!view->isVisible()) - return; - - auto visibleIndex = view->indexAt(view->rect().topLeft()); - while (visibleIndex.isValid()) - { - if (isDescriptionIndex(visibleIndex)) - emit sizeHintChanged(visibleIndex); - visibleIndex = view->indexBelow(visibleIndex); - } -} +#include "contentmanagerview.h" +#include +#include "kiwixapp.h" +#include "contentmanagerdelegate.h" +#include +#include "ui_contentmanagerview.h" + +ContentManagerView::ContentManagerView(QWidget *parent) + : QWidget(parent), mp_ui(new Ui::contentmanagerview) +{ + mp_ui->setupUi(this); + mp_ui->m_view->setSortingEnabled(true); + mp_ui->m_view->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/_contentManager.css")); + mp_ui->m_view->setContextMenuPolicy(Qt::CustomContextMenu); + auto managerDelegate = new ContentManagerDelegate(); + mp_ui->m_view->setItemDelegate(managerDelegate); + mp_ui->m_view->setCursor(Qt::PointingHandCursor); + + loader = new KiwixLoader(mp_ui->loading); + mp_ui->stackedWidget->setCurrentIndex(0); + + connect(mp_ui->m_view, &QTreeView::clicked, this, &ContentManagerView::onClicked); + connect(mp_ui->m_view, &QTreeView::expanded, this, &ContentManagerView::onExpanded); + connect(this, &ContentManagerView::sizeHintChanged, managerDelegate, &QStyledItemDelegate::sizeHintChanged); + + // Needed to reveal the situation with downloads not being updated timely + // (due to aria2c becoming unresponsive when saving to slow storage) + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, [this]() { this->update(); }); + timer->start(1000); +} + +ContentManagerView::~ContentManagerView() +{ + +} + +void ContentManagerView::showLoader(bool show) +{ + mp_ui->stackedWidget->setCurrentIndex(show); + if (show) { + loader->startAnimation(); + } else { + loader->stopAnimation(); + } +} + +void ContentManagerView::onClicked(QModelIndex index) +{ + if (index.column() == (mp_ui->m_view->model()->columnCount() - 1)) + return; + + auto zeroColIndex = index.siblingAtColumn(0); + if (mp_ui->m_view->isExpanded(zeroColIndex)) { + mp_ui->m_view->collapse(zeroColIndex); + } else { + mp_ui->m_view->expand(zeroColIndex); + } +} + +void ContentManagerView::onExpanded(QModelIndex index) +{ + if (!mp_ui->m_view->isFirstColumnSpanned(0, index)) + mp_ui->m_view->setFirstColumnSpanned(0, index, true); +} + +/** + * @brief Notify delegate to update size hint of the visible description rows. + */ +void ContentManagerView::updateSizeHint() +{ + auto view = this->getView(); + if (!view->isVisible()) + return; + + auto visibleIndex = view->indexAt(view->rect().topLeft()); + while (visibleIndex.isValid()) + { + if (isDescriptionIndex(visibleIndex)) + emit sizeHintChanged(visibleIndex); + visibleIndex = view->indexBelow(visibleIndex); + } +} diff --git a/src/contentmanagerview.h b/src/contentmanagerview.h index ec5174d17..96a755d57 100644 --- a/src/contentmanagerview.h +++ b/src/contentmanagerview.h @@ -1,35 +1,35 @@ -#ifndef CONTENTMANAGERVIEW_H -#define CONTENTMANAGERVIEW_H - -#include -#include "ui_contentmanagerview.h" -#include "kiwixloader.h" - -namespace Ui { -class contentmanagerview; -} - -class ContentManagerView : public QWidget -{ - Q_OBJECT - -public: - explicit ContentManagerView(QWidget *parent = nullptr); - ~ContentManagerView(); - QTreeView* getView() { return mp_ui->m_view; } - -public slots: - void showLoader(bool show); - void onClicked(QModelIndex index); - void onExpanded(QModelIndex index); - void updateSizeHint(); - -signals: - void sizeHintChanged(const QModelIndex& index); - -private: - Ui::contentmanagerview *mp_ui; - KiwixLoader *loader; -}; - -#endif // CONTENTMANAGERVIEW_H +#ifndef CONTENTMANAGERVIEW_H +#define CONTENTMANAGERVIEW_H + +#include +#include "ui_contentmanagerview.h" +#include "kiwixloader.h" + +namespace Ui { +class contentmanagerview; +} + +class ContentManagerView : public QWidget +{ + Q_OBJECT + +public: + explicit ContentManagerView(QWidget *parent = nullptr); + ~ContentManagerView(); + QTreeView* getView() { return mp_ui->m_view; } + +public slots: + void showLoader(bool show); + void onClicked(QModelIndex index); + void onExpanded(QModelIndex index); + void updateSizeHint(); + +signals: + void sizeHintChanged(const QModelIndex& index); + +private: + Ui::contentmanagerview *mp_ui; + KiwixLoader *loader; +}; + +#endif // CONTENTMANAGERVIEW_H diff --git a/src/contentmanagerview.ui b/src/contentmanagerview.ui index 2acacf59c..6be3859f0 100644 --- a/src/contentmanagerview.ui +++ b/src/contentmanagerview.ui @@ -1,49 +1,49 @@ - - - contentmanagerview - - - - 0 - 0 - 400 - 300 - - - - Form - - - - - - 0 - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - - + + + contentmanagerview + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + 0 + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/contenttypefilter.cpp b/src/contenttypefilter.cpp index e303f321a..baf4c8059 100644 --- a/src/contenttypefilter.cpp +++ b/src/contenttypefilter.cpp @@ -1,26 +1,26 @@ -#include "contenttypefilter.h" -#include "kiwixapp.h" - -ContentTypeFilter::ContentTypeFilter(QString name, QWidget *parent) -: QCheckBox(parent), - m_name(name) -{ - setTristate(true); - - m_states[Qt::Unchecked] = gt("no-filter"); - m_states[Qt::PartiallyChecked] = gt("yes"); - m_states[Qt::Checked] = gt("no"); - setText(gt(m_name) + " : " + m_states[checkState()]); - setStyleSheet("* { color: #666666; }"); - #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - connect(this, &QCheckBox::checkStateChanged, this, &ContentTypeFilter::onStateChanged); - #else - connect(this, &QCheckBox::stateChanged, this, &ContentTypeFilter::onStateChanged); - #endif -} - -void ContentTypeFilter::onStateChanged(int state) -{ - setText(gt(m_name) + " : " + m_states[static_cast(state)]); - setStyleSheet((state == 0) ? "*{color: #666666;}" : "*{font-weight: bold; color: black;}"); -} +#include "contenttypefilter.h" +#include "kiwixapp.h" + +ContentTypeFilter::ContentTypeFilter(QString name, QWidget *parent) +: QCheckBox(parent), + m_name(name) +{ + setTristate(true); + + m_states[Qt::Unchecked] = gt("no-filter"); + m_states[Qt::PartiallyChecked] = gt("yes"); + m_states[Qt::Checked] = gt("no"); + setText(gt(m_name) + " : " + m_states[checkState()]); + setStyleSheet("* { color: #666666; }"); + #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + connect(this, &QCheckBox::checkStateChanged, this, &ContentTypeFilter::onStateChanged); + #else + connect(this, &QCheckBox::stateChanged, this, &ContentTypeFilter::onStateChanged); + #endif +} + +void ContentTypeFilter::onStateChanged(int state) +{ + setText(gt(m_name) + " : " + m_states[static_cast(state)]); + setStyleSheet((state == 0) ? "*{color: #666666;}" : "*{font-weight: bold; color: black;}"); +} diff --git a/src/contenttypefilter.h b/src/contenttypefilter.h index ede75b9bb..7abb6c46b 100644 --- a/src/contenttypefilter.h +++ b/src/contenttypefilter.h @@ -1,25 +1,25 @@ -#ifndef CONTENTTYPEFILTER_H -#define CONTENTTYPEFILTER_H - -#include -#include - -class ContentTypeFilter : public QCheckBox -{ - Q_OBJECT - -public: - explicit ContentTypeFilter(QString name, QWidget *parent = nullptr); - virtual ~ContentTypeFilter() {} - - QString getName() { return m_name; } - -public slots: - void onStateChanged(int state); - -private: - QString m_name; - QMap m_states; -}; - -#endif // CONTENTTYPEFILTER_H +#ifndef CONTENTTYPEFILTER_H +#define CONTENTTYPEFILTER_H + +#include +#include + +class ContentTypeFilter : public QCheckBox +{ + Q_OBJECT + +public: + explicit ContentTypeFilter(QString name, QWidget *parent = nullptr); + virtual ~ContentTypeFilter() {} + + QString getName() { return m_name; } + +public slots: + void onStateChanged(int state); + +private: + QString m_name; + QMap m_states; +}; + +#endif // CONTENTTYPEFILTER_H diff --git a/src/css_constants.h b/src/css_constants.h index 337b0f63f..816dadf99 100644 --- a/src/css_constants.h +++ b/src/css_constants.h @@ -1,80 +1,80 @@ -#ifndef CSS_CONSTANTS_H -#define CSS_CONSTANTS_H - -/** - * @brief The need for this file is due to the lack of support to retrieve CSS - * values of Qt Widgets. Such deficiency means every code that depend on a CSS - * values need to be updated on change to that value. This file makes it so that - * a dependent CSS value only need to be updated here instead of its every use. - * - * - The CSS values in this file should create appropriate namespaces similar to - * a CSS hierarchy. - * - The classes are defined in resources/style.css unless specified. - * - Naming convention should follow Javascript's style naming. - * - Comments should be added to the duplicated css properties in css files. - */ -namespace CSS -{ - -namespace QTabBar { -namespace tab { - const int padding = 4; - const int border = 1; -} -} - -namespace SearchBar{ - const int margin = 2; - const int border = 1; -} - -namespace MultiZimButton { -namespace QListWidget { -namespace item { - const int paddingHorizontal = 5; - const int border = 1; -} - const int paddingVertical = 5; -} - -namespace QScrollBar { - const int width = 5; -} -} - -namespace ZimItemWidget { -namespace QLabel { - const int lineHeight = 24; -} -} - -namespace TopWidget { -namespace QToolButton { -namespace backButton { - const int marginLeft = 6; -} -} -} - -/* In _contentManager.css */ -namespace ContentManagerCSS { -namespace QTreeView { - const int padding = 4; -} -} - -/* In popup.css */ -namespace PopupCSS { -namespace QHeaderView { -namespace section { - const int marginTop = 5; - const int lineHeight = 24; - const int paddingVertical = 5; - const int paddingLeft = 10; -} -} -} - -} - -#endif // CSS_CONSTANTS_H +#ifndef CSS_CONSTANTS_H +#define CSS_CONSTANTS_H + +/** + * @brief The need for this file is due to the lack of support to retrieve CSS + * values of Qt Widgets. Such deficiency means every code that depend on a CSS + * values need to be updated on change to that value. This file makes it so that + * a dependent CSS value only need to be updated here instead of its every use. + * + * - The CSS values in this file should create appropriate namespaces similar to + * a CSS hierarchy. + * - The classes are defined in resources/style.css unless specified. + * - Naming convention should follow Javascript's style naming. + * - Comments should be added to the duplicated css properties in css files. + */ +namespace CSS +{ + +namespace QTabBar { +namespace tab { + const int padding = 4; + const int border = 1; +} +} + +namespace SearchBar{ + const int margin = 2; + const int border = 1; +} + +namespace MultiZimButton { +namespace QListWidget { +namespace item { + const int paddingHorizontal = 5; + const int border = 1; +} + const int paddingVertical = 5; +} + +namespace QScrollBar { + const int width = 5; +} +} + +namespace ZimItemWidget { +namespace QLabel { + const int lineHeight = 24; +} +} + +namespace TopWidget { +namespace QToolButton { +namespace backButton { + const int marginLeft = 6; +} +} +} + +/* In _contentManager.css */ +namespace ContentManagerCSS { +namespace QTreeView { + const int padding = 4; +} +} + +/* In popup.css */ +namespace PopupCSS { +namespace QHeaderView { +namespace section { + const int marginTop = 5; + const int lineHeight = 24; + const int paddingVertical = 5; + const int paddingLeft = 10; +} +} +} + +} + +#endif // CSS_CONSTANTS_H diff --git a/src/descriptionnode.cpp b/src/descriptionnode.cpp index fd5c54913..a1ec78fca 100644 --- a/src/descriptionnode.cpp +++ b/src/descriptionnode.cpp @@ -1,50 +1,50 @@ -#include "descriptionnode.h" -#include "rownode.h" - -DescriptionNode::DescriptionNode(QString desc, std::weak_ptr parent) - : m_desc(desc), m_parentItem(parent) -{} - -DescriptionNode::~DescriptionNode() -{} - -std::shared_ptr DescriptionNode::parentItem() -{ - std::shared_ptr temp = m_parentItem.lock(); - if (!temp) - return nullptr; - return temp; -} - -QString DescriptionNode::getBookId() const -{ - std::shared_ptr temp = m_parentItem.lock(); - if (!temp) - return QString(); - return temp->getBookId(); -} - -int DescriptionNode::childCount() const -{ - return 0; -} - -int DescriptionNode::columnCount() const -{ - return 1; -} - -QVariant DescriptionNode::data(int column) -{ - if (column == 0) - return m_desc; - return QVariant(); -} - -int DescriptionNode::row() const -{ - std::shared_ptr temp = m_parentItem.lock(); - if (!temp) - return 0; - return temp->row(); -} +#include "descriptionnode.h" +#include "rownode.h" + +DescriptionNode::DescriptionNode(QString desc, std::weak_ptr parent) + : m_desc(desc), m_parentItem(parent) +{} + +DescriptionNode::~DescriptionNode() +{} + +std::shared_ptr DescriptionNode::parentItem() +{ + std::shared_ptr temp = m_parentItem.lock(); + if (!temp) + return nullptr; + return temp; +} + +QString DescriptionNode::getBookId() const +{ + std::shared_ptr temp = m_parentItem.lock(); + if (!temp) + return QString(); + return temp->getBookId(); +} + +int DescriptionNode::childCount() const +{ + return 0; +} + +int DescriptionNode::columnCount() const +{ + return 1; +} + +QVariant DescriptionNode::data(int column) +{ + if (column == 0) + return m_desc; + return QVariant(); +} + +int DescriptionNode::row() const +{ + std::shared_ptr temp = m_parentItem.lock(); + if (!temp) + return 0; + return temp->row(); +} diff --git a/src/descriptionnode.h b/src/descriptionnode.h index 63323d959..9a8c185a6 100644 --- a/src/descriptionnode.h +++ b/src/descriptionnode.h @@ -1,26 +1,26 @@ -#ifndef DESCRIPTIONNODE_H -#define DESCRIPTIONNODE_H - -#include -#include "node.h" - -class RowNode; - -class DescriptionNode : public Node -{ -public: - DescriptionNode(QString desc, std::weak_ptr parent); - ~DescriptionNode(); - std::shared_ptr parentItem() override; - int childCount() const override; - int columnCount() const override; - QVariant data(int column) override; - int row() const override; - QString getBookId() const override; - -private: - QString m_desc; - std::weak_ptr m_parentItem; -}; - -#endif // DESCRIPTIONNODE_H +#ifndef DESCRIPTIONNODE_H +#define DESCRIPTIONNODE_H + +#include +#include "node.h" + +class RowNode; + +class DescriptionNode : public Node +{ +public: + DescriptionNode(QString desc, std::weak_ptr parent); + ~DescriptionNode(); + std::shared_ptr parentItem() override; + int childCount() const override; + int columnCount() const override; + QVariant data(int column) override; + int row() const override; + QString getBookId() const override; + +private: + QString m_desc; + std::weak_ptr m_parentItem; +}; + +#endif // DESCRIPTIONNODE_H diff --git a/src/downloadmanagement.cpp b/src/downloadmanagement.cpp index 16a4f7cdc..12ed7dfff 100644 --- a/src/downloadmanagement.cpp +++ b/src/downloadmanagement.cpp @@ -1,402 +1,402 @@ -#include "downloadmanagement.h" - -#include "kiwixapp.h" -#include "kiwixmessagebox.h" - -#include -#include - -//////////////////////////////////////////////////////////////////////////////// -// DowloadState -//////////////////////////////////////////////////////////////////////////////// - -namespace -{ - -QString convertToUnits(double bytes) -{ - QStringList units = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"}; - int unitIndex = 0; - while (bytes >= 1024 && unitIndex < units.size()) { - bytes /= 1024; - unitIndex++; - } - - const auto preciseBytes = QString::number(bytes, 'g', 3); - return preciseBytes + " " + units[unitIndex]; -} - -DownloadState::Status getDownloadStatus(QString status) -{ - if ( status == "active" ) return DownloadState::DOWNLOADING; - if ( status == "paused" ) return DownloadState::PAUSED; - if ( status == "waiting" ) return DownloadState::WAITING; - if ( status == "error" ) return DownloadState::DOWNLOAD_ERROR; - return DownloadState::UNKNOWN; -} - -} // unnamed namespace - -bool DownloadState::isLateUpdateInfo(const DownloadInfo& info) const -{ - if ( ! stateChangeHasBeenRequested() ) - return false; - - const auto updateRequestTime = info["updateRequestTime"].toDouble(); - return updateRequestTime < lastUpdated.time_since_epoch().count(); -} - -void DownloadState::update(const DownloadInfo& info) -{ - const auto completedBytes = info["completedLength"].toDouble(); - const double percentage = completedBytes / info["totalLength"].toDouble(); - - progress = QString::number(100 * percentage, 'g', 3).toDouble(); - completedLength = convertToUnits(completedBytes); - downloadSpeed = convertToUnits(info["downloadSpeed"].toDouble()) + "/s"; - if ( !isLateUpdateInfo(info) ) { - status = getDownloadStatus(info["status"].toString()); - } - lastUpdated = std::chrono::steady_clock::now(); -} - -double DownloadState::timeSinceLastUpdate() const -{ - typedef std::chrono::duration Seconds; - - const auto dt = std::chrono::steady_clock::now() - lastUpdated; - return std::chrono::duration_cast(dt).count(); -} - -QString DownloadState::getDownloadSpeed() const -{ - return timeSinceLastUpdate() > 2.0 ? "---" : downloadSpeed; -} - -void DownloadState::changeState(Action action) -{ - const auto oldStatus = status; - if ( action == PAUSE ) { - if ( status == DOWNLOADING ) { - status = PAUSE_REQUESTED; - } - } else if ( action == RESUME ) { - if ( status == PAUSED ) { - status = RESUME_REQUESTED; - } - } else if ( action == CANCEL ) { - if ( status == DOWNLOADING || status == PAUSED ) { - status = CANCEL_REQUESTED; - } - } - - if ( status != oldStatus ) { - lastUpdated = std::chrono::steady_clock::now(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// DowloadManager -//////////////////////////////////////////////////////////////////////////////// - -namespace -{ - -kiwix::Downloader* createDownloader() -{ - try { - return new kiwix::Downloader(getDataDirectory().toStdString()); - } catch (std::exception& e) { - QMessageBox::critical(nullptr, gt("error-downloader-window-title"), - gt("error-downloader-launch-message") + "

" + e.what()); - return nullptr; - } -} - -} // unnamed namespace - -DownloadManager::DownloadManager(const Library* lib) - : mp_library(lib) - , mp_downloader(createDownloader()) -{ - restoreDownloads(); -} - -DownloadManager::~DownloadManager() -{ - if ( mp_downloadUpdaterThread ) - { - QThread* t = mp_downloadUpdaterThread; - mp_downloadUpdaterThread = nullptr; // tell the thread to terminate - - // At this point the thread may be stuck waiting for data. - // Let's wake it up. - m_requestQueue.enqueue({DownloadState::UPDATE, ""}); - t->wait(); - } -} - -bool DownloadManager::downloadingFunctionalityAvailable() const -{ - return mp_downloader.get() != nullptr; -} - -void DownloadManager::processDownloadActions() -{ - while ( mp_downloadUpdaterThread != nullptr ) { - const Request req = m_requestQueue.dequeue(); - if ( !req.bookId.isEmpty() ) { - switch ( req.action ) { - case DownloadState::START: startDownload(req.bookId); break; - case DownloadState::PAUSE: pauseDownload(req.bookId); break; - case DownloadState::RESUME: resumeDownload(req.bookId); break; - case DownloadState::CANCEL: cancelDownload(req.bookId); break; - case DownloadState::UPDATE: updateDownload(req.bookId); break; - } - } - } -} - -void DownloadManager::startDownloadUpdaterThread() -{ - // so that DownloadInfo can be copied across threads - qRegisterMetaType("DownloadInfo"); - - mp_downloadUpdaterThread = QThread::create([=]() { - processDownloadActions(); - }); - - mp_downloadUpdaterThread->start(); - - QTimer *timer = new QTimer(this); - connect(timer, &QTimer::timeout, [this]() { - if ( m_requestQueue.isEmpty() ) { - for ( const auto& bookId : m_downloads.keys() ) { - addRequest(DownloadState::UPDATE, bookId); - } - } - }); - timer->start(1000); -} - -void DownloadManager::restoreDownloads() -{ - for ( const auto& bookId : mp_library->getBookIds() ) { - const kiwix::Book& book = mp_library->getBookById(bookId); - if ( ! book.getDownloadId().empty() ) { - const auto newDownload = std::make_shared(); - m_downloads.set(bookId, newDownload); - } - } -} - -void DownloadManager::updateDownload(QString bookId) -{ - const auto updateRequestTime = std::chrono::steady_clock::now(); - DownloadInfo downloadInfo; - try { - downloadInfo = getDownloadInfo(bookId); - } catch ( ... ) { - emit downloadDisappeared(bookId); - return; - } - - downloadInfo["updateRequestTime"] = double(updateRequestTime.time_since_epoch().count()); - emit downloadUpdated(bookId, downloadInfo); -} - -namespace -{ - -QString downloadStatus2QString(kiwix::Download::StatusResult status) -{ - switch(status){ - case kiwix::Download::K_ACTIVE: return "active"; - case kiwix::Download::K_WAITING: return "waiting"; - case kiwix::Download::K_PAUSED: return "paused"; - case kiwix::Download::K_ERROR: return "error"; - case kiwix::Download::K_COMPLETE: return "completed"; - case kiwix::Download::K_REMOVED: return "removed"; - default: return "unknown"; - } -} - -} // unnamed namespace - -DownloadInfo DownloadManager::getDownloadInfo(QString bookId) const -{ - auto& b = mp_library->getBookById(bookId); - const auto d = mp_downloader->getDownload(b.getDownloadId()); - d->updateStatus(true); - - return { - { "status" , downloadStatus2QString(d->getStatus()) }, - { "completedLength" , QString::number(d->getCompletedLength()) }, - { "totalLength" , QString::number(d->getTotalLength()) }, - { "downloadSpeed" , QString::number(d->getDownloadSpeed()) }, - { "path" , QString::fromStdString(d->getPath()) } - }; -} - -namespace -{ - -void throwDownloadUnavailableError() -{ - throw KiwixAppError(gt("download-unavailable"), - gt("download-unavailable-text")); -} - -uint64_t getMaxFileSize(const QStorageInfo& storageInfo) -{ - const uint64_t GiB = 1024UL*1024UL*1024UL; - - // Maximum file size limits of filesystems that are expected to be - // encountered in practice and cause problems should be entered below - static const QMap problematicFileSystems = { -// The output of QStorageInfo::fileSystemType() is platform dependent -#ifdef _WIN32 - // XXX: find out the real name for FAT32 as reported under Windows - { "FAT32", 4*GiB - 1 } -#else - { "vfat", 4*GiB - 1 } -#endif - }; - - const uint64_t virtuallyNoLimit = std::numeric_limits::max(); - const auto fsType = storageInfo.fileSystemType(); - return problematicFileSystems.value(fsType, virtuallyNoLimit); -} - -void checkThatBookCanBeSaved(const kiwix::Book& book, QString targetDir) -{ - const QFileInfo targetDirInfo(targetDir); - if ( !targetDirInfo.isDir() ) { - throw KiwixAppError(gt("download-storage-error"), - gt("download-dir-missing")); - } - - // XXX: This may lie under Windows - // XXX: (see https://doc.qt.io/qt-5/qfile.html#platform-specific-issues) - if ( !targetDirInfo.isWritable() ) { - throw KiwixAppError(gt("download-storage-error"), - gt("download-dir-not-writable")); - } - - QStorageInfo storage(targetDir); - auto bytesAvailable = storage.bytesAvailable(); - if (bytesAvailable == -1 || book.getSize() > (unsigned long long) bytesAvailable) { - throw KiwixAppError(gt("download-storage-error"), - gt("download-storage-error-text")); - } - - if ( book.getSize() > getMaxFileSize(storage) ) { - throw KiwixAppError(gt("download-storage-error"), - gt("download-exceeds-max-file-size")); - } -} - -} // unnamed namespace - - -void DownloadManager::checkThatBookCanBeDownloaded(const kiwix::Book& book, const QString& downloadDirPath) -{ - if ( ! DownloadManager::downloadingFunctionalityAvailable() ) - throwDownloadUnavailableError(); - - checkThatBookCanBeSaved(book, downloadDirPath); -} - -std::string DownloadManager::startDownload(const kiwix::Book& book, const QString& downloadDirPath) -{ - const std::string& url = book.getUrl(); - const QString bookId = QString::fromStdString(book.getId()); - - std::string downloadId; - try { - const auto d = mp_downloader->startDownload(url, downloadDirPath.toStdString()); - downloadId = d->getDid(); - } catch (std::exception& e) { - throwDownloadUnavailableError(); - } - - return downloadId; -} - -void DownloadManager::addRequest(Action action, QString bookId) -{ - if ( action == DownloadState::START ) { - m_downloads.set(bookId, std::make_shared()); - } - - if ( const auto downloadState = getDownloadState(bookId) ) { - m_requestQueue.enqueue({action, bookId}); - if ( action != DownloadState::UPDATE ) { - downloadState->changeState(action); - } - } -} - -void DownloadManager::pauseDownload(const QString& bookId) -{ - const auto downloadId = mp_library->getBookById(bookId).getDownloadId(); - if ( downloadId.empty() ) { - // Completion of the download has been detected (and its id was reset) - // before the pause-download action was triggered (most likely through - // the context menu which can stay open for an arbitrarily long time, - // or, unlikely, through the ⏸ button during the last milliseconds of - // the download progress). - return; - } - - auto download = mp_downloader->getDownload(downloadId); - if (download->getStatus() == kiwix::Download::K_ACTIVE) { - try { - download->pauseDownload(); - } catch (const kiwix::AriaError&) { - // Download has completed before the pause request was handled. - // Most likely the download was already complete at the time - // when ContentManager::pauseBook() started executing, but its - // completion was not yet detected (and/or handled) by the download - // updater thread. - } - } -} - -void DownloadManager::resumeDownload(const QString& bookId) -{ - auto& b = mp_library->getBookById(bookId); - auto download = mp_downloader->getDownload(b.getDownloadId()); - if (download->getStatus() == kiwix::Download::K_PAUSED) { - download->resumeDownload(); - } -} - -void DownloadManager::cancelDownload(const QString& bookId) -{ - const auto downloadId = mp_library->getBookById(bookId).getDownloadId(); - if ( downloadId.empty() ) { - // Completion of the download has been detected (and its id was reset) - // before the confirmation to cancel the download was granted. - return; - } - - auto download = mp_downloader->getDownload(downloadId); - try { - download->cancelDownload(); - } catch (const kiwix::AriaError&) { - // Download has completed before the cancel request was handled. - // Most likely the download was already complete at the time - // when ContentManager::reallyCancelBook() started executing, but - // its completion was not yet detected (and/or handled) by the - // download updater thread (letting the code pass past the empty - // downloadId check above). - return; - } - emit downloadCancelled(bookId); -} - -void DownloadManager::removeDownload(QString bookId) -{ - m_downloads.remove(bookId); -} +#include "downloadmanagement.h" + +#include "kiwixapp.h" +#include "kiwixmessagebox.h" + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// DowloadState +//////////////////////////////////////////////////////////////////////////////// + +namespace +{ + +QString convertToUnits(double bytes) +{ + QStringList units = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"}; + int unitIndex = 0; + while (bytes >= 1024 && unitIndex < units.size()) { + bytes /= 1024; + unitIndex++; + } + + const auto preciseBytes = QString::number(bytes, 'g', 3); + return preciseBytes + " " + units[unitIndex]; +} + +DownloadState::Status getDownloadStatus(QString status) +{ + if ( status == "active" ) return DownloadState::DOWNLOADING; + if ( status == "paused" ) return DownloadState::PAUSED; + if ( status == "waiting" ) return DownloadState::WAITING; + if ( status == "error" ) return DownloadState::DOWNLOAD_ERROR; + return DownloadState::UNKNOWN; +} + +} // unnamed namespace + +bool DownloadState::isLateUpdateInfo(const DownloadInfo& info) const +{ + if ( ! stateChangeHasBeenRequested() ) + return false; + + const auto updateRequestTime = info["updateRequestTime"].toDouble(); + return updateRequestTime < lastUpdated.time_since_epoch().count(); +} + +void DownloadState::update(const DownloadInfo& info) +{ + const auto completedBytes = info["completedLength"].toDouble(); + const double percentage = completedBytes / info["totalLength"].toDouble(); + + progress = QString::number(100 * percentage, 'g', 3).toDouble(); + completedLength = convertToUnits(completedBytes); + downloadSpeed = convertToUnits(info["downloadSpeed"].toDouble()) + "/s"; + if ( !isLateUpdateInfo(info) ) { + status = getDownloadStatus(info["status"].toString()); + } + lastUpdated = std::chrono::steady_clock::now(); +} + +double DownloadState::timeSinceLastUpdate() const +{ + typedef std::chrono::duration Seconds; + + const auto dt = std::chrono::steady_clock::now() - lastUpdated; + return std::chrono::duration_cast(dt).count(); +} + +QString DownloadState::getDownloadSpeed() const +{ + return timeSinceLastUpdate() > 2.0 ? "---" : downloadSpeed; +} + +void DownloadState::changeState(Action action) +{ + const auto oldStatus = status; + if ( action == PAUSE ) { + if ( status == DOWNLOADING ) { + status = PAUSE_REQUESTED; + } + } else if ( action == RESUME ) { + if ( status == PAUSED ) { + status = RESUME_REQUESTED; + } + } else if ( action == CANCEL ) { + if ( status == DOWNLOADING || status == PAUSED ) { + status = CANCEL_REQUESTED; + } + } + + if ( status != oldStatus ) { + lastUpdated = std::chrono::steady_clock::now(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// DowloadManager +//////////////////////////////////////////////////////////////////////////////// + +namespace +{ + +kiwix::Downloader* createDownloader() +{ + try { + return new kiwix::Downloader(getDataDirectory().toStdString()); + } catch (std::exception& e) { + QMessageBox::critical(nullptr, gt("error-downloader-window-title"), + gt("error-downloader-launch-message") + "

" + e.what()); + return nullptr; + } +} + +} // unnamed namespace + +DownloadManager::DownloadManager(const Library* lib) + : mp_library(lib) + , mp_downloader(createDownloader()) +{ + restoreDownloads(); +} + +DownloadManager::~DownloadManager() +{ + if ( mp_downloadUpdaterThread ) + { + QThread* t = mp_downloadUpdaterThread; + mp_downloadUpdaterThread = nullptr; // tell the thread to terminate + + // At this point the thread may be stuck waiting for data. + // Let's wake it up. + m_requestQueue.enqueue({DownloadState::UPDATE, ""}); + t->wait(); + } +} + +bool DownloadManager::downloadingFunctionalityAvailable() const +{ + return mp_downloader.get() != nullptr; +} + +void DownloadManager::processDownloadActions() +{ + while ( mp_downloadUpdaterThread != nullptr ) { + const Request req = m_requestQueue.dequeue(); + if ( !req.bookId.isEmpty() ) { + switch ( req.action ) { + case DownloadState::START: startDownload(req.bookId); break; + case DownloadState::PAUSE: pauseDownload(req.bookId); break; + case DownloadState::RESUME: resumeDownload(req.bookId); break; + case DownloadState::CANCEL: cancelDownload(req.bookId); break; + case DownloadState::UPDATE: updateDownload(req.bookId); break; + } + } + } +} + +void DownloadManager::startDownloadUpdaterThread() +{ + // so that DownloadInfo can be copied across threads + qRegisterMetaType("DownloadInfo"); + + mp_downloadUpdaterThread = QThread::create([=]() { + processDownloadActions(); + }); + + mp_downloadUpdaterThread->start(); + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, [this]() { + if ( m_requestQueue.isEmpty() ) { + for ( const auto& bookId : m_downloads.keys() ) { + addRequest(DownloadState::UPDATE, bookId); + } + } + }); + timer->start(1000); +} + +void DownloadManager::restoreDownloads() +{ + for ( const auto& bookId : mp_library->getBookIds() ) { + const kiwix::Book& book = mp_library->getBookById(bookId); + if ( ! book.getDownloadId().empty() ) { + const auto newDownload = std::make_shared(); + m_downloads.set(bookId, newDownload); + } + } +} + +void DownloadManager::updateDownload(QString bookId) +{ + const auto updateRequestTime = std::chrono::steady_clock::now(); + DownloadInfo downloadInfo; + try { + downloadInfo = getDownloadInfo(bookId); + } catch ( ... ) { + emit downloadDisappeared(bookId); + return; + } + + downloadInfo["updateRequestTime"] = double(updateRequestTime.time_since_epoch().count()); + emit downloadUpdated(bookId, downloadInfo); +} + +namespace +{ + +QString downloadStatus2QString(kiwix::Download::StatusResult status) +{ + switch(status){ + case kiwix::Download::K_ACTIVE: return "active"; + case kiwix::Download::K_WAITING: return "waiting"; + case kiwix::Download::K_PAUSED: return "paused"; + case kiwix::Download::K_ERROR: return "error"; + case kiwix::Download::K_COMPLETE: return "completed"; + case kiwix::Download::K_REMOVED: return "removed"; + default: return "unknown"; + } +} + +} // unnamed namespace + +DownloadInfo DownloadManager::getDownloadInfo(QString bookId) const +{ + auto& b = mp_library->getBookById(bookId); + const auto d = mp_downloader->getDownload(b.getDownloadId()); + d->updateStatus(true); + + return { + { "status" , downloadStatus2QString(d->getStatus()) }, + { "completedLength" , QString::number(d->getCompletedLength()) }, + { "totalLength" , QString::number(d->getTotalLength()) }, + { "downloadSpeed" , QString::number(d->getDownloadSpeed()) }, + { "path" , QString::fromStdString(d->getPath()) } + }; +} + +namespace +{ + +void throwDownloadUnavailableError() +{ + throw KiwixAppError(gt("download-unavailable"), + gt("download-unavailable-text")); +} + +uint64_t getMaxFileSize(const QStorageInfo& storageInfo) +{ + const uint64_t GiB = 1024UL*1024UL*1024UL; + + // Maximum file size limits of filesystems that are expected to be + // encountered in practice and cause problems should be entered below + static const QMap problematicFileSystems = { +// The output of QStorageInfo::fileSystemType() is platform dependent +#ifdef _WIN32 + // XXX: find out the real name for FAT32 as reported under Windows + { "FAT32", 4*GiB - 1 } +#else + { "vfat", 4*GiB - 1 } +#endif + }; + + const uint64_t virtuallyNoLimit = std::numeric_limits::max(); + const auto fsType = storageInfo.fileSystemType(); + return problematicFileSystems.value(fsType, virtuallyNoLimit); +} + +void checkThatBookCanBeSaved(const kiwix::Book& book, QString targetDir) +{ + const QFileInfo targetDirInfo(targetDir); + if ( !targetDirInfo.isDir() ) { + throw KiwixAppError(gt("download-storage-error"), + gt("download-dir-missing")); + } + + // XXX: This may lie under Windows + // XXX: (see https://doc.qt.io/qt-5/qfile.html#platform-specific-issues) + if ( !targetDirInfo.isWritable() ) { + throw KiwixAppError(gt("download-storage-error"), + gt("download-dir-not-writable")); + } + + QStorageInfo storage(targetDir); + auto bytesAvailable = storage.bytesAvailable(); + if (bytesAvailable == -1 || book.getSize() > (unsigned long long) bytesAvailable) { + throw KiwixAppError(gt("download-storage-error"), + gt("download-storage-error-text")); + } + + if ( book.getSize() > getMaxFileSize(storage) ) { + throw KiwixAppError(gt("download-storage-error"), + gt("download-exceeds-max-file-size")); + } +} + +} // unnamed namespace + + +void DownloadManager::checkThatBookCanBeDownloaded(const kiwix::Book& book, const QString& downloadDirPath) +{ + if ( ! DownloadManager::downloadingFunctionalityAvailable() ) + throwDownloadUnavailableError(); + + checkThatBookCanBeSaved(book, downloadDirPath); +} + +std::string DownloadManager::startDownload(const kiwix::Book& book, const QString& downloadDirPath) +{ + const std::string& url = book.getUrl(); + const QString bookId = QString::fromStdString(book.getId()); + + std::string downloadId; + try { + const auto d = mp_downloader->startDownload(url, downloadDirPath.toStdString()); + downloadId = d->getDid(); + } catch (std::exception& e) { + throwDownloadUnavailableError(); + } + + return downloadId; +} + +void DownloadManager::addRequest(Action action, QString bookId) +{ + if ( action == DownloadState::START ) { + m_downloads.set(bookId, std::make_shared()); + } + + if ( const auto downloadState = getDownloadState(bookId) ) { + m_requestQueue.enqueue({action, bookId}); + if ( action != DownloadState::UPDATE ) { + downloadState->changeState(action); + } + } +} + +void DownloadManager::pauseDownload(const QString& bookId) +{ + const auto downloadId = mp_library->getBookById(bookId).getDownloadId(); + if ( downloadId.empty() ) { + // Completion of the download has been detected (and its id was reset) + // before the pause-download action was triggered (most likely through + // the context menu which can stay open for an arbitrarily long time, + // or, unlikely, through the ⏸ button during the last milliseconds of + // the download progress). + return; + } + + auto download = mp_downloader->getDownload(downloadId); + if (download->getStatus() == kiwix::Download::K_ACTIVE) { + try { + download->pauseDownload(); + } catch (const kiwix::AriaError&) { + // Download has completed before the pause request was handled. + // Most likely the download was already complete at the time + // when ContentManager::pauseBook() started executing, but its + // completion was not yet detected (and/or handled) by the download + // updater thread. + } + } +} + +void DownloadManager::resumeDownload(const QString& bookId) +{ + auto& b = mp_library->getBookById(bookId); + auto download = mp_downloader->getDownload(b.getDownloadId()); + if (download->getStatus() == kiwix::Download::K_PAUSED) { + download->resumeDownload(); + } +} + +void DownloadManager::cancelDownload(const QString& bookId) +{ + const auto downloadId = mp_library->getBookById(bookId).getDownloadId(); + if ( downloadId.empty() ) { + // Completion of the download has been detected (and its id was reset) + // before the confirmation to cancel the download was granted. + return; + } + + auto download = mp_downloader->getDownload(downloadId); + try { + download->cancelDownload(); + } catch (const kiwix::AriaError&) { + // Download has completed before the cancel request was handled. + // Most likely the download was already complete at the time + // when ContentManager::reallyCancelBook() started executing, but + // its completion was not yet detected (and/or handled) by the + // download updater thread (letting the code pass past the empty + // downloadId check above). + return; + } + emit downloadCancelled(bookId); +} + +void DownloadManager::removeDownload(QString bookId) +{ + m_downloads.remove(bookId); +} diff --git a/src/downloadmanagement.h b/src/downloadmanagement.h index 7e6cddd71..c29e41629 100644 --- a/src/downloadmanagement.h +++ b/src/downloadmanagement.h @@ -1,209 +1,209 @@ -#ifndef DOWNLOADMANAGEMENT_H -#define DOWNLOADMANAGEMENT_H - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "library.h" - -typedef QMap DownloadInfo; - -template -class ThreadSafePriorityQueue -{ -public: - void enqueue(const T& x) - { - const QMutexLocker threadSafetyGuarantee(&m_mutex); - m_queue.push(x); - m_queueIsNotEmpty.wakeAll(); - } - - T dequeue() - { - const QMutexLocker threadSafetyGuarantee(&m_mutex); - if ( m_queue.empty() ) - m_queueIsNotEmpty.wait(&m_mutex); - - const T ret = m_queue.top(); - m_queue.pop(); - return ret; - } - - bool isEmpty() const - { - const QMutexLocker threadSafetyGuarantee(&m_mutex); - return m_queue.empty(); - } - -private: // data - mutable QMutex m_mutex; - std::priority_queue m_queue; - QWaitCondition m_queueIsNotEmpty; -}; - -class DownloadState -{ -public: // types - enum Action { - UPDATE, - START, - PAUSE, - RESUME, - CANCEL - }; - - enum Status { - UNKNOWN, - WAITING, - DOWNLOAD_ERROR, - DOWNLOADING, - PAUSE_REQUESTED, - PAUSED, - RESUME_REQUESTED, - CANCEL_REQUESTED - }; - -public: // data - - double progress = 0; - QString completedLength; - -public: // functions - void update(const DownloadInfo& info); - QString getDownloadSpeed() const; - Status getStatus() const { return status; } - void changeState(Action action); - bool stateChangeHasBeenRequested() const - { - return status == PAUSE_REQUESTED - || status == RESUME_REQUESTED - || status == CANCEL_REQUESTED; - } - bool isLateUpdateInfo(const DownloadInfo& info) const; - - // time in seconds since last update - double timeSinceLastUpdate() const; - -private: // data - Status status = UNKNOWN; - QString downloadSpeed; - std::chrono::steady_clock::time_point lastUpdated; -}; - -class DownloadManager : public QObject -{ - Q_OBJECT - -public: // types - typedef std::shared_ptr DownloadStatePtr; - typedef DownloadState::Action Action; - -private: - // BookId -> DownloadState map - class Downloads - { - private: - typedef QMap ImplType; - - public: - void set(const QString& id, DownloadStatePtr d) { - const QMutexLocker threadSafetyGuarantee(&mutex); - impl[id] = d; - } - - DownloadStatePtr value(const QString& id) const { - const QMutexLocker threadSafetyGuarantee(&mutex); - return impl.value(id); - } - - QList keys() const { - const QMutexLocker threadSafetyGuarantee(&mutex); - return impl.keys(); - } - - void remove(const QString& id) { - const QMutexLocker threadSafetyGuarantee(&mutex); - impl.remove(id); - } - - private: - ImplType impl; - mutable QMutex mutex; - }; - -public: // functions - explicit DownloadManager(const Library* lib); - virtual ~DownloadManager(); - - bool downloadingFunctionalityAvailable() const; - - void startDownloadUpdaterThread(); - - DownloadInfo getDownloadInfo(QString bookId) const; - void restoreDownloads(); - - void addRequest(Action action, QString bookId); - - // Throws a KiwixAppError in case of any foreseeable problem preventing a - // successful download - void checkThatBookCanBeDownloaded(const kiwix::Book& book, const QString& downloadDirPath); - - void removeDownload(QString bookId); - - DownloadStatePtr getDownloadState(QString bookId) const - { - return m_downloads.value(bookId); - } - -signals: - void error(QString errSummary, QString errDetails); - void downloadUpdated(QString bookId, const DownloadInfo& ); - void downloadCancelled(QString bookId); - void downloadDisappeared(QString bookId); - -protected: - // returns the download id - std::string startDownload(const kiwix::Book& book, const QString& downloadDirPath); - -private: // types - struct Request - { - Action action; - QString bookId; - - bool operator<(const Request& other) const { - return this->action < other.action; - } - }; - - typedef ThreadSafePriorityQueue RequestQueue; - -private: // functions - void processDownloadActions(); - virtual void startDownload(QString bookId) = 0; - void pauseDownload(const QString& bookId); - void resumeDownload(const QString& bookId); - void updateDownload(QString bookId); - void cancelDownload(const QString& bookId); - -private: // data - const Library* const mp_library; - const std::unique_ptr mp_downloader; - Downloads m_downloads; - QThread* mp_downloadUpdaterThread = nullptr; - RequestQueue m_requestQueue; -}; - -#endif // DOWNLOADMANAGEMENT_H +#ifndef DOWNLOADMANAGEMENT_H +#define DOWNLOADMANAGEMENT_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "library.h" + +typedef QMap DownloadInfo; + +template +class ThreadSafePriorityQueue +{ +public: + void enqueue(const T& x) + { + const QMutexLocker threadSafetyGuarantee(&m_mutex); + m_queue.push(x); + m_queueIsNotEmpty.wakeAll(); + } + + T dequeue() + { + const QMutexLocker threadSafetyGuarantee(&m_mutex); + if ( m_queue.empty() ) + m_queueIsNotEmpty.wait(&m_mutex); + + const T ret = m_queue.top(); + m_queue.pop(); + return ret; + } + + bool isEmpty() const + { + const QMutexLocker threadSafetyGuarantee(&m_mutex); + return m_queue.empty(); + } + +private: // data + mutable QMutex m_mutex; + std::priority_queue m_queue; + QWaitCondition m_queueIsNotEmpty; +}; + +class DownloadState +{ +public: // types + enum Action { + UPDATE, + START, + PAUSE, + RESUME, + CANCEL + }; + + enum Status { + UNKNOWN, + WAITING, + DOWNLOAD_ERROR, + DOWNLOADING, + PAUSE_REQUESTED, + PAUSED, + RESUME_REQUESTED, + CANCEL_REQUESTED + }; + +public: // data + + double progress = 0; + QString completedLength; + +public: // functions + void update(const DownloadInfo& info); + QString getDownloadSpeed() const; + Status getStatus() const { return status; } + void changeState(Action action); + bool stateChangeHasBeenRequested() const + { + return status == PAUSE_REQUESTED + || status == RESUME_REQUESTED + || status == CANCEL_REQUESTED; + } + bool isLateUpdateInfo(const DownloadInfo& info) const; + + // time in seconds since last update + double timeSinceLastUpdate() const; + +private: // data + Status status = UNKNOWN; + QString downloadSpeed; + std::chrono::steady_clock::time_point lastUpdated; +}; + +class DownloadManager : public QObject +{ + Q_OBJECT + +public: // types + typedef std::shared_ptr DownloadStatePtr; + typedef DownloadState::Action Action; + +private: + // BookId -> DownloadState map + class Downloads + { + private: + typedef QMap ImplType; + + public: + void set(const QString& id, DownloadStatePtr d) { + const QMutexLocker threadSafetyGuarantee(&mutex); + impl[id] = d; + } + + DownloadStatePtr value(const QString& id) const { + const QMutexLocker threadSafetyGuarantee(&mutex); + return impl.value(id); + } + + QList keys() const { + const QMutexLocker threadSafetyGuarantee(&mutex); + return impl.keys(); + } + + void remove(const QString& id) { + const QMutexLocker threadSafetyGuarantee(&mutex); + impl.remove(id); + } + + private: + ImplType impl; + mutable QMutex mutex; + }; + +public: // functions + explicit DownloadManager(const Library* lib); + virtual ~DownloadManager(); + + bool downloadingFunctionalityAvailable() const; + + void startDownloadUpdaterThread(); + + DownloadInfo getDownloadInfo(QString bookId) const; + void restoreDownloads(); + + void addRequest(Action action, QString bookId); + + // Throws a KiwixAppError in case of any foreseeable problem preventing a + // successful download + void checkThatBookCanBeDownloaded(const kiwix::Book& book, const QString& downloadDirPath); + + void removeDownload(QString bookId); + + DownloadStatePtr getDownloadState(QString bookId) const + { + return m_downloads.value(bookId); + } + +signals: + void error(QString errSummary, QString errDetails); + void downloadUpdated(QString bookId, const DownloadInfo& ); + void downloadCancelled(QString bookId); + void downloadDisappeared(QString bookId); + +protected: + // returns the download id + std::string startDownload(const kiwix::Book& book, const QString& downloadDirPath); + +private: // types + struct Request + { + Action action; + QString bookId; + + bool operator<(const Request& other) const { + return this->action < other.action; + } + }; + + typedef ThreadSafePriorityQueue RequestQueue; + +private: // functions + void processDownloadActions(); + virtual void startDownload(QString bookId) = 0; + void pauseDownload(const QString& bookId); + void resumeDownload(const QString& bookId); + void updateDownload(QString bookId); + void cancelDownload(const QString& bookId); + +private: // data + const Library* const mp_library; + const std::unique_ptr mp_downloader; + Downloads m_downloads; + QThread* mp_downloadUpdaterThread = nullptr; + RequestQueue m_requestQueue; +}; + +#endif // DOWNLOADMANAGEMENT_H diff --git a/src/findinpagebar.cpp b/src/findinpagebar.cpp index 051198865..6c672db38 100644 --- a/src/findinpagebar.cpp +++ b/src/findinpagebar.cpp @@ -1,70 +1,70 @@ -#include "findinpagebar.h" -#include "ui_findinpagebar.h" -#include "kiwixapp.h" - -FindInPageBar::FindInPageBar(QWidget *parent) : - QFrame(parent), - mp_ui(new Ui::FindInPageBar) -{ - mp_ui->setupUi(this); - mp_findLineEdit = mp_ui->findEdit; - connect(mp_ui->hideButton, &QPushButton::released, - this, &FindInPageBar::findClose); - connect(mp_ui->fNextButton, &QPushButton::released, - this, &FindInPageBar::findNext); - connect(mp_ui->fPreviousButton, &QPushButton::released, - this, &FindInPageBar::findPrevious); - connect(mp_findLineEdit, &QLineEdit::returnPressed, - this, &FindInPageBar::findNext); -} - -FindInPageBar::~FindInPageBar() -{ - delete mp_ui; -} - -void FindInPageBar::findNext() -{ - auto searchText = mp_findLineEdit->text(); - if (searchText.isEmpty()) - return; - auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!current) - return; - auto page = current->page(); - page->findText(searchText); -} - -void FindInPageBar::findPrevious() -{ - auto searchText = mp_findLineEdit->text(); - if (searchText.isEmpty()) - return; - auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!current) - return; - auto page = current->page(); - page->findText(searchText, QWebEnginePage::FindBackward); -} - -void FindInPageBar::findClose() -{ - auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!current) - return; - auto page = current->page(); - page->findText(""); - current->setFocus(); - close(); -} - -void FindInPageBar::keyPressEvent(QKeyEvent *event) -{ - switch(event->key()) { - case Qt::Key_Escape: - findClose(); - break; - default: - QWidget::keyPressEvent(event); - } +#include "findinpagebar.h" +#include "ui_findinpagebar.h" +#include "kiwixapp.h" + +FindInPageBar::FindInPageBar(QWidget *parent) : + QFrame(parent), + mp_ui(new Ui::FindInPageBar) +{ + mp_ui->setupUi(this); + mp_findLineEdit = mp_ui->findEdit; + connect(mp_ui->hideButton, &QPushButton::released, + this, &FindInPageBar::findClose); + connect(mp_ui->fNextButton, &QPushButton::released, + this, &FindInPageBar::findNext); + connect(mp_ui->fPreviousButton, &QPushButton::released, + this, &FindInPageBar::findPrevious); + connect(mp_findLineEdit, &QLineEdit::returnPressed, + this, &FindInPageBar::findNext); +} + +FindInPageBar::~FindInPageBar() +{ + delete mp_ui; +} + +void FindInPageBar::findNext() +{ + auto searchText = mp_findLineEdit->text(); + if (searchText.isEmpty()) + return; + auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); + if (!current) + return; + auto page = current->page(); + page->findText(searchText); +} + +void FindInPageBar::findPrevious() +{ + auto searchText = mp_findLineEdit->text(); + if (searchText.isEmpty()) + return; + auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); + if (!current) + return; + auto page = current->page(); + page->findText(searchText, QWebEnginePage::FindBackward); +} + +void FindInPageBar::findClose() +{ + auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); + if (!current) + return; + auto page = current->page(); + page->findText(""); + current->setFocus(); + close(); +} + +void FindInPageBar::keyPressEvent(QKeyEvent *event) +{ + switch(event->key()) { + case Qt::Key_Escape: + findClose(); + break; + default: + QWidget::keyPressEvent(event); + } } \ No newline at end of file diff --git a/src/findinpagebar.h b/src/findinpagebar.h index 0632bc1e5..37401a10f 100644 --- a/src/findinpagebar.h +++ b/src/findinpagebar.h @@ -1,34 +1,34 @@ -#ifndef FINDINPAGEBAR_H -#define FINDINPAGEBAR_H - -#include -#include - -namespace Ui { -class FindInPageBar; -} - -class FindInPageBar : public QFrame -{ - Q_OBJECT - -public: - explicit FindInPageBar(QWidget *parent = nullptr); - ~FindInPageBar(); - - QLineEdit* getFindLineEdit() { return mp_findLineEdit; }; - -public slots: - void findNext(); - void findPrevious(); - void findClose(); - -protected: - void keyPressEvent(QKeyEvent *event); - -private: - Ui::FindInPageBar *mp_ui; - QLineEdit *mp_findLineEdit; -}; - -#endif // FINDINPAGEBAR_H +#ifndef FINDINPAGEBAR_H +#define FINDINPAGEBAR_H + +#include +#include + +namespace Ui { +class FindInPageBar; +} + +class FindInPageBar : public QFrame +{ + Q_OBJECT + +public: + explicit FindInPageBar(QWidget *parent = nullptr); + ~FindInPageBar(); + + QLineEdit* getFindLineEdit() { return mp_findLineEdit; }; + +public slots: + void findNext(); + void findPrevious(); + void findClose(); + +protected: + void keyPressEvent(QKeyEvent *event); + +private: + Ui::FindInPageBar *mp_ui; + QLineEdit *mp_findLineEdit; +}; + +#endif // FINDINPAGEBAR_H diff --git a/src/findinpagebar.ui b/src/findinpagebar.ui index c7e9b4053..dabf36d1d 100644 --- a/src/findinpagebar.ui +++ b/src/findinpagebar.ui @@ -1,82 +1,82 @@ - - - FindInPageBar - - - - 0 - 0 - 833 - 43 - - - - - 0 - 0 - - - - Frame - - - - - - Find in page - - - - - - - - :/icons/search_forward.svg:/icons/search_forward.svg - - - true - - - - - - - - :/icons/search_backward.svg:/icons/search_backward.svg - - - true - - - - - - - Qt::Horizontal - - - - 350 - 20 - - - - - - - - - :/icons/close.svg:/icons/close.svg - - - true - - - - - - - - - - + + + FindInPageBar + + + + 0 + 0 + 833 + 43 + + + + + 0 + 0 + + + + Frame + + + + + + Find in page + + + + + + + + :/icons/search_forward.svg:/icons/search_forward.svg + + + true + + + + + + + + :/icons/search_backward.svg:/icons/search_backward.svg + + + true + + + + + + + Qt::Horizontal + + + + 350 + 20 + + + + + + + + + :/icons/close.svg:/icons/close.svg + + + true + + + + + + + + + + diff --git a/src/flowlayout.cpp b/src/flowlayout.cpp index 69ba13df2..44190cd53 100644 --- a/src/flowlayout.cpp +++ b/src/flowlayout.cpp @@ -1,229 +1,229 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include "flowlayout.h" -#include -#include -//! [1] -FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) - : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} - -FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) - : m_hSpace(hSpacing), m_vSpace(vSpacing) -{ - setContentsMargins(margin, margin, margin, margin); -} -//! [1] - -//! [2] -FlowLayout::~FlowLayout() -{ - QLayoutItem *item; - while ((item = takeAt(0))) - delete item; -} -//! [2] - -//! [3] -void FlowLayout::addItem(QLayoutItem *item) -{ - itemList.append(item); -} -//! [3] - -//! [4] -int FlowLayout::horizontalSpacing() const -{ - if (m_hSpace >= 0) { - return m_hSpace; - } else { - return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); - } -} - -int FlowLayout::verticalSpacing() const -{ - if (m_vSpace >= 0) { - return m_vSpace; - } else { - return smartSpacing(QStyle::PM_LayoutVerticalSpacing); - } -} -//! [4] - -//! [5] -int FlowLayout::count() const -{ - return itemList.size(); -} - -QLayoutItem *FlowLayout::itemAt(int index) const -{ - return itemList.value(index); -} - -QLayoutItem *FlowLayout::takeAt(int index) -{ - if (index >= 0 && index < itemList.size()) - return itemList.takeAt(index); - return nullptr; -} -//! [5] - -//! [6] -Qt::Orientations FlowLayout::expandingDirections() const -{ - return { }; -} -//! [6] - -//! [7] -bool FlowLayout::hasHeightForWidth() const -{ - return true; -} - -int FlowLayout::heightForWidth(int width) const -{ - int height = doLayout(QRect(0, 0, width, 0), true); - return height; -} -//! [7] - -//! [8] -void FlowLayout::setGeometry(const QRect &rect) -{ - QLayout::setGeometry(rect); - doLayout(rect, false); -} - -QSize FlowLayout::sizeHint() const -{ - return minimumSize(); -} - -QSize FlowLayout::minimumSize() const -{ - QSize size; - for (const QLayoutItem *item : std::as_const(itemList)) - size = size.expandedTo(item->minimumSize()); - - const QMargins margins = contentsMargins(); - size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); - return size; -} -//! [8] - -//! [9] -int FlowLayout::doLayout(const QRect &rect, bool testOnly) const -{ - int left, top, right, bottom; - getContentsMargins(&left, &top, &right, &bottom); - QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); - int x = effectiveRect.x(); - int y = effectiveRect.y(); - int lineHeight = 0; -//! [9] - -//! [10] - for (QLayoutItem *item : std::as_const(itemList)) { - const QWidget *wid = item->widget(); - int spaceX = horizontalSpacing(); - if (spaceX == -1) - spaceX = wid->style()->layoutSpacing( - QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); - int spaceY = verticalSpacing(); - if (spaceY == -1) - spaceY = wid->style()->layoutSpacing( - QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); -//! [10] -//! [11] - int nextX = x + item->sizeHint().width() + spaceX; - if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { - x = effectiveRect.x(); - y = y + lineHeight + spaceY; - nextX = x + item->sizeHint().width() + spaceX; - lineHeight = 0; - } - - if (!testOnly) - item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); - - x = nextX; - lineHeight = qMax(lineHeight, item->sizeHint().height()); - } - return y + lineHeight - rect.y() + bottom; -} -//! [11] -//! [12] -int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const -{ - QObject *parent = this->parent(); - if (!parent) { - return -1; - } else if (parent->isWidgetType()) { - QWidget *pw = static_cast(parent); - return pw->style()->pixelMetric(pm, nullptr, pw); - } else { - return static_cast(parent)->spacing(); - } -} -//! [12] - -void FlowLayout::insertWidget(int index, QWidget *w) { - addWidget(w); - if (index < 0) - index = 0; - if (index >= itemList.size()) - index = itemList.size() - 1; - itemList.move(indexOf(w), index); -} +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "flowlayout.h" +#include +#include +//! [1] +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} +//! [1] + +//! [2] +FlowLayout::~FlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} +//! [2] + +//! [3] +void FlowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} +//! [3] + +//! [4] +int FlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int FlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} +//! [4] + +//! [5] +int FlowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *FlowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *FlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + return nullptr; +} +//! [5] + +//! [6] +Qt::Orientations FlowLayout::expandingDirections() const +{ + return { }; +} +//! [6] + +//! [7] +bool FlowLayout::hasHeightForWidth() const +{ + return true; +} + +int FlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} +//! [7] + +//! [8] +void FlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize FlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize FlowLayout::minimumSize() const +{ + QSize size; + for (const QLayoutItem *item : std::as_const(itemList)) + size = size.expandedTo(item->minimumSize()); + + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); + return size; +} +//! [8] + +//! [9] +int FlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; +//! [9] + +//! [10] + for (QLayoutItem *item : std::as_const(itemList)) { + const QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); +//! [10] +//! [11] + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} +//! [11] +//! [12] +int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } else { + return static_cast(parent)->spacing(); + } +} +//! [12] + +void FlowLayout::insertWidget(int index, QWidget *w) { + addWidget(w); + if (index < 0) + index = 0; + if (index >= itemList.size()) + index = itemList.size() - 1; + itemList.move(indexOf(w), index); +} diff --git a/src/flowlayout.h b/src/flowlayout.h index da4e7b101..bcc91c581 100644 --- a/src/flowlayout.h +++ b/src/flowlayout.h @@ -1,90 +1,90 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef FLOWLAYOUT_H -#define FLOWLAYOUT_H - -#include -#include -#include -//! [0] -class FlowLayout : public QLayout -{ - Q_OBJECT -public: - explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); - explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); - ~FlowLayout(); - - void addItem(QLayoutItem *item) override; - int horizontalSpacing() const; - int verticalSpacing() const; - Qt::Orientations expandingDirections() const override; - bool hasHeightForWidth() const override; - int heightForWidth(int) const override; - int count() const override; - QLayoutItem *itemAt(int index) const override; - QSize minimumSize() const override; - void setGeometry(const QRect &rect) override; - QSize sizeHint() const override; - QLayoutItem *takeAt(int index) override; - void insertWidget(int index, QWidget *w); - -private: - int doLayout(const QRect &rect, bool testOnly) const; - int smartSpacing(QStyle::PixelMetric pm) const; - - QList itemList; - int m_hSpace; - int m_vSpace; -}; -//! [0] - -#endif // FLOWLAYOUT_H +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include +#include +//! [0] +class FlowLayout : public QLayout +{ + Q_OBJECT +public: + explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem *item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem *itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + QSize sizeHint() const override; + QLayoutItem *takeAt(int index) override; + void insertWidget(int index, QWidget *w); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; +//! [0] + +#endif // FLOWLAYOUT_H diff --git a/src/fullscreennotification.cpp b/src/fullscreennotification.cpp index 641002b2c..19c48d27d 100644 --- a/src/fullscreennotification.cpp +++ b/src/fullscreennotification.cpp @@ -1,48 +1,48 @@ -#include "fullscreennotification.h" -#include "kiwixapp.h" -#include -#include -#include - -FullScreenNotification::FullScreenNotification(QWidget *parent) - : QLabel(parent) - , m_previouslyVisible(false) -{ - setText(gt("fullscreen-notification")); - setStyleSheet( - "font-size: 24px;" - "color: white;" - "background-color: black;" - "border-color: white;" - "border-width: 2px;" - "border-style: solid;" - "padding: 100px"); - setAttribute(Qt::WA_TransparentForMouseEvents); - - auto effect = new QGraphicsOpacityEffect; - effect->setOpacity(1); - setGraphicsEffect(effect); - - auto animations = new QSequentialAnimationGroup(this); - animations->addPause(3000); - auto opacityAnimation = new QPropertyAnimation(effect, "opacity", animations); - opacityAnimation->setDuration(2000); - opacityAnimation->setStartValue(1.0); - opacityAnimation->setEndValue(0.0); - opacityAnimation->setEasingCurve(QEasingCurve::OutQuad); - animations->addAnimation(opacityAnimation); - - connect(this, &FullScreenNotification::shown, - [animations](){ animations->start(); }); - - connect(animations, &QAbstractAnimation::finished, - [this](){ this->hide(); }); -} - -void FullScreenNotification::showEvent(QShowEvent *event) -{ - QLabel::showEvent(event); - if (!m_previouslyVisible && isVisible()) - emit shown(); - m_previouslyVisible = isVisible(); +#include "fullscreennotification.h" +#include "kiwixapp.h" +#include +#include +#include + +FullScreenNotification::FullScreenNotification(QWidget *parent) + : QLabel(parent) + , m_previouslyVisible(false) +{ + setText(gt("fullscreen-notification")); + setStyleSheet( + "font-size: 24px;" + "color: white;" + "background-color: black;" + "border-color: white;" + "border-width: 2px;" + "border-style: solid;" + "padding: 100px"); + setAttribute(Qt::WA_TransparentForMouseEvents); + + auto effect = new QGraphicsOpacityEffect; + effect->setOpacity(1); + setGraphicsEffect(effect); + + auto animations = new QSequentialAnimationGroup(this); + animations->addPause(3000); + auto opacityAnimation = new QPropertyAnimation(effect, "opacity", animations); + opacityAnimation->setDuration(2000); + opacityAnimation->setStartValue(1.0); + opacityAnimation->setEndValue(0.0); + opacityAnimation->setEasingCurve(QEasingCurve::OutQuad); + animations->addAnimation(opacityAnimation); + + connect(this, &FullScreenNotification::shown, + [animations](){ animations->start(); }); + + connect(animations, &QAbstractAnimation::finished, + [this](){ this->hide(); }); +} + +void FullScreenNotification::showEvent(QShowEvent *event) +{ + QLabel::showEvent(event); + if (!m_previouslyVisible && isVisible()) + emit shown(); + m_previouslyVisible = isVisible(); } \ No newline at end of file diff --git a/src/fullscreennotification.h b/src/fullscreennotification.h index cdec582e4..c53c1e1d4 100644 --- a/src/fullscreennotification.h +++ b/src/fullscreennotification.h @@ -1,22 +1,22 @@ -#ifndef FULLSCREENNOTIFICATION_H -#define FULLSCREENNOTIFICATION_H - -#include - -class FullScreenNotification : public QLabel -{ - Q_OBJECT -public: - FullScreenNotification(QWidget *parent = nullptr); - -protected: - void showEvent(QShowEvent *event) override; - -signals: - void shown(); - -private: - bool m_previouslyVisible; -}; - +#ifndef FULLSCREENNOTIFICATION_H +#define FULLSCREENNOTIFICATION_H + +#include + +class FullScreenNotification : public QLabel +{ + Q_OBJECT +public: + FullScreenNotification(QWidget *parent = nullptr); + +protected: + void showEvent(QShowEvent *event) override; + +signals: + void shown(); + +private: + bool m_previouslyVisible; +}; + #endif // FULLSCREENNOTIFICATION_H \ No newline at end of file diff --git a/src/fullscreenwindow.cpp b/src/fullscreenwindow.cpp index bff20198b..ecfdd1792 100644 --- a/src/fullscreenwindow.cpp +++ b/src/fullscreenwindow.cpp @@ -1,46 +1,46 @@ -class QMenu; - -#include "fullscreenwindow.h" -#include - -FullScreenWindow::FullScreenWindow(QWebEngineView *oldView, QWidget *parent) - : QWidget(parent) - , m_view(new QWebEngineView(this)) - , m_notification(new FullScreenNotification(this)) - , m_oldView(oldView) - , m_oldGeometry(oldView->window()->geometry()) -{ - m_view->stackUnder(m_notification); - - auto exitAction = new QAction(this); - exitAction->setShortcut(Qt::Key_Escape); - connect(exitAction, &QAction::triggered, [this]() { - m_view->triggerPageAction(QWebEnginePage::ExitFullScreen); - }); - addAction(exitAction); - - m_view->setPage(m_oldView->page()); - setGeometry(m_oldGeometry); - showFullScreen(); - m_oldView->window()->hide(); -} - -FullScreenWindow::~FullScreenWindow() -{ - m_oldView->setPage(m_view->page()); - m_oldView->window()->setGeometry(m_oldGeometry); - m_oldView->window()->show(); - hide(); -} - -void FullScreenWindow::resizeEvent(QResizeEvent *event) -{ - QRect viewGeometry(QPoint(0, 0), size()); - m_view->setGeometry(viewGeometry); - - QRect notificationGeometry(QPoint(0, 0), m_notification->sizeHint()); - notificationGeometry.moveCenter(viewGeometry.center()); - m_notification->setGeometry(notificationGeometry); - - QWidget::resizeEvent(event); -} +class QMenu; + +#include "fullscreenwindow.h" +#include + +FullScreenWindow::FullScreenWindow(QWebEngineView *oldView, QWidget *parent) + : QWidget(parent) + , m_view(new QWebEngineView(this)) + , m_notification(new FullScreenNotification(this)) + , m_oldView(oldView) + , m_oldGeometry(oldView->window()->geometry()) +{ + m_view->stackUnder(m_notification); + + auto exitAction = new QAction(this); + exitAction->setShortcut(Qt::Key_Escape); + connect(exitAction, &QAction::triggered, [this]() { + m_view->triggerPageAction(QWebEnginePage::ExitFullScreen); + }); + addAction(exitAction); + + m_view->setPage(m_oldView->page()); + setGeometry(m_oldGeometry); + showFullScreen(); + m_oldView->window()->hide(); +} + +FullScreenWindow::~FullScreenWindow() +{ + m_oldView->setPage(m_view->page()); + m_oldView->window()->setGeometry(m_oldGeometry); + m_oldView->window()->show(); + hide(); +} + +void FullScreenWindow::resizeEvent(QResizeEvent *event) +{ + QRect viewGeometry(QPoint(0, 0), size()); + m_view->setGeometry(viewGeometry); + + QRect notificationGeometry(QPoint(0, 0), m_notification->sizeHint()); + notificationGeometry.moveCenter(viewGeometry.center()); + m_notification->setGeometry(notificationGeometry); + + QWidget::resizeEvent(event); +} diff --git a/src/fullscreenwindow.h b/src/fullscreenwindow.h index 1d6d016ec..b8caa7fa5 100644 --- a/src/fullscreenwindow.h +++ b/src/fullscreenwindow.h @@ -1,32 +1,32 @@ -#ifndef FULLSCREENWINDOW_H -#define FULLSCREENWINDOW_H - -class QMenu; -#include -#include -#include "fullscreennotification.h" - -QT_BEGIN_NAMESPACE -class QWebEngineView; -QT_END_NAMESPACE - -class FullScreenNotification; - -class FullScreenWindow : public QWidget -{ - Q_OBJECT -public: - explicit FullScreenWindow(QWebEngineView *oldView, QWidget *parent = nullptr); - ~FullScreenWindow(); - -protected: - void resizeEvent(QResizeEvent *event) override; - -private: - QWebEngineView *m_view; - FullScreenNotification *m_notification; - QWebEngineView *m_oldView; - QRect m_oldGeometry; -}; - -#endif // FULLSCREENWINDOW_H +#ifndef FULLSCREENWINDOW_H +#define FULLSCREENWINDOW_H + +class QMenu; +#include +#include +#include "fullscreennotification.h" + +QT_BEGIN_NAMESPACE +class QWebEngineView; +QT_END_NAMESPACE + +class FullScreenNotification; + +class FullScreenWindow : public QWidget +{ + Q_OBJECT +public: + explicit FullScreenWindow(QWebEngineView *oldView, QWidget *parent = nullptr); + ~FullScreenWindow(); + +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + QWebEngineView *m_view; + FullScreenNotification *m_notification; + QWebEngineView *m_oldView; + QRect m_oldGeometry; +}; + +#endif // FULLSCREENWINDOW_H diff --git a/src/kconstants.h b/src/kconstants.h index 8aa1ac8f8..eeac57ee7 100644 --- a/src/kconstants.h +++ b/src/kconstants.h @@ -1,6 +1,6 @@ -#ifndef KCONSTANTS_H -#define KCONSTANTS_H - -#define SYSTEMTITLEBAR 1 - -#endif // KCONSTANTS_H +#ifndef KCONSTANTS_H +#define KCONSTANTS_H + +#define SYSTEMTITLEBAR 1 + +#endif // KCONSTANTS_H diff --git a/src/kiwixapp.cpp b/src/kiwixapp.cpp index 81814b1c9..3f418c81c 100644 --- a/src/kiwixapp.cpp +++ b/src/kiwixapp.cpp @@ -1,603 +1,603 @@ -#include "kiwixapp.h" -#include "zim/error.h" -#include "zim/version.h" -#include "kiwix/tools.h" -#include "kiwix/version.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(Q_OS_WIN) -#include -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include -#else -#include -#endif -#endif - -const QString DEFAULT_SAVE_DIR = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - -//////////////////////////////////////////////////////////////////////////////// -// KiwixApp -//////////////////////////////////////////////////////////////////////////////// - -KiwixApp::KiwixApp(int& argc, char *argv[]) - : QtSingleApplication("kiwix-desktop", argc, argv), - m_profile(), - m_libraryDirectory(findLibraryDirectory()), - m_library(m_libraryDirectory), - mp_manager(nullptr), - mp_mainWindow(nullptr), - mp_nameMapper(std::make_shared(m_library.getKiwixLibrary(), false)), - m_server(m_library.getKiwixLibrary(), mp_nameMapper), - mp_session(nullptr) -{ - /* Place session file in our global library path */ - QDir dir(m_libraryDirectory); - mp_session = new QSettings(dir.filePath("kiwix-desktop.session"), - QSettings::IniFormat, this); - try { - m_translation.setTranslation(QLocale()); - } catch (std::exception& e) { - QMessageBox::critical(nullptr, "Translation error", e.what()); - return; - } - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QString path = QLibraryInfo::location(QLibraryInfo::TranslationsPath); -#else - QString path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); -#endif - loadAndInstallTranslations(m_qtTranslator, "qt", path); - loadAndInstallTranslations(m_appTranslator, "kiwix-desktop", ":/i18n/"); - - QFontDatabase::addApplicationFont(":/fonts/Selawik/selawkb.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Selawik/selawkl.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Selawik/selawksb.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Selawik/selawksl.ttf"); - QFontDatabase::addApplicationFont(":/fonts/Selawik/selawk.ttf"); - setFont(QFont("Selawik")); -} - -void KiwixApp::loadAndInstallTranslations(QTranslator& translator, const QString& filename, const QString& directory) { - if (translator.load(QLocale(), filename, "_", directory)) { - installTranslator(&translator); - } -} - -void KiwixApp::init() -{ - mp_manager = new ContentManager(&m_library); - mp_manager->setLocal(!m_library.getBookIds().isEmpty()); - - auto icon = QIcon(); - icon.addFile(":/icons/kiwix-app-icons-square.svg"); - setWindowIcon(icon); - - setApplicationName("Kiwix"); - setDesktopFileName("kiwix.desktop"); - setStyleSheet(parseStyleFromFile(":/css/style.css")); - - createActions(); - mp_mainWindow = new MainWindow; - getTabWidget()->setContentManagerView(mp_manager->getView()); - const auto newTabAction = getAction(KiwixApp::NewTabAction); - getTabWidget()->setNewTabButton(newTabAction); - connect(newTabAction, &QAction::triggered, this, &KiwixApp::newTab); - postInit(); - mp_errorDialog = new QErrorMessage(mp_mainWindow); - setActivationWindow(mp_mainWindow); - mp_mainWindow->show(); -#ifdef Q_OS_WIN - QWindow *window = mp_mainWindow->windowHandle(); - if (window) { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - QWindowsWindowFunctions::setHasBorderInFullScreen(window, true); -#else - auto nativeWindow = window->nativeInterface(); - if (nativeWindow) { - nativeWindow->setHasBorderInFullScreen(true); - } -#endif - } -#endif - connect(this, &QtSingleApplication::messageReceived, this, [=](const QString &message) { - if (!message.isEmpty()) { - this->openZimFile(message); - } - }); - - restoreWindowState(); -} - -void KiwixApp::setupDirectoryMonitoring() -{ - QString monitorDir = m_settingsManager.getMonitorDir(); - QString downloadDir = m_settingsManager.getDownloadDir(); - auto dirList = QSet({monitorDir, downloadDir}); - mp_manager->setMonitoredDirectories(dirList); -} - -KiwixApp::~KiwixApp() -{ - m_server.stop(); - if (mp_manager) { - delete mp_manager; - } - if (mp_mainWindow) { - delete mp_mainWindow; - } -} - -void KiwixApp::newTab() -{ - getTabWidget()->createNewTab(true, false); - auto& searchBarLineEdit = getSearchBar().getLineEdit(); - searchBarLineEdit.setFocus(Qt::MouseFocusReason); - searchBarLineEdit.clear(); - searchBarLineEdit.clearSuggestions(); - searchBarLineEdit.hideSuggestions(); -} - -QString KiwixApp::findLibraryDirectory() -{ - auto dataDir = getDataDirectory(); - if (isPortableMode()) - return dataDir; - - auto libraryFile = QFileInfo(dataDir, "library.xml"); - if (libraryFile.exists()) - return dataDir; - - // Check if this is a pre-release version with wrong directory. - auto oldDataDir = QDir(dataDir); - oldDataDir.cdUp(); - libraryFile = QFileInfo(oldDataDir, "library.xml"); - if (libraryFile.exists()) { - // We have to move all zim files and xml file to the new dataDir - for (auto& fileInfo: oldDataDir.entryInfoList({"*.zim", "library*.xml"})) { - auto newFileInfo = QFileInfo(dataDir, fileInfo.fileName()); - QFile::rename(fileInfo.absoluteFilePath(), newFileInfo.absoluteFilePath()); - } - // Aria2 stores information about the current download using absolute path. - // Let's remove everything. User will lose its ongoing download but it should be pretty rare. - for (auto& fileInfo: oldDataDir.entryInfoList({"*.aria2", "*.meta4", "kiwix.session"})) - QFile::remove(fileInfo.absoluteFilePath()); - } - - // This is a first launch - return dataDir; -} - -void KiwixApp::restoreTabs() -{ - QStringList tabsToOpen = mp_session->value("reopenTabList").toStringList(); - - /* Restart a new session to prevent duplicate records in openURL */ - saveListOfOpenTabs(); - if (m_settingsManager.getReopenTab()) - { - for (const auto &zimUrl : tabsToOpen) - { - if (zimUrl == "SettingsTab") - getTabWidget()->openOrSwitchToSettingsTab(); - else if (zimUrl.isEmpty()) - getTabWidget()->createNewTab(true, true); - else - openUrl(QUrl(zimUrl)); - } - } - - /* Restore current tab index. */ - getTabWidget()->setCurrentIndex(mp_session->value("currentTabIndex", 0).toInt()); -} - -KiwixApp *KiwixApp::instance() -{ - return static_cast(QApplication::instance()); -} - -QString gt(const QString &key) { - return KiwixApp::instance()->getText(key); -} - -void KiwixApp::openZimFile(const QString &zimfile) -{ - QString _zimfile; - if (zimfile.isEmpty()) { - QString importDir = mp_session->value("zim-import-dir").toString(); - if (importDir.isEmpty()) { - importDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - if (importDir.isEmpty()) { importDir = QDir::currentPath(); } - } - _zimfile = QFileDialog::getOpenFileName(getMainWindow(), gt("open-zim"), importDir, "ZIM Files (*.zim);;Split ZIM Files (*.zimaa)"); - - if (_zimfile.isEmpty()) { return; } - _zimfile = QDir::toNativeSeparators(_zimfile); - QFileInfo fileInfo(_zimfile); - mp_session->setValue("zim-import-dir", fileInfo.absolutePath()); - } - QString zimId; - const auto &validZimFile = zimfile.isEmpty() ? _zimfile : zimfile; - try { - zimId = m_library.openBookFromPath(validZimFile); - } catch (const std::exception& e) { - auto text = gt("zim-open-fail-text"); - text = text.replace("{{ZIM}}", validZimFile); - showMessage(text, gt("zim-open-fail-title"), QMessageBox::Warning); - return; - } - openUrl(QUrl("zim://"+zimId+".zim/")); -} - -void KiwixApp::printPage() -{ - if(!getTabWidget()->currentZimView()) - return; - - QPrinter* printer = new QPrinter(); - QPrintDialog printDialog(printer, mp_mainWindow); - printDialog.setStyle(nullptr); - printDialog.setStyleSheet(""); - if (printDialog.exec() == QDialog::Accepted) { - auto webview = getTabWidget()->currentWebView(); - if(!webview) - return; - - const auto onPrintFinished = [=](bool success) { - if (!success) { - showMessage(gt("print-page-error"), gt("error-title"), QMessageBox::Critical); - } - delete printer; - }; -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - webview->page()->print(printer, onPrintFinished); -#else - webview->print(printer); - connect(webview, &QWebEngineView::printFinished, this, onPrintFinished); -#endif - } -} - -void KiwixApp::openUrl(const QString &url, bool newTab) { - openUrl(QUrl(url), newTab); -} - -void KiwixApp::openUrl(const QUrl &url, bool newTab) { - getTabWidget()->openUrl(url, newTab); -} - -void KiwixApp::openRandomUrl(bool newTab) -{ - auto zimId = getTabWidget()->currentZimId(); - if (zimId.isEmpty()) { - return; - } - - try { - auto archive = m_library.getArchive(zimId); - auto entry = archive->getRandomEntry(); - - QUrl url; - url.setScheme("zim"); - url.setHost(zimId + ".zim"); - url.setPath("/" + QString::fromStdString(entry.getPath())); - openUrl(url, newTab); - } catch (const zim::EntryNotFound& e) { - showMessage(gt("random-article-error"), gt("error-title"), QMessageBox::Information); - } catch (std::out_of_range& e) { - showMessage(gt("error-archive"), gt("error-title"), QMessageBox::Information); - } -} - -void KiwixApp::showMessage(const QString &message, const QString &title, const enum QMessageBox::Icon &icon) -{ - QMessageBox msgBox( - icon, //Icon - title, //Title - message, //Text - QMessageBox::Ok //Buttons - ); - msgBox.exec(); -} - -QAction *KiwixApp::getAction(KiwixApp::Actions action) -{ - return mpa_actions[action]; -} - -bool KiwixApp::isCurrentArticleBookmarked() -{ - auto zimId = getTabWidget()->currentZimId().toStdString(); - if (zimId.empty()) { - return false; - } - auto url = getTabWidget()->currentArticleUrl().toStdString(); - - for (auto& bookmark: getLibrary()->getBookmarks()) { - if (bookmark.getBookId() == zimId && bookmark.getUrl() == url) { - return true; - } - } - return false; -} - -void KiwixApp::setMonitorDir(const QString &dir) { - m_settingsManager.setMonitorDir(dir); - setupDirectoryMonitoring(); -} - -#define CREATE_ACTION(ID, TEXT) \ - mpa_actions[ID] = new QAction(TEXT) -#define CREATE_ACTION_ICON(ID, ICON, TEXT) \ - mpa_actions[ID] = new QAction(QIcon(":/icons/" ICON ".svg"), TEXT); -#define SET_SHORTCUT(ID, TEXT, SHORTCUT) \ - mpa_actions[ID]->setShortcut(SHORTCUT); \ - mpa_actions[ID]->setToolTip(TEXT + " (" + QKeySequence(SHORTCUT).toString() + ")" ) -#define CREATE_ACTION_SHORTCUT(ID, TEXT, SHORTCUT) \ - CREATE_ACTION(ID, TEXT); \ - SET_SHORTCUT(ID, TEXT, SHORTCUT) -#define CREATE_ACTION_ICON_SHORTCUT(ID, ICON, TEXT, SHORTCUT) \ - CREATE_ACTION_ICON(ID, ICON, TEXT) \ - SET_SHORTCUT(ID, TEXT, SHORTCUT) -#define CREATE_ACTION_ONOFF_ICON_SHORTCUT(ID, ON_ICON, OFF_ICON, TEXT, SHORTCUT) \ - CREATE_ACTION(ID, TEXT); \ - SET_SHORTCUT(ID, TEXT, SHORTCUT); \ - mpa_actions[ID]->setCheckable(true); \ - { QIcon icon; \ - icon.addPixmap(QPixmap(":/icons/" ON_ICON ".svg"), QIcon::Normal, QIcon::On); \ - icon.addPixmap(QPixmap(":/icons/" OFF_ICON, ".svg"), QIcon::Normal, QIcon::Off); \ - mpa_actions[ID]->setIcon(icon); } -#define SET_SHORTCUTS(ID, TEXT, SHORTCUTS) \ - mpa_actions[ID]->setShortcuts(SHORTCUTS); \ - mpa_actions[ID]->setToolTip(TEXT + " (" + SHORTCUTS.first().toString() + ")" ) -#define CREATE_ACTION_SHORTCUTS(ID, TEXT, SHORTCUTS) \ - CREATE_ACTION(ID, TEXT); \ - SET_SHORTCUTS(ID, TEXT, SHORTCUTS) -#define CREATE_ACTION_ICON_SHORTCUTS(ID, ICON, TEXT, SHORTCUTS) \ - CREATE_ACTION_ICON(ID, ICON, TEXT) \ - SET_SHORTCUTS(ID, TEXT, SHORTCUTS) -#define HIDE_ACTION(ID) mpa_actions[ID]->setVisible(false) -#define DISABLE_ACTION(ID) mpa_actions[ID]->setDisabled(true) - -void KiwixApp::createActions() -{ - CREATE_ACTION_ICON_SHORTCUT(KiwixServeAction, "share", gt("local-kiwix-server"), QKeySequence(Qt::CTRL | Qt::Key_I)); - - CREATE_ACTION_ICON_SHORTCUT(RandomArticleAction, "random", gt("random-article"), QKeySequence(Qt::CTRL | Qt::Key_R)); - connect(mpa_actions[RandomArticleAction], &QAction::triggered, - this, [=]() { this->openRandomUrl(false); }); - - CREATE_ACTION_ICON_SHORTCUT(OpenHomePageAction, "home-button", gt("home-page"), QKeySequence(Qt::ALT | Qt::Key_Home)); - - if (QGuiApplication::isLeftToRight()) { - CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-left", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Left)); - CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-right", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Right)); - CREATE_ACTION_ICON(ScrollNextTabAction, "caret-right-solid", gt("scroll-next-tab")); - CREATE_ACTION_ICON(ScrollPreviousTabAction, "caret-left-solid", gt("scroll-previous-tab")); - CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_Tab)); - CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageDown )); - CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab)); - CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageUp )); - } else { - CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-right", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Right)); - CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-left", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Left)); - CREATE_ACTION_ICON(ScrollNextTabAction, "caret-left-solid", gt("scroll-next-tab")); - CREATE_ACTION_ICON(ScrollPreviousTabAction, "caret-right-solid", gt("scroll-previous-tab")); - CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab)); - CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageUp)); - CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::Key_Tab)); - CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageDown )); - } - DISABLE_ACTION(HistoryBackAction); - DISABLE_ACTION(HistoryForwardAction); - - CREATE_ACTION_SHORTCUT(ReadArticleAction, gt("read-article"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_A)); - CREATE_ACTION_SHORTCUT(ReadTextAction, gt("read-text"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_S)); - CREATE_ACTION_SHORTCUT(ReadStopAction, gt("read-stop"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_X)); - CREATE_ACTION_SHORTCUT(ToggleTTSLanguageAction, gt("select-read-language"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_L)); - CREATE_ACTION_SHORTCUT(ToggleTTSVoiceAction, gt("select-read-voice"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_V)); - CREATE_ACTION_SHORTCUT(IncreaseTTSSpeedAction, gt("increase-tts-speed"), QKeySequence(Qt::Key_Greater)); - CREATE_ACTION_SHORTCUT(DecreaseTTSSpeedAction, gt("decrease-tts-speed"), QKeySequence(Qt::Key_Less)); - mpa_actions[ToggleTTSLanguageAction]->setCheckable(true); - mpa_actions[ToggleTTSVoiceAction]->setCheckable(true); - - CREATE_ACTION_ICON_SHORTCUT(PrintAction, "print", gt("print"), QKeySequence::Print); - connect(mpa_actions[PrintAction], &QAction::triggered, - this, &KiwixApp::printPage); - - CREATE_ACTION_ICON_SHORTCUT(NewTabAction,"new-tab-icon", gt("new-tab"), QKeySequence::AddTab); - - CREATE_ACTION_SHORTCUTS(CloseCurrentTabAction, gt("close-tab"), QList({QKeySequence(Qt::CTRL | Qt::Key_F4), QKeySequence(Qt::CTRL | Qt::Key_W)})); - - CREATE_ACTION_SHORTCUT(ReopenClosedTabAction, gt("reopen-closed-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_T)); - HIDE_ACTION(ReopenClosedTabAction); - - CREATE_ACTION_SHORTCUT(BrowseLibraryAction, gt("browse-library"), QKeySequence(Qt::CTRL | Qt::Key_E)); - HIDE_ACTION(BrowseLibraryAction); - - CREATE_ACTION_ICON_SHORTCUT(OpenFileAction, "open-file", gt("open-file"), QKeySequence::Open); - connect(mpa_actions[OpenFileAction], &QAction::triggered, - this, [=]() { openZimFile(); }); - - CREATE_ACTION(OpenRecentAction, gt("open-recent")); - HIDE_ACTION(OpenRecentAction); - - CREATE_ACTION_SHORTCUT(SavePageAsAction, gt("save-page-as"), QKeySequence::Save); - - CREATE_ACTION_SHORTCUTS(SearchArticleAction, gt("search-article"), QList({QKeySequence(Qt::Key_F6), QKeySequence(Qt::CTRL | Qt::Key_L), QKeySequence(Qt::ALT | Qt::Key_D)})); - - CREATE_ACTION_SHORTCUT(SearchLibraryAction, gt("search-in-library"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_R)); - HIDE_ACTION(SearchLibraryAction); - - CREATE_ACTION(FindInPageAction, gt("find-in-page")); - mpa_actions[FindInPageAction]->setShortcuts({QKeySequence::Find, Qt::Key_F3}); - connect(mpa_actions[FindInPageAction], &QAction::triggered, - this, [=]() { getTabWidget()->openFindInPageBar(); }); - - const auto fullScreenKeySeq = QKeySequence(QKeySequence::FullScreen).isEmpty() - ? QKeySequence(Qt::Key_F11) - : QKeySequence(QKeySequence::FullScreen); - CREATE_ACTION_ICON_SHORTCUT(ToggleFullscreenAction, "full-screen-enter", gt("set-fullscreen"), fullScreenKeySeq); - connect(mpa_actions[ToggleFullscreenAction], &QAction::toggled, - this, [=](bool checked) { - auto action = mpa_actions[ToggleFullscreenAction]; - action->setIcon( - QIcon(checked ? ":/icons/full-screen-exit.svg" : ":/icons/full-screen-enter.svg")); - action->setText(checked ? gt("quit-fullscreen") : gt("set-fullscreen")); - }); - mpa_actions[ToggleFullscreenAction]->setCheckable(true); - - CREATE_ACTION_ICON_SHORTCUT(ToggleTOCAction, "toc", gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::Key_M)); - mpa_actions[ToggleTOCAction]->setCheckable(true); - - CREATE_ACTION_ICON_SHORTCUT(OpenMultiZimAction, "filter", gt("search-options"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L)); - - CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL | Qt::Key_B)); - - CREATE_ACTION(ExportReadingListAction, gt("export-reading-list")); - - CREATE_ACTION(ImportReadingListAction, gt("import-reading-list")); - - CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleAddBookmarkAction, "star-active", "star", gt("add-bookmark"), QKeySequence(Qt::CTRL | Qt::Key_D)); - - CREATE_ACTION_SHORTCUTS(ZoomInAction, gt("zoom-in"), QList({QKeySequence::ZoomIn, QKeySequence(Qt::CTRL | Qt::Key_Equal)})); - - CREATE_ACTION_SHORTCUT(ZoomOutAction, gt("zoom-out"), QKeySequence::ZoomOut); - - CREATE_ACTION_SHORTCUT(ZoomResetAction, gt("zoom-reset"), QKeySequence(Qt::CTRL | Qt::Key_0)); - - CREATE_ACTION_SHORTCUT(HelpAction, gt("help"), QKeySequence::HelpContents); - HIDE_ACTION(HelpAction); - - CREATE_ACTION(FeedbackAction, gt("feedback")); - HIDE_ACTION(FeedbackAction); - - CREATE_ACTION(ReportBugAction, gt("report-a-bug")); - HIDE_ACTION(ReportBugAction); - - CREATE_ACTION(RequestFeatureAction, gt("request-a-feature")); - HIDE_ACTION(RequestFeatureAction); - - CREATE_ACTION(AboutAction, gt("about-kiwix")); - - CREATE_ACTION_ICON_SHORTCUT(SettingAction, "settings", gt("settings"), QKeySequence(Qt::Key_F12)); - - CREATE_ACTION_ICON_SHORTCUT(DonateAction, "donate", gt("donate-to-support-kiwix"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D)); - - CREATE_ACTION_ICON_SHORTCUT(ExitAction, "exit", gt("exit"), QKeySequence::Quit); -} - -void KiwixApp::postInit() { - connect(getTabWidget(), &TabBar::tabDisplayed, - this, &KiwixApp::handleItemsState); - emit(m_library.booksChanged()); - connect(&m_library, &Library::booksChanged, this, &KiwixApp::updateNameMapper); - handleItemsState(TabType::LibraryTab); -} - -void KiwixApp::handleItemsState(TabType tabType) -{ - auto libraryOrSettingsTab = (tabType == TabType::LibraryTab || tabType == TabType::SettingsTab); - auto notBookmarkableTab = libraryOrSettingsTab || getTabWidget()->currentArticleUrl().isEmpty(); - auto app = KiwixApp::instance(); - app->getAction(KiwixApp::ToggleTOCAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::ToggleReadingListAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::ToggleAddBookmarkAction)->setDisabled(notBookmarkableTab); - app->getAction(KiwixApp::FindInPageAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::ZoomInAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::ZoomOutAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::ZoomResetAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::RandomArticleAction)->setDisabled(libraryOrSettingsTab); - app->getAction(KiwixApp::OpenHomePageAction)->setDisabled(libraryOrSettingsTab); - - /* Non-Zim tabs are not bookmarkable therefore never in reading list. */ - if (notBookmarkableTab) - app->getAction(KiwixApp::ToggleAddBookmarkAction)->setChecked(false); -} - -void KiwixApp::updateNameMapper() -{ - mp_nameMapper->update(); -} - -void KiwixApp::printVersions(std::ostream& out) { - out << version.toStdString() << std::endl; - out << "+ libqt (compile time) " << QT_VERSION_STR << std::endl; - out << "+ libqt (run time) " << qVersion() << std::endl << std::endl; - kiwix::printVersions(out); - out << std::endl; - zim::printVersions(out); -} - -QString KiwixApp::parseStyleFromFile(QString filePath) -{ - QFile file(filePath); - file.open(QFile::ReadOnly); - QString styleSheet = QString(file.readAll()); - file.close(); - return styleSheet; -} - -void KiwixApp::saveListOfOpenTabs() -{ - return mp_session->setValue("reopenTabList", getTabWidget()->getTabUrls()); -} - -void KiwixApp::saveWindowState() -{ - mp_session->setValue("geometry", getMainWindow()->saveGeometry()); - mp_session->setValue("windowState", getMainWindow()->saveState()); -} - -void KiwixApp::saveVoiceName(const QString& langName, const QString& voiceName) -{ - mp_session->setValue("voice/" + langName, voiceName); -} - -void KiwixApp::saveTtsSpeed(const QString& langName, double speed) -{ - mp_session->setValue("speed/" + langName, speed); -} - -void KiwixApp::restoreWindowState() -{ - getMainWindow()->restoreGeometry(mp_session->value("geometry").toByteArray()); - getMainWindow()->restoreState(mp_session->value("windowState").toByteArray()); -} - -void KiwixApp::saveCurrentTabIndex() -{ - return mp_session->setValue("currentTabIndex", getTabWidget()->currentIndex()); -} - -void KiwixApp::savePrevSaveDir(const QString &prevSaveDir) -{ - mp_session->setValue("prevSaveDir", prevSaveDir); -} - -QString KiwixApp::getSavedVoiceName(const QString& langName) const -{ - return mp_session->value("voice/" + langName, "").toString(); -} - -double KiwixApp::getSavedTtsSpeed(const QString& langName) const -{ - return mp_session->value("speed/" + langName, 1.0).toDouble(); // Default: 1.0 (normal speed) -} - -QString KiwixApp::getPrevSaveDir() const -{ - QString prevSaveDir = mp_session->value("prevSaveDir", DEFAULT_SAVE_DIR).toString(); - QDir dir(prevSaveDir); - return dir.exists() ? prevSaveDir : DEFAULT_SAVE_DIR; -} +#include "kiwixapp.h" +#include "zim/error.h" +#include "zim/version.h" +#include "kiwix/tools.h" +#include "kiwix/version.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(Q_OS_WIN) +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif +#endif + +const QString DEFAULT_SAVE_DIR = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + +//////////////////////////////////////////////////////////////////////////////// +// KiwixApp +//////////////////////////////////////////////////////////////////////////////// + +KiwixApp::KiwixApp(int& argc, char *argv[]) + : QtSingleApplication("kiwix-desktop", argc, argv), + m_profile(), + m_libraryDirectory(findLibraryDirectory()), + m_library(m_libraryDirectory), + mp_manager(nullptr), + mp_mainWindow(nullptr), + mp_nameMapper(std::make_shared(m_library.getKiwixLibrary(), false)), + m_server(m_library.getKiwixLibrary(), mp_nameMapper), + mp_session(nullptr) +{ + /* Place session file in our global library path */ + QDir dir(m_libraryDirectory); + mp_session = new QSettings(dir.filePath("kiwix-desktop.session"), + QSettings::IniFormat, this); + try { + m_translation.setTranslation(QLocale()); + } catch (std::exception& e) { + QMessageBox::critical(nullptr, "Translation error", e.what()); + return; + } + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QString path = QLibraryInfo::location(QLibraryInfo::TranslationsPath); +#else + QString path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); +#endif + loadAndInstallTranslations(m_qtTranslator, "qt", path); + loadAndInstallTranslations(m_appTranslator, "kiwix-desktop", ":/i18n/"); + + QFontDatabase::addApplicationFont(":/fonts/Selawik/selawkb.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Selawik/selawkl.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Selawik/selawksb.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Selawik/selawksl.ttf"); + QFontDatabase::addApplicationFont(":/fonts/Selawik/selawk.ttf"); + setFont(QFont("Selawik")); +} + +void KiwixApp::loadAndInstallTranslations(QTranslator& translator, const QString& filename, const QString& directory) { + if (translator.load(QLocale(), filename, "_", directory)) { + installTranslator(&translator); + } +} + +void KiwixApp::init() +{ + mp_manager = new ContentManager(&m_library); + mp_manager->setLocal(!m_library.getBookIds().isEmpty()); + + auto icon = QIcon(); + icon.addFile(":/icons/kiwix-app-icons-square.svg"); + setWindowIcon(icon); + + setApplicationName("Kiwix"); + setDesktopFileName("kiwix.desktop"); + setStyleSheet(parseStyleFromFile(":/css/style.css")); + + createActions(); + mp_mainWindow = new MainWindow; + getTabWidget()->setContentManagerView(mp_manager->getView()); + const auto newTabAction = getAction(KiwixApp::NewTabAction); + getTabWidget()->setNewTabButton(newTabAction); + connect(newTabAction, &QAction::triggered, this, &KiwixApp::newTab); + postInit(); + mp_errorDialog = new QErrorMessage(mp_mainWindow); + setActivationWindow(mp_mainWindow); + mp_mainWindow->show(); +#ifdef Q_OS_WIN + QWindow *window = mp_mainWindow->windowHandle(); + if (window) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QWindowsWindowFunctions::setHasBorderInFullScreen(window, true); +#else + auto nativeWindow = window->nativeInterface(); + if (nativeWindow) { + nativeWindow->setHasBorderInFullScreen(true); + } +#endif + } +#endif + connect(this, &QtSingleApplication::messageReceived, this, [=](const QString &message) { + if (!message.isEmpty()) { + this->openZimFile(message); + } + }); + + restoreWindowState(); +} + +void KiwixApp::setupDirectoryMonitoring() +{ + QString monitorDir = m_settingsManager.getMonitorDir(); + QString downloadDir = m_settingsManager.getDownloadDir(); + auto dirList = QSet({monitorDir, downloadDir}); + mp_manager->setMonitoredDirectories(dirList); +} + +KiwixApp::~KiwixApp() +{ + m_server.stop(); + if (mp_manager) { + delete mp_manager; + } + if (mp_mainWindow) { + delete mp_mainWindow; + } +} + +void KiwixApp::newTab() +{ + getTabWidget()->createNewTab(true, false); + auto& searchBarLineEdit = getSearchBar().getLineEdit(); + searchBarLineEdit.setFocus(Qt::MouseFocusReason); + searchBarLineEdit.clear(); + searchBarLineEdit.clearSuggestions(); + searchBarLineEdit.hideSuggestions(); +} + +QString KiwixApp::findLibraryDirectory() +{ + auto dataDir = getDataDirectory(); + if (isPortableMode()) + return dataDir; + + auto libraryFile = QFileInfo(dataDir, "library.xml"); + if (libraryFile.exists()) + return dataDir; + + // Check if this is a pre-release version with wrong directory. + auto oldDataDir = QDir(dataDir); + oldDataDir.cdUp(); + libraryFile = QFileInfo(oldDataDir, "library.xml"); + if (libraryFile.exists()) { + // We have to move all zim files and xml file to the new dataDir + for (auto& fileInfo: oldDataDir.entryInfoList({"*.zim", "library*.xml"})) { + auto newFileInfo = QFileInfo(dataDir, fileInfo.fileName()); + QFile::rename(fileInfo.absoluteFilePath(), newFileInfo.absoluteFilePath()); + } + // Aria2 stores information about the current download using absolute path. + // Let's remove everything. User will lose its ongoing download but it should be pretty rare. + for (auto& fileInfo: oldDataDir.entryInfoList({"*.aria2", "*.meta4", "kiwix.session"})) + QFile::remove(fileInfo.absoluteFilePath()); + } + + // This is a first launch + return dataDir; +} + +void KiwixApp::restoreTabs() +{ + QStringList tabsToOpen = mp_session->value("reopenTabList").toStringList(); + + /* Restart a new session to prevent duplicate records in openURL */ + saveListOfOpenTabs(); + if (m_settingsManager.getReopenTab()) + { + for (const auto &zimUrl : tabsToOpen) + { + if (zimUrl == "SettingsTab") + getTabWidget()->openOrSwitchToSettingsTab(); + else if (zimUrl.isEmpty()) + getTabWidget()->createNewTab(true, true); + else + openUrl(QUrl(zimUrl)); + } + } + + /* Restore current tab index. */ + getTabWidget()->setCurrentIndex(mp_session->value("currentTabIndex", 0).toInt()); +} + +KiwixApp *KiwixApp::instance() +{ + return static_cast(QApplication::instance()); +} + +QString gt(const QString &key) { + return KiwixApp::instance()->getText(key); +} + +void KiwixApp::openZimFile(const QString &zimfile) +{ + QString _zimfile; + if (zimfile.isEmpty()) { + QString importDir = mp_session->value("zim-import-dir").toString(); + if (importDir.isEmpty()) { + importDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + if (importDir.isEmpty()) { importDir = QDir::currentPath(); } + } + _zimfile = QFileDialog::getOpenFileName(getMainWindow(), gt("open-zim"), importDir, "ZIM Files (*.zim);;Split ZIM Files (*.zimaa)"); + + if (_zimfile.isEmpty()) { return; } + _zimfile = QDir::toNativeSeparators(_zimfile); + QFileInfo fileInfo(_zimfile); + mp_session->setValue("zim-import-dir", fileInfo.absolutePath()); + } + QString zimId; + const auto &validZimFile = zimfile.isEmpty() ? _zimfile : zimfile; + try { + zimId = m_library.openBookFromPath(validZimFile); + } catch (const std::exception& e) { + auto text = gt("zim-open-fail-text"); + text = text.replace("{{ZIM}}", validZimFile); + showMessage(text, gt("zim-open-fail-title"), QMessageBox::Warning); + return; + } + openUrl(QUrl("zim://"+zimId+".zim/")); +} + +void KiwixApp::printPage() +{ + if(!getTabWidget()->currentZimView()) + return; + + QPrinter* printer = new QPrinter(); + QPrintDialog printDialog(printer, mp_mainWindow); + printDialog.setStyle(nullptr); + printDialog.setStyleSheet(""); + if (printDialog.exec() == QDialog::Accepted) { + auto webview = getTabWidget()->currentWebView(); + if(!webview) + return; + + const auto onPrintFinished = [=](bool success) { + if (!success) { + showMessage(gt("print-page-error"), gt("error-title"), QMessageBox::Critical); + } + delete printer; + }; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + webview->page()->print(printer, onPrintFinished); +#else + webview->print(printer); + connect(webview, &QWebEngineView::printFinished, this, onPrintFinished); +#endif + } +} + +void KiwixApp::openUrl(const QString &url, bool newTab) { + openUrl(QUrl(url), newTab); +} + +void KiwixApp::openUrl(const QUrl &url, bool newTab) { + getTabWidget()->openUrl(url, newTab); +} + +void KiwixApp::openRandomUrl(bool newTab) +{ + auto zimId = getTabWidget()->currentZimId(); + if (zimId.isEmpty()) { + return; + } + + try { + auto archive = m_library.getArchive(zimId); + auto entry = archive->getRandomEntry(); + + QUrl url; + url.setScheme("zim"); + url.setHost(zimId + ".zim"); + url.setPath("/" + QString::fromStdString(entry.getPath())); + openUrl(url, newTab); + } catch (const zim::EntryNotFound& e) { + showMessage(gt("random-article-error"), gt("error-title"), QMessageBox::Information); + } catch (std::out_of_range& e) { + showMessage(gt("error-archive"), gt("error-title"), QMessageBox::Information); + } +} + +void KiwixApp::showMessage(const QString &message, const QString &title, const enum QMessageBox::Icon &icon) +{ + QMessageBox msgBox( + icon, //Icon + title, //Title + message, //Text + QMessageBox::Ok //Buttons + ); + msgBox.exec(); +} + +QAction *KiwixApp::getAction(KiwixApp::Actions action) +{ + return mpa_actions[action]; +} + +bool KiwixApp::isCurrentArticleBookmarked() +{ + auto zimId = getTabWidget()->currentZimId().toStdString(); + if (zimId.empty()) { + return false; + } + auto url = getTabWidget()->currentArticleUrl().toStdString(); + + for (auto& bookmark: getLibrary()->getBookmarks()) { + if (bookmark.getBookId() == zimId && bookmark.getUrl() == url) { + return true; + } + } + return false; +} + +void KiwixApp::setMonitorDir(const QString &dir) { + m_settingsManager.setMonitorDir(dir); + setupDirectoryMonitoring(); +} + +#define CREATE_ACTION(ID, TEXT) \ + mpa_actions[ID] = new QAction(TEXT) +#define CREATE_ACTION_ICON(ID, ICON, TEXT) \ + mpa_actions[ID] = new QAction(QIcon(":/icons/" ICON ".svg"), TEXT); +#define SET_SHORTCUT(ID, TEXT, SHORTCUT) \ + mpa_actions[ID]->setShortcut(SHORTCUT); \ + mpa_actions[ID]->setToolTip(TEXT + " (" + QKeySequence(SHORTCUT).toString() + ")" ) +#define CREATE_ACTION_SHORTCUT(ID, TEXT, SHORTCUT) \ + CREATE_ACTION(ID, TEXT); \ + SET_SHORTCUT(ID, TEXT, SHORTCUT) +#define CREATE_ACTION_ICON_SHORTCUT(ID, ICON, TEXT, SHORTCUT) \ + CREATE_ACTION_ICON(ID, ICON, TEXT) \ + SET_SHORTCUT(ID, TEXT, SHORTCUT) +#define CREATE_ACTION_ONOFF_ICON_SHORTCUT(ID, ON_ICON, OFF_ICON, TEXT, SHORTCUT) \ + CREATE_ACTION(ID, TEXT); \ + SET_SHORTCUT(ID, TEXT, SHORTCUT); \ + mpa_actions[ID]->setCheckable(true); \ + { QIcon icon; \ + icon.addPixmap(QPixmap(":/icons/" ON_ICON ".svg"), QIcon::Normal, QIcon::On); \ + icon.addPixmap(QPixmap(":/icons/" OFF_ICON, ".svg"), QIcon::Normal, QIcon::Off); \ + mpa_actions[ID]->setIcon(icon); } +#define SET_SHORTCUTS(ID, TEXT, SHORTCUTS) \ + mpa_actions[ID]->setShortcuts(SHORTCUTS); \ + mpa_actions[ID]->setToolTip(TEXT + " (" + SHORTCUTS.first().toString() + ")" ) +#define CREATE_ACTION_SHORTCUTS(ID, TEXT, SHORTCUTS) \ + CREATE_ACTION(ID, TEXT); \ + SET_SHORTCUTS(ID, TEXT, SHORTCUTS) +#define CREATE_ACTION_ICON_SHORTCUTS(ID, ICON, TEXT, SHORTCUTS) \ + CREATE_ACTION_ICON(ID, ICON, TEXT) \ + SET_SHORTCUTS(ID, TEXT, SHORTCUTS) +#define HIDE_ACTION(ID) mpa_actions[ID]->setVisible(false) +#define DISABLE_ACTION(ID) mpa_actions[ID]->setDisabled(true) + +void KiwixApp::createActions() +{ + CREATE_ACTION_ICON_SHORTCUT(KiwixServeAction, "share", gt("local-kiwix-server"), QKeySequence(Qt::CTRL | Qt::Key_I)); + + CREATE_ACTION_ICON_SHORTCUT(RandomArticleAction, "random", gt("random-article"), QKeySequence(Qt::CTRL | Qt::Key_R)); + connect(mpa_actions[RandomArticleAction], &QAction::triggered, + this, [=]() { this->openRandomUrl(false); }); + + CREATE_ACTION_ICON_SHORTCUT(OpenHomePageAction, "home-button", gt("home-page"), QKeySequence(Qt::ALT | Qt::Key_Home)); + + if (QGuiApplication::isLeftToRight()) { + CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-left", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Left)); + CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-right", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Right)); + CREATE_ACTION_ICON(ScrollNextTabAction, "caret-right-solid", gt("scroll-next-tab")); + CREATE_ACTION_ICON(ScrollPreviousTabAction, "caret-left-solid", gt("scroll-previous-tab")); + CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_Tab)); + CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageDown )); + CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab)); + CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageUp )); + } else { + CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-right", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Right)); + CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-left", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Left)); + CREATE_ACTION_ICON(ScrollNextTabAction, "caret-left-solid", gt("scroll-next-tab")); + CREATE_ACTION_ICON(ScrollPreviousTabAction, "caret-right-solid", gt("scroll-previous-tab")); + CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab)); + CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageUp)); + CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::Key_Tab)); + CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::Key_PageDown )); + } + DISABLE_ACTION(HistoryBackAction); + DISABLE_ACTION(HistoryForwardAction); + + CREATE_ACTION_SHORTCUT(ReadArticleAction, gt("read-article"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_A)); + CREATE_ACTION_SHORTCUT(ReadTextAction, gt("read-text"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_S)); + CREATE_ACTION_SHORTCUT(ReadStopAction, gt("read-stop"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_X)); + CREATE_ACTION_SHORTCUT(ToggleTTSLanguageAction, gt("select-read-language"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_L)); + CREATE_ACTION_SHORTCUT(ToggleTTSVoiceAction, gt("select-read-voice"), QKeySequence(Qt::ALT | Qt::SHIFT | Qt::Key_V)); + CREATE_ACTION_SHORTCUT(IncreaseTTSSpeedAction, gt("increase-tts-speed"), QKeySequence(Qt::Key_Greater)); + CREATE_ACTION_SHORTCUT(DecreaseTTSSpeedAction, gt("decrease-tts-speed"), QKeySequence(Qt::Key_Less)); + mpa_actions[ToggleTTSLanguageAction]->setCheckable(true); + mpa_actions[ToggleTTSVoiceAction]->setCheckable(true); + + CREATE_ACTION_ICON_SHORTCUT(PrintAction, "print", gt("print"), QKeySequence::Print); + connect(mpa_actions[PrintAction], &QAction::triggered, + this, &KiwixApp::printPage); + + CREATE_ACTION_ICON_SHORTCUT(NewTabAction,"new-tab-icon", gt("new-tab"), QKeySequence::AddTab); + + CREATE_ACTION_SHORTCUTS(CloseCurrentTabAction, gt("close-tab"), QList({QKeySequence(Qt::CTRL | Qt::Key_F4), QKeySequence(Qt::CTRL | Qt::Key_W)})); + + CREATE_ACTION_SHORTCUT(ReopenClosedTabAction, gt("reopen-closed-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_T)); + HIDE_ACTION(ReopenClosedTabAction); + + CREATE_ACTION_SHORTCUT(BrowseLibraryAction, gt("browse-library"), QKeySequence(Qt::CTRL | Qt::Key_E)); + HIDE_ACTION(BrowseLibraryAction); + + CREATE_ACTION_ICON_SHORTCUT(OpenFileAction, "open-file", gt("open-file"), QKeySequence::Open); + connect(mpa_actions[OpenFileAction], &QAction::triggered, + this, [=]() { openZimFile(); }); + + CREATE_ACTION(OpenRecentAction, gt("open-recent")); + HIDE_ACTION(OpenRecentAction); + + CREATE_ACTION_SHORTCUT(SavePageAsAction, gt("save-page-as"), QKeySequence::Save); + + CREATE_ACTION_SHORTCUTS(SearchArticleAction, gt("search-article"), QList({QKeySequence(Qt::Key_F6), QKeySequence(Qt::CTRL | Qt::Key_L), QKeySequence(Qt::ALT | Qt::Key_D)})); + + CREATE_ACTION_SHORTCUT(SearchLibraryAction, gt("search-in-library"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_R)); + HIDE_ACTION(SearchLibraryAction); + + CREATE_ACTION(FindInPageAction, gt("find-in-page")); + mpa_actions[FindInPageAction]->setShortcuts({QKeySequence::Find, Qt::Key_F3}); + connect(mpa_actions[FindInPageAction], &QAction::triggered, + this, [=]() { getTabWidget()->openFindInPageBar(); }); + + const auto fullScreenKeySeq = QKeySequence(QKeySequence::FullScreen).isEmpty() + ? QKeySequence(Qt::Key_F11) + : QKeySequence(QKeySequence::FullScreen); + CREATE_ACTION_ICON_SHORTCUT(ToggleFullscreenAction, "full-screen-enter", gt("set-fullscreen"), fullScreenKeySeq); + connect(mpa_actions[ToggleFullscreenAction], &QAction::toggled, + this, [=](bool checked) { + auto action = mpa_actions[ToggleFullscreenAction]; + action->setIcon( + QIcon(checked ? ":/icons/full-screen-exit.svg" : ":/icons/full-screen-enter.svg")); + action->setText(checked ? gt("quit-fullscreen") : gt("set-fullscreen")); + }); + mpa_actions[ToggleFullscreenAction]->setCheckable(true); + + CREATE_ACTION_ICON_SHORTCUT(ToggleTOCAction, "toc", gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::Key_M)); + mpa_actions[ToggleTOCAction]->setCheckable(true); + + CREATE_ACTION_ICON_SHORTCUT(OpenMultiZimAction, "filter", gt("search-options"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L)); + + CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL | Qt::Key_B)); + + CREATE_ACTION(ExportReadingListAction, gt("export-reading-list")); + + CREATE_ACTION(ImportReadingListAction, gt("import-reading-list")); + + CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleAddBookmarkAction, "star-active", "star", gt("add-bookmark"), QKeySequence(Qt::CTRL | Qt::Key_D)); + + CREATE_ACTION_SHORTCUTS(ZoomInAction, gt("zoom-in"), QList({QKeySequence::ZoomIn, QKeySequence(Qt::CTRL | Qt::Key_Equal)})); + + CREATE_ACTION_SHORTCUT(ZoomOutAction, gt("zoom-out"), QKeySequence::ZoomOut); + + CREATE_ACTION_SHORTCUT(ZoomResetAction, gt("zoom-reset"), QKeySequence(Qt::CTRL | Qt::Key_0)); + + CREATE_ACTION_SHORTCUT(HelpAction, gt("help"), QKeySequence::HelpContents); + HIDE_ACTION(HelpAction); + + CREATE_ACTION(FeedbackAction, gt("feedback")); + HIDE_ACTION(FeedbackAction); + + CREATE_ACTION(ReportBugAction, gt("report-a-bug")); + HIDE_ACTION(ReportBugAction); + + CREATE_ACTION(RequestFeatureAction, gt("request-a-feature")); + HIDE_ACTION(RequestFeatureAction); + + CREATE_ACTION(AboutAction, gt("about-kiwix")); + + CREATE_ACTION_ICON_SHORTCUT(SettingAction, "settings", gt("settings"), QKeySequence(Qt::Key_F12)); + + CREATE_ACTION_ICON_SHORTCUT(DonateAction, "donate", gt("donate-to-support-kiwix"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_D)); + + CREATE_ACTION_ICON_SHORTCUT(ExitAction, "exit", gt("exit"), QKeySequence::Quit); +} + +void KiwixApp::postInit() { + connect(getTabWidget(), &TabBar::tabDisplayed, + this, &KiwixApp::handleItemsState); + emit(m_library.booksChanged()); + connect(&m_library, &Library::booksChanged, this, &KiwixApp::updateNameMapper); + handleItemsState(TabType::LibraryTab); +} + +void KiwixApp::handleItemsState(TabType tabType) +{ + auto libraryOrSettingsTab = (tabType == TabType::LibraryTab || tabType == TabType::SettingsTab); + auto notBookmarkableTab = libraryOrSettingsTab || getTabWidget()->currentArticleUrl().isEmpty(); + auto app = KiwixApp::instance(); + app->getAction(KiwixApp::ToggleTOCAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::ToggleReadingListAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::ToggleAddBookmarkAction)->setDisabled(notBookmarkableTab); + app->getAction(KiwixApp::FindInPageAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::ZoomInAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::ZoomOutAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::ZoomResetAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::RandomArticleAction)->setDisabled(libraryOrSettingsTab); + app->getAction(KiwixApp::OpenHomePageAction)->setDisabled(libraryOrSettingsTab); + + /* Non-Zim tabs are not bookmarkable therefore never in reading list. */ + if (notBookmarkableTab) + app->getAction(KiwixApp::ToggleAddBookmarkAction)->setChecked(false); +} + +void KiwixApp::updateNameMapper() +{ + mp_nameMapper->update(); +} + +void KiwixApp::printVersions(std::ostream& out) { + out << version.toStdString() << std::endl; + out << "+ libqt (compile time) " << QT_VERSION_STR << std::endl; + out << "+ libqt (run time) " << qVersion() << std::endl << std::endl; + kiwix::printVersions(out); + out << std::endl; + zim::printVersions(out); +} + +QString KiwixApp::parseStyleFromFile(QString filePath) +{ + QFile file(filePath); + file.open(QFile::ReadOnly); + QString styleSheet = QString(file.readAll()); + file.close(); + return styleSheet; +} + +void KiwixApp::saveListOfOpenTabs() +{ + return mp_session->setValue("reopenTabList", getTabWidget()->getTabUrls()); +} + +void KiwixApp::saveWindowState() +{ + mp_session->setValue("geometry", getMainWindow()->saveGeometry()); + mp_session->setValue("windowState", getMainWindow()->saveState()); +} + +void KiwixApp::saveVoiceName(const QString& langName, const QString& voiceName) +{ + mp_session->setValue("voice/" + langName, voiceName); +} + +void KiwixApp::saveTtsSpeed(const QString& langName, double speed) +{ + mp_session->setValue("speed/" + langName, speed); +} + +void KiwixApp::restoreWindowState() +{ + getMainWindow()->restoreGeometry(mp_session->value("geometry").toByteArray()); + getMainWindow()->restoreState(mp_session->value("windowState").toByteArray()); +} + +void KiwixApp::saveCurrentTabIndex() +{ + return mp_session->setValue("currentTabIndex", getTabWidget()->currentIndex()); +} + +void KiwixApp::savePrevSaveDir(const QString &prevSaveDir) +{ + mp_session->setValue("prevSaveDir", prevSaveDir); +} + +QString KiwixApp::getSavedVoiceName(const QString& langName) const +{ + return mp_session->value("voice/" + langName, "").toString(); +} + +double KiwixApp::getSavedTtsSpeed(const QString& langName) const +{ + return mp_session->value("speed/" + langName, 1.0).toDouble(); // Default: 1.0 (normal speed) +} + +QString KiwixApp::getPrevSaveDir() const +{ + QString prevSaveDir = mp_session->value("prevSaveDir", DEFAULT_SAVE_DIR).toString(); + QDir dir(prevSaveDir); + return dir.exists() ? prevSaveDir : DEFAULT_SAVE_DIR; +} diff --git a/src/kiwixapp.h b/src/kiwixapp.h index 36ad170e0..32ae0e371 100644 --- a/src/kiwixapp.h +++ b/src/kiwixapp.h @@ -1,154 +1,154 @@ -#ifndef KIWIXAPP_H -#define KIWIXAPP_H - -#include "library.h" -#include "contentmanager.h" -#include "tabbar.h" -#include "mainwindow.h" -#include "kiwix/downloader.h" -#include -#include "kprofile.h" -#include "settingsmanager.h" -#include "translation.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -typedef TabBar::TabType TabType; - -class KiwixApp : public QtSingleApplication -{ - Q_OBJECT - -public: - enum Actions { - KiwixServeAction, - RandomArticleAction, - OpenHomePageAction, - PrintAction, - NewTabAction, - CloseCurrentTabAction, - ReopenClosedTabAction, - BrowseLibraryAction, - OpenFileAction, - OpenRecentAction, - OpenMultiZimAction, - SavePageAsAction, - SearchArticleAction, - SearchLibraryAction, - FindInPageAction, - ToggleFullscreenAction, - ToggleTOCAction, - ToggleReadingListAction, - ToggleAddBookmarkAction, - ToggleTTSLanguageAction, - ToggleTTSVoiceAction, - IncreaseTTSSpeedAction, - DecreaseTTSSpeedAction, - ZoomInAction, - ZoomOutAction, - ZoomResetAction, - NextTabAction, - PreviousTabAction, - HistoryBackAction, - HistoryForwardAction, - ReadTextAction, - ReadArticleAction, - ReadStopAction, - HelpAction, - FeedbackAction, - ReportBugAction, - RequestFeatureAction, - AboutAction, - SettingAction, - DonateAction, - ExitAction, - ExportReadingListAction, - ImportReadingListAction, - ScrollPreviousTabAction, - ScrollNextTabAction, - MAX_ACTION - }; - - KiwixApp(int& argc, char *argv[]); - virtual ~KiwixApp(); - static KiwixApp* instance(); - void init(); - - void openRandomUrl(bool newTab=true); - - void showMessage(const QString& message, const QString& title, const enum QMessageBox::Icon& icon); - - KProfile* getProfile() { return &m_profile; } - Library* getLibrary() { return &m_library; } - MainWindow* getMainWindow() { return mp_mainWindow; } - ContentManager* getContentManager() { return mp_manager; } - TabBar* getTabWidget() { return getMainWindow()->getTabBar(); } - SearchBar& getSearchBar() { return getMainWindow()->getTopWidget()->getSearchBar(); } - QAction* getAction(Actions action); - QString getLibraryDirectory() { return m_libraryDirectory; }; - kiwix::Server* getLocalServer() { return &m_server; } - SettingsManager* getSettingsManager() { return &m_settingsManager; }; - QString getText(const QString &key) { return m_translation.getText(key); }; - void setMonitorDir(const QString &dir); - bool isCurrentArticleBookmarked(); - QString parseStyleFromFile(QString filePath); - void saveListOfOpenTabs(); - void saveWindowState(); - void saveVoiceName(const QString& langName, const QString& voiceName); - void saveTtsSpeed(const QString& langName, double speed); - void restoreWindowState(); - void saveCurrentTabIndex(); - void savePrevSaveDir(const QString& prevSaveDir); - QString getSavedVoiceName(const QString& langName) const; - double getSavedTtsSpeed(const QString& langName) const; - QString getPrevSaveDir() const; - void restoreTabs(); - void setupDirectoryMonitoring(); - -public slots: - void newTab(); - void openZimFile(const QString& zimfile=""); - void openUrl(const QString& url, bool newTab=true); - void openUrl(const QUrl& url, bool newTab=true); - void printPage(); - void handleItemsState(TabType); - void updateNameMapper(); - void printVersions(std::ostream& out = std::cout); - -protected: - void createActions(); - void postInit(); - -private: - QTranslator m_qtTranslator, m_appTranslator; - SettingsManager m_settingsManager; - KProfile m_profile; - QString m_libraryDirectory; - Library m_library; - ContentManager* mp_manager; - MainWindow* mp_mainWindow; - QErrorMessage* mp_errorDialog; - std::shared_ptr mp_nameMapper; - kiwix::Server m_server; - Translation m_translation; - QSettings* mp_session; - - QAction* mpa_actions[MAX_ACTION]; - - QString findLibraryDirectory(); - void loadAndInstallTranslations(QTranslator& translator, const QString& filename, const QString& directory); -}; - -QString gt(const QString &key); -#define _STR(...) # __VA_ARGS__ -#define STR(X) _STR(X) -static QString version = STR(VERSION); -#endif // KIWIXAPP_H +#ifndef KIWIXAPP_H +#define KIWIXAPP_H + +#include "library.h" +#include "contentmanager.h" +#include "tabbar.h" +#include "mainwindow.h" +#include "kiwix/downloader.h" +#include +#include "kprofile.h" +#include "settingsmanager.h" +#include "translation.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef TabBar::TabType TabType; + +class KiwixApp : public QtSingleApplication +{ + Q_OBJECT + +public: + enum Actions { + KiwixServeAction, + RandomArticleAction, + OpenHomePageAction, + PrintAction, + NewTabAction, + CloseCurrentTabAction, + ReopenClosedTabAction, + BrowseLibraryAction, + OpenFileAction, + OpenRecentAction, + OpenMultiZimAction, + SavePageAsAction, + SearchArticleAction, + SearchLibraryAction, + FindInPageAction, + ToggleFullscreenAction, + ToggleTOCAction, + ToggleReadingListAction, + ToggleAddBookmarkAction, + ToggleTTSLanguageAction, + ToggleTTSVoiceAction, + IncreaseTTSSpeedAction, + DecreaseTTSSpeedAction, + ZoomInAction, + ZoomOutAction, + ZoomResetAction, + NextTabAction, + PreviousTabAction, + HistoryBackAction, + HistoryForwardAction, + ReadTextAction, + ReadArticleAction, + ReadStopAction, + HelpAction, + FeedbackAction, + ReportBugAction, + RequestFeatureAction, + AboutAction, + SettingAction, + DonateAction, + ExitAction, + ExportReadingListAction, + ImportReadingListAction, + ScrollPreviousTabAction, + ScrollNextTabAction, + MAX_ACTION + }; + + KiwixApp(int& argc, char *argv[]); + virtual ~KiwixApp(); + static KiwixApp* instance(); + void init(); + + void openRandomUrl(bool newTab=true); + + void showMessage(const QString& message, const QString& title, const enum QMessageBox::Icon& icon); + + KProfile* getProfile() { return &m_profile; } + Library* getLibrary() { return &m_library; } + MainWindow* getMainWindow() { return mp_mainWindow; } + ContentManager* getContentManager() { return mp_manager; } + TabBar* getTabWidget() { return getMainWindow()->getTabBar(); } + SearchBar& getSearchBar() { return getMainWindow()->getTopWidget()->getSearchBar(); } + QAction* getAction(Actions action); + QString getLibraryDirectory() { return m_libraryDirectory; }; + kiwix::Server* getLocalServer() { return &m_server; } + SettingsManager* getSettingsManager() { return &m_settingsManager; }; + QString getText(const QString &key) { return m_translation.getText(key); }; + void setMonitorDir(const QString &dir); + bool isCurrentArticleBookmarked(); + QString parseStyleFromFile(QString filePath); + void saveListOfOpenTabs(); + void saveWindowState(); + void saveVoiceName(const QString& langName, const QString& voiceName); + void saveTtsSpeed(const QString& langName, double speed); + void restoreWindowState(); + void saveCurrentTabIndex(); + void savePrevSaveDir(const QString& prevSaveDir); + QString getSavedVoiceName(const QString& langName) const; + double getSavedTtsSpeed(const QString& langName) const; + QString getPrevSaveDir() const; + void restoreTabs(); + void setupDirectoryMonitoring(); + +public slots: + void newTab(); + void openZimFile(const QString& zimfile=""); + void openUrl(const QString& url, bool newTab=true); + void openUrl(const QUrl& url, bool newTab=true); + void printPage(); + void handleItemsState(TabType); + void updateNameMapper(); + void printVersions(std::ostream& out = std::cout); + +protected: + void createActions(); + void postInit(); + +private: + QTranslator m_qtTranslator, m_appTranslator; + SettingsManager m_settingsManager; + KProfile m_profile; + QString m_libraryDirectory; + Library m_library; + ContentManager* mp_manager; + MainWindow* mp_mainWindow; + QErrorMessage* mp_errorDialog; + std::shared_ptr mp_nameMapper; + kiwix::Server m_server; + Translation m_translation; + QSettings* mp_session; + + QAction* mpa_actions[MAX_ACTION]; + + QString findLibraryDirectory(); + void loadAndInstallTranslations(QTranslator& translator, const QString& filename, const QString& directory); +}; + +QString gt(const QString &key); +#define _STR(...) # __VA_ARGS__ +#define STR(X) _STR(X) +static QString version = STR(VERSION); +#endif // KIWIXAPP_H diff --git a/src/kiwixchoicebox.cpp b/src/kiwixchoicebox.cpp index 3e3d5cb80..0f8da859a 100644 --- a/src/kiwixchoicebox.cpp +++ b/src/kiwixchoicebox.cpp @@ -1,282 +1,282 @@ -#include "kiwixchoicebox.h" -#include "ui_kiwixchoicebox.h" -#include "klistwidgetitem.h" -#include "kiwixapp.h" -#include "choiceitem.h" -#include -#include "choiceitem.h" -#include "kiwixapp.h" -#include -#include -#include -#include -#include "kiwixlineedit.h" -#include "kiwixlistwidget.h" - -KiwixChoiceBox::KiwixChoiceBox(QWidget *parent) : - QWidget(parent), - ui(new Ui::kiwixchoicebox) -{ - ui->setupUi(this); - auto styleSheet = KiwixApp::instance()->parseStyleFromFile(":/css/choiceBox.css"); - this->setStyleSheet(styleSheet); - ui->clearButton->setText(gt("clear")); - ui->clearButton->setToolTip(gt("clear-filter")); - - choiceLabel = ui->choiceLabel; - choiceLabel->setText(gt("undefined")); - - choiceSelector = new KiwixListWidget(parent); - choiceSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); - choiceSelector->setMaximumWidth(250); - // allow maximum 6 elements - choiceSelector->setMaximumHeight(KListWidgetItem::getItemHeight() * 6); - choiceSelector->setCursor(Qt::PointingHandCursor); - choiceSelector->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); - choiceSelector->setFocusPolicy(Qt::FocusPolicy::NoFocus); - choiceSelector->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); - choiceSelector->setStyleSheet(styleSheet); - choiceSelector->setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection); - - currentChoicesLayout = new FlowLayout(ui->currentChoices, 4, 2, 2); - searcher = new KiwixLineEdit(); - searcher->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - searcher->setFixedWidth(20); - currentChoicesLayout->addWidget(searcher); - connect(choiceSelector, &QListWidget::itemPressed, this, [=](QListWidgetItem *item) { - searcher->clear(); - if (item->isSelected()) { - addSelection(item); - } else { - removeSelection(item); - } - }); - - connect(searcher, &QLineEdit::textChanged, [=](QString search) { - searcher->setStyleSheet("QLineEdit{color: #666;}"); - QFontMetrics fm = searcher->fontMetrics(); - auto w = fm.horizontalAdvance(search) + 20; - if (w + 4 < ui->currentChoices->width()) { - searcher->setFixedWidth(w); - ui->currentChoices->resize(ui->currentChoices->width(), currentChoicesLayout->minimumHeightForWidth(ui->currentChoices->width())); - } - int visibleItems = 0; - for (auto i = 0; i < choiceSelector->count(); i++) { - auto itemAtRow = choiceSelector->item(i); - itemAtRow->setHidden(!itemAtRow->text().contains(search, Qt::CaseSensitivity::CaseInsensitive)); - visibleItems += !(itemAtRow->isHidden()); - } - choiceSelector->setVisibleItems(visibleItems); - adjustSize(); - choiceSelector->setVisible(true); - }); - - connect(searcher, &KiwixLineEdit::clicked, [=]() { - showOptions(); - }); - - choiceSelector->setVisible(false); - searcher->setStyleSheet("QLineEdit{color: #999;}"); - - connect(searcher, &KiwixLineEdit::focusedOut, [=]() { - hideOptions(); - }); - - connect(searcher, &KiwixLineEdit::focusedIn, [=]() { - showOptions(); - }); - - ui->clearButton->setCursor(Qt::PointingHandCursor); - connect(ui->clearButton, &QPushButton::clicked, [=]() { - clearSelections(); - emit(choiceUpdated(getCurrentSelected())); - hideOptions(); - }); - - connect(this, &KiwixChoiceBox::choiceUpdated, [=]() { - if (choiceSelector->selectedItems().isEmpty()) - showPlaceholder(); - choiceSelector->setVisible(false); - }); - - connect(this, &KiwixChoiceBox::clicked, [=]() { - showOptions(); - }); -} - -KiwixChoiceBox::~KiwixChoiceBox() -{ - delete ui; -} - -/* -When the lineEdit is currently focused, -if the outer widget is pressed, lineEdit loses focus and hides the options. -When mouseRelease event is called, it will create a flicker effect: - - Clicking and holding causes the lineEdit to lose focus and hide the options - - Release causes the options to show up again. -Showing the options on a mousePress doesn't allow this -*/ -void KiwixChoiceBox::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) { - emit(clicked()); - } -} - -void KiwixChoiceBox::hideOptions() -{ - if (choiceSelector->selectedItems().isEmpty()) { - showPlaceholder(); - } - searcher->setStyleSheet("QLineEdit{color: #999;}"); - choiceSelector->setVisible(false); - ui->currentChoices->setStyleSheet("#currentChoices{border: 1px solid #ccc;}"); - searcher->clearFocus(); -} - -void KiwixChoiceBox::showOptions() -{ - ui->currentChoices->setStyleSheet("#currentChoices{border: 2px solid #4e63ad;}"); - adjustSize(); - choiceSelector->setVisible(true); - choiceSelector->raise(); - searcher->setPlaceholderText(""); - searcher->setFocus(); -} - -void KiwixChoiceBox::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Escape) { - hideOptions(); - } else if (event->key() == Qt::Key_Down) { - if (!choiceSelector->isVisible()) - showOptions(); - choiceSelector->moveDown(); - } else if (event->key() == Qt::Key_Up) { - choiceSelector->moveUp(); - } else if ((event->key() == Qt::Key_Enter) || (event->key() == Qt::Key_Return)) { - choiceSelector->selectCurrent(); - } -} - -bool KiwixChoiceBox::addSelection(QListWidgetItem *item, bool updateRequired) -{ - auto key = item->text(); - auto value = item->data(Qt::UserRole).toString(); - auto chItem = new ChoiceItem(key, value); - connect(chItem, &ChoiceItem::closeButtonClicked, [=](QString text) { - auto selectionItems = choiceSelector->findItems(text, Qt::MatchExactly); - if (selectionItems.size() != 1) return; - removeSelection(selectionItems[0]); - }); - chItem->setObjectName(key); - currentChoicesLayout->insertWidget(ui->currentChoices->children().count() - 2, chItem); - searcher->setFixedWidth(20); - // put on top of list - item = choiceSelector->takeItem(choiceSelector->row(item)); - choiceSelector->insertItem(0, item); - item->setSelected(true); - - searcher->setFixedWidth(20); - if (updateRequired) { - searcher->setFocus(); - emit(choiceUpdated(getCurrentSelected())); - } - return true; -} - -bool KiwixChoiceBox::removeSelection(QListWidgetItem *item) -{ - auto chItem = ui->currentChoices->findChild(item->text()); - chItem->deleteLater(); - // selected items are always shown at top, put it after the last selected item - item->setSelected(false); - auto selItems = choiceSelector->selectedItems(); - item = choiceSelector->takeItem(choiceSelector->row(item)); - choiceSelector->insertItem(selItems.size(), item); - emit(choiceUpdated(getCurrentSelected())); - return true; -} - -void KiwixChoiceBox::clearSelections() -{ - for (auto &item : choiceSelector->selectedItems()) { - item->setSelected(false); - auto chItem = ui->currentChoices->findChild(item->text()); - ui->currentChoices->layout()->removeWidget(chItem); - delete chItem; - } -} - -void KiwixChoiceBox::showPlaceholder() -{ - searcher->clear(); - searcher->setPlaceholderText(gt(m_type + "-searcher-placeholder")); - // Putting width based on placeholder contents - QFontMetrics fm = searcher->fontMetrics(); - auto w = fm.boundingRect(gt(m_type + "-searcher-placeholder")).width(); - searcher->setFixedWidth(w + 20); -} - -QString beautifyString(QString word) -{ - word = word.replace("_", " "); - word[0] = word[0].toUpper(); - return word; -} - -void KiwixChoiceBox::setSelections(SelectionList selections, SelectionList defaultSelection) -{ - clearSelections(); - choiceSelector->clear(); - for (const auto &selection: selections) - { - auto item = new KListWidgetItem(beautifyString(selection.first)); - item->setData(Qt::UserRole, selection.second); - choiceSelector->addItem(item); - } - - for (const auto &defSel : defaultSelection) { - QString displayStr = beautifyString(defSel.first); - auto itemList = choiceSelector->findItems(displayStr, Qt::MatchExactly); - if (itemList.isEmpty()) { - auto item = new KListWidgetItem(displayStr); - item->setData(Qt::UserRole, defSel.second); - choiceSelector->addItem(item); - addSelection(item, false); - } else { - addSelection(itemList[0], false); - } - } - - if (choiceSelector->selectedItems().isEmpty()) - showPlaceholder(); - choiceSelector->setVisibleItems(choiceSelector->count()); - adjustSize(); -} - -void KiwixChoiceBox::adjustSize() -{ - QWidget::adjustSize(); - choiceSelector->adjustSize(); - choiceSelector->setGeometry(this->x() + ui->currentChoices->x(), - this->y() + ui->currentChoices->y() + ui->currentChoices->height(), - choiceSelector->width(), - choiceSelector->getVisibleItems() * KListWidgetItem::getItemHeight() + 2); // 2 is for the border so that all elements are visible -} - -void KiwixChoiceBox::setType(QString type) -{ - ui->choiceLabel->setText(gt(type)); - m_type = type; -} - -KiwixChoiceBox::SelectionList KiwixChoiceBox::getCurrentSelected() -{ - SelectionList selections; - for (auto &item : choiceSelector->selectedItems()) { - selections.append({item->text(), item->data(Qt::UserRole).toString()}); - } - return selections; -} +#include "kiwixchoicebox.h" +#include "ui_kiwixchoicebox.h" +#include "klistwidgetitem.h" +#include "kiwixapp.h" +#include "choiceitem.h" +#include +#include "choiceitem.h" +#include "kiwixapp.h" +#include +#include +#include +#include +#include "kiwixlineedit.h" +#include "kiwixlistwidget.h" + +KiwixChoiceBox::KiwixChoiceBox(QWidget *parent) : + QWidget(parent), + ui(new Ui::kiwixchoicebox) +{ + ui->setupUi(this); + auto styleSheet = KiwixApp::instance()->parseStyleFromFile(":/css/choiceBox.css"); + this->setStyleSheet(styleSheet); + ui->clearButton->setText(gt("clear")); + ui->clearButton->setToolTip(gt("clear-filter")); + + choiceLabel = ui->choiceLabel; + choiceLabel->setText(gt("undefined")); + + choiceSelector = new KiwixListWidget(parent); + choiceSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + choiceSelector->setMaximumWidth(250); + // allow maximum 6 elements + choiceSelector->setMaximumHeight(KListWidgetItem::getItemHeight() * 6); + choiceSelector->setCursor(Qt::PointingHandCursor); + choiceSelector->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel); + choiceSelector->setFocusPolicy(Qt::FocusPolicy::NoFocus); + choiceSelector->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + choiceSelector->setStyleSheet(styleSheet); + choiceSelector->setSelectionMode(QAbstractItemView::SelectionMode::MultiSelection); + + currentChoicesLayout = new FlowLayout(ui->currentChoices, 4, 2, 2); + searcher = new KiwixLineEdit(); + searcher->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + searcher->setFixedWidth(20); + currentChoicesLayout->addWidget(searcher); + connect(choiceSelector, &QListWidget::itemPressed, this, [=](QListWidgetItem *item) { + searcher->clear(); + if (item->isSelected()) { + addSelection(item); + } else { + removeSelection(item); + } + }); + + connect(searcher, &QLineEdit::textChanged, [=](QString search) { + searcher->setStyleSheet("QLineEdit{color: #666;}"); + QFontMetrics fm = searcher->fontMetrics(); + auto w = fm.horizontalAdvance(search) + 20; + if (w + 4 < ui->currentChoices->width()) { + searcher->setFixedWidth(w); + ui->currentChoices->resize(ui->currentChoices->width(), currentChoicesLayout->minimumHeightForWidth(ui->currentChoices->width())); + } + int visibleItems = 0; + for (auto i = 0; i < choiceSelector->count(); i++) { + auto itemAtRow = choiceSelector->item(i); + itemAtRow->setHidden(!itemAtRow->text().contains(search, Qt::CaseSensitivity::CaseInsensitive)); + visibleItems += !(itemAtRow->isHidden()); + } + choiceSelector->setVisibleItems(visibleItems); + adjustSize(); + choiceSelector->setVisible(true); + }); + + connect(searcher, &KiwixLineEdit::clicked, [=]() { + showOptions(); + }); + + choiceSelector->setVisible(false); + searcher->setStyleSheet("QLineEdit{color: #999;}"); + + connect(searcher, &KiwixLineEdit::focusedOut, [=]() { + hideOptions(); + }); + + connect(searcher, &KiwixLineEdit::focusedIn, [=]() { + showOptions(); + }); + + ui->clearButton->setCursor(Qt::PointingHandCursor); + connect(ui->clearButton, &QPushButton::clicked, [=]() { + clearSelections(); + emit(choiceUpdated(getCurrentSelected())); + hideOptions(); + }); + + connect(this, &KiwixChoiceBox::choiceUpdated, [=]() { + if (choiceSelector->selectedItems().isEmpty()) + showPlaceholder(); + choiceSelector->setVisible(false); + }); + + connect(this, &KiwixChoiceBox::clicked, [=]() { + showOptions(); + }); +} + +KiwixChoiceBox::~KiwixChoiceBox() +{ + delete ui; +} + +/* +When the lineEdit is currently focused, +if the outer widget is pressed, lineEdit loses focus and hides the options. +When mouseRelease event is called, it will create a flicker effect: + - Clicking and holding causes the lineEdit to lose focus and hide the options + - Release causes the options to show up again. +Showing the options on a mousePress doesn't allow this +*/ +void KiwixChoiceBox::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + emit(clicked()); + } +} + +void KiwixChoiceBox::hideOptions() +{ + if (choiceSelector->selectedItems().isEmpty()) { + showPlaceholder(); + } + searcher->setStyleSheet("QLineEdit{color: #999;}"); + choiceSelector->setVisible(false); + ui->currentChoices->setStyleSheet("#currentChoices{border: 1px solid #ccc;}"); + searcher->clearFocus(); +} + +void KiwixChoiceBox::showOptions() +{ + ui->currentChoices->setStyleSheet("#currentChoices{border: 2px solid #4e63ad;}"); + adjustSize(); + choiceSelector->setVisible(true); + choiceSelector->raise(); + searcher->setPlaceholderText(""); + searcher->setFocus(); +} + +void KiwixChoiceBox::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) { + hideOptions(); + } else if (event->key() == Qt::Key_Down) { + if (!choiceSelector->isVisible()) + showOptions(); + choiceSelector->moveDown(); + } else if (event->key() == Qt::Key_Up) { + choiceSelector->moveUp(); + } else if ((event->key() == Qt::Key_Enter) || (event->key() == Qt::Key_Return)) { + choiceSelector->selectCurrent(); + } +} + +bool KiwixChoiceBox::addSelection(QListWidgetItem *item, bool updateRequired) +{ + auto key = item->text(); + auto value = item->data(Qt::UserRole).toString(); + auto chItem = new ChoiceItem(key, value); + connect(chItem, &ChoiceItem::closeButtonClicked, [=](QString text) { + auto selectionItems = choiceSelector->findItems(text, Qt::MatchExactly); + if (selectionItems.size() != 1) return; + removeSelection(selectionItems[0]); + }); + chItem->setObjectName(key); + currentChoicesLayout->insertWidget(ui->currentChoices->children().count() - 2, chItem); + searcher->setFixedWidth(20); + // put on top of list + item = choiceSelector->takeItem(choiceSelector->row(item)); + choiceSelector->insertItem(0, item); + item->setSelected(true); + + searcher->setFixedWidth(20); + if (updateRequired) { + searcher->setFocus(); + emit(choiceUpdated(getCurrentSelected())); + } + return true; +} + +bool KiwixChoiceBox::removeSelection(QListWidgetItem *item) +{ + auto chItem = ui->currentChoices->findChild(item->text()); + chItem->deleteLater(); + // selected items are always shown at top, put it after the last selected item + item->setSelected(false); + auto selItems = choiceSelector->selectedItems(); + item = choiceSelector->takeItem(choiceSelector->row(item)); + choiceSelector->insertItem(selItems.size(), item); + emit(choiceUpdated(getCurrentSelected())); + return true; +} + +void KiwixChoiceBox::clearSelections() +{ + for (auto &item : choiceSelector->selectedItems()) { + item->setSelected(false); + auto chItem = ui->currentChoices->findChild(item->text()); + ui->currentChoices->layout()->removeWidget(chItem); + delete chItem; + } +} + +void KiwixChoiceBox::showPlaceholder() +{ + searcher->clear(); + searcher->setPlaceholderText(gt(m_type + "-searcher-placeholder")); + // Putting width based on placeholder contents + QFontMetrics fm = searcher->fontMetrics(); + auto w = fm.boundingRect(gt(m_type + "-searcher-placeholder")).width(); + searcher->setFixedWidth(w + 20); +} + +QString beautifyString(QString word) +{ + word = word.replace("_", " "); + word[0] = word[0].toUpper(); + return word; +} + +void KiwixChoiceBox::setSelections(SelectionList selections, SelectionList defaultSelection) +{ + clearSelections(); + choiceSelector->clear(); + for (const auto &selection: selections) + { + auto item = new KListWidgetItem(beautifyString(selection.first)); + item->setData(Qt::UserRole, selection.second); + choiceSelector->addItem(item); + } + + for (const auto &defSel : defaultSelection) { + QString displayStr = beautifyString(defSel.first); + auto itemList = choiceSelector->findItems(displayStr, Qt::MatchExactly); + if (itemList.isEmpty()) { + auto item = new KListWidgetItem(displayStr); + item->setData(Qt::UserRole, defSel.second); + choiceSelector->addItem(item); + addSelection(item, false); + } else { + addSelection(itemList[0], false); + } + } + + if (choiceSelector->selectedItems().isEmpty()) + showPlaceholder(); + choiceSelector->setVisibleItems(choiceSelector->count()); + adjustSize(); +} + +void KiwixChoiceBox::adjustSize() +{ + QWidget::adjustSize(); + choiceSelector->adjustSize(); + choiceSelector->setGeometry(this->x() + ui->currentChoices->x(), + this->y() + ui->currentChoices->y() + ui->currentChoices->height(), + choiceSelector->width(), + choiceSelector->getVisibleItems() * KListWidgetItem::getItemHeight() + 2); // 2 is for the border so that all elements are visible +} + +void KiwixChoiceBox::setType(QString type) +{ + ui->choiceLabel->setText(gt(type)); + m_type = type; +} + +KiwixChoiceBox::SelectionList KiwixChoiceBox::getCurrentSelected() +{ + SelectionList selections; + for (auto &item : choiceSelector->selectedItems()) { + selections.append({item->text(), item->data(Qt::UserRole).toString()}); + } + return selections; +} diff --git a/src/kiwixchoicebox.h b/src/kiwixchoicebox.h index 8efdfe3a5..0d1d55abc 100644 --- a/src/kiwixchoicebox.h +++ b/src/kiwixchoicebox.h @@ -1,62 +1,62 @@ -#ifndef KIWIXCHOICEBOX_H -#define KIWIXCHOICEBOX_H - -#include -#include -#include -#include -#include -#include -#include -#include "flowlayout.h" -#include -#include - -class ChoiceItem; -class KiwixLineEdit; -class KiwixListWidget; - -namespace Ui { -class kiwixchoicebox; -} - -class KiwixChoiceBox : public QWidget -{ - Q_OBJECT - -public: - typedef QList> SelectionList; - - explicit KiwixChoiceBox(QWidget *parent = nullptr); - void setType(QString type); - void setSelections(SelectionList selections, SelectionList defaultSelection); - ~KiwixChoiceBox(); - void adjustSize(); - -protected: - void keyPressEvent(QKeyEvent* event) override; - void mousePressEvent(QMouseEvent *event) override; - -signals: - void choiceUpdated(SelectionList); - void clicked(); - -private: - Ui::kiwixchoicebox *ui; - QLabel *choiceLabel; - QLineEdit *choiceSearch; - KiwixListWidget *choiceSelector; - FlowLayout *currentChoicesLayout; - KiwixLineEdit *searcher; - SelectionList getCurrentSelected(); - bool removeSelection(QListWidgetItem *item); - void clearSelections(); - bool addSelection(QListWidgetItem *item, bool updateRequired = true); - void showOptions(); - void hideOptions(); - void showPlaceholder(); - QString m_type; - bool m_sliderMoved = false; -}; - -#endif // KIWIXCHOICEBOX_H +#ifndef KIWIXCHOICEBOX_H +#define KIWIXCHOICEBOX_H + +#include +#include +#include +#include +#include +#include +#include +#include "flowlayout.h" +#include +#include + +class ChoiceItem; +class KiwixLineEdit; +class KiwixListWidget; + +namespace Ui { +class kiwixchoicebox; +} + +class KiwixChoiceBox : public QWidget +{ + Q_OBJECT + +public: + typedef QList> SelectionList; + + explicit KiwixChoiceBox(QWidget *parent = nullptr); + void setType(QString type); + void setSelections(SelectionList selections, SelectionList defaultSelection); + ~KiwixChoiceBox(); + void adjustSize(); + +protected: + void keyPressEvent(QKeyEvent* event) override; + void mousePressEvent(QMouseEvent *event) override; + +signals: + void choiceUpdated(SelectionList); + void clicked(); + +private: + Ui::kiwixchoicebox *ui; + QLabel *choiceLabel; + QLineEdit *choiceSearch; + KiwixListWidget *choiceSelector; + FlowLayout *currentChoicesLayout; + KiwixLineEdit *searcher; + SelectionList getCurrentSelected(); + bool removeSelection(QListWidgetItem *item); + void clearSelections(); + bool addSelection(QListWidgetItem *item, bool updateRequired = true); + void showOptions(); + void hideOptions(); + void showPlaceholder(); + QString m_type; + bool m_sliderMoved = false; +}; + +#endif // KIWIXCHOICEBOX_H diff --git a/src/kiwixlineedit.cpp b/src/kiwixlineedit.cpp index 1f03cedac..a0fdc68be 100644 --- a/src/kiwixlineedit.cpp +++ b/src/kiwixlineedit.cpp @@ -1,29 +1,29 @@ -#include "kiwixlineedit.h" - -KiwixLineEdit::KiwixLineEdit(QWidget *parent) : QLineEdit(parent) -{ - installEventFilter(this); -} - -KiwixLineEdit::~KiwixLineEdit() -{ -} - -void KiwixLineEdit::resizeEvent(QResizeEvent *event) -{ - QLineEdit::resizeEvent(event); - adjustSize(); -} -bool KiwixLineEdit::eventFilter(QObject* object, QEvent* event) -{ - Q_UNUSED(object); - - if (event->type() == QEvent::MouseButtonPress) { - emit(clicked()); - } else if (event->type() == QEvent::FocusIn) { - emit(focusedIn()); - } else if (event->type() == QEvent::FocusOut) { - emit(focusedOut()); - } - return false; -} +#include "kiwixlineedit.h" + +KiwixLineEdit::KiwixLineEdit(QWidget *parent) : QLineEdit(parent) +{ + installEventFilter(this); +} + +KiwixLineEdit::~KiwixLineEdit() +{ +} + +void KiwixLineEdit::resizeEvent(QResizeEvent *event) +{ + QLineEdit::resizeEvent(event); + adjustSize(); +} +bool KiwixLineEdit::eventFilter(QObject* object, QEvent* event) +{ + Q_UNUSED(object); + + if (event->type() == QEvent::MouseButtonPress) { + emit(clicked()); + } else if (event->type() == QEvent::FocusIn) { + emit(focusedIn()); + } else if (event->type() == QEvent::FocusOut) { + emit(focusedOut()); + } + return false; +} diff --git a/src/kiwixlineedit.h b/src/kiwixlineedit.h index 6b25b8595..d465cb2da 100644 --- a/src/kiwixlineedit.h +++ b/src/kiwixlineedit.h @@ -1,20 +1,20 @@ -#ifndef KIWIXLINEEDIT_H -#define KIWIXLINEEDIT_H - -#include -#include - -class KiwixLineEdit : public QLineEdit { - Q_OBJECT; -public: - KiwixLineEdit(QWidget *parent = nullptr); - ~KiwixLineEdit(); -protected: - void resizeEvent(QResizeEvent *event) override; - bool eventFilter(QObject* object, QEvent* event) override; -signals: - void clicked(); - void focusedIn(); - void focusedOut(); -}; -#endif // KIWIXLINEEDIT_H +#ifndef KIWIXLINEEDIT_H +#define KIWIXLINEEDIT_H + +#include +#include + +class KiwixLineEdit : public QLineEdit { + Q_OBJECT; +public: + KiwixLineEdit(QWidget *parent = nullptr); + ~KiwixLineEdit(); +protected: + void resizeEvent(QResizeEvent *event) override; + bool eventFilter(QObject* object, QEvent* event) override; +signals: + void clicked(); + void focusedIn(); + void focusedOut(); +}; +#endif // KIWIXLINEEDIT_H diff --git a/src/kiwixlistwidget.cpp b/src/kiwixlistwidget.cpp index 0d1f58bc7..704e2b7f4 100644 --- a/src/kiwixlistwidget.cpp +++ b/src/kiwixlistwidget.cpp @@ -1,108 +1,108 @@ -#include "kiwixlistwidget.h" -#include -#include -#include -#include -#include "kiwixapp.h" -#include "klistwidgetitem.h" - -KiwixListWidget::KiwixListWidget(QWidget *parent) - : QListWidget(parent), currRow(0), m_visibleItems(count()) -{ - connect(this, &KiwixListWidget::currRowChanged, this, &KiwixListWidget::handleCurrRowChange); - setMouseTracking(true); -} - -void KiwixListWidget::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::LeftButton) { - selectCurrent(item(m_mouseIndex)); - } -} - -void KiwixListWidget::mouseMoveEvent(QMouseEvent *event) -{ - int oldRow = currRow; - m_mouseIndex = row(itemAt(event->pos())); - currRow = m_mouseIndex; - emit(currRowChanged(oldRow, currRow)); -} - -void KiwixListWidget::hideEvent(QHideEvent *event) -{ - auto currItem = dynamic_cast(item(currRow)); - if (currItem) - currItem->disableHighlight(); - QListWidget::hideEvent(event); -} - -void KiwixListWidget::resizeEvent(QResizeEvent *e) -{ - int oldRow = currRow; - for (auto i = 0; i < count(); i++) { - auto itemAtRow = item(i); - if (!itemAtRow->isHidden() && !itemAtRow->isSelected()) { - currRow = i; - break; - } - } - emit(currRowChanged(oldRow, currRow)); - QListWidget::resizeEvent(e); -} - -void KiwixListWidget::handleCurrRowChange(int oldRow, int newRow) -{ - auto prevItem = dynamic_cast(item(oldRow)); - if (prevItem) { - prevItem->disableHighlight(); - } - auto currItem = dynamic_cast(item(newRow)); - if (currItem) { - currItem->enableHighlight(); - scrollToItem(currItem, QAbstractItemView::EnsureVisible); - } - emit(dataChanged(QModelIndex(), QModelIndex())); -} - -void KiwixListWidget::moveUp() -{ - if (selectedItems().size() == count()) - return; - KListWidgetItem *currItem = dynamic_cast(item(currRow)); - int oldRow = currRow; - do { - currRow--; - if (currRow < 0) currRow = count() - 1; - currItem = dynamic_cast(item(currRow)); - } while (currItem->isSelected() || currItem->isHidden()); - emit(currRowChanged(oldRow, currRow)); -} - -void KiwixListWidget::moveDown() -{ - if (selectedItems().size() == count()) - return; - KListWidgetItem *currItem = dynamic_cast(item(currRow)); - int oldRow = currRow; - do { - currRow++; - if (currRow == count()) currRow = 0; - currItem = dynamic_cast(item(currRow)); - } while (currItem->isSelected() || currItem->isHidden()); - emit(currRowChanged(oldRow, currRow)); -} - -void KiwixListWidget::selectCurrent() -{ - selectCurrent(item(currRow)); -} - -void KiwixListWidget::selectCurrent(QListWidgetItem *item) -{ - auto currItem = dynamic_cast(item); - if (currItem && !currItem->isSelected() && !currItem->isHidden()) { - currItem->disableHighlight(); - currItem->setSelected(!currItem->isSelected()); - emit(itemPressed(currItem)); - } -} +#include "kiwixlistwidget.h" +#include +#include +#include +#include +#include "kiwixapp.h" +#include "klistwidgetitem.h" + +KiwixListWidget::KiwixListWidget(QWidget *parent) + : QListWidget(parent), currRow(0), m_visibleItems(count()) +{ + connect(this, &KiwixListWidget::currRowChanged, this, &KiwixListWidget::handleCurrRowChange); + setMouseTracking(true); +} + +void KiwixListWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + selectCurrent(item(m_mouseIndex)); + } +} + +void KiwixListWidget::mouseMoveEvent(QMouseEvent *event) +{ + int oldRow = currRow; + m_mouseIndex = row(itemAt(event->pos())); + currRow = m_mouseIndex; + emit(currRowChanged(oldRow, currRow)); +} + +void KiwixListWidget::hideEvent(QHideEvent *event) +{ + auto currItem = dynamic_cast(item(currRow)); + if (currItem) + currItem->disableHighlight(); + QListWidget::hideEvent(event); +} + +void KiwixListWidget::resizeEvent(QResizeEvent *e) +{ + int oldRow = currRow; + for (auto i = 0; i < count(); i++) { + auto itemAtRow = item(i); + if (!itemAtRow->isHidden() && !itemAtRow->isSelected()) { + currRow = i; + break; + } + } + emit(currRowChanged(oldRow, currRow)); + QListWidget::resizeEvent(e); +} + +void KiwixListWidget::handleCurrRowChange(int oldRow, int newRow) +{ + auto prevItem = dynamic_cast(item(oldRow)); + if (prevItem) { + prevItem->disableHighlight(); + } + auto currItem = dynamic_cast(item(newRow)); + if (currItem) { + currItem->enableHighlight(); + scrollToItem(currItem, QAbstractItemView::EnsureVisible); + } + emit(dataChanged(QModelIndex(), QModelIndex())); +} + +void KiwixListWidget::moveUp() +{ + if (selectedItems().size() == count()) + return; + KListWidgetItem *currItem = dynamic_cast(item(currRow)); + int oldRow = currRow; + do { + currRow--; + if (currRow < 0) currRow = count() - 1; + currItem = dynamic_cast(item(currRow)); + } while (currItem->isSelected() || currItem->isHidden()); + emit(currRowChanged(oldRow, currRow)); +} + +void KiwixListWidget::moveDown() +{ + if (selectedItems().size() == count()) + return; + KListWidgetItem *currItem = dynamic_cast(item(currRow)); + int oldRow = currRow; + do { + currRow++; + if (currRow == count()) currRow = 0; + currItem = dynamic_cast(item(currRow)); + } while (currItem->isSelected() || currItem->isHidden()); + emit(currRowChanged(oldRow, currRow)); +} + +void KiwixListWidget::selectCurrent() +{ + selectCurrent(item(currRow)); +} + +void KiwixListWidget::selectCurrent(QListWidgetItem *item) +{ + auto currItem = dynamic_cast(item); + if (currItem && !currItem->isSelected() && !currItem->isHidden()) { + currItem->disableHighlight(); + currItem->setSelected(!currItem->isSelected()); + emit(itemPressed(currItem)); + } +} diff --git a/src/kiwixlistwidget.h b/src/kiwixlistwidget.h index 0f4cd0780..e964b554d 100644 --- a/src/kiwixlistwidget.h +++ b/src/kiwixlistwidget.h @@ -1,36 +1,36 @@ -#ifndef KIWIXLISTWIDGET_H -#define KIWIXLISTWIDGET_H - -#include - -class KiwixListWidget : public QListWidget { - Q_OBJECT - -public: - KiwixListWidget(QWidget *parent = nullptr); - void moveUp(); - void moveDown(); - void selectCurrent(); - void selectCurrent(QListWidgetItem *item); - void setVisibleItems(int visibleItems) { m_visibleItems = visibleItems; } - int getVisibleItems() { return m_visibleItems; } - -protected: - void hideEvent(QHideEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - -signals: - void currRowChanged(int oldRow, int newRow); - -private slots: - void handleCurrRowChange(int oldRow, int newRow); - -private: - int currRow; - int m_visibleItems; - int m_mouseIndex; -}; - -#endif // KIWIXLISTWIDGET_H +#ifndef KIWIXLISTWIDGET_H +#define KIWIXLISTWIDGET_H + +#include + +class KiwixListWidget : public QListWidget { + Q_OBJECT + +public: + KiwixListWidget(QWidget *parent = nullptr); + void moveUp(); + void moveDown(); + void selectCurrent(); + void selectCurrent(QListWidgetItem *item); + void setVisibleItems(int visibleItems) { m_visibleItems = visibleItems; } + int getVisibleItems() { return m_visibleItems; } + +protected: + void hideEvent(QHideEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +signals: + void currRowChanged(int oldRow, int newRow); + +private slots: + void handleCurrRowChange(int oldRow, int newRow); + +private: + int currRow; + int m_visibleItems; + int m_mouseIndex; +}; + +#endif // KIWIXLISTWIDGET_H diff --git a/src/kiwixloader.cpp b/src/kiwixloader.cpp index b4c5063a6..766da52c2 100644 --- a/src/kiwixloader.cpp +++ b/src/kiwixloader.cpp @@ -1,77 +1,77 @@ -#include "kiwixloader.h" -#include -#include -#include -#include - -KiwixLoader::KiwixLoader(QWidget *parent) - : QWidget(parent), m_timer(nullptr) -{ - setFixedSize(parent->width(), parent->height()); - m_timer = new QTimer(this); - connect(m_timer, &QTimer::timeout, this, &KiwixLoader::updateAnimation); -} - -KiwixLoader::~KiwixLoader() -{ -} - -void KiwixLoader::stopAnimation() -{ - m_timer->stop(); -} - -void KiwixLoader::startAnimation() -{ - m_timer->start(20); -} - -void createArc(QPainter &painter, int startAngle, int spanAngle, QRect rectangle, QPen pen) -{ - painter.setRenderHint(QPainter::Antialiasing); - int arcX = rectangle.x(); - int arcY = rectangle.y(); - int arcW = rectangle.width(); - int arcH = rectangle.height(); - QPainterPath path; - path.moveTo(arcX + arcW, arcY + arcH/2); - path.arcTo(rectangle, startAngle, spanAngle); - painter.strokePath(path, pen); -} - -void KiwixLoader::paintEvent(QPaintEvent *event) -{ - Q_UNUSED(event); - - QPainter painter(this); - painter.setRenderHint(QPainter::Antialiasing); - - int width = 100; - int height = 100; - setFixedSize(this->parentWidget()->width(), this->parentWidget()->height()); - int centerX = this->parentWidget()->width()/2 - width; - int centerY = this->parentWidget()->height()/2 - height; - - QPen pen; - pen.setWidth(5); - painter.setPen(pen); - painter.setRenderHint(QPainter::Antialiasing); - - QRect rectangle(centerX, centerY, width, height); - - pen.setColor("#eaecf0"); - createArc(painter, 0, 360, rectangle, pen); - - int startAngle = 0; - int spanAngle = -progress; - pen.setColor("#3366cc"); - createArc(painter, startAngle, spanAngle, rectangle, pen); -} - -void KiwixLoader::updateAnimation() -{ - progress += 10; - if (progress == 360) - progress = 0; - update(); -} +#include "kiwixloader.h" +#include +#include +#include +#include + +KiwixLoader::KiwixLoader(QWidget *parent) + : QWidget(parent), m_timer(nullptr) +{ + setFixedSize(parent->width(), parent->height()); + m_timer = new QTimer(this); + connect(m_timer, &QTimer::timeout, this, &KiwixLoader::updateAnimation); +} + +KiwixLoader::~KiwixLoader() +{ +} + +void KiwixLoader::stopAnimation() +{ + m_timer->stop(); +} + +void KiwixLoader::startAnimation() +{ + m_timer->start(20); +} + +void createArc(QPainter &painter, int startAngle, int spanAngle, QRect rectangle, QPen pen) +{ + painter.setRenderHint(QPainter::Antialiasing); + int arcX = rectangle.x(); + int arcY = rectangle.y(); + int arcW = rectangle.width(); + int arcH = rectangle.height(); + QPainterPath path; + path.moveTo(arcX + arcW, arcY + arcH/2); + path.arcTo(rectangle, startAngle, spanAngle); + painter.strokePath(path, pen); +} + +void KiwixLoader::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + int width = 100; + int height = 100; + setFixedSize(this->parentWidget()->width(), this->parentWidget()->height()); + int centerX = this->parentWidget()->width()/2 - width; + int centerY = this->parentWidget()->height()/2 - height; + + QPen pen; + pen.setWidth(5); + painter.setPen(pen); + painter.setRenderHint(QPainter::Antialiasing); + + QRect rectangle(centerX, centerY, width, height); + + pen.setColor("#eaecf0"); + createArc(painter, 0, 360, rectangle, pen); + + int startAngle = 0; + int spanAngle = -progress; + pen.setColor("#3366cc"); + createArc(painter, startAngle, spanAngle, rectangle, pen); +} + +void KiwixLoader::updateAnimation() +{ + progress += 10; + if (progress == 360) + progress = 0; + update(); +} diff --git a/src/kiwixloader.h b/src/kiwixloader.h index 34a77ca30..5222dc0c7 100644 --- a/src/kiwixloader.h +++ b/src/kiwixloader.h @@ -1,28 +1,28 @@ -#ifndef KIWIXLOADER_H -#define KIWIXLOADER_H - -#include -#include - -class KiwixLoader : public QWidget -{ - Q_OBJECT - -public: - explicit KiwixLoader(QWidget *parent = nullptr); - ~KiwixLoader(); - void startAnimation(); - void stopAnimation(); - -protected: - void paintEvent(QPaintEvent *event) override; - -private slots: - void updateAnimation(); - -private: - QTimer *m_timer; - int progress = 0; -}; - -#endif // KIWIXLOADER_H +#ifndef KIWIXLOADER_H +#define KIWIXLOADER_H + +#include +#include + +class KiwixLoader : public QWidget +{ + Q_OBJECT + +public: + explicit KiwixLoader(QWidget *parent = nullptr); + ~KiwixLoader(); + void startAnimation(); + void stopAnimation(); + +protected: + void paintEvent(QPaintEvent *event) override; + +private slots: + void updateAnimation(); + +private: + QTimer *m_timer; + int progress = 0; +}; + +#endif // KIWIXLOADER_H diff --git a/src/kiwixmessagebox.cpp b/src/kiwixmessagebox.cpp index 2de9c8928..79a94b6bc 100644 --- a/src/kiwixmessagebox.cpp +++ b/src/kiwixmessagebox.cpp @@ -1,66 +1,66 @@ -#include "kiwixmessagebox.h" -#include "ui_kiwixmessagebox.h" -#include -#include "kiwixapp.h" - -KiwixMessageBox::KiwixMessageBox(QString confirmTitle, QString confirmText, bool okDialog, QWidget *parent, - QString leftAction, QString rightAction) : - QDialog(parent), m_confirmTitle(confirmTitle), m_confirmText(confirmText), - ui(new Ui::kiwixmessagebox) -{ - ui->setupUi(this); - setWindowFlag(Qt::FramelessWindowHint, true); - setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/messageBox.css")); - connect(ui->yesButton, &QPushButton::clicked, [=]() { - emit yesClicked(); - m_result = YesClicked; - accept(); - }); - connect(ui->noButton, &QPushButton::clicked, [=]() { - emit noClicked(); - m_result = NoClicked; - reject(); - }); - connect(ui->okButton, &QPushButton::clicked, [=]() { - emit okClicked(); - m_result = OkClicked; - }); - connect(ui->closeButton, &QPushButton::clicked, [=]() { - this->close(); - m_result = CloseClicked; - }); - ui->confirmText->setText(confirmText); - ui->confirmTitle->setText(confirmTitle); - ui->yesButton->setText(leftAction); - ui->noButton->setText(rightAction); - ui->okButton->setText(gt("ok")); - ui->okButton->hide(); - if (okDialog) { - ui->yesButton->hide(); - ui->noButton->hide(); - ui->okButton->show(); - } -} - -KiwixMessageBox::~KiwixMessageBox() -{ - delete ui; -} - -void showInfoBox(QString title, QString text, QWidget *parent) -{ - KiwixMessageBox *dialog = new KiwixMessageBox(title, text, true, parent); - dialog->show(); - QObject::connect(dialog, &KiwixMessageBox::okClicked, [=]() { - dialog->deleteLater(); - }); -} - -KiwixMessageBox::Result showKiwixMessageBox(QString title, QString text, QWidget *parent, QString leftTitle, QString rightTitle) -{ - KiwixMessageBox *dialog = new KiwixMessageBox(title, text, false, parent, leftTitle, rightTitle); - QObject::connect(dialog, &KiwixMessageBox::finished, [=]() { - dialog->deleteLater(); - }); - return dialog->execDialog(); -} +#include "kiwixmessagebox.h" +#include "ui_kiwixmessagebox.h" +#include +#include "kiwixapp.h" + +KiwixMessageBox::KiwixMessageBox(QString confirmTitle, QString confirmText, bool okDialog, QWidget *parent, + QString leftAction, QString rightAction) : + QDialog(parent), m_confirmTitle(confirmTitle), m_confirmText(confirmText), + ui(new Ui::kiwixmessagebox) +{ + ui->setupUi(this); + setWindowFlag(Qt::FramelessWindowHint, true); + setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/messageBox.css")); + connect(ui->yesButton, &QPushButton::clicked, [=]() { + emit yesClicked(); + m_result = YesClicked; + accept(); + }); + connect(ui->noButton, &QPushButton::clicked, [=]() { + emit noClicked(); + m_result = NoClicked; + reject(); + }); + connect(ui->okButton, &QPushButton::clicked, [=]() { + emit okClicked(); + m_result = OkClicked; + }); + connect(ui->closeButton, &QPushButton::clicked, [=]() { + this->close(); + m_result = CloseClicked; + }); + ui->confirmText->setText(confirmText); + ui->confirmTitle->setText(confirmTitle); + ui->yesButton->setText(leftAction); + ui->noButton->setText(rightAction); + ui->okButton->setText(gt("ok")); + ui->okButton->hide(); + if (okDialog) { + ui->yesButton->hide(); + ui->noButton->hide(); + ui->okButton->show(); + } +} + +KiwixMessageBox::~KiwixMessageBox() +{ + delete ui; +} + +void showInfoBox(QString title, QString text, QWidget *parent) +{ + KiwixMessageBox *dialog = new KiwixMessageBox(title, text, true, parent); + dialog->show(); + QObject::connect(dialog, &KiwixMessageBox::okClicked, [=]() { + dialog->deleteLater(); + }); +} + +KiwixMessageBox::Result showKiwixMessageBox(QString title, QString text, QWidget *parent, QString leftTitle, QString rightTitle) +{ + KiwixMessageBox *dialog = new KiwixMessageBox(title, text, false, parent, leftTitle, rightTitle); + QObject::connect(dialog, &KiwixMessageBox::finished, [=]() { + dialog->deleteLater(); + }); + return dialog->execDialog(); +} diff --git a/src/kiwixmessagebox.h b/src/kiwixmessagebox.h index bee76a563..7ab3954b5 100644 --- a/src/kiwixmessagebox.h +++ b/src/kiwixmessagebox.h @@ -1,80 +1,80 @@ -#ifndef KIWIXMESSAGEBOX_H -#define KIWIXMESSAGEBOX_H - -#include -#include "kiwixapp.h" - -#include - -namespace Ui { -class kiwixmessagebox; -} - -class KiwixMessageBox : public QDialog -{ - Q_OBJECT - -public: - KiwixMessageBox(QString confirmTitle, QString confirmText, bool okDialog, QWidget *parent = nullptr, - QString leftAction = gt("yes"), QString rightAction = gt("no")); - ~KiwixMessageBox(); - enum Result { - YesClicked, - NoClicked, - OkClicked, - CloseClicked - }; - Result execDialog() { QDialog::exec(); return m_result; } - -signals: - void yesClicked(); - void noClicked(); - void okClicked(); - -private: - QString m_confirmTitle; - QString m_confirmText; - Ui::kiwixmessagebox *ui; - Result m_result; -}; - - -void showInfoBox(QString title, QString text, QWidget *parent = nullptr); -KiwixMessageBox::Result showKiwixMessageBox(QString title, QString text, QWidget *parent, QString leftTitle, QString rightTitle); - -template -void showConfirmBox(QString title, QString text, QWidget *parent, - YesAction yesAction) -{ - KiwixMessageBox *dialog = new KiwixMessageBox(title, text, false, parent); - dialog->show(); - QObject::connect(dialog, &KiwixMessageBox::yesClicked, [=]() { - yesAction(); - dialog->deleteLater(); - }); - QObject::connect(dialog, &KiwixMessageBox::noClicked, [=]() { - dialog->deleteLater(); - }); -} - -class KiwixAppError : public std::runtime_error -{ -public: - KiwixAppError(const QString& summary, const QString& details) - : std::runtime_error(summary.toStdString()) - , m_details(details) - {} - - QString summary() const { return QString::fromStdString(what()); } - QString details() const { return m_details; } - -private: - QString m_details; -}; - -inline void showErrorBox(const KiwixAppError& err, QWidget *parent = nullptr) -{ - showInfoBox(err.summary(), err.details(), parent); -} - -#endif // KIWIXMESSAGEBOX_H +#ifndef KIWIXMESSAGEBOX_H +#define KIWIXMESSAGEBOX_H + +#include +#include "kiwixapp.h" + +#include + +namespace Ui { +class kiwixmessagebox; +} + +class KiwixMessageBox : public QDialog +{ + Q_OBJECT + +public: + KiwixMessageBox(QString confirmTitle, QString confirmText, bool okDialog, QWidget *parent = nullptr, + QString leftAction = gt("yes"), QString rightAction = gt("no")); + ~KiwixMessageBox(); + enum Result { + YesClicked, + NoClicked, + OkClicked, + CloseClicked + }; + Result execDialog() { QDialog::exec(); return m_result; } + +signals: + void yesClicked(); + void noClicked(); + void okClicked(); + +private: + QString m_confirmTitle; + QString m_confirmText; + Ui::kiwixmessagebox *ui; + Result m_result; +}; + + +void showInfoBox(QString title, QString text, QWidget *parent = nullptr); +KiwixMessageBox::Result showKiwixMessageBox(QString title, QString text, QWidget *parent, QString leftTitle, QString rightTitle); + +template +void showConfirmBox(QString title, QString text, QWidget *parent, + YesAction yesAction) +{ + KiwixMessageBox *dialog = new KiwixMessageBox(title, text, false, parent); + dialog->show(); + QObject::connect(dialog, &KiwixMessageBox::yesClicked, [=]() { + yesAction(); + dialog->deleteLater(); + }); + QObject::connect(dialog, &KiwixMessageBox::noClicked, [=]() { + dialog->deleteLater(); + }); +} + +class KiwixAppError : public std::runtime_error +{ +public: + KiwixAppError(const QString& summary, const QString& details) + : std::runtime_error(summary.toStdString()) + , m_details(details) + {} + + QString summary() const { return QString::fromStdString(what()); } + QString details() const { return m_details; } + +private: + QString m_details; +}; + +inline void showErrorBox(const KiwixAppError& err, QWidget *parent = nullptr) +{ + showInfoBox(err.summary(), err.details(), parent); +} + +#endif // KIWIXMESSAGEBOX_H diff --git a/src/kiwixwebchannelobject.h b/src/kiwixwebchannelobject.h index 9e37d38da..69c9035ba 100644 --- a/src/kiwixwebchannelobject.h +++ b/src/kiwixwebchannelobject.h @@ -1,20 +1,22 @@ -#ifndef KIWIXWEBCHANNELOBJECT_H -#define KIWIXWEBCHANNELOBJECT_H - -#include - -class KiwixWebChannelObject : public QObject -{ - Q_OBJECT - -public: - explicit KiwixWebChannelObject(QObject *parent = nullptr) : QObject(parent) {}; - - Q_INVOKABLE void sendHeadersJSONStr(const QString& headersJSONStr) { emit headersChanged(headersJSONStr); }; - -signals: - void headersChanged(const QString& headersJSONStr); - void navigationRequested(const QString& url, const QString& anchor); -}; - -#endif // KIWIXWEBCHANNELOBJECT_H +#ifndef KIWIXWEBCHANNELOBJECT_H +#define KIWIXWEBCHANNELOBJECT_H + +#include + +class KiwixWebChannelObject : public QObject +{ + Q_OBJECT + +public: + explicit KiwixWebChannelObject(QObject *parent = nullptr) : QObject(parent) {}; + + Q_INVOKABLE void sendHeadersJSONStr(const QString& headersJSONStr) { emit headersChanged(headersJSONStr); }; + Q_INVOKABLE void sendConsoleMessage(const QString& message) { emit consoleMessageReceived(message); }; + +signals: + void headersChanged(const QString& headersJSONStr); + void navigationRequested(const QString& url, const QString& anchor); + void consoleMessageReceived(const QString& message); +}; + +#endif // KIWIXWEBCHANNELOBJECT_H diff --git a/src/klistwidgetitem.cpp b/src/klistwidgetitem.cpp index b77ef6076..371676e29 100644 --- a/src/klistwidgetitem.cpp +++ b/src/klistwidgetitem.cpp @@ -1,41 +1,41 @@ -#include "klistwidgetitem.h" - -int KListWidgetItem::m_itemHeight = 35; - -KListWidgetItem::KListWidgetItem(QString text) - : QListWidgetItem (text) -{ - setSizeHint(QSize(200, m_itemHeight)); - setBackground(QColor("white")); - setForeground(QColor("#666666")); -} - -void KListWidgetItem::disableHighlight() -{ - isHighlighted = false; -} - -void KListWidgetItem::enableHighlight() -{ - isHighlighted = true; -} - -QVariant KListWidgetItem::data(int role) const -{ - QVariant v = QListWidgetItem::data(role); - if( isSelected()) { - if (role == Qt::FontRole) { - QFont font = v.value(); - font.setBold( true ); - v = QVariant::fromValue( font ); - } - } - if (isHighlighted) { - if (role == Qt::BackgroundRole) { - v = QVariant::fromValue(QColor("#4e63ad")); - } else if (role == Qt::ForegroundRole) { - v = QVariant::fromValue(QColor("white")); - } - } - return v; -} +#include "klistwidgetitem.h" + +int KListWidgetItem::m_itemHeight = 35; + +KListWidgetItem::KListWidgetItem(QString text) + : QListWidgetItem (text) +{ + setSizeHint(QSize(200, m_itemHeight)); + setBackground(QColor("white")); + setForeground(QColor("#666666")); +} + +void KListWidgetItem::disableHighlight() +{ + isHighlighted = false; +} + +void KListWidgetItem::enableHighlight() +{ + isHighlighted = true; +} + +QVariant KListWidgetItem::data(int role) const +{ + QVariant v = QListWidgetItem::data(role); + if( isSelected()) { + if (role == Qt::FontRole) { + QFont font = v.value(); + font.setBold( true ); + v = QVariant::fromValue( font ); + } + } + if (isHighlighted) { + if (role == Qt::BackgroundRole) { + v = QVariant::fromValue(QColor("#4e63ad")); + } else if (role == Qt::ForegroundRole) { + v = QVariant::fromValue(QColor("white")); + } + } + return v; +} diff --git a/src/klistwidgetitem.h b/src/klistwidgetitem.h index 493fe0734..f0c4c4c54 100644 --- a/src/klistwidgetitem.h +++ b/src/klistwidgetitem.h @@ -1,19 +1,19 @@ -#ifndef KLISTWIDGETITEM_H -#define KLISTWIDGETITEM_H - -#include - -class KListWidgetItem : public QListWidgetItem -{ -public: - KListWidgetItem(QString text); - QVariant data(int role) const; - static int getItemHeight() { return m_itemHeight; }; - void disableHighlight(); - void enableHighlight(); -private: - static int m_itemHeight; - bool isHighlighted = false; -}; - -#endif // KLISTWIDGETITEM_H +#ifndef KLISTWIDGETITEM_H +#define KLISTWIDGETITEM_H + +#include + +class KListWidgetItem : public QListWidgetItem +{ +public: + KListWidgetItem(QString text); + QVariant data(int role) const; + static int getItemHeight() { return m_itemHeight; }; + void disableHighlight(); + void enableHighlight(); +private: + static int m_itemHeight; + bool isHighlighted = false; +}; + +#endif // KLISTWIDGETITEM_H diff --git a/src/kprofile.cpp b/src/kprofile.cpp index 6c5eb28bf..747744af4 100644 --- a/src/kprofile.cpp +++ b/src/kprofile.cpp @@ -1,174 +1,174 @@ -#include "kprofile.h" - -#include "kiwixapp.h" -#include "kiwixmessagebox.h" - -#include -#include -#include -#include -#include -#include - -namespace -{ - -QWebEngineScript getScript(QString filename, - QWebEngineScript::InjectionPoint point = QWebEngineScript::DocumentReady) -{ - QWebEngineScript script; - script.setInjectionPoint(point); - script.setWorldId(QWebEngineScript::UserWorld); - - QFile scriptFile(filename); - scriptFile.open(QIODevice::ReadOnly); - script.setSourceCode(scriptFile.readAll()); - return script; -} - -} - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - #define DownloadFinishedSignal WebEngineDownloadType::finished -#else - #define DownloadFinishedSignal WebEngineDownloadType::isFinishedChanged -#endif - -QString askForSaveFilePath(const QString& suggestedName) -{ - const auto app = KiwixApp::instance(); - const QString suggestedPath = app->getPrevSaveDir() + "/" + suggestedName; - const QString extension = suggestedName.section(".", -1); - const QString filter = extension.isEmpty() ? "" : "(*." + extension + ")"; - QString fileName = QFileDialog::getSaveFileName( - app->getMainWindow(), gt("save-file-as-window-title"), - suggestedPath, filter); - - if (fileName.isEmpty()) - return QString(); - - if (!fileName.endsWith(extension)) { - fileName.append(extension); - } - app->savePrevSaveDir(QFileInfo(fileName).absolutePath()); - return fileName; -} - -KProfile::KProfile(QObject *parent) : - QWebEngineProfile(parent) -{ - connect(this, &QWebEngineProfile::downloadRequested, this, &KProfile::startDownload); - installUrlSchemeHandler("zim", &m_schemeHandler); - settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); -#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) // Earlier than Qt 5.13 - setRequestInterceptor(new ExternalReqInterceptor(this)); -#else // Qt 5.13 and later - setUrlRequestInterceptor(new ExternalReqInterceptor(this)); -#endif - - scripts()->insert(getScript(":/js/headerAnchor.js")); - scripts()->insert(getScript(":/qtwebchannel/qwebchannel.js", - QWebEngineScript::DocumentCreation)); -} - -namespace -{ - -void setDownloadFilePath(WebEngineDownloadType* download, QString filePath) -{ -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - // WebEngineDownloadType is QWebEngineDownloadItem - // and no QWebEngineDownloadItem::setDownloadFileName() yet - download->setPath(filePath); -#else - // Same API for QWebEngineDownloadItem and QWebEngineDownloadRequest - download->setDownloadFileName(filePath); -#endif -} - -QString getDownloadFilePath(WebEngineDownloadType* download) -{ -#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) - // WebEngineDownloadType is QWebEngineDownloadItem - // and no QWebEngineDownloadItem::downloadFileName() yet - return QUrl(download->path()).fileName(); -#else - // Same API for QWebEngineDownloadItem and QWebEngineDownloadRequest - return download->downloadFileName(); -#endif -} - -} // unnamed namespace - -void KProfile::openFile(WebEngineDownloadType* download) -{ - const QString defaultFileName = getDownloadFilePath(download); - QTemporaryFile tempFile(QDir::tempPath() + "/XXXXXX." + QFileInfo(defaultFileName).suffix()); - tempFile.setAutoRemove(false); - if (tempFile.open()) { - QString tempFilePath = tempFile.fileName(); - tempFile.close(); - setDownloadFilePath(download, tempFilePath); - connect(download, &DownloadFinishedSignal, [tempFilePath]() { - if(!QDesktopServices::openUrl(QUrl::fromLocalFile(tempFilePath))) - showInfoBox(gt("error-title"), gt("error-opening-file"), KiwixApp::instance()->getMainWindow()); - }); - download->accept(); - } else { - qDebug()<<"tmp file err"; - download->cancel(); - } -} - -void KProfile::saveFile(WebEngineDownloadType* download) -{ - const QString defaultFileName = getDownloadFilePath(download); - const QString fileName = askForSaveFilePath(defaultFileName); - if (fileName.isEmpty()) { - download->cancel(); - return; - } - - setDownloadFilePath(download, fileName); - connect(download, &DownloadFinishedSignal, this, &KProfile::downloadFinished); - download->accept(); -} - -void KProfile::downloadFinished() -{ - showInfoBox(gt("download-finished"), - gt("download-finished-message"), - KiwixApp::instance()->getMainWindow() - ); -} - -void KProfile::startDownload(WebEngineDownloadType* download) -{ - const auto res = showKiwixMessageBox( - gt("save-or-open"), - gt("save-or-open-text"), - KiwixApp::instance()->getMainWindow(), - gt("save-file"), - gt("open-file") - ); - - if (res == KiwixMessageBox::YesClicked) { - saveFile(download); - return; - } - if (res == KiwixMessageBox::NoClicked) { - openFile(download); - return; - } - download->cancel(); -} - -void ExternalReqInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) -{ - const QString reqUrl = info.requestUrl().toString(); - if (!reqUrl.startsWith("zim://")) - { - qDebug() << "Blocked external request to URL: " << reqUrl; - info.block(true); - } -} +#include "kprofile.h" + +#include "kiwixapp.h" +#include "kiwixmessagebox.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + +QWebEngineScript getScript(QString filename, + QWebEngineScript::InjectionPoint point = QWebEngineScript::DocumentReady) +{ + QWebEngineScript script; + script.setInjectionPoint(point); + script.setWorldId(QWebEngineScript::UserWorld); + + QFile scriptFile(filename); + scriptFile.open(QIODevice::ReadOnly); + script.setSourceCode(scriptFile.readAll()); + return script; +} + +} + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + #define DownloadFinishedSignal WebEngineDownloadType::finished +#else + #define DownloadFinishedSignal WebEngineDownloadType::isFinishedChanged +#endif + +QString askForSaveFilePath(const QString& suggestedName) +{ + const auto app = KiwixApp::instance(); + const QString suggestedPath = app->getPrevSaveDir() + "/" + suggestedName; + const QString extension = suggestedName.section(".", -1); + const QString filter = extension.isEmpty() ? "" : "(*." + extension + ")"; + QString fileName = QFileDialog::getSaveFileName( + app->getMainWindow(), gt("save-file-as-window-title"), + suggestedPath, filter); + + if (fileName.isEmpty()) + return QString(); + + if (!fileName.endsWith(extension)) { + fileName.append(extension); + } + app->savePrevSaveDir(QFileInfo(fileName).absolutePath()); + return fileName; +} + +KProfile::KProfile(QObject *parent) : + QWebEngineProfile(parent) +{ + connect(this, &QWebEngineProfile::downloadRequested, this, &KProfile::startDownload); + installUrlSchemeHandler("zim", &m_schemeHandler); + settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); +#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) // Earlier than Qt 5.13 + setRequestInterceptor(new ExternalReqInterceptor(this)); +#else // Qt 5.13 and later + setUrlRequestInterceptor(new ExternalReqInterceptor(this)); +#endif + + scripts()->insert(getScript(":/js/headerAnchor.js")); + scripts()->insert(getScript(":/qtwebchannel/qwebchannel.js", + QWebEngineScript::DocumentCreation)); +} + +namespace +{ + +void setDownloadFilePath(WebEngineDownloadType* download, QString filePath) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + // WebEngineDownloadType is QWebEngineDownloadItem + // and no QWebEngineDownloadItem::setDownloadFileName() yet + download->setPath(filePath); +#else + // Same API for QWebEngineDownloadItem and QWebEngineDownloadRequest + download->setDownloadFileName(filePath); +#endif +} + +QString getDownloadFilePath(WebEngineDownloadType* download) +{ +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) + // WebEngineDownloadType is QWebEngineDownloadItem + // and no QWebEngineDownloadItem::downloadFileName() yet + return QUrl(download->path()).fileName(); +#else + // Same API for QWebEngineDownloadItem and QWebEngineDownloadRequest + return download->downloadFileName(); +#endif +} + +} // unnamed namespace + +void KProfile::openFile(WebEngineDownloadType* download) +{ + const QString defaultFileName = getDownloadFilePath(download); + QTemporaryFile tempFile(QDir::tempPath() + "/XXXXXX." + QFileInfo(defaultFileName).suffix()); + tempFile.setAutoRemove(false); + if (tempFile.open()) { + QString tempFilePath = tempFile.fileName(); + tempFile.close(); + setDownloadFilePath(download, tempFilePath); + connect(download, &DownloadFinishedSignal, [tempFilePath]() { + if(!QDesktopServices::openUrl(QUrl::fromLocalFile(tempFilePath))) + showInfoBox(gt("error-title"), gt("error-opening-file"), KiwixApp::instance()->getMainWindow()); + }); + download->accept(); + } else { + qDebug()<<"tmp file err"; + download->cancel(); + } +} + +void KProfile::saveFile(WebEngineDownloadType* download) +{ + const QString defaultFileName = getDownloadFilePath(download); + const QString fileName = askForSaveFilePath(defaultFileName); + if (fileName.isEmpty()) { + download->cancel(); + return; + } + + setDownloadFilePath(download, fileName); + connect(download, &DownloadFinishedSignal, this, &KProfile::downloadFinished); + download->accept(); +} + +void KProfile::downloadFinished() +{ + showInfoBox(gt("download-finished"), + gt("download-finished-message"), + KiwixApp::instance()->getMainWindow() + ); +} + +void KProfile::startDownload(WebEngineDownloadType* download) +{ + const auto res = showKiwixMessageBox( + gt("save-or-open"), + gt("save-or-open-text"), + KiwixApp::instance()->getMainWindow(), + gt("save-file"), + gt("open-file") + ); + + if (res == KiwixMessageBox::YesClicked) { + saveFile(download); + return; + } + if (res == KiwixMessageBox::NoClicked) { + openFile(download); + return; + } + download->cancel(); +} + +void ExternalReqInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + const QString reqUrl = info.requestUrl().toString(); + if (!reqUrl.startsWith("zim://")) + { + qDebug() << "Blocked external request to URL: " << reqUrl; + info.block(true); + } +} diff --git a/src/kprofile.h b/src/kprofile.h index 42a9d6273..cd0cc14f0 100644 --- a/src/kprofile.h +++ b/src/kprofile.h @@ -1,51 +1,51 @@ -#ifndef KPROFILE_H -#define KPROFILE_H - -#include -#include -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -#include -typedef QWebEngineDownloadItem WebEngineDownloadType; -#else -#include -typedef QWebEngineDownloadRequest WebEngineDownloadType; -#endif - -#include "urlschemehandler.h" - -class KProfile : public QWebEngineProfile -{ - Q_OBJECT -public: - KProfile(QObject *parent = nullptr); - -private: - UrlSchemeHandler m_schemeHandler; - -signals: - -private slots: - void startDownload(WebEngineDownloadType*); - void downloadFinished(); - void saveFile(WebEngineDownloadType*); - void openFile(WebEngineDownloadType*); -}; - -/** - * @brief Intercepts and blocks a request if it is not native to our zim file. - * https://stackoverflow.com/questions/70721311/qwebview-disable-external-resources - */ -class ExternalReqInterceptor : public QWebEngineUrlRequestInterceptor -{ - Q_OBJECT -public: - explicit ExternalReqInterceptor(QObject *parent = nullptr) - : QWebEngineUrlRequestInterceptor(parent) - { - } - -protected: - void interceptRequest(QWebEngineUrlRequestInfo &info) override; -}; - -#endif // KPROFILE_H +#ifndef KPROFILE_H +#define KPROFILE_H + +#include +#include +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +typedef QWebEngineDownloadItem WebEngineDownloadType; +#else +#include +typedef QWebEngineDownloadRequest WebEngineDownloadType; +#endif + +#include "urlschemehandler.h" + +class KProfile : public QWebEngineProfile +{ + Q_OBJECT +public: + KProfile(QObject *parent = nullptr); + +private: + UrlSchemeHandler m_schemeHandler; + +signals: + +private slots: + void startDownload(WebEngineDownloadType*); + void downloadFinished(); + void saveFile(WebEngineDownloadType*); + void openFile(WebEngineDownloadType*); +}; + +/** + * @brief Intercepts and blocks a request if it is not native to our zim file. + * https://stackoverflow.com/questions/70721311/qwebview-disable-external-resources + */ +class ExternalReqInterceptor : public QWebEngineUrlRequestInterceptor +{ + Q_OBJECT +public: + explicit ExternalReqInterceptor(QObject *parent = nullptr) + : QWebEngineUrlRequestInterceptor(parent) + { + } + +protected: + void interceptRequest(QWebEngineUrlRequestInfo &info) override; +}; + +#endif // KPROFILE_H diff --git a/src/library.cpp b/src/library.cpp index aeea52a26..60fccf342 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -1,244 +1,244 @@ -#include "library.h" -#include "kiwixapp.h" - -#include -#include -#include - -#include -#include - - -class LibraryManipulator: public kiwix::LibraryManipulator { - public: - LibraryManipulator(Library* p_library) - : kiwix::LibraryManipulator(p_library->getKiwixLibrary()) - , mp_library(p_library) - {} - virtual ~LibraryManipulator() {} - bool addBookToLibrary(kiwix::Book book) { - auto ret = mp_library->mp_library->addBook(book); - emit(mp_library->booksChanged()); - return ret; - } - void addBookmarkToLibrary(kiwix::Bookmark bookmark) { - mp_library->mp_library->addBookmark(bookmark); - } - Library* mp_library; -}; - -Library::Library(const QString& libraryDirectory) - : mp_library(kiwix::Library::create()), - m_libraryDirectory(libraryDirectory) -{ - auto manager = kiwix::Manager(LibraryManipulator(this)); - manager.readFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(),"library.xml"), false); - manager.readBookmarkFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(),"library.bookmarks.xml")); - emit(booksChanged()); -} - -Library::~Library() -{ - save(); -} - -QString Library::openBookFromPath(const QString &zimPath) -{ - try { - auto& book = mp_library->getBookByPath(zimPath.toStdString()); - return QString::fromStdString(book.getId()); - } catch(std::out_of_range& e) { } - - kiwix::Manager manager(mp_library); - auto id = manager.addBookFromPathAndGetId(zimPath.toStdString()); - if (id == "") { - throw std::invalid_argument("invalid zim file"); - } - save(); - emit(booksChanged()); - return QString::fromStdString(id); -} - -std::shared_ptr Library::getArchive(const QString &zimId) -{ - const auto archive = mp_library->getArchiveById(zimId.toStdString()); - if ( ! archive ) { - throw std::out_of_range("ZIM file doesn't exist (or cannot be opened)"); - } - return archive; -} - -std::shared_ptr Library::getSearcher(const QString &zimId) -{ - return mp_library->getSearcherById(zimId.toStdString()); -} - -QIcon Library::getBookIcon(const QString &zimId) -{ - static QIcon defaultIcon = QIcon(":/icons/placeholder-icon.png"); - try - { - const auto& book = getBookById(zimId); - const auto illustration = book.getIllustration(48); - const auto& content = illustration->getData(); - QPixmap pixmap; - pixmap.loadFromData((const uchar*)content.data(), content.size()); - return QIcon(pixmap); - } - catch (...) - { - return defaultIcon; - } -} - -QStringList Library::getBookIds() const -{ - QStringList list; - for(auto& id: mp_library->getBooksIds()) { - list.append(QString::fromStdString(id)); - } - return list; -} - -QStringList Library::listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending) const -{ - QStringList list; - auto bookIds = mp_library->filter(filter); - mp_library->sort(bookIds, sortBy, ascending); - for(auto& id: bookIds) { - list.append(QString::fromStdString(id)); - } - return list; -} - -void Library::addBookToLibrary(kiwix::Book &book) -{ - mp_library->addBook(book); -} - -void Library::removeBookFromLibraryById(const QString& id) { - mp_library->removeBookById(id.toStdString()); -} - -namespace -{ - -const char BEINGDOWNLOADEDSUFFIX[] = ".beingdownloadedbykiwix"; - -std::string pseudoPathOfAFileBeingDownloaded(const std::string& path) -{ - return path + BEINGDOWNLOADEDSUFFIX; -} - -std::string dropSuffix(const std::string& str, const std::string& suffix) -{ - const size_t s = suffix.size(); - const size_t n = str.size(); - return n > s && str.substr(n - s, s) == suffix - ? str.substr(0, n - s) - : str; -} - -} // unnamed namespace - -std::string Library::getBookFilePath(const QString& bookId) const -{ - const auto& book = getBookById(bookId); - const std::string bookPath = book.getPath(); - return book.getDownloadId().empty() - ? bookPath - : dropSuffix(bookPath, BEINGDOWNLOADEDSUFFIX); -} - -void Library::addBookBeingDownloaded(const kiwix::Book& book, QString downloadDir) -{ - const QString downloadUrl = QString::fromStdString(book.getUrl()); - - // XXX: This works if the URL is a direct link to a ZIM file - // XXX: rather than to a torrent or a metalink file. In those cases - // XXX: the file name of the download will be discovered after the - // XXX: the metalink or torrent file is downloaded. Then the real - // XXX: file name of a book must be set via updateBookBeingDownloaded(); - const QString fileName = downloadUrl.split('/').back(); - - const QString path = QDir(downloadDir).absoluteFilePath(fileName); - const std::string stlPath = QDir::toNativeSeparators(path).toStdString(); - - kiwix::Book bookCopy(book); - bookCopy.setPath(pseudoPathOfAFileBeingDownloaded(stlPath)); - addBookToLibrary(bookCopy); -} - -void Library::updateBookBeingDownloaded(const QString& bookId, const QString& bookPath) -{ - if ( bookPath.isEmpty() ) - return; - - const kiwix::Book& book = getBookById(bookId); - const auto bookPseudoPath = pseudoPathOfAFileBeingDownloaded(bookPath.toStdString()); - if ( bookPseudoPath != book.getPath() ) { - kiwix::Book bookCopy(book); - bookCopy.setPath(bookPseudoPath); - mp_library->addOrUpdateBook(bookCopy); - save(); - } -} - -bool Library::isBeingDownloadedByUs(QString path) const -{ - const auto fakePath = pseudoPathOfAFileBeingDownloaded(path.toStdString()); - try { - mp_library->getBookByPath(fakePath); - return true; - } catch ( const std::out_of_range& ) { - return false; - } -} - -void Library::addBookmark(kiwix::Bookmark &bookmark) -{ - mp_library->addBookmark(bookmark); - emit bookmarksChanged(); -} - -void Library::removeBookmark(const QString &zimId, const QString &url) -{ - mp_library->removeBookmark(zimId.toStdString(), url.toStdString()); - emit bookmarksChanged(); -} - -void Library::save() -{ - mp_library->writeToFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(),"library.xml")); - mp_library->writeBookmarksToFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(), "library.bookmarks.xml")); -} - -Library::QStringSet Library::getLibraryZimsFromDir(QString dir) const -{ - QStringSet zimsInDir; - for (auto str : getBookIds()) { - auto filePath = QString::fromStdString(getBookById(str).getPath()); - if ( filePath.endsWith(BEINGDOWNLOADEDSUFFIX) ) - continue; - const QFileInfo fileInfo(filePath); - if (fileInfo.absoluteDir() == dir) { - zimsInDir.insert(fileInfo.fileName()); - } - } - return zimsInDir; -} - -bool Library::readBookMarksFile(const std::string &filename) -{ - kiwix::Manager manager(mp_library); - if (!manager.readBookmarkFile(filename)) - return false; - - emit bookmarksChanged(); - return true; -} - -const kiwix::Book &Library::getBookById(QString id) const -{ - return mp_library->getBookById(id.toStdString()); -} +#include "library.h" +#include "kiwixapp.h" + +#include +#include +#include + +#include +#include + + +class LibraryManipulator: public kiwix::LibraryManipulator { + public: + LibraryManipulator(Library* p_library) + : kiwix::LibraryManipulator(p_library->getKiwixLibrary()) + , mp_library(p_library) + {} + virtual ~LibraryManipulator() {} + bool addBookToLibrary(kiwix::Book book) { + auto ret = mp_library->mp_library->addBook(book); + emit(mp_library->booksChanged()); + return ret; + } + void addBookmarkToLibrary(kiwix::Bookmark bookmark) { + mp_library->mp_library->addBookmark(bookmark); + } + Library* mp_library; +}; + +Library::Library(const QString& libraryDirectory) + : mp_library(kiwix::Library::create()), + m_libraryDirectory(libraryDirectory) +{ + auto manager = kiwix::Manager(LibraryManipulator(this)); + manager.readFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(),"library.xml"), false); + manager.readBookmarkFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(),"library.bookmarks.xml")); + emit(booksChanged()); +} + +Library::~Library() +{ + save(); +} + +QString Library::openBookFromPath(const QString &zimPath) +{ + try { + auto& book = mp_library->getBookByPath(zimPath.toStdString()); + return QString::fromStdString(book.getId()); + } catch(std::out_of_range& e) { } + + kiwix::Manager manager(mp_library); + auto id = manager.addBookFromPathAndGetId(zimPath.toStdString()); + if (id == "") { + throw std::invalid_argument("invalid zim file"); + } + save(); + emit(booksChanged()); + return QString::fromStdString(id); +} + +std::shared_ptr Library::getArchive(const QString &zimId) +{ + const auto archive = mp_library->getArchiveById(zimId.toStdString()); + if ( ! archive ) { + throw std::out_of_range("ZIM file doesn't exist (or cannot be opened)"); + } + return archive; +} + +std::shared_ptr Library::getSearcher(const QString &zimId) +{ + return mp_library->getSearcherById(zimId.toStdString()); +} + +QIcon Library::getBookIcon(const QString &zimId) +{ + static QIcon defaultIcon = QIcon(":/icons/placeholder-icon.png"); + try + { + const auto& book = getBookById(zimId); + const auto illustration = book.getIllustration(48); + const auto& content = illustration->getData(); + QPixmap pixmap; + pixmap.loadFromData((const uchar*)content.data(), content.size()); + return QIcon(pixmap); + } + catch (...) + { + return defaultIcon; + } +} + +QStringList Library::getBookIds() const +{ + QStringList list; + for(auto& id: mp_library->getBooksIds()) { + list.append(QString::fromStdString(id)); + } + return list; +} + +QStringList Library::listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending) const +{ + QStringList list; + auto bookIds = mp_library->filter(filter); + mp_library->sort(bookIds, sortBy, ascending); + for(auto& id: bookIds) { + list.append(QString::fromStdString(id)); + } + return list; +} + +void Library::addBookToLibrary(kiwix::Book &book) +{ + mp_library->addBook(book); +} + +void Library::removeBookFromLibraryById(const QString& id) { + mp_library->removeBookById(id.toStdString()); +} + +namespace +{ + +const char BEINGDOWNLOADEDSUFFIX[] = ".beingdownloadedbykiwix"; + +std::string pseudoPathOfAFileBeingDownloaded(const std::string& path) +{ + return path + BEINGDOWNLOADEDSUFFIX; +} + +std::string dropSuffix(const std::string& str, const std::string& suffix) +{ + const size_t s = suffix.size(); + const size_t n = str.size(); + return n > s && str.substr(n - s, s) == suffix + ? str.substr(0, n - s) + : str; +} + +} // unnamed namespace + +std::string Library::getBookFilePath(const QString& bookId) const +{ + const auto& book = getBookById(bookId); + const std::string bookPath = book.getPath(); + return book.getDownloadId().empty() + ? bookPath + : dropSuffix(bookPath, BEINGDOWNLOADEDSUFFIX); +} + +void Library::addBookBeingDownloaded(const kiwix::Book& book, QString downloadDir) +{ + const QString downloadUrl = QString::fromStdString(book.getUrl()); + + // XXX: This works if the URL is a direct link to a ZIM file + // XXX: rather than to a torrent or a metalink file. In those cases + // XXX: the file name of the download will be discovered after the + // XXX: the metalink or torrent file is downloaded. Then the real + // XXX: file name of a book must be set via updateBookBeingDownloaded(); + const QString fileName = downloadUrl.split('/').back(); + + const QString path = QDir(downloadDir).absoluteFilePath(fileName); + const std::string stlPath = QDir::toNativeSeparators(path).toStdString(); + + kiwix::Book bookCopy(book); + bookCopy.setPath(pseudoPathOfAFileBeingDownloaded(stlPath)); + addBookToLibrary(bookCopy); +} + +void Library::updateBookBeingDownloaded(const QString& bookId, const QString& bookPath) +{ + if ( bookPath.isEmpty() ) + return; + + const kiwix::Book& book = getBookById(bookId); + const auto bookPseudoPath = pseudoPathOfAFileBeingDownloaded(bookPath.toStdString()); + if ( bookPseudoPath != book.getPath() ) { + kiwix::Book bookCopy(book); + bookCopy.setPath(bookPseudoPath); + mp_library->addOrUpdateBook(bookCopy); + save(); + } +} + +bool Library::isBeingDownloadedByUs(QString path) const +{ + const auto fakePath = pseudoPathOfAFileBeingDownloaded(path.toStdString()); + try { + mp_library->getBookByPath(fakePath); + return true; + } catch ( const std::out_of_range& ) { + return false; + } +} + +void Library::addBookmark(kiwix::Bookmark &bookmark) +{ + mp_library->addBookmark(bookmark); + emit bookmarksChanged(); +} + +void Library::removeBookmark(const QString &zimId, const QString &url) +{ + mp_library->removeBookmark(zimId.toStdString(), url.toStdString()); + emit bookmarksChanged(); +} + +void Library::save() +{ + mp_library->writeToFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(),"library.xml")); + mp_library->writeBookmarksToFile(kiwix::appendToDirectory(m_libraryDirectory.toStdString(), "library.bookmarks.xml")); +} + +Library::QStringSet Library::getLibraryZimsFromDir(QString dir) const +{ + QStringSet zimsInDir; + for (auto str : getBookIds()) { + auto filePath = QString::fromStdString(getBookById(str).getPath()); + if ( filePath.endsWith(BEINGDOWNLOADEDSUFFIX) ) + continue; + const QFileInfo fileInfo(filePath); + if (fileInfo.absoluteDir() == dir) { + zimsInDir.insert(fileInfo.fileName()); + } + } + return zimsInDir; +} + +bool Library::readBookMarksFile(const std::string &filename) +{ + kiwix::Manager manager(mp_library); + if (!manager.readBookmarkFile(filename)) + return false; + + emit bookmarksChanged(); + return true; +} + +const kiwix::Book &Library::getBookById(QString id) const +{ + return mp_library->getBookById(id.toStdString()); +} diff --git a/src/library.h b/src/library.h index 374265dbe..c1e9799d0 100644 --- a/src/library.h +++ b/src/library.h @@ -1,66 +1,66 @@ -#ifndef LIBRARY_H -#define LIBRARY_H - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define TQS(v) (QString::fromStdString(v)) -#define FORWARD_GETTER(METH) QString METH() const { return TQS(mp_book->METH()); } - -#undef FORWARD_GETTER -#undef TQS - -class LibraryManipulator; - -class Library : public QObject -{ - Q_OBJECT - Q_PROPERTY(QStringList bookIds READ getBookIds NOTIFY booksChanged) -public: - typedef QSet QStringSet; - - Library(const QString& libraryDirectory); - virtual ~Library(); - QString openBookFromPath(const QString& zimPath); - std::shared_ptr getArchive(const QString& zimId); - std::shared_ptr getSearcher(const QString& zimId); - QIcon getBookIcon(const QString& zimId); - QStringList getBookIds() const; - QStringList listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending) const; - const std::vector getBookmarks(bool onlyValidBookmarks = false) const { return mp_library->getBookmarks(onlyValidBookmarks); } - QStringSet getLibraryZimsFromDir(QString dir) const; - void addBookToLibrary(kiwix::Book& book); - void addBookBeingDownloaded(const kiwix::Book& book, QString downloadDir); - bool isBeingDownloadedByUs(QString path) const; - void updateBookBeingDownloaded(const QString& bookId, const QString& bookPath); - std::string getBookFilePath(const QString& bookId) const; - void removeBookFromLibraryById(const QString& id); - void addBookmark(kiwix::Bookmark& bookmark); - void removeBookmark(const QString& zimId, const QString& url); - bool readBookMarksFile(const std::string& filename); - void save(); - kiwix::LibraryPtr getKiwixLibrary() { return mp_library; } -public slots: - const kiwix::Book& getBookById(QString id) const; - -signals: - void booksChanged(); - void bookmarksChanged(); - -private: - kiwix::LibraryPtr mp_library; - QString m_libraryDirectory; -friend class LibraryManipulator; -}; - -#endif // LIBRARY_H +#ifndef LIBRARY_H +#define LIBRARY_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TQS(v) (QString::fromStdString(v)) +#define FORWARD_GETTER(METH) QString METH() const { return TQS(mp_book->METH()); } + +#undef FORWARD_GETTER +#undef TQS + +class LibraryManipulator; + +class Library : public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList bookIds READ getBookIds NOTIFY booksChanged) +public: + typedef QSet QStringSet; + + Library(const QString& libraryDirectory); + virtual ~Library(); + QString openBookFromPath(const QString& zimPath); + std::shared_ptr getArchive(const QString& zimId); + std::shared_ptr getSearcher(const QString& zimId); + QIcon getBookIcon(const QString& zimId); + QStringList getBookIds() const; + QStringList listBookIds(const kiwix::Filter& filter, kiwix::supportedListSortBy sortBy, bool ascending) const; + const std::vector getBookmarks(bool onlyValidBookmarks = false) const { return mp_library->getBookmarks(onlyValidBookmarks); } + QStringSet getLibraryZimsFromDir(QString dir) const; + void addBookToLibrary(kiwix::Book& book); + void addBookBeingDownloaded(const kiwix::Book& book, QString downloadDir); + bool isBeingDownloadedByUs(QString path) const; + void updateBookBeingDownloaded(const QString& bookId, const QString& bookPath); + std::string getBookFilePath(const QString& bookId) const; + void removeBookFromLibraryById(const QString& id); + void addBookmark(kiwix::Bookmark& bookmark); + void removeBookmark(const QString& zimId, const QString& url); + bool readBookMarksFile(const std::string& filename); + void save(); + kiwix::LibraryPtr getKiwixLibrary() { return mp_library; } +public slots: + const kiwix::Book& getBookById(QString id) const; + +signals: + void booksChanged(); + void bookmarksChanged(); + +private: + kiwix::LibraryPtr mp_library; + QString m_libraryDirectory; +friend class LibraryManipulator; +}; + +#endif // LIBRARY_H diff --git a/src/localkiwixserver.cpp b/src/localkiwixserver.cpp index 7bfb6bcaa..8a4a8e83a 100644 --- a/src/localkiwixserver.cpp +++ b/src/localkiwixserver.cpp @@ -1,134 +1,134 @@ -#include "localkiwixserver.h" -#include "ui_localkiwixserver.h" -#include "kiwixapp.h" -#include -#include -#include -#include - -LocalKiwixServer::LocalKiwixServer(QWidget *parent) : - QDialog(parent), - ui(new Ui::LocalKiwixServer) -{ - setWindowFlag(Qt::FramelessWindowHint, true); - ui->setupUi(this); - - QFile styleFile(":/css/localServer.css"); - styleFile.open(QIODevice::ReadOnly); - auto byteContent = styleFile.readAll(); - styleFile.close(); - QString style(byteContent); - setStyleSheet(style); - - mp_server = KiwixApp::instance()->getLocalServer(); - m_port = KiwixApp::instance()->getSettingsManager()->getKiwixServerPort(); - - connect(ui->KiwixServerButton, SIGNAL(clicked()), this, SLOT(runOrStopServer())); - connect(ui->OpenInBrowserButton, SIGNAL(clicked()), this, SLOT(openInBrowser())); - connect(ui->closeButton, &QPushButton::clicked, this, &LocalKiwixServer::close); - connect(ui->PortChooser, &QLineEdit::textChanged, ui->PortChooser, [=](const QString &text) { - if (text.toInt() > 65535) { - QString validText = text; - validText.chop(1); - ui->PortChooser->setText(validText); - } else if (text.toInt() < 1) { - QString defaultPort = QString::number(KiwixApp::instance()->getSettingsManager()->getKiwixServerPort()); - ui->PortChooser->setText(defaultPort); - } - }); - - const auto interfacesMap = kiwix::getNetworkInterfacesIPv4Or6(); - QVector interfaces; - interfaces.reserve(interfacesMap.size() + 1); - for (const auto &interfacePair : interfacesMap) { - QString ipv4 = QString::fromStdString(interfacePair.second.addr); - if (!ipv4.isEmpty() && !ipv4.startsWith("169.254")) interfaces.push_back(ipv4); - QString ipv6 = QString::fromStdString(interfacePair.second.addr6); - if (!ipv6.isEmpty()) interfaces.push_back(ipv6); - } - std::sort(interfaces.begin(), interfaces.end()); - interfaces.push_front(QString(gt("ipv6"))); - interfaces.push_front(QString(gt("ipv4"))); - interfaces.push_front(QString(gt("all_ips"))); - for (const auto &interface : interfaces) { - ui->IpChooser->addItem(interface); - } - QString ipAddress = KiwixApp::instance()->getSettingsManager()->getKiwixServerIpAddress(); - if (ipAddress == "0.0.0.0") ipAddress = "ipv4"; - ui->IpChooser->setCurrentText(ipAddress == "all_ips" || ipAddress == "ipv4" || ipAddress == "ipv6" ? gt(ipAddress) : ipAddress); - ui->PortChooser->setText(QString::number(m_port)); - ui->PortChooser->setValidator(new QIntValidator(1, 65535, this)); - ui->KiwixServerButton->setStyleSheet("QPushButton {background-color: RoyalBlue;" - "color: white;" - "padding: 5px;" - "border-radius: 3px;}" - "QPushButton:hover {background-color: DodgerBlue;}"); - ui->label->setText(gt("local-kiwix-server")); - ui->KiwixServerText->setText(gt("kiwix-server-description")); - ui->OpenInBrowserButton->setText(gt("open-in-browser")); - ui->KiwixServerButton->setText(gt("start-kiwix-server")); - ui->closeButton->setText(gt("close")); -} - -LocalKiwixServer::~LocalKiwixServer() -{ - delete ui; -} - -void LocalKiwixServer::openInBrowser() -{ - QUrl url; - url.setScheme("http"); - url.setHost(m_ipAddress); - url.setPort(m_port); - QDesktopServices::openUrl(url); -} - -void LocalKiwixServer::runOrStopServer() -{ - if (!m_active) { - auto settingsManager = KiwixApp::instance()->getSettingsManager(); - m_port = ui->PortChooser->text().toInt(); - mp_server->setPort(m_port); - settingsManager->setKiwixServerPort(m_port); - mp_server->setIpMode(kiwix::IpMode::AUTO); - mp_server->setAddress(""); - if (ui->IpChooser->currentText() == gt("all_ips")) { - mp_server->setIpMode(kiwix::IpMode::ALL); - settingsManager->setKiwixServerIpAddress("all_ips"); - } else if (ui->IpChooser->currentText() == gt("ipv4")) { - mp_server->setIpMode(kiwix::IpMode::IPV4); - settingsManager->setKiwixServerIpAddress("ipv4"); - } else if (ui->IpChooser->currentText() == gt("ipv6")) { - mp_server->setIpMode(kiwix::IpMode::IPV6); - settingsManager->setKiwixServerIpAddress("ipv6"); - } else { - mp_server->setAddress(ui->IpChooser->currentText().toStdString()); - settingsManager->setKiwixServerIpAddress(ui->IpChooser->currentText()); - } - if (!mp_server->start()) { - QMessageBox messageBox; - messageBox.critical(0,gt("error-title"),gt("error-launch-server-message")); - return; - } - kiwix::IpAddress serverAddress = mp_server->getAddress(); - m_ipAddress = QString::fromStdString(serverAddress.addr.empty() ? serverAddress.addr6 : serverAddress.addr); - if (m_ipAddress.contains(':')) m_ipAddress = "[" + m_ipAddress + "]"; - ui->IpAddress->setText("http://" + m_ipAddress + ":" + QString::number(m_port)); - ui->IpAddress->setReadOnly(true); - m_active = true; - } else { - mp_server->stop(); - m_active = false; - } - - if (m_active) { - ui->KiwixServerButton->setText(gt("stop-kiwix-server")); - ui->KiwixServerText->setText(gt("kiwix-server-running-message")); - ui->stackedWidget->setCurrentIndex(1); - } else { - ui->KiwixServerButton->setText(gt("start-kiwix-server")); - ui->KiwixServerText->setText(gt("kiwix-server-description")); - ui->stackedWidget->setCurrentIndex(0); - } -} +#include "localkiwixserver.h" +#include "ui_localkiwixserver.h" +#include "kiwixapp.h" +#include +#include +#include +#include + +LocalKiwixServer::LocalKiwixServer(QWidget *parent) : + QDialog(parent), + ui(new Ui::LocalKiwixServer) +{ + setWindowFlag(Qt::FramelessWindowHint, true); + ui->setupUi(this); + + QFile styleFile(":/css/localServer.css"); + styleFile.open(QIODevice::ReadOnly); + auto byteContent = styleFile.readAll(); + styleFile.close(); + QString style(byteContent); + setStyleSheet(style); + + mp_server = KiwixApp::instance()->getLocalServer(); + m_port = KiwixApp::instance()->getSettingsManager()->getKiwixServerPort(); + + connect(ui->KiwixServerButton, SIGNAL(clicked()), this, SLOT(runOrStopServer())); + connect(ui->OpenInBrowserButton, SIGNAL(clicked()), this, SLOT(openInBrowser())); + connect(ui->closeButton, &QPushButton::clicked, this, &LocalKiwixServer::close); + connect(ui->PortChooser, &QLineEdit::textChanged, ui->PortChooser, [=](const QString &text) { + if (text.toInt() > 65535) { + QString validText = text; + validText.chop(1); + ui->PortChooser->setText(validText); + } else if (text.toInt() < 1) { + QString defaultPort = QString::number(KiwixApp::instance()->getSettingsManager()->getKiwixServerPort()); + ui->PortChooser->setText(defaultPort); + } + }); + + const auto interfacesMap = kiwix::getNetworkInterfacesIPv4Or6(); + QVector interfaces; + interfaces.reserve(interfacesMap.size() + 1); + for (const auto &interfacePair : interfacesMap) { + QString ipv4 = QString::fromStdString(interfacePair.second.addr); + if (!ipv4.isEmpty() && !ipv4.startsWith("169.254")) interfaces.push_back(ipv4); + QString ipv6 = QString::fromStdString(interfacePair.second.addr6); + if (!ipv6.isEmpty()) interfaces.push_back(ipv6); + } + std::sort(interfaces.begin(), interfaces.end()); + interfaces.push_front(QString(gt("ipv6"))); + interfaces.push_front(QString(gt("ipv4"))); + interfaces.push_front(QString(gt("all_ips"))); + for (const auto &interface : interfaces) { + ui->IpChooser->addItem(interface); + } + QString ipAddress = KiwixApp::instance()->getSettingsManager()->getKiwixServerIpAddress(); + if (ipAddress == "0.0.0.0") ipAddress = "ipv4"; + ui->IpChooser->setCurrentText(ipAddress == "all_ips" || ipAddress == "ipv4" || ipAddress == "ipv6" ? gt(ipAddress) : ipAddress); + ui->PortChooser->setText(QString::number(m_port)); + ui->PortChooser->setValidator(new QIntValidator(1, 65535, this)); + ui->KiwixServerButton->setStyleSheet("QPushButton {background-color: RoyalBlue;" + "color: white;" + "padding: 5px;" + "border-radius: 3px;}" + "QPushButton:hover {background-color: DodgerBlue;}"); + ui->label->setText(gt("local-kiwix-server")); + ui->KiwixServerText->setText(gt("kiwix-server-description")); + ui->OpenInBrowserButton->setText(gt("open-in-browser")); + ui->KiwixServerButton->setText(gt("start-kiwix-server")); + ui->closeButton->setText(gt("close")); +} + +LocalKiwixServer::~LocalKiwixServer() +{ + delete ui; +} + +void LocalKiwixServer::openInBrowser() +{ + QUrl url; + url.setScheme("http"); + url.setHost(m_ipAddress); + url.setPort(m_port); + QDesktopServices::openUrl(url); +} + +void LocalKiwixServer::runOrStopServer() +{ + if (!m_active) { + auto settingsManager = KiwixApp::instance()->getSettingsManager(); + m_port = ui->PortChooser->text().toInt(); + mp_server->setPort(m_port); + settingsManager->setKiwixServerPort(m_port); + mp_server->setIpMode(kiwix::IpMode::AUTO); + mp_server->setAddress(""); + if (ui->IpChooser->currentText() == gt("all_ips")) { + mp_server->setIpMode(kiwix::IpMode::ALL); + settingsManager->setKiwixServerIpAddress("all_ips"); + } else if (ui->IpChooser->currentText() == gt("ipv4")) { + mp_server->setIpMode(kiwix::IpMode::IPV4); + settingsManager->setKiwixServerIpAddress("ipv4"); + } else if (ui->IpChooser->currentText() == gt("ipv6")) { + mp_server->setIpMode(kiwix::IpMode::IPV6); + settingsManager->setKiwixServerIpAddress("ipv6"); + } else { + mp_server->setAddress(ui->IpChooser->currentText().toStdString()); + settingsManager->setKiwixServerIpAddress(ui->IpChooser->currentText()); + } + if (!mp_server->start()) { + QMessageBox messageBox; + messageBox.critical(0,gt("error-title"),gt("error-launch-server-message")); + return; + } + kiwix::IpAddress serverAddress = mp_server->getAddress(); + m_ipAddress = QString::fromStdString(serverAddress.addr.empty() ? serverAddress.addr6 : serverAddress.addr); + if (m_ipAddress.contains(':')) m_ipAddress = "[" + m_ipAddress + "]"; + ui->IpAddress->setText("http://" + m_ipAddress + ":" + QString::number(m_port)); + ui->IpAddress->setReadOnly(true); + m_active = true; + } else { + mp_server->stop(); + m_active = false; + } + + if (m_active) { + ui->KiwixServerButton->setText(gt("stop-kiwix-server")); + ui->KiwixServerText->setText(gt("kiwix-server-running-message")); + ui->stackedWidget->setCurrentIndex(1); + } else { + ui->KiwixServerButton->setText(gt("start-kiwix-server")); + ui->KiwixServerText->setText(gt("kiwix-server-description")); + ui->stackedWidget->setCurrentIndex(0); + } +} diff --git a/src/localkiwixserver.h b/src/localkiwixserver.h index 6f479fcae..d9bcbb941 100644 --- a/src/localkiwixserver.h +++ b/src/localkiwixserver.h @@ -1,31 +1,31 @@ -#ifndef LOCALKIWIXSERVER_H -#define LOCALKIWIXSERVER_H - -#include -#include - -namespace Ui { -class LocalKiwixServer; -} - -class LocalKiwixServer : public QDialog -{ - Q_OBJECT - -public: - explicit LocalKiwixServer(QWidget *parent = nullptr); - ~LocalKiwixServer(); - -public slots: - void runOrStopServer(); - void openInBrowser(); - -private: - Ui::LocalKiwixServer *ui; - kiwix::Server* mp_server; - bool m_active = false; - QString m_ipAddress; - int m_port; -}; - -#endif // LOCALKIWIXSERVER_H +#ifndef LOCALKIWIXSERVER_H +#define LOCALKIWIXSERVER_H + +#include +#include + +namespace Ui { +class LocalKiwixServer; +} + +class LocalKiwixServer : public QDialog +{ + Q_OBJECT + +public: + explicit LocalKiwixServer(QWidget *parent = nullptr); + ~LocalKiwixServer(); + +public slots: + void runOrStopServer(); + void openInBrowser(); + +private: + Ui::LocalKiwixServer *ui; + kiwix::Server* mp_server; + bool m_active = false; + QString m_ipAddress; + int m_port; +}; + +#endif // LOCALKIWIXSERVER_H diff --git a/src/main.cpp b/src/main.cpp index 8c4152038..2c094160b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,100 +1,100 @@ -#include - -#include "kiwixapp.h" - -#include -#include -#include -#ifdef Q_OS_WIN - #include - #include -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) - #include -#endif - -#ifdef Q_OS_WIN -bool wasAppStartedFromARemoteDrive() -{ - const std::string exePath = kiwix::getExecutablePath(); - - // Usual mounting point for Windows Network Drives - if ( exePath.substr(0, 2) == "\\\\" ) - return true; - - // Support macOS Parallels shared folders. - // See https://kb.parallels.com/130138 - if ( exePath.substr(0, 7) == "C:\\Mac\\" ) - return true; - - // Last chance to identify as remote drive - return GetDriveTypeA(exePath.substr(0, 3).c_str()) == DRIVE_REMOTE; -} -#endif - -int main(int argc, char *argv[]) -{ -// Small hack to make QtWebEngine works with AppImage. -// See /~https://github.com/probonopd/linuxdeployqt/issues/554 - if (qEnvironmentVariableIsSet("APPIMAGE")) { - qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu --no-sandbox"); - QApplication::setAttribute(Qt::AA_UseOpenGLES); - } -// End of hack ^^^ - -#ifdef Q_OS_WIN - const bool appWasStartedFromARemoteDrive = wasAppStartedFromARemoteDrive(); - if ( appWasStartedFromARemoteDrive ) { - // This has to be done early before the qtwebengine dll is loaded. - // We notify the user about the implications and ask for confirmation - // a little later (see closer to the bottom of this function). - qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--no-sandbox")); - } -#endif - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // High DPI Scaling is enabled by default in Qt6. This attribute no longer exists in 6.0 and later - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) - QWebEngineUrlScheme scheme("zim"); - QWebEngineUrlScheme::registerScheme(scheme); -#endif - KiwixApp a(argc, argv); - - QCommandLineParser parser; - parser.setApplicationDescription(QStringLiteral("The Kiwix Desktop is a viewer/manager of ZIM files for GNU/Linux and Microsoft Windows OSes.")); - parser.addHelpOption(); - parser.addPositionalArgument("zimfile", "The zim file"); - - // Set version string - std::ostringstream versions; - a.printVersions(versions); - a.setApplicationVersion(QString::fromStdString(versions.str())); - parser.addVersionOption(); - - parser.process(a); - auto positionalArguments = parser.positionalArguments(); - if (a.isRunning()) { - for (QString zimfile : positionalArguments) { - a.sendMessage(zimfile); - } - return 0; - } - a.init(); -#ifdef Q_OS_WIN - if ( appWasStartedFromARemoteDrive ) { - int result = QMessageBox::question(nullptr, gt("about-kiwix-desktop-title"), gt("disable-sandbox"), QMessageBox::Yes | QMessageBox::No); - if (result == QMessageBox::No) return 0; - } -#endif - - /* Restore Tabs before directory monitoring to ensure we know what tabs user had. */ - a.restoreTabs(); - a.setupDirectoryMonitoring(); - - for (QString zimfile : positionalArguments) { - a.openZimFile(zimfile); - } - return a.exec(); -} +#include + +#include "kiwixapp.h" + +#include +#include +#include +#ifdef Q_OS_WIN + #include + #include +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + #include +#endif + +#ifdef Q_OS_WIN +bool wasAppStartedFromARemoteDrive() +{ + const std::string exePath = kiwix::getExecutablePath(); + + // Usual mounting point for Windows Network Drives + if ( exePath.substr(0, 2) == "\\\\" ) + return true; + + // Support macOS Parallels shared folders. + // See https://kb.parallels.com/130138 + if ( exePath.substr(0, 7) == "C:\\Mac\\" ) + return true; + + // Last chance to identify as remote drive + return GetDriveTypeA(exePath.substr(0, 3).c_str()) == DRIVE_REMOTE; +} +#endif + +int main(int argc, char *argv[]) +{ +// Small hack to make QtWebEngine works with AppImage. +// See /~https://github.com/probonopd/linuxdeployqt/issues/554 + if (qEnvironmentVariableIsSet("APPIMAGE")) { + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu --no-sandbox"); + QApplication::setAttribute(Qt::AA_UseOpenGLES); + } +// End of hack ^^^ + +#ifdef Q_OS_WIN + const bool appWasStartedFromARemoteDrive = wasAppStartedFromARemoteDrive(); + if ( appWasStartedFromARemoteDrive ) { + // This has to be done early before the qtwebengine dll is loaded. + // We notify the user about the implications and ask for confirmation + // a little later (see closer to the bottom of this function). + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", QByteArray("--no-sandbox")); + } +#endif + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + // High DPI Scaling is enabled by default in Qt6. This attribute no longer exists in 6.0 and later + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) + QWebEngineUrlScheme scheme("zim"); + QWebEngineUrlScheme::registerScheme(scheme); +#endif + KiwixApp a(argc, argv); + + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("The Kiwix Desktop is a viewer/manager of ZIM files for GNU/Linux and Microsoft Windows OSes.")); + parser.addHelpOption(); + parser.addPositionalArgument("zimfile", "The zim file"); + + // Set version string + std::ostringstream versions; + a.printVersions(versions); + a.setApplicationVersion(QString::fromStdString(versions.str())); + parser.addVersionOption(); + + parser.process(a); + auto positionalArguments = parser.positionalArguments(); + if (a.isRunning()) { + for (QString zimfile : positionalArguments) { + a.sendMessage(zimfile); + } + return 0; + } + a.init(); +#ifdef Q_OS_WIN + if ( appWasStartedFromARemoteDrive ) { + int result = QMessageBox::question(nullptr, gt("about-kiwix-desktop-title"), gt("disable-sandbox"), QMessageBox::Yes | QMessageBox::No); + if (result == QMessageBox::No) return 0; + } +#endif + + /* Restore Tabs before directory monitoring to ensure we know what tabs user had. */ + a.restoreTabs(); + a.setupDirectoryMonitoring(); + + for (QString zimfile : positionalArguments) { + a.openZimFile(zimfile); + } + return a.exec(); +} diff --git a/src/mainmenu.cpp b/src/mainmenu.cpp index 7528f83d4..f0eb979a6 100644 --- a/src/mainmenu.cpp +++ b/src/mainmenu.cpp @@ -1,75 +1,75 @@ -#include "mainmenu.h" -#include "kiwixapp.h" -#include "menuproxystyle.h" - -#include - -#define ADD_ACTION(ID) addAction(app->getAction(KiwixApp::ID)); - -MainMenu::MainMenu(QWidget *parent) : - QMenu(parent) -{ - auto app = KiwixApp::instance(); - setStyle(new MenuProxyStyle()); - - ADD_ACTION(KiwixServeAction); - ADD_ACTION(RandomArticleAction); - ADD_ACTION(PrintAction); - addSeparator(); - - m_fileMenu.setTitle(gt("file")); - m_fileMenu.ADD_ACTION(NewTabAction); - m_fileMenu.ADD_ACTION(CloseCurrentTabAction); - m_fileMenu.ADD_ACTION(ReopenClosedTabAction); - m_fileMenu.ADD_ACTION(BrowseLibraryAction); - m_fileMenu.ADD_ACTION(OpenFileAction); - m_fileMenu.ADD_ACTION(OpenRecentAction); - m_fileMenu.ADD_ACTION(ExportReadingListAction); - m_fileMenu.ADD_ACTION(ImportReadingListAction); - m_fileMenu.ADD_ACTION(SavePageAsAction); - - addMenu(&m_fileMenu); - - m_editMenu.setTitle(gt("edit")); - m_editMenu.ADD_ACTION(SearchArticleAction); - m_editMenu.ADD_ACTION(SearchLibraryAction); - m_editMenu.ADD_ACTION(FindInPageAction); - m_editMenu.ADD_ACTION(ToggleAddBookmarkAction); - m_editMenu.ADD_ACTION(OpenMultiZimAction); -#if defined(QT_TEXTTOSPEECH_LIB) - m_editMenu.ADD_ACTION(ReadArticleAction); - m_editMenu.ADD_ACTION(ReadTextAction); - m_editMenu.ADD_ACTION(ReadStopAction); -#endif - addMenu(&m_editMenu); - - m_viewMenu.setTitle(gt("view")); - m_viewMenu.ADD_ACTION(ToggleFullscreenAction); - m_viewMenu.ADD_ACTION(ToggleTOCAction); - m_viewMenu.ADD_ACTION(ToggleReadingListAction); - m_viewMenu.ADD_ACTION(ToggleTTSLanguageAction); - m_viewMenu.ADD_ACTION(ToggleTTSVoiceAction); - m_viewMenu.ADD_ACTION(IncreaseTTSSpeedAction); - m_viewMenu.ADD_ACTION(DecreaseTTSSpeedAction); - m_viewMenu.ADD_ACTION(ZoomInAction); - m_viewMenu.ADD_ACTION(ZoomOutAction); - m_viewMenu.ADD_ACTION(ZoomResetAction); - addMenu(&m_viewMenu); - - m_toolsMenu.setTitle(gt("tools")); -// m_toolsMenu.addAction(); -// addMenu(&m_toolsMenu); - - m_helpMenu.setTitle(gt("help")); - m_helpMenu.ADD_ACTION(HelpAction); - m_helpMenu.ADD_ACTION(FeedbackAction); - m_helpMenu.ADD_ACTION(ReportBugAction); - m_helpMenu.ADD_ACTION(RequestFeatureAction); - m_helpMenu.ADD_ACTION(AboutAction); - addMenu(&m_helpMenu); - - addSeparator(); - ADD_ACTION(SettingAction); - ADD_ACTION(DonateAction); - ADD_ACTION(ExitAction); -} +#include "mainmenu.h" +#include "kiwixapp.h" +#include "menuproxystyle.h" + +#include + +#define ADD_ACTION(ID) addAction(app->getAction(KiwixApp::ID)); + +MainMenu::MainMenu(QWidget *parent) : + QMenu(parent) +{ + auto app = KiwixApp::instance(); + setStyle(new MenuProxyStyle()); + + ADD_ACTION(KiwixServeAction); + ADD_ACTION(RandomArticleAction); + ADD_ACTION(PrintAction); + addSeparator(); + + m_fileMenu.setTitle(gt("file")); + m_fileMenu.ADD_ACTION(NewTabAction); + m_fileMenu.ADD_ACTION(CloseCurrentTabAction); + m_fileMenu.ADD_ACTION(ReopenClosedTabAction); + m_fileMenu.ADD_ACTION(BrowseLibraryAction); + m_fileMenu.ADD_ACTION(OpenFileAction); + m_fileMenu.ADD_ACTION(OpenRecentAction); + m_fileMenu.ADD_ACTION(ExportReadingListAction); + m_fileMenu.ADD_ACTION(ImportReadingListAction); + m_fileMenu.ADD_ACTION(SavePageAsAction); + + addMenu(&m_fileMenu); + + m_editMenu.setTitle(gt("edit")); + m_editMenu.ADD_ACTION(SearchArticleAction); + m_editMenu.ADD_ACTION(SearchLibraryAction); + m_editMenu.ADD_ACTION(FindInPageAction); + m_editMenu.ADD_ACTION(ToggleAddBookmarkAction); + m_editMenu.ADD_ACTION(OpenMultiZimAction); +#if defined(QT_TEXTTOSPEECH_LIB) + m_editMenu.ADD_ACTION(ReadArticleAction); + m_editMenu.ADD_ACTION(ReadTextAction); + m_editMenu.ADD_ACTION(ReadStopAction); +#endif + addMenu(&m_editMenu); + + m_viewMenu.setTitle(gt("view")); + m_viewMenu.ADD_ACTION(ToggleFullscreenAction); + m_viewMenu.ADD_ACTION(ToggleTOCAction); + m_viewMenu.ADD_ACTION(ToggleReadingListAction); + m_viewMenu.ADD_ACTION(ToggleTTSLanguageAction); + m_viewMenu.ADD_ACTION(ToggleTTSVoiceAction); + m_viewMenu.ADD_ACTION(IncreaseTTSSpeedAction); + m_viewMenu.ADD_ACTION(DecreaseTTSSpeedAction); + m_viewMenu.ADD_ACTION(ZoomInAction); + m_viewMenu.ADD_ACTION(ZoomOutAction); + m_viewMenu.ADD_ACTION(ZoomResetAction); + addMenu(&m_viewMenu); + + m_toolsMenu.setTitle(gt("tools")); +// m_toolsMenu.addAction(); +// addMenu(&m_toolsMenu); + + m_helpMenu.setTitle(gt("help")); + m_helpMenu.ADD_ACTION(HelpAction); + m_helpMenu.ADD_ACTION(FeedbackAction); + m_helpMenu.ADD_ACTION(ReportBugAction); + m_helpMenu.ADD_ACTION(RequestFeatureAction); + m_helpMenu.ADD_ACTION(AboutAction); + addMenu(&m_helpMenu); + + addSeparator(); + ADD_ACTION(SettingAction); + ADD_ACTION(DonateAction); + ADD_ACTION(ExitAction); +} diff --git a/src/mainmenu.h b/src/mainmenu.h index e816052f2..b0f44e7f2 100644 --- a/src/mainmenu.h +++ b/src/mainmenu.h @@ -1,20 +1,20 @@ -#ifndef MAINMENU_H -#define MAINMENU_H - -#include - -class MainMenu : public QMenu -{ - Q_OBJECT -public: - MainMenu(QWidget *parent=nullptr); - -private: - QMenu m_fileMenu; - QMenu m_editMenu; - QMenu m_viewMenu; - QMenu m_toolsMenu; - QMenu m_helpMenu; -}; - -#endif // MAINMENU_H +#ifndef MAINMENU_H +#define MAINMENU_H + +#include + +class MainMenu : public QMenu +{ + Q_OBJECT +public: + MainMenu(QWidget *parent=nullptr); + +private: + QMenu m_fileMenu; + QMenu m_editMenu; + QMenu m_viewMenu; + QMenu m_toolsMenu; + QMenu m_helpMenu; +}; + +#endif // MAINMENU_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index a298e9c27..edbe39263 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,238 +1,238 @@ - -#include "mainwindow.h" -#include "portutils.h" -#include "ui_mainwindow.h" -#include "ui_about.h" - -#include "kiwixapp.h" -#include "kconstants.h" - -#include - - -MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), - mp_ui(new Ui::MainWindow), - mp_about(new About(this)), - mp_localKiwixServer(new LocalKiwixServer(this)) -{ - QWidget::setAttribute(Qt::WA_AlwaysShowToolTips); - mp_ui->setupUi(this); - - mp_ui->tabBar->setExpanding(false); - mp_ui->tabBar->setStackedWidget(mp_ui->mainView); - - auto app = KiwixApp::instance(); - addAction(app->getAction(KiwixApp::ToggleFullscreenAction)); - addAction(app->getAction(KiwixApp::NextTabAction)); - addAction(app->getAction(KiwixApp::PreviousTabAction)); - - mp_ui->newTabSideButton->setDefaultAction(app->getAction(KiwixApp::NewTabAction)); - mp_ui->nextTabButton->setDefaultAction(app->getAction(KiwixApp::ScrollNextTabAction)); - mp_ui->prevTabButton->setDefaultAction(app->getAction(KiwixApp::ScrollPreviousTabAction)); - - connect(mp_ui->tabBar, &TabBar::sizeChanged, this, &MainWindow::updateTabButtons); - connect(mp_ui->tabBar, &QTabBar::currentChanged, this, &MainWindow::updateTabButtons); - connect(mp_ui->tabBar, &TabBar::tabRemovedSignal, this, &MainWindow::updateTabButtons); - connect(mp_ui->tabBar, &TabBar::tabInsertedSignal, this, &MainWindow::updateTabButtons); - - connect(mp_ui->nextTabButton, &QToolButton::triggered, mp_ui->tabBar, &TabBar::scrollNextTab); - connect(mp_ui->prevTabButton, &QToolButton::triggered, mp_ui->tabBar, &TabBar::scrollPreviousTab); - - connect(app->getAction(KiwixApp::ExitAction), &QAction::triggered, - this, &QMainWindow::close); - connect(app->getAction(KiwixApp::ToggleFullscreenAction), &QAction::triggered, - this, &MainWindow::toggleFullScreen); - connect(app->getAction(KiwixApp::ToggleReadingListAction), &QAction::toggled, - this, &MainWindow::readingListToggled); - connect(app->getAction(KiwixApp::ToggleTOCAction), &QAction::toggled, - this, &MainWindow::tableOfContentToggled); - connect(app->getAction(KiwixApp::AboutAction), &QAction::triggered, - mp_about, &QDialog::show); - connect(app->getAction(KiwixApp::DonateAction), &QAction::triggered, - this, [=]() { QDesktopServices::openUrl(QUrl("https://donate.kiwix.org")); }); - connect(app->getAction(KiwixApp::KiwixServeAction), &QAction::triggered, - mp_localKiwixServer, &QDialog::show); - - connect(mp_ui->tabBar, &TabBar::currentTitleChanged, this, [=](const QString& title) { - if (!title.isEmpty() && !title.startsWith("zim://")) - setWindowTitle(title + " - Kiwix"); - else - setWindowTitle(gt("window-title")); - }); - - addAction(app->getAction(KiwixApp::OpenHomePageAction)); - -#if !SYSTEMTITLEBAR - setWindowFlags(Qt::Window | Qt::CustomizeWindowHint); -#endif - - connect(mp_ui->tabBar, &QTabBar::currentChanged, - mp_ui->mainToolBar, &TopWidget::updateBackForwardButtons); - connect(mp_ui->tabBar, &TabBar::tabDisplayed, - this, [=](TabType tabType) { - tabChanged(tabType); - }); - - connect(mp_ui->tabBar, &TabBar::currentTitleChanged, - &(mp_ui->mainToolBar->getSearchBar()), &SearchBar::currentTitleChanged); - // This signal emited more often than the history really updated - // but for now we have no better signal for it. - connect(mp_ui->tabBar, &TabBar::currentTitleChanged, - mp_ui->mainToolBar, &TopWidget::updateBackForwardButtons); - - connect(mp_ui->tabBar, &TabBar::webActionEnabledChanged, - mp_ui->mainToolBar, &TopWidget::handleWebActionEnabledChanged); - - mp_ui->contentmanagerside->setContentManager(app->getContentManager()); - mp_ui->sideBar->setCurrentWidget(mp_ui->contentmanagerside); -} - -MainWindow::~MainWindow() -{ - delete mp_ui; -} - -void MainWindow::toggleFullScreen() { - if (isFullScreen()) { - QApplication::instance()->removeEventFilter(this); - showTabAndTop(); - showNormal(); - } - else { - QApplication::instance()->installEventFilter(this); - hideTabAndTop(); - showFullScreen(); - } -} - -void MainWindow::hideTabAndTop() { - getTabBar()->hide(); - getTopWidget()->hide(); -} - -void MainWindow::showTabAndTop() { - getTabBar()->show(); - getTopWidget()->show(); -} - -void MainWindow::updateTabButtons() -{ - auto tabBar = getTabBar(); - QRect tabBarRect = getTabBar()->rect(); - QRect newTabButtonRect = tabBar->tabRect(tabBar->count() - 1); - - /* Decision is made at half way of the new button tab for smoothness */ - newTabButtonRect.setWidth(newTabButtonRect.width() / 2); - bool newTabButtonVisible = tabBarRect.contains(newTabButtonRect); - mp_ui->newTabSideButton->setHidden(newTabButtonVisible); - - int lastTabIndex = tabBar->realTabCount() - 1; - QRect firstTabRect = tabBar->tabRect(0); - QRect lastTabRect = tabBar->tabRect(lastTabIndex); - - bool firstVisible = tabBarRect.contains(firstTabRect); - bool lastVisible = tabBarRect.contains(lastTabRect); - bool noOverFlow = firstVisible && lastVisible; - - mp_ui->prevTabButton->setHidden(noOverFlow); - mp_ui->nextTabButton->setHidden(noOverFlow); - - mp_ui->prevTabButton->setDisabled(tabBar->currentIndex() == 0); - mp_ui->nextTabButton->setDisabled(tabBar->currentIndex() == lastTabIndex); -} - -bool MainWindow::eventFilter(QObject* /*object*/, QEvent* event) -{ - if (event->type() == QEvent::MouseMove && isFullScreen()) - { - const auto mouseEvent = static_cast(event); - const int tabRegion = getTabBar()->height() + getTopWidget()->height() + 30; - int clickY = portutils::getY(*mouseEvent); - // We don't have to check for visibilty as calling hide() on a hidden widget, or show() on a non-hidden widget is a no-op - if (clickY == 0) { - showTabAndTop(); - } else if(clickY >= tabRegion) { - hideTabAndTop(); - } - } - return false; -} - -void MainWindow::closeEvent(QCloseEvent *event) -{ - KiwixApp::instance()->saveWindowState(); - QMainWindow::closeEvent(event); -} - -void MainWindow::resizeEvent(QResizeEvent *event) -{ - QMainWindow::resizeEvent(event); - KiwixApp::instance()->getContentManager()->getView()->updateSizeHint(); - updateTabButtons(); -} - -void checkActionNoSignal(KiwixApp::Actions actionFlag, bool checked) -{ - const auto action = KiwixApp::instance()->getAction(actionFlag); - const bool oldState = action->blockSignals(true); - action->setChecked(checked); - action->blockSignals(oldState); -} - -void MainWindow::readingListToggled(bool state) -{ - if (state) { - checkActionNoSignal(KiwixApp::ToggleTOCAction, false); - mp_ui->sideBar->setCurrentWidget(mp_ui->readinglistbar); - mp_ui->sideBar->show(); - } - else { - mp_ui->sideBar->hide(); - } -} - -void MainWindow::tableOfContentToggled(bool state) -{ - if (state) { - checkActionNoSignal(KiwixApp::ToggleReadingListAction, false); - mp_ui->sideBar->setCurrentWidget(mp_ui->tableofcontentbar); - mp_ui->sideBar->show(); - } - else { - mp_ui->sideBar->hide(); - } -} - -void MainWindow::tabChanged(TabType tabType) -{ - QAction *readingList = KiwixApp::instance()->getAction(KiwixApp::ToggleReadingListAction); - QAction *tableOfContent = KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction); - if (tabType == TabType::SettingsTab) { - mp_ui->sideBar->hide(); - } else if(tabType == TabType::LibraryTab) { - mp_ui->sideBar->setCurrentWidget(mp_ui->contentmanagerside); - mp_ui->sideBar->show(); - } else if (readingList->isChecked()) { - readingListToggled(true); - } else if (tableOfContent->isChecked()) { - tableOfContentToggled(true); - } else { - mp_ui->sideBar->hide(); - } -} - -TabBar* MainWindow::getTabBar() -{ - return mp_ui->tabBar; -} - -TopWidget *MainWindow::getTopWidget() -{ - return mp_ui->mainToolBar; -} - -TableOfContentBar *MainWindow::getTableOfContentBar() -{ - return mp_ui->tableofcontentbar; -} + +#include "mainwindow.h" +#include "portutils.h" +#include "ui_mainwindow.h" +#include "ui_about.h" + +#include "kiwixapp.h" +#include "kconstants.h" + +#include + + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + mp_ui(new Ui::MainWindow), + mp_about(new About(this)), + mp_localKiwixServer(new LocalKiwixServer(this)) +{ + QWidget::setAttribute(Qt::WA_AlwaysShowToolTips); + mp_ui->setupUi(this); + + mp_ui->tabBar->setExpanding(false); + mp_ui->tabBar->setStackedWidget(mp_ui->mainView); + + auto app = KiwixApp::instance(); + addAction(app->getAction(KiwixApp::ToggleFullscreenAction)); + addAction(app->getAction(KiwixApp::NextTabAction)); + addAction(app->getAction(KiwixApp::PreviousTabAction)); + + mp_ui->newTabSideButton->setDefaultAction(app->getAction(KiwixApp::NewTabAction)); + mp_ui->nextTabButton->setDefaultAction(app->getAction(KiwixApp::ScrollNextTabAction)); + mp_ui->prevTabButton->setDefaultAction(app->getAction(KiwixApp::ScrollPreviousTabAction)); + + connect(mp_ui->tabBar, &TabBar::sizeChanged, this, &MainWindow::updateTabButtons); + connect(mp_ui->tabBar, &QTabBar::currentChanged, this, &MainWindow::updateTabButtons); + connect(mp_ui->tabBar, &TabBar::tabRemovedSignal, this, &MainWindow::updateTabButtons); + connect(mp_ui->tabBar, &TabBar::tabInsertedSignal, this, &MainWindow::updateTabButtons); + + connect(mp_ui->nextTabButton, &QToolButton::triggered, mp_ui->tabBar, &TabBar::scrollNextTab); + connect(mp_ui->prevTabButton, &QToolButton::triggered, mp_ui->tabBar, &TabBar::scrollPreviousTab); + + connect(app->getAction(KiwixApp::ExitAction), &QAction::triggered, + this, &QMainWindow::close); + connect(app->getAction(KiwixApp::ToggleFullscreenAction), &QAction::triggered, + this, &MainWindow::toggleFullScreen); + connect(app->getAction(KiwixApp::ToggleReadingListAction), &QAction::toggled, + this, &MainWindow::readingListToggled); + connect(app->getAction(KiwixApp::ToggleTOCAction), &QAction::toggled, + this, &MainWindow::tableOfContentToggled); + connect(app->getAction(KiwixApp::AboutAction), &QAction::triggered, + mp_about, &QDialog::show); + connect(app->getAction(KiwixApp::DonateAction), &QAction::triggered, + this, [=]() { QDesktopServices::openUrl(QUrl("https://donate.kiwix.org")); }); + connect(app->getAction(KiwixApp::KiwixServeAction), &QAction::triggered, + mp_localKiwixServer, &QDialog::show); + + connect(mp_ui->tabBar, &TabBar::currentTitleChanged, this, [=](const QString& title) { + if (!title.isEmpty() && !title.startsWith("zim://")) + setWindowTitle(title + " - Kiwix"); + else + setWindowTitle(gt("window-title")); + }); + + addAction(app->getAction(KiwixApp::OpenHomePageAction)); + +#if !SYSTEMTITLEBAR + setWindowFlags(Qt::Window | Qt::CustomizeWindowHint); +#endif + + connect(mp_ui->tabBar, &QTabBar::currentChanged, + mp_ui->mainToolBar, &TopWidget::updateBackForwardButtons); + connect(mp_ui->tabBar, &TabBar::tabDisplayed, + this, [=](TabType tabType) { + tabChanged(tabType); + }); + + connect(mp_ui->tabBar, &TabBar::currentTitleChanged, + &(mp_ui->mainToolBar->getSearchBar()), &SearchBar::currentTitleChanged); + // This signal emited more often than the history really updated + // but for now we have no better signal for it. + connect(mp_ui->tabBar, &TabBar::currentTitleChanged, + mp_ui->mainToolBar, &TopWidget::updateBackForwardButtons); + + connect(mp_ui->tabBar, &TabBar::webActionEnabledChanged, + mp_ui->mainToolBar, &TopWidget::handleWebActionEnabledChanged); + + mp_ui->contentmanagerside->setContentManager(app->getContentManager()); + mp_ui->sideBar->setCurrentWidget(mp_ui->contentmanagerside); +} + +MainWindow::~MainWindow() +{ + delete mp_ui; +} + +void MainWindow::toggleFullScreen() { + if (isFullScreen()) { + QApplication::instance()->removeEventFilter(this); + showTabAndTop(); + showNormal(); + } + else { + QApplication::instance()->installEventFilter(this); + hideTabAndTop(); + showFullScreen(); + } +} + +void MainWindow::hideTabAndTop() { + getTabBar()->hide(); + getTopWidget()->hide(); +} + +void MainWindow::showTabAndTop() { + getTabBar()->show(); + getTopWidget()->show(); +} + +void MainWindow::updateTabButtons() +{ + auto tabBar = getTabBar(); + QRect tabBarRect = getTabBar()->rect(); + QRect newTabButtonRect = tabBar->tabRect(tabBar->count() - 1); + + /* Decision is made at half way of the new button tab for smoothness */ + newTabButtonRect.setWidth(newTabButtonRect.width() / 2); + bool newTabButtonVisible = tabBarRect.contains(newTabButtonRect); + mp_ui->newTabSideButton->setHidden(newTabButtonVisible); + + int lastTabIndex = tabBar->realTabCount() - 1; + QRect firstTabRect = tabBar->tabRect(0); + QRect lastTabRect = tabBar->tabRect(lastTabIndex); + + bool firstVisible = tabBarRect.contains(firstTabRect); + bool lastVisible = tabBarRect.contains(lastTabRect); + bool noOverFlow = firstVisible && lastVisible; + + mp_ui->prevTabButton->setHidden(noOverFlow); + mp_ui->nextTabButton->setHidden(noOverFlow); + + mp_ui->prevTabButton->setDisabled(tabBar->currentIndex() == 0); + mp_ui->nextTabButton->setDisabled(tabBar->currentIndex() == lastTabIndex); +} + +bool MainWindow::eventFilter(QObject* /*object*/, QEvent* event) +{ + if (event->type() == QEvent::MouseMove && isFullScreen()) + { + const auto mouseEvent = static_cast(event); + const int tabRegion = getTabBar()->height() + getTopWidget()->height() + 30; + int clickY = portutils::getY(*mouseEvent); + // We don't have to check for visibilty as calling hide() on a hidden widget, or show() on a non-hidden widget is a no-op + if (clickY == 0) { + showTabAndTop(); + } else if(clickY >= tabRegion) { + hideTabAndTop(); + } + } + return false; +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + KiwixApp::instance()->saveWindowState(); + QMainWindow::closeEvent(event); +} + +void MainWindow::resizeEvent(QResizeEvent *event) +{ + QMainWindow::resizeEvent(event); + KiwixApp::instance()->getContentManager()->getView()->updateSizeHint(); + updateTabButtons(); +} + +void checkActionNoSignal(KiwixApp::Actions actionFlag, bool checked) +{ + const auto action = KiwixApp::instance()->getAction(actionFlag); + const bool oldState = action->blockSignals(true); + action->setChecked(checked); + action->blockSignals(oldState); +} + +void MainWindow::readingListToggled(bool state) +{ + if (state) { + checkActionNoSignal(KiwixApp::ToggleTOCAction, false); + mp_ui->sideBar->setCurrentWidget(mp_ui->readinglistbar); + mp_ui->sideBar->show(); + } + else { + mp_ui->sideBar->hide(); + } +} + +void MainWindow::tableOfContentToggled(bool state) +{ + if (state) { + checkActionNoSignal(KiwixApp::ToggleReadingListAction, false); + mp_ui->sideBar->setCurrentWidget(mp_ui->tableofcontentbar); + mp_ui->sideBar->show(); + } + else { + mp_ui->sideBar->hide(); + } +} + +void MainWindow::tabChanged(TabType tabType) +{ + QAction *readingList = KiwixApp::instance()->getAction(KiwixApp::ToggleReadingListAction); + QAction *tableOfContent = KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction); + if (tabType == TabType::SettingsTab) { + mp_ui->sideBar->hide(); + } else if(tabType == TabType::LibraryTab) { + mp_ui->sideBar->setCurrentWidget(mp_ui->contentmanagerside); + mp_ui->sideBar->show(); + } else if (readingList->isChecked()) { + readingListToggled(true); + } else if (tableOfContent->isChecked()) { + tableOfContentToggled(true); + } else { + mp_ui->sideBar->hide(); + } +} + +TabBar* MainWindow::getTabBar() +{ + return mp_ui->tabBar; +} + +TopWidget *MainWindow::getTopWidget() +{ + return mp_ui->mainToolBar; +} + +TableOfContentBar *MainWindow::getTableOfContentBar() +{ + return mp_ui->tableofcontentbar; +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 4e580d1a8..d25b685a5 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -1,52 +1,52 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include "webview.h" -#include "tabbar.h" -#include "topwidget.h" -#include "about.h" -#include "contentmanagerside.h" -#include "localkiwixserver.h" - -namespace Ui { -class MainWindow; -} - -class TableOfContentBar; - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget *parent = 0); - ~MainWindow(); - - TabBar* getTabBar(); - TopWidget* getTopWidget(); - QWidget getMainView(); - TableOfContentBar *getTableOfContentBar(); - -protected: - bool eventFilter(QObject* object, QEvent* event) override; - void closeEvent(QCloseEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - -private slots: - void toggleFullScreen(); - void tabChanged(TabBar::TabType); - void readingListToggled(bool state); - void tableOfContentToggled(bool state); - void hideTabAndTop(); - void showTabAndTop(); - void updateTabButtons(); - -private: - Ui::MainWindow *mp_ui; - About *mp_about; - LocalKiwixServer *mp_localKiwixServer; -}; - -#endif // MAINWINDOW_H +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include "webview.h" +#include "tabbar.h" +#include "topwidget.h" +#include "about.h" +#include "contentmanagerside.h" +#include "localkiwixserver.h" + +namespace Ui { +class MainWindow; +} + +class TableOfContentBar; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + + TabBar* getTabBar(); + TopWidget* getTopWidget(); + QWidget getMainView(); + TableOfContentBar *getTableOfContentBar(); + +protected: + bool eventFilter(QObject* object, QEvent* event) override; + void closeEvent(QCloseEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + +private slots: + void toggleFullScreen(); + void tabChanged(TabBar::TabType); + void readingListToggled(bool state); + void tableOfContentToggled(bool state); + void hideTabAndTop(); + void showTabAndTop(); + void updateTabButtons(); + +private: + Ui::MainWindow *mp_ui; + About *mp_about; + LocalKiwixServer *mp_localKiwixServer; +}; + +#endif // MAINWINDOW_H diff --git a/src/menuproxystyle.h b/src/menuproxystyle.h index 893c868b3..6509468a6 100644 --- a/src/menuproxystyle.h +++ b/src/menuproxystyle.h @@ -1,19 +1,19 @@ -#include - -class MenuProxyStyle: public QProxyStyle -{ - Q_OBJECT - - public: - MenuProxyStyle(QStyle * style = 0) : QProxyStyle(style) {} - MenuProxyStyle(const QString & key) : QProxyStyle(key) {} - - virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0 ) const { - switch ( metric ) { - case QStyle::PM_SmallIconSize: - return 25; - default: - return QProxyStyle::pixelMetric( metric, option, widget ); - } - } -}; +#include + +class MenuProxyStyle: public QProxyStyle +{ + Q_OBJECT + + public: + MenuProxyStyle(QStyle * style = 0) : QProxyStyle(style) {} + MenuProxyStyle(const QString & key) : QProxyStyle(key) {} + + virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption * option = 0, const QWidget * widget = 0 ) const { + switch ( metric ) { + case QStyle::PM_SmallIconSize: + return 25; + default: + return QProxyStyle::pixelMetric( metric, option, widget ); + } + } +}; diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index 25b5379b1..901da3989 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -1,167 +1,167 @@ -#include -#include -#include -#include -#include -#include "kiwixapp.h" -#include "multizimbutton.h" -#include "css_constants.h" - -QString getElidedText(const QFont& font, int length, const QString& text); - -MultiZimButton::MultiZimButton(QWidget *parent) : - QToolButton(parent), - mp_buttonList(new QListWidget), - mp_radioButtonGroup(new QButtonGroup(this)) -{ - setMenu(new QMenu(this)); - setPopupMode(QToolButton::InstantPopup); - setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::OpenMultiZimAction)); - connect(this, &QToolButton::triggered, this, &MultiZimButton::showMenu); - - const auto popupAction = new QWidgetAction(menu()); - popupAction->setDefaultWidget(mp_buttonList); - menu()->addAction(popupAction); - - connect(mp_buttonList, &QListWidget::currentRowChanged, this, [=](int row){ - if (const auto widget = getZimWidget(row)) - widget->getRadioButton()->setChecked(true); - }); -} - -void MultiZimButton::updateDisplay() -{ - mp_buttonList->clear(); - for (const auto& button : mp_radioButtonGroup->buttons()) - mp_radioButtonGroup->removeButton(button); - - const auto library = KiwixApp::instance()->getLibrary(); - const auto view = KiwixApp::instance()->getTabWidget()->currentWebView(); - QListWidgetItem* currentItem = nullptr; - QIcon currentIcon; - const int paddingTopBot = CSS::MultiZimButton::QListWidget::paddingVertical * 2; - const int itemHeight = paddingTopBot + CSS::ZimItemWidget::QLabel::lineHeight; - for (const auto& bookId : library->getBookIds()) - { - try - { - library->getArchive(bookId); - } catch (...) { continue; } - - const QString bookTitle = QString::fromStdString(library->getBookById(bookId).getTitle()); - const QIcon zimIcon = library->getBookIcon(bookId); - - const auto item = new QListWidgetItem(); - item->setData(Qt::UserRole, bookId); - item->setData(Qt::DisplayRole, bookTitle); - item->setSizeHint(QSize(0, itemHeight)); - - if (view && view->zimId() == bookId) - { - currentItem = item; - currentIcon = zimIcon; - continue; - } - - mp_buttonList->addItem(item); - setItemZimWidget(item, bookTitle, zimIcon); - } - - mp_buttonList->sortItems(); - if (currentItem) - { - mp_buttonList->insertItem(0, currentItem); - - const auto title = currentItem->data(Qt::DisplayRole).toString(); - setItemZimWidget(currentItem, "*" + title, currentIcon); - } - - /* Display should not be used other than for sorting. */ - for (int i = 0; i < mp_buttonList->count(); i++) - mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant()); - - setDisabled(mp_buttonList->model()->rowCount() == 0); - - mp_buttonList->scrollToTop(); - mp_buttonList->setCurrentRow(0); - - /* We set a maximum display height for list. Respect padding. */ - const int listHeight = itemHeight * std::min(7, mp_buttonList->count()); - mp_buttonList->setFixedHeight(listHeight + paddingTopBot); - mp_buttonList->setFixedWidth(menu()->width()); -} - -QStringList MultiZimButton::getZimIds() const -{ - QStringList idList; - for (int row = 0; row < mp_buttonList->count(); row++) - { - const auto widget = getZimWidget(row); - if (widget && widget->getRadioButton()->isChecked()) - idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString()); - } - return idList; -} - -ZimItemWidget *MultiZimButton::getZimWidget(int row) const -{ - const auto widget = mp_buttonList->itemWidget(mp_buttonList->item(row)); - return qobject_cast(widget); -} - -void MultiZimButton::setItemZimWidget(QListWidgetItem *item, - const QString &title, const QIcon &icon) -{ - const auto zimWidget = new ZimItemWidget(title, icon); - mp_radioButtonGroup->addButton(zimWidget->getRadioButton()); - mp_buttonList->setItemWidget(item, zimWidget); -} - -ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) : - QWidget(parent), - textLabel(new QLabel(this)), - iconLabel(new QLabel(this)), - radioBt(new QRadioButton(this)) -{ - setLayout(new QHBoxLayout); - - const int paddingHorizontal = CSS::MultiZimButton::QListWidget::item::paddingHorizontal; - layout()->setSpacing(paddingHorizontal); - layout()->setContentsMargins(0, 0, 0, 0); - - const int iconWidth = CSS::ZimItemWidget::QLabel::lineHeight; - const QSize iconSize = QSize(iconWidth, iconWidth); - iconLabel->setPixmap(icon.pixmap(iconSize)); - - /* Align text on same side irregardless of text direction. */ - const bool needAlignReverse = KiwixApp::isRightToLeft() == text.isRightToLeft(); - const auto align = needAlignReverse ? Qt::AlignLeft : Qt::AlignRight; - textLabel->setAlignment({Qt::AlignVCenter | align}); - - /* Need to align checkmark with select all button. Avoid scroller from - changing checkmark position by always leaving out space on scroller's - side. Do this by align items to the other side and reducing the total - length. textLabel is the only expandable element here. - - We set textLabel width to make sure the entire length always leave out - a fixed amount of white space for scroller. - */ - layout()->setAlignment({Qt::AlignVCenter | Qt::AlignLeading}); - const auto menu = KiwixApp::instance()->getSearchBar().getMultiZimButton().menu(); - const int iconAndCheckerWidth = iconWidth * 2; - const int totalSpacing = paddingHorizontal * 4; - - /* Add an extra border to counteract item border on one side */ - const int border = CSS::MultiZimButton::QListWidget::item::border; - const int scrollerWidth = CSS::MultiZimButton::QScrollBar::width; - const int contentWidthExcludeText = iconAndCheckerWidth + totalSpacing + scrollerWidth + border; - const int labelWidth = menu->width() - contentWidthExcludeText; - textLabel->setFixedWidth(labelWidth); - - QString elidedText = getElidedText(textLabel->font(), labelWidth, text); - textLabel->setText(elidedText == text ? text : elidedText.trimmed() + "(...)"); - - layout()->addWidget(iconLabel); - layout()->addWidget(textLabel); - layout()->addWidget(radioBt); -} +#include +#include +#include +#include +#include +#include "kiwixapp.h" +#include "multizimbutton.h" +#include "css_constants.h" + +QString getElidedText(const QFont& font, int length, const QString& text); + +MultiZimButton::MultiZimButton(QWidget *parent) : + QToolButton(parent), + mp_buttonList(new QListWidget), + mp_radioButtonGroup(new QButtonGroup(this)) +{ + setMenu(new QMenu(this)); + setPopupMode(QToolButton::InstantPopup); + setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::OpenMultiZimAction)); + connect(this, &QToolButton::triggered, this, &MultiZimButton::showMenu); + + const auto popupAction = new QWidgetAction(menu()); + popupAction->setDefaultWidget(mp_buttonList); + menu()->addAction(popupAction); + + connect(mp_buttonList, &QListWidget::currentRowChanged, this, [=](int row){ + if (const auto widget = getZimWidget(row)) + widget->getRadioButton()->setChecked(true); + }); +} + +void MultiZimButton::updateDisplay() +{ + mp_buttonList->clear(); + for (const auto& button : mp_radioButtonGroup->buttons()) + mp_radioButtonGroup->removeButton(button); + + const auto library = KiwixApp::instance()->getLibrary(); + const auto view = KiwixApp::instance()->getTabWidget()->currentWebView(); + QListWidgetItem* currentItem = nullptr; + QIcon currentIcon; + const int paddingTopBot = CSS::MultiZimButton::QListWidget::paddingVertical * 2; + const int itemHeight = paddingTopBot + CSS::ZimItemWidget::QLabel::lineHeight; + for (const auto& bookId : library->getBookIds()) + { + try + { + library->getArchive(bookId); + } catch (...) { continue; } + + const QString bookTitle = QString::fromStdString(library->getBookById(bookId).getTitle()); + const QIcon zimIcon = library->getBookIcon(bookId); + + const auto item = new QListWidgetItem(); + item->setData(Qt::UserRole, bookId); + item->setData(Qt::DisplayRole, bookTitle); + item->setSizeHint(QSize(0, itemHeight)); + + if (view && view->zimId() == bookId) + { + currentItem = item; + currentIcon = zimIcon; + continue; + } + + mp_buttonList->addItem(item); + setItemZimWidget(item, bookTitle, zimIcon); + } + + mp_buttonList->sortItems(); + if (currentItem) + { + mp_buttonList->insertItem(0, currentItem); + + const auto title = currentItem->data(Qt::DisplayRole).toString(); + setItemZimWidget(currentItem, "*" + title, currentIcon); + } + + /* Display should not be used other than for sorting. */ + for (int i = 0; i < mp_buttonList->count(); i++) + mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant()); + + setDisabled(mp_buttonList->model()->rowCount() == 0); + + mp_buttonList->scrollToTop(); + mp_buttonList->setCurrentRow(0); + + /* We set a maximum display height for list. Respect padding. */ + const int listHeight = itemHeight * std::min(7, mp_buttonList->count()); + mp_buttonList->setFixedHeight(listHeight + paddingTopBot); + mp_buttonList->setFixedWidth(menu()->width()); +} + +QStringList MultiZimButton::getZimIds() const +{ + QStringList idList; + for (int row = 0; row < mp_buttonList->count(); row++) + { + const auto widget = getZimWidget(row); + if (widget && widget->getRadioButton()->isChecked()) + idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString()); + } + return idList; +} + +ZimItemWidget *MultiZimButton::getZimWidget(int row) const +{ + const auto widget = mp_buttonList->itemWidget(mp_buttonList->item(row)); + return qobject_cast(widget); +} + +void MultiZimButton::setItemZimWidget(QListWidgetItem *item, + const QString &title, const QIcon &icon) +{ + const auto zimWidget = new ZimItemWidget(title, icon); + mp_radioButtonGroup->addButton(zimWidget->getRadioButton()); + mp_buttonList->setItemWidget(item, zimWidget); +} + +ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) : + QWidget(parent), + textLabel(new QLabel(this)), + iconLabel(new QLabel(this)), + radioBt(new QRadioButton(this)) +{ + setLayout(new QHBoxLayout); + + const int paddingHorizontal = CSS::MultiZimButton::QListWidget::item::paddingHorizontal; + layout()->setSpacing(paddingHorizontal); + layout()->setContentsMargins(0, 0, 0, 0); + + const int iconWidth = CSS::ZimItemWidget::QLabel::lineHeight; + const QSize iconSize = QSize(iconWidth, iconWidth); + iconLabel->setPixmap(icon.pixmap(iconSize)); + + /* Align text on same side irregardless of text direction. */ + const bool needAlignReverse = KiwixApp::isRightToLeft() == text.isRightToLeft(); + const auto align = needAlignReverse ? Qt::AlignLeft : Qt::AlignRight; + textLabel->setAlignment({Qt::AlignVCenter | align}); + + /* Need to align checkmark with select all button. Avoid scroller from + changing checkmark position by always leaving out space on scroller's + side. Do this by align items to the other side and reducing the total + length. textLabel is the only expandable element here. + + We set textLabel width to make sure the entire length always leave out + a fixed amount of white space for scroller. + */ + layout()->setAlignment({Qt::AlignVCenter | Qt::AlignLeading}); + const auto menu = KiwixApp::instance()->getSearchBar().getMultiZimButton().menu(); + const int iconAndCheckerWidth = iconWidth * 2; + const int totalSpacing = paddingHorizontal * 4; + + /* Add an extra border to counteract item border on one side */ + const int border = CSS::MultiZimButton::QListWidget::item::border; + const int scrollerWidth = CSS::MultiZimButton::QScrollBar::width; + const int contentWidthExcludeText = iconAndCheckerWidth + totalSpacing + scrollerWidth + border; + const int labelWidth = menu->width() - contentWidthExcludeText; + textLabel->setFixedWidth(labelWidth); + + QString elidedText = getElidedText(textLabel->font(), labelWidth, text); + textLabel->setText(elidedText == text ? text : elidedText.trimmed() + "(...)"); + + layout()->addWidget(iconLabel); + layout()->addWidget(textLabel); + layout()->addWidget(radioBt); +} diff --git a/src/multizimbutton.h b/src/multizimbutton.h index 0f20ffeb8..290bc5815 100644 --- a/src/multizimbutton.h +++ b/src/multizimbutton.h @@ -1,44 +1,44 @@ -#ifndef MULTIZIMBUTTON_H -#define MULTIZIMBUTTON_H - -#include - -class QListWidget; -class QButtonGroup; -class QListWidgetItem; -class QRadioButton; -class QLabel; - -class ZimItemWidget : public QWidget { - Q_OBJECT - -public: - ZimItemWidget(QString text, QIcon icon, QWidget *parent = nullptr); - - QRadioButton* getRadioButton() const { return radioBt; } - -private: - QLabel* textLabel; - QLabel* iconLabel; - QRadioButton* radioBt; -}; - -class MultiZimButton : public QToolButton { - Q_OBJECT - -public: - explicit MultiZimButton(QWidget *parent = nullptr); - -public slots: - void updateDisplay(); - QStringList getZimIds() const; - -private: - QListWidget* mp_buttonList; - QButtonGroup* mp_radioButtonGroup; - - ZimItemWidget* getZimWidget(int row) const; - void setItemZimWidget(QListWidgetItem* item, const QString& title, const QIcon& icon); -}; - -#endif // MULTIZIMBUTTON_H +#ifndef MULTIZIMBUTTON_H +#define MULTIZIMBUTTON_H + +#include + +class QListWidget; +class QButtonGroup; +class QListWidgetItem; +class QRadioButton; +class QLabel; + +class ZimItemWidget : public QWidget { + Q_OBJECT + +public: + ZimItemWidget(QString text, QIcon icon, QWidget *parent = nullptr); + + QRadioButton* getRadioButton() const { return radioBt; } + +private: + QLabel* textLabel; + QLabel* iconLabel; + QRadioButton* radioBt; +}; + +class MultiZimButton : public QToolButton { + Q_OBJECT + +public: + explicit MultiZimButton(QWidget *parent = nullptr); + +public slots: + void updateDisplay(); + QStringList getZimIds() const; + +private: + QListWidget* mp_buttonList; + QButtonGroup* mp_radioButtonGroup; + + ZimItemWidget* getZimWidget(int row) const; + void setItemZimWidget(QListWidgetItem* item, const QString& title, const QIcon& icon); +}; + +#endif // MULTIZIMBUTTON_H diff --git a/src/node.h b/src/node.h index 743847da8..dc8e84eb4 100644 --- a/src/node.h +++ b/src/node.h @@ -1,20 +1,20 @@ -#ifndef NODE_H -#define NODE_H - -#include -#include - -class Node : public std::enable_shared_from_this -{ -public: - virtual ~Node() = default; - virtual std::shared_ptr parentItem() = 0; - virtual int childCount() const = 0; - virtual int columnCount() const = 0; - virtual QVariant data(int column) = 0; - virtual int row() const = 0; - virtual QString getBookId() const = 0; -}; - - -#endif // NODE_H +#ifndef NODE_H +#define NODE_H + +#include +#include + +class Node : public std::enable_shared_from_this +{ +public: + virtual ~Node() = default; + virtual std::shared_ptr parentItem() = 0; + virtual int childCount() const = 0; + virtual int columnCount() const = 0; + virtual QVariant data(int column) = 0; + virtual int row() const = 0; + virtual QString getBookId() const = 0; +}; + + +#endif // NODE_H diff --git a/src/opdsrequestmanager.cpp b/src/opdsrequestmanager.cpp index a3346fe4c..f996f7f06 100644 --- a/src/opdsrequestmanager.cpp +++ b/src/opdsrequestmanager.cpp @@ -1,104 +1,104 @@ -#include "opdsrequestmanager.h" -#include "kiwixapp.h" - -OpdsRequestManager::OpdsRequestManager() -{ -} - -QString OpdsRequestManager::getCatalogHost() -{ - const char* const envVarVal = getenv("KIWIX_CATALOG_HOST"); - return envVarVal - ? envVarVal - : "library.kiwix.org"; -} - -int OpdsRequestManager::getCatalogPort() -{ - const char* const envVarVal = getenv("KIWIX_CATALOG_PORT"); - return envVarVal - ? atoi(envVarVal) - : 443; -} - -void OpdsRequestManager::doUpdate(const QString& currentLanguage, const QString& categoryFilter) -{ - QUrlQuery query; - - // Service worker ZIM files are not (yet) supported - QStringList excludeTags("_sw:yes"); - - // Add filter by language (if necessary) - if (currentLanguage != "") { - query.addQueryItem("lang", currentLanguage); - } - - // Request all results (no pagination) - query.addQueryItem("count", QString::number(-1)); - - // Add filter by category (if necessary) - if (categoryFilter != "") { - query.addQueryItem("category", categoryFilter); - } - - auto mp_reply = opdsResponseFromPath("/catalog/search", query); - connect(mp_reply, &QNetworkReply::finished, this, [=]() { - receiveContent(mp_reply); - }); -} - -QNetworkReply* OpdsRequestManager::opdsResponseFromPath(const QString &path, const QUrlQuery &query) -{ - QUrl url; - const int port = getCatalogPort(); - url.setScheme(port == 443 ? "https" : "http"); - url.setHost(getCatalogHost()); - url.setPort(port); - url.setPath(path); - url.setQuery(query); - qInfo() << "Downloading" << url.toString(QUrl::FullyEncoded); - QNetworkRequest request(url); - return m_networkManager.get(request); -} - -void OpdsRequestManager::getLanguagesFromOpds() -{ - auto mp_reply = opdsResponseFromPath("/catalog/v2/languages"); - connect(mp_reply, &QNetworkReply::finished, this, [=]() { - receiveLanguages(mp_reply); - }); -} - -void OpdsRequestManager::getCategoriesFromOpds() -{ - auto mp_reply = opdsResponseFromPath("/catalog/v2/categories"); - connect(mp_reply, &QNetworkReply::finished, this, [=]() { - receiveCategories(mp_reply); - }); -} - -QString replyContent(QNetworkReply *mp_reply) -{ - QString content; - if (mp_reply->error() != QNetworkReply::OperationCanceledError) { - content = mp_reply->readAll().data(); - } - mp_reply->deleteLater(); - mp_reply = nullptr; - return content; -} - -void OpdsRequestManager::receiveLanguages(QNetworkReply *mp_reply) -{ - emit(languagesReceived(replyContent(mp_reply))); -} - -void OpdsRequestManager::receiveCategories(QNetworkReply *mp_reply) -{ - emit(categoriesReceived(replyContent(mp_reply))); -} - -void OpdsRequestManager::receiveContent(QNetworkReply *mp_reply) -{ - emit(requestReceived(replyContent(mp_reply))); -} +#include "opdsrequestmanager.h" +#include "kiwixapp.h" + +OpdsRequestManager::OpdsRequestManager() +{ +} + +QString OpdsRequestManager::getCatalogHost() +{ + const char* const envVarVal = getenv("KIWIX_CATALOG_HOST"); + return envVarVal + ? envVarVal + : "library.kiwix.org"; +} + +int OpdsRequestManager::getCatalogPort() +{ + const char* const envVarVal = getenv("KIWIX_CATALOG_PORT"); + return envVarVal + ? atoi(envVarVal) + : 443; +} + +void OpdsRequestManager::doUpdate(const QString& currentLanguage, const QString& categoryFilter) +{ + QUrlQuery query; + + // Service worker ZIM files are not (yet) supported + QStringList excludeTags("_sw:yes"); + + // Add filter by language (if necessary) + if (currentLanguage != "") { + query.addQueryItem("lang", currentLanguage); + } + + // Request all results (no pagination) + query.addQueryItem("count", QString::number(-1)); + + // Add filter by category (if necessary) + if (categoryFilter != "") { + query.addQueryItem("category", categoryFilter); + } + + auto mp_reply = opdsResponseFromPath("/catalog/search", query); + connect(mp_reply, &QNetworkReply::finished, this, [=]() { + receiveContent(mp_reply); + }); +} + +QNetworkReply* OpdsRequestManager::opdsResponseFromPath(const QString &path, const QUrlQuery &query) +{ + QUrl url; + const int port = getCatalogPort(); + url.setScheme(port == 443 ? "https" : "http"); + url.setHost(getCatalogHost()); + url.setPort(port); + url.setPath(path); + url.setQuery(query); + qInfo() << "Downloading" << url.toString(QUrl::FullyEncoded); + QNetworkRequest request(url); + return m_networkManager.get(request); +} + +void OpdsRequestManager::getLanguagesFromOpds() +{ + auto mp_reply = opdsResponseFromPath("/catalog/v2/languages"); + connect(mp_reply, &QNetworkReply::finished, this, [=]() { + receiveLanguages(mp_reply); + }); +} + +void OpdsRequestManager::getCategoriesFromOpds() +{ + auto mp_reply = opdsResponseFromPath("/catalog/v2/categories"); + connect(mp_reply, &QNetworkReply::finished, this, [=]() { + receiveCategories(mp_reply); + }); +} + +QString replyContent(QNetworkReply *mp_reply) +{ + QString content; + if (mp_reply->error() != QNetworkReply::OperationCanceledError) { + content = mp_reply->readAll().data(); + } + mp_reply->deleteLater(); + mp_reply = nullptr; + return content; +} + +void OpdsRequestManager::receiveLanguages(QNetworkReply *mp_reply) +{ + emit(languagesReceived(replyContent(mp_reply))); +} + +void OpdsRequestManager::receiveCategories(QNetworkReply *mp_reply) +{ + emit(categoriesReceived(replyContent(mp_reply))); +} + +void OpdsRequestManager::receiveContent(QNetworkReply *mp_reply) +{ + emit(requestReceived(replyContent(mp_reply))); +} diff --git a/src/opdsrequestmanager.h b/src/opdsrequestmanager.h index b14f9a293..defd0cfe7 100644 --- a/src/opdsrequestmanager.h +++ b/src/opdsrequestmanager.h @@ -1,41 +1,41 @@ -#ifndef OPDSREQUESTMANAGER_H -#define OPDSREQUESTMANAGER_H - -#include -#include -#include -#include - -class OpdsRequestManager : public QObject -{ - Q_OBJECT - -public: - OpdsRequestManager(); - ~OpdsRequestManager() {} - -public: - void doUpdate(const QString& currentLanguage, const QString& categoryFilter); - void getLanguagesFromOpds(); - void getCategoriesFromOpds(); - -private: - QNetworkAccessManager m_networkManager; - QNetworkReply* opdsResponseFromPath(const QString &path, const QUrlQuery &query = QUrlQuery()); - -signals: - void requestReceived(const QString&); - void languagesReceived(const QString&); - void categoriesReceived(const QString&); - -public slots: - void receiveContent(QNetworkReply*); - void receiveLanguages(QNetworkReply*); - void receiveCategories(QNetworkReply*); - -public: - static QString getCatalogHost(); - static int getCatalogPort(); -}; - -#endif // OPDSREQUESTMANAGER_H +#ifndef OPDSREQUESTMANAGER_H +#define OPDSREQUESTMANAGER_H + +#include +#include +#include +#include + +class OpdsRequestManager : public QObject +{ + Q_OBJECT + +public: + OpdsRequestManager(); + ~OpdsRequestManager() {} + +public: + void doUpdate(const QString& currentLanguage, const QString& categoryFilter); + void getLanguagesFromOpds(); + void getCategoriesFromOpds(); + +private: + QNetworkAccessManager m_networkManager; + QNetworkReply* opdsResponseFromPath(const QString &path, const QUrlQuery &query = QUrlQuery()); + +signals: + void requestReceived(const QString&); + void languagesReceived(const QString&); + void categoriesReceived(const QString&); + +public slots: + void receiveContent(QNetworkReply*); + void receiveLanguages(QNetworkReply*); + void receiveCategories(QNetworkReply*); + +public: + static QString getCatalogHost(); + static int getCatalogPort(); +}; + +#endif // OPDSREQUESTMANAGER_H diff --git a/src/portutils.h b/src/portutils.h index a54ec8f43..2f4984827 100644 --- a/src/portutils.h +++ b/src/portutils.h @@ -1,39 +1,39 @@ -#ifndef PORTUTILS_H -#define PORTUTILS_H - -#include - -namespace portutils { - - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Earlier than Qt6 - inline int getX(const QMouseEvent& e) { - return e.x(); - } - - inline int getY(const QMouseEvent& e) { - return e.y(); - } - - inline QPoint getGlobalPos(const QMouseEvent& e) { - return e.globalPos(); - } - -#else // Qt6 and later - inline int getX(const QMouseEvent& e) { - return e.position().x(); - } - - inline int getY(const QMouseEvent& e) { - return e.position().y(); - } - - inline QPoint getGlobalPos(const QMouseEvent& e) { - return e.globalPosition().toPoint(); - } - -#endif - -} - -#endif // PORTUTILS_H +#ifndef PORTUTILS_H +#define PORTUTILS_H + +#include + +namespace portutils { + + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Earlier than Qt6 + inline int getX(const QMouseEvent& e) { + return e.x(); + } + + inline int getY(const QMouseEvent& e) { + return e.y(); + } + + inline QPoint getGlobalPos(const QMouseEvent& e) { + return e.globalPos(); + } + +#else // Qt6 and later + inline int getX(const QMouseEvent& e) { + return e.position().x(); + } + + inline int getY(const QMouseEvent& e) { + return e.position().y(); + } + + inline QPoint getGlobalPos(const QMouseEvent& e) { + return e.globalPosition().toPoint(); + } + +#endif + +} + +#endif // PORTUTILS_H diff --git a/src/readinglistbar.cpp b/src/readinglistbar.cpp index 0ce5a26d7..560b1f791 100644 --- a/src/readinglistbar.cpp +++ b/src/readinglistbar.cpp @@ -1,146 +1,146 @@ -#include "readinglistbar.h" -#include "ui_readinglistbar.h" -#include "kiwixapp.h" -#include "zim/error.h" -#include "zim/item.h" - -#include -#include -#include - -const QString documentsDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); - -ReadingListBar::ReadingListBar(QWidget *parent) : - QFrame(parent), - ui(new Ui::readinglistbar) -{ - ui->setupUi(this); - ui->titleLabel->setText(gt("reading-list")); - - connect(KiwixApp::instance()->getLibrary(), &Library::bookmarksChanged, - this, &ReadingListBar::setupList); - connect(ui->listWidget, &QListWidget::itemClicked, - this, &ReadingListBar::onItemClicked); - connect(ui->listWidget, &QListWidget::itemDoubleClicked, - this, &ReadingListBar::onItemDoubleClicked); - connect(ui->listWidget, &QListWidget::itemPressed, - this, [this](QListWidgetItem* item) { - onItemPressed(item, QGuiApplication::mouseButtons()); - }); - connect(ui->listWidget, &QListWidget::itemActivated, - this, [this](QListWidgetItem* item) { - onItemActivated(item, QGuiApplication::mouseButtons()); - }); - - setupList(); - - auto app = KiwixApp::instance(); - auto exportAction = app->getAction(KiwixApp::ExportReadingListAction); - auto importAction = app->getAction(KiwixApp::ImportReadingListAction); - connect(exportAction, &QAction::triggered, this, &ReadingListBar::onExport); - connect(importAction, &QAction::triggered, this, &ReadingListBar::onImport); - - QMenu *portMenu = new QMenu(this); - portMenu->addAction(exportAction); - portMenu->addAction(importAction); - ui->readingListMenuButton->setMenu(portMenu); -} - -ReadingListBar::~ReadingListBar() -{ - delete ui; -} - -void ReadingListBar::setupList() -{ - auto library = KiwixApp::instance()->getLibrary(); - auto bookmarks = library->getBookmarks(); - auto listWidget = ui->listWidget; - listWidget->clear(); - for(auto& bookmark:bookmarks) { - const auto zimId = QString::fromStdString(bookmark.getBookId()); - try { - library->getArchive(zimId); - } catch (std::out_of_range& e) { - continue; - } - new QListWidgetItem( - library->getBookIcon(zimId), - QString::fromStdString(bookmark.getTitle()), - listWidget); - } -} - -// Receives single clicks -void ReadingListBar::onItemClicked(QListWidgetItem* item) -{ - clickKind = 1; - QTimer::singleShot(QApplication::doubleClickInterval(), [=]() { // Give time for a double click be handled - if (clickKind == 1) { - openUrl(item, false); - } - }); -} - -// Receives double clicks -void ReadingListBar::onItemDoubleClicked(QListWidgetItem* item) -{ - clickKind = 2; - openUrl(item, true); -} - -// Receives single and middle click -void ReadingListBar::onItemPressed(QListWidgetItem* item, Qt::MouseButtons buttons) -{ - if (buttons & Qt::MiddleButton) { - openUrl(item, true); - } -} - -// Receives left clicks and activation key -void ReadingListBar::onItemActivated(QListWidgetItem* item, Qt::MouseButtons buttons) -{ - if (!buttons) { // clicks are handled elsewhere, handle only the activation key case - openUrl(item, true); - } -} - -void ReadingListBar::onExport() -{ - auto app = KiwixApp::instance(); - auto kiwixLibrary = app->getLibrary()->getKiwixLibrary(); - auto suggestedFilePath = documentsDir + "/kiwix_readinglist.xml"; - QString fileName = QFileDialog::getSaveFileName(app->getMainWindow(), - gt("save-file-as-window-title"), - suggestedFilePath, "(*.xml)"); - if (fileName.isEmpty()) - return; - - if (!kiwixLibrary->writeBookmarksToFile(fileName.toStdString())) - app->showMessage(gt("export-reading-list-error"), gt("error-title"), QMessageBox::Information); -} - -void ReadingListBar::onImport() -{ - auto app = KiwixApp::instance(); - auto library = app->getLibrary(); - QString fileName = QFileDialog::getOpenFileName(app->getMainWindow(), - gt("open-file"), - documentsDir, "(*.xml)"); - if (fileName.isEmpty()) - return; - - if (!library->readBookMarksFile(fileName.toStdString())) - app->showMessage(gt("import-reading-list-error"), gt("error-title"), QMessageBox::Information); -} - -void ReadingListBar::openUrl(QListWidgetItem* item, bool newTab) -{ - int index = ui->listWidget->row(item); - auto bookmark = KiwixApp::instance()->getLibrary()->getBookmarks(true).at(index); - QUrl url; - url.setScheme("zim"); - url.setHost(QString::fromStdString(bookmark.getBookId())+".zim"); - url.setPath(QString::fromStdString(bookmark.getUrl())); - KiwixApp::instance()->openUrl(url, newTab); -} +#include "readinglistbar.h" +#include "ui_readinglistbar.h" +#include "kiwixapp.h" +#include "zim/error.h" +#include "zim/item.h" + +#include +#include +#include + +const QString documentsDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + +ReadingListBar::ReadingListBar(QWidget *parent) : + QFrame(parent), + ui(new Ui::readinglistbar) +{ + ui->setupUi(this); + ui->titleLabel->setText(gt("reading-list")); + + connect(KiwixApp::instance()->getLibrary(), &Library::bookmarksChanged, + this, &ReadingListBar::setupList); + connect(ui->listWidget, &QListWidget::itemClicked, + this, &ReadingListBar::onItemClicked); + connect(ui->listWidget, &QListWidget::itemDoubleClicked, + this, &ReadingListBar::onItemDoubleClicked); + connect(ui->listWidget, &QListWidget::itemPressed, + this, [this](QListWidgetItem* item) { + onItemPressed(item, QGuiApplication::mouseButtons()); + }); + connect(ui->listWidget, &QListWidget::itemActivated, + this, [this](QListWidgetItem* item) { + onItemActivated(item, QGuiApplication::mouseButtons()); + }); + + setupList(); + + auto app = KiwixApp::instance(); + auto exportAction = app->getAction(KiwixApp::ExportReadingListAction); + auto importAction = app->getAction(KiwixApp::ImportReadingListAction); + connect(exportAction, &QAction::triggered, this, &ReadingListBar::onExport); + connect(importAction, &QAction::triggered, this, &ReadingListBar::onImport); + + QMenu *portMenu = new QMenu(this); + portMenu->addAction(exportAction); + portMenu->addAction(importAction); + ui->readingListMenuButton->setMenu(portMenu); +} + +ReadingListBar::~ReadingListBar() +{ + delete ui; +} + +void ReadingListBar::setupList() +{ + auto library = KiwixApp::instance()->getLibrary(); + auto bookmarks = library->getBookmarks(); + auto listWidget = ui->listWidget; + listWidget->clear(); + for(auto& bookmark:bookmarks) { + const auto zimId = QString::fromStdString(bookmark.getBookId()); + try { + library->getArchive(zimId); + } catch (std::out_of_range& e) { + continue; + } + new QListWidgetItem( + library->getBookIcon(zimId), + QString::fromStdString(bookmark.getTitle()), + listWidget); + } +} + +// Receives single clicks +void ReadingListBar::onItemClicked(QListWidgetItem* item) +{ + clickKind = 1; + QTimer::singleShot(QApplication::doubleClickInterval(), [=]() { // Give time for a double click be handled + if (clickKind == 1) { + openUrl(item, false); + } + }); +} + +// Receives double clicks +void ReadingListBar::onItemDoubleClicked(QListWidgetItem* item) +{ + clickKind = 2; + openUrl(item, true); +} + +// Receives single and middle click +void ReadingListBar::onItemPressed(QListWidgetItem* item, Qt::MouseButtons buttons) +{ + if (buttons & Qt::MiddleButton) { + openUrl(item, true); + } +} + +// Receives left clicks and activation key +void ReadingListBar::onItemActivated(QListWidgetItem* item, Qt::MouseButtons buttons) +{ + if (!buttons) { // clicks are handled elsewhere, handle only the activation key case + openUrl(item, true); + } +} + +void ReadingListBar::onExport() +{ + auto app = KiwixApp::instance(); + auto kiwixLibrary = app->getLibrary()->getKiwixLibrary(); + auto suggestedFilePath = documentsDir + "/kiwix_readinglist.xml"; + QString fileName = QFileDialog::getSaveFileName(app->getMainWindow(), + gt("save-file-as-window-title"), + suggestedFilePath, "(*.xml)"); + if (fileName.isEmpty()) + return; + + if (!kiwixLibrary->writeBookmarksToFile(fileName.toStdString())) + app->showMessage(gt("export-reading-list-error"), gt("error-title"), QMessageBox::Information); +} + +void ReadingListBar::onImport() +{ + auto app = KiwixApp::instance(); + auto library = app->getLibrary(); + QString fileName = QFileDialog::getOpenFileName(app->getMainWindow(), + gt("open-file"), + documentsDir, "(*.xml)"); + if (fileName.isEmpty()) + return; + + if (!library->readBookMarksFile(fileName.toStdString())) + app->showMessage(gt("import-reading-list-error"), gt("error-title"), QMessageBox::Information); +} + +void ReadingListBar::openUrl(QListWidgetItem* item, bool newTab) +{ + int index = ui->listWidget->row(item); + auto bookmark = KiwixApp::instance()->getLibrary()->getBookmarks(true).at(index); + QUrl url; + url.setScheme("zim"); + url.setHost(QString::fromStdString(bookmark.getBookId())+".zim"); + url.setPath(QString::fromStdString(bookmark.getUrl())); + KiwixApp::instance()->openUrl(url, newTab); +} diff --git a/src/readinglistbar.h b/src/readinglistbar.h index 526a08733..19c711221 100644 --- a/src/readinglistbar.h +++ b/src/readinglistbar.h @@ -1,33 +1,33 @@ -#ifndef READINGLISTBAR_H -#define READINGLISTBAR_H - -#include -#include - -namespace Ui { -class readinglistbar; -} - -class ReadingListBar : public QFrame -{ - Q_OBJECT - -public: - explicit ReadingListBar(QWidget *parent = nullptr); - ~ReadingListBar(); - -public slots: - void setupList(); - void onItemClicked(QListWidgetItem* item); - void onItemDoubleClicked(QListWidgetItem *item); - void onItemPressed(QListWidgetItem* item, Qt::MouseButtons buttons); - void onItemActivated(QListWidgetItem *item, Qt::MouseButtons buttons); - void onExport(); - void onImport(); -private: - Ui::readinglistbar *ui; - int clickKind; - void openUrl(QListWidgetItem* item, bool newTab); -}; - -#endif // READINGLISTBAR_H +#ifndef READINGLISTBAR_H +#define READINGLISTBAR_H + +#include +#include + +namespace Ui { +class readinglistbar; +} + +class ReadingListBar : public QFrame +{ + Q_OBJECT + +public: + explicit ReadingListBar(QWidget *parent = nullptr); + ~ReadingListBar(); + +public slots: + void setupList(); + void onItemClicked(QListWidgetItem* item); + void onItemDoubleClicked(QListWidgetItem *item); + void onItemPressed(QListWidgetItem* item, Qt::MouseButtons buttons); + void onItemActivated(QListWidgetItem *item, Qt::MouseButtons buttons); + void onExport(); + void onImport(); +private: + Ui::readinglistbar *ui; + int clickKind; + void openUrl(QListWidgetItem* item, bool newTab); +}; + +#endif // READINGLISTBAR_H diff --git a/src/readinglistbar.ui b/src/readinglistbar.ui index 7a68214ca..ba5b5db51 100644 --- a/src/readinglistbar.ui +++ b/src/readinglistbar.ui @@ -1,122 +1,122 @@ - - - readinglistbar - - - - 0 - 0 - 400 - 300 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 16 - - - - Reading List - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - :/icons/more-vertical.svg:/icons/more-vertical.svg - - - - 25 - 25 - - - - true - - - - - - - - - Qt::Horizontal - - - - - - - QFrame::NoFrame - - - 0 - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - Qt::ElideRight - - - false - - - true - - - true - - - - - - - - - - + + + readinglistbar + + + + 0 + 0 + 400 + 300 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 16 + + + + Reading List + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + :/icons/more-vertical.svg:/icons/more-vertical.svg + + + + 25 + 25 + + + + true + + + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + 0 + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + Qt::ElideRight + + + false + + + true + + + true + + + + + + + + + + diff --git a/src/rownode.cpp b/src/rownode.cpp index 7211f5f63..b457db8be 100644 --- a/src/rownode.cpp +++ b/src/rownode.cpp @@ -1,84 +1,84 @@ -#include "rownode.h" -#include -#include "kiwixapp.h" -#include "descriptionnode.h" - -//////////////////////////////////////////////////////////////////////////////// -// RowNode -//////////////////////////////////////////////////////////////////////////////// - -RowNode::RowNode(QList itemData, QString bookId, std::weak_ptr parent) - : m_itemData(itemData), m_parentItem(parent), m_bookId(bookId) -{ -} - -RowNode::~RowNode() -{} - -void RowNode::appendChild(std::shared_ptr item) -{ - m_childItems.append(item); -} - -std::shared_ptr RowNode::child(int row) -{ - if (row < 0 || row >= m_childItems.size()) - return nullptr; - return m_childItems.at(row); -} - -int RowNode::childCount() const -{ - return m_childItems.count(); -} - -int RowNode::columnCount() const -{ - return 6; -} - -std::shared_ptr RowNode::parentItem() -{ - std::shared_ptr temp = m_parentItem.lock(); - if (!temp) - return nullptr; - return temp; -} - -QVariant RowNode::data(int column) -{ - if (column < 0 || column >= m_itemData.size()) - return QVariant(); - return m_itemData.at(column); -} - -int RowNode::row() const -{ - try { - std::shared_ptr temp = m_parentItem.lock(); - if (temp) { - auto nodePtr = std::const_pointer_cast(shared_from_this()); - return temp->m_childItems.indexOf(nodePtr); - } - } catch(...) { - return 0; - } - - return 0; -} - -bool RowNode::isChild(Node *candidate) -{ - if (!candidate) - return false; - for (auto item : m_childItems) { - if (candidate == item.get()) - return true; - } - return false; -} - -void RowNode::setDownloadState(std::shared_ptr ds) -{ - m_downloadState = ds; -} +#include "rownode.h" +#include +#include "kiwixapp.h" +#include "descriptionnode.h" + +//////////////////////////////////////////////////////////////////////////////// +// RowNode +//////////////////////////////////////////////////////////////////////////////// + +RowNode::RowNode(QList itemData, QString bookId, std::weak_ptr parent) + : m_itemData(itemData), m_parentItem(parent), m_bookId(bookId) +{ +} + +RowNode::~RowNode() +{} + +void RowNode::appendChild(std::shared_ptr item) +{ + m_childItems.append(item); +} + +std::shared_ptr RowNode::child(int row) +{ + if (row < 0 || row >= m_childItems.size()) + return nullptr; + return m_childItems.at(row); +} + +int RowNode::childCount() const +{ + return m_childItems.count(); +} + +int RowNode::columnCount() const +{ + return 6; +} + +std::shared_ptr RowNode::parentItem() +{ + std::shared_ptr temp = m_parentItem.lock(); + if (!temp) + return nullptr; + return temp; +} + +QVariant RowNode::data(int column) +{ + if (column < 0 || column >= m_itemData.size()) + return QVariant(); + return m_itemData.at(column); +} + +int RowNode::row() const +{ + try { + std::shared_ptr temp = m_parentItem.lock(); + if (temp) { + auto nodePtr = std::const_pointer_cast(shared_from_this()); + return temp->m_childItems.indexOf(nodePtr); + } + } catch(...) { + return 0; + } + + return 0; +} + +bool RowNode::isChild(Node *candidate) +{ + if (!candidate) + return false; + for (auto item : m_childItems) { + if (candidate == item.get()) + return true; + } + return false; +} + +void RowNode::setDownloadState(std::shared_ptr ds) +{ + m_downloadState = ds; +} diff --git a/src/rownode.h b/src/rownode.h index f751e0028..fb808b3fd 100644 --- a/src/rownode.h +++ b/src/rownode.h @@ -1,39 +1,39 @@ -#ifndef ROWNODE_H -#define ROWNODE_H - -#include "node.h" -#include -#include -#include "kiwix/book.h" -#include "downloadmanagement.h" - -class RowNode : public Node -{ -public: - explicit RowNode(QList itemData, QString bookId, std::weak_ptr parentItem); - ~RowNode(); - std::shared_ptr parentItem() override; - std::shared_ptr child(int row); - void appendChild(std::shared_ptr child); - int childCount() const override; - int columnCount() const override; - QVariant data(int column) override; - int row() const override; - QString getBookId() const override { return m_bookId; } - void setIconData(QByteArray iconData) { m_itemData[0] = iconData; } - bool isChild(Node* candidate); - - - void setDownloadState(std::shared_ptr ds); - std::shared_ptr getDownloadState() { return m_downloadState; } - -private: - QList m_itemData; - QList> m_childItems; - std::weak_ptr m_parentItem; - QString m_bookId; - std::shared_ptr m_downloadState; -}; - - -#endif // ROWNODE_H +#ifndef ROWNODE_H +#define ROWNODE_H + +#include "node.h" +#include +#include +#include "kiwix/book.h" +#include "downloadmanagement.h" + +class RowNode : public Node +{ +public: + explicit RowNode(QList itemData, QString bookId, std::weak_ptr parentItem); + ~RowNode(); + std::shared_ptr parentItem() override; + std::shared_ptr child(int row); + void appendChild(std::shared_ptr child); + int childCount() const override; + int columnCount() const override; + QVariant data(int column) override; + int row() const override; + QString getBookId() const override { return m_bookId; } + void setIconData(QByteArray iconData) { m_itemData[0] = iconData; } + bool isChild(Node* candidate); + + + void setDownloadState(std::shared_ptr ds); + std::shared_ptr getDownloadState() { return m_downloadState; } + +private: + QList m_itemData; + QList> m_childItems; + std::weak_ptr m_parentItem; + QString m_bookId; + std::shared_ptr m_downloadState; +}; + + +#endif // ROWNODE_H diff --git a/src/searchbar.cpp b/src/searchbar.cpp index 3ecd18346..324f29931 100644 --- a/src/searchbar.cpp +++ b/src/searchbar.cpp @@ -1,443 +1,443 @@ -#include "searchbar.h" - -#include -#include -#include -#include - -#include "kiwixapp.h" -#include "suggestionlistworker.h" -#include "css_constants.h" -#include "suggestionlistdelegate.h" - -namespace HeaderSectionCSS = CSS::PopupCSS::QHeaderView::section; - -BookmarkButton::BookmarkButton(QWidget *parent) : - QToolButton(parent) -{ - connect(this, &QToolButton::triggered, this, &BookmarkButton::on_buttonClicked); - connect(this, &QToolButton::triggered, this, &BookmarkButton::update_display); - setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::Actions::ToggleAddBookmarkAction)); - - auto library = KiwixApp::instance()->getLibrary(); - connect(library, &Library::bookmarksChanged, this, &BookmarkButton::update_display); -} - -void BookmarkButton::update_display() -{ - auto isBookMarked = KiwixApp::instance()->isCurrentArticleBookmarked(); - auto buttonText = isBookMarked ? gt("remove-bookmark") : gt("add-bookmark"); - defaultAction()->setChecked(isBookMarked); - defaultAction()->setToolTip(buttonText); - defaultAction()->setText(buttonText); -} - -void BookmarkButton::on_buttonClicked() -{ - auto kiwixApp = KiwixApp::instance(); - auto library = kiwixApp->getLibrary(); - auto tabWidget = kiwixApp->getTabWidget(); - if (kiwixApp->isCurrentArticleBookmarked()) { - auto zimid = tabWidget->currentZimId(); - library->removeBookmark( - zimid, tabWidget->currentArticleUrl() - ); - } else { - kiwix::Bookmark bookmark; - auto zimid = tabWidget->currentZimId().toStdString(); - bookmark.setBookId(zimid); - bookmark.setUrl(tabWidget->currentArticleUrl().toStdString()); - bookmark.setTitle(tabWidget->currentArticleTitle().toStdString()); - library->addBookmark(bookmark); - } - library->save(); -} - -SearchBarLineEdit::SearchBarLineEdit(QWidget *parent) : - QLineEdit(parent), - m_suggestionView(new QTreeView), - m_completer(&m_suggestionModel, this) -{ - installEventFilter(this); - setAlignment(KiwixApp::isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft); - mp_typingTimer = new QTimer(this); - mp_typingTimer->setSingleShot(true); - - /* Placeholder does not affect line edit alignment and is aligned to line - edit purely by text direction (LTR leading RTL ending). Thus, we need - directional mask to make it LTR at leading position. - https://stackoverflow.com/questions/66430215/english-and-arabic-mixed-string-not-ordered-correctly-qt - */ - const QString ltrConversionChar(u8"\u200e"); - setPlaceholderText(ltrConversionChar + gt("search")); - setToolTip(gt("search")); - m_completer.setCompletionMode(QCompleter::UnfilteredPopupCompletion); - m_completer.setCaseSensitivity(Qt::CaseInsensitive); - - /* The items should be less than fetch size to enable scrolling. */ - m_completer.setMaxVisibleItems(SuggestionListWorker::getFetchSize() / 2); - m_completer.setWidget(this); - - /* QCompleter's uses default list views, which do not have headers. */ - m_completer.setPopup(m_suggestionView); - - /* The Delegate was overwritten by setPopup(), which is not style-aware */ - m_suggestionView->setItemDelegate(new SuggestionListDelegate(this)); - m_suggestionView->header()->setStretchLastSection(true); - m_suggestionView->setRootIsDecorated(false); - m_suggestionView->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/popup.css")); - - const int contentHeight = HeaderSectionCSS::lineHeight; - m_suggestionView->setIconSize(QSize(contentHeight, contentHeight)); - - /* The suggestionView sizing unfortunately is not aware of headers. We - have to do this manually. We also sized header the same as items. - */ - connect(&m_suggestionModel, &QAbstractListModel::modelReset, [=](){ - /* +1 for header. */ - const int maxItem = m_completer.maxVisibleItems(); - const int count = std::min(m_suggestionModel.rowCount(), maxItem) + 1; - - const int itemHeight = m_suggestionView->sizeHintForRow(0); - - /* Extra space styling above header and below last suggestion item. */ - const int extraMargin = 2 * HeaderSectionCSS::marginTop; - m_suggestionView->setFixedHeight(itemHeight * count + extraMargin); - }); - - connect(m_suggestionView->verticalScrollBar(), &QScrollBar::valueChanged, - this, &SearchBarLineEdit::onScroll); - - qRegisterMetaType>("QList"); - connect(mp_typingTimer, &QTimer::timeout, this, &SearchBarLineEdit::updateCompletion); - - connect(this, &QLineEdit::textEdited, this, - [=](const QString &text) { - m_searchbarInput = text; - m_returnPressed = false; - mp_typingTimer->start(100); - }); - connect(this, &QLineEdit::textChanged, this, - [=](const QString &text) { - Q_UNUSED(text) - if (m_returnPressed) { - this->setText(m_searchbarInput); - } - - /* Empty text is LTR which changes the line edit alignment. - Need to explicitly align right. This is a generalized - solution that aligns text to the direction of the app. */ - bool isSameDirection = text.isRightToLeft() == KiwixApp::isRightToLeft(); - setAlignment(isSameDirection ? Qt::AlignLeft : Qt::AlignRight); - }); - connect(this, &QLineEdit::returnPressed, this, [=]() { - m_returnPressed = true; - - /* Open default index when nothing is selected. - - Key_Return can be pressed during typing, where suggestions no longer - match the text typed. Hence the suggestionsValid check. - */ - const bool suggestionsValid = m_suggestionsAreValidFor == m_searchbarInput; - if (!m_suggestionView->currentIndex().isValid() && suggestionsValid) - openCompletion(getDefaulSuggestionIndex()); - }); - - auto app = KiwixApp::instance(); - connect(app->getAction(KiwixApp::SearchArticleAction), &QAction::triggered, - this, [=]() { - this->selectAll(); - this->setFocus(Qt::ShortcutFocusReason); - }); -} - -void SearchBarLineEdit::hideSuggestions() -{ - m_suggestionView->hide(); -} - -bool SearchBarLineEdit::eventFilter(QObject *, QEvent *event) -{ - if (!(m_aboutToScrollPastEnd && m_moreSuggestionsAreAvailable)) - return false; - - if (const auto e = dynamic_cast(event)) - { - const auto key = e->key(); - const bool isScrollDownKey = key == Qt::Key_Down || key == Qt::Key_PageDown; - const bool noModifiers = e->modifiers().testFlag(Qt::NoModifier); - - if (isScrollDownKey && noModifiers) - { - m_aboutToScrollPastEnd = false; - fetchMoreSuggestions(); - return true; - } - } - return false; -} - -void SearchBarLineEdit::clearSuggestions() -{ - m_suggestionModel.resetSuggestions(); - m_moreSuggestionsAreAvailable = false; -} - -void SearchBarLineEdit::on_currentTitleChanged(const QString& title) -{ - if (this->hasFocus()) { - return; - } - if (!title.startsWith("zim://")) { - setText(title); - } else { - setText(""); - } - m_title = title; -} - -void SearchBarLineEdit::focusInEvent( QFocusEvent* event) -{ - setReadOnly(false); - if (event->reason() == Qt::MouseFocusReason && text() == m_title) { - clear(); - } - if (event->reason() == Qt::ActiveWindowFocusReason || - event->reason() == Qt::MouseFocusReason || - event->reason() == Qt::ShortcutFocusReason || - event->reason() == Qt::PopupFocusReason) { - connect(&m_completer, QOverload::of(&QCompleter::activated), - this, &QLineEdit::setText,Qt::UniqueConnection); - - connect(&m_completer, QOverload::of(&QCompleter::activated), - this, QOverload::of(&SearchBarLineEdit::openCompletion), - Qt::UniqueConnection); - - connect(&m_completer, QOverload::of(&QCompleter::highlighted), this, - [=](const QModelIndex &index){ - setText(index.isValid() ? index.data().toString() : m_searchbarInput); - }, - Qt::UniqueConnection); - } - QLineEdit::focusInEvent(event); -} - -void SearchBarLineEdit::focusOutEvent(QFocusEvent* event) -{ - setReadOnly(true); - if (event->reason() == Qt::MouseFocusReason && text().isEmpty()) { - setText(m_title); - } - deselect(); - disconnect(&m_completer, nullptr, this, nullptr); - return QLineEdit::focusOutEvent(event); -} - -void SearchBarLineEdit::updateCompletion() -{ - mp_typingTimer->stop(); - clearSuggestions(); - const auto& multiZim = KiwixApp::instance()->getSearchBar().getMultiZimButton(); - if (multiZim.getZimIds().isEmpty() || m_searchbarInput.isEmpty()) { - hideSuggestions(); - return; - } - m_token++; - fetchSuggestions(&SearchBarLineEdit::onInitialSuggestions); -} - -void SearchBarLineEdit::fetchMoreSuggestions() -{ - /* TODO: Refactor suggestion worker to re-use zim::SuggestionSearcher for - fetching more suggestion in a single archive. Currently we create a - searcher for every fetch, and discarded after one use. - */ - fetchSuggestions(&SearchBarLineEdit::onAdditionalSuggestions); -} - -void SearchBarLineEdit::onScroll(int value) -{ - if (!m_moreSuggestionsAreAvailable) - { - m_aboutToScrollPastEnd = false; - return; - } - - /* Scrolling using key_down past end will teleport scroller to the top. - We undo this here. Block signal to avoid recursion. We cannot find a way - to intercept the scrolling in eventFilter so, until we find out how, this - code is here to stay. - */ - if (!m_suggestionView->currentIndex().isValid()) - { - const auto old = m_suggestionView->verticalScrollBar()->blockSignals(true); - m_suggestionView->scrollToBottom(); - m_suggestionView->verticalScrollBar()->blockSignals(old); - return; - } - - const auto suggestionScroller = m_suggestionView->verticalScrollBar(); - const auto scrollMin = suggestionScroller->minimum(); - const auto scrollMax = suggestionScroller->maximum(); - const bool scrolledToEnd = value == suggestionScroller->maximum(); - if (m_aboutToScrollPastEnd) - { - if (scrolledToEnd) - { - /* The user's intention to scroll past end has been confirmed */ - fetchMoreSuggestions(); - m_aboutToScrollPastEnd = false; /* Relax until next time */ - } - else - { - /* Scrolling past end did not happen - remove the extra scroll - room created for detecting the intention of scrolling past end - */ - suggestionScroller->setRange(scrollMin, scrollMax - 1); - m_aboutToScrollPastEnd = false; /* ... and relax */ - } - } - else if (scrolledToEnd) - { - /* The user has scrolled to end - monitor for furthur scrolling */ - m_aboutToScrollPastEnd = true; - /* Create some fictitious room for an extra scroll */ - suggestionScroller->setRange(scrollMin, scrollMax + 1); - } -} - -void SearchBarLineEdit::openCompletion(const QModelIndex &index) -{ - if (index.isValid()) - { - const QUrl url = index.data(Qt::UserRole).toUrl(); - const auto app = KiwixApp::instance(); - const bool newTab = app->getTabWidget()->currentWebView() == nullptr; - QTimer::singleShot(0, [=](){KiwixApp::instance()->openUrl(url, newTab);}); - } -} - -void SearchBarLineEdit::onInitialSuggestions(int) -{ - if (m_returnPressed) { - openCompletion(getDefaulSuggestionIndex()); - } else { - m_completer.complete(getCompleterRect()); - - /* Select nothing by default */ - const auto completerSelModel = m_suggestionView->selectionModel(); - completerSelModel->setCurrentIndex(QModelIndex(), QItemSelectionModel::Current); - } -} - -void SearchBarLineEdit::onAdditionalSuggestions(int start) -{ - /* Set selection to be at the last row of the previous list */ - const auto completerStartIdx = m_suggestionView->model()->index(start, 0); - m_suggestionView->setCurrentIndex(completerStartIdx); - m_suggestionView->show(); -} - -void SearchBarLineEdit::fetchSuggestions(NewSuggestionHandlerFuncPtr callback) -{ - const int start = m_suggestionModel.countOfRegularSuggestions(); - const auto searchText = m_searchbarInput; - const auto suggestionWorker = new SuggestionListWorker(searchText, m_token, start, this); - connect(suggestionWorker, &SuggestionListWorker::searchFinished, this, - [=] (const QList& suggestionList, int token) { - if (token != m_token) { - return; - } - - m_suggestionModel.append(suggestionList); - m_suggestionsAreValidFor = searchText; - const int listSize = suggestionList.size(); - const bool hasFullText = listSize > 0 && suggestionList.back().isFullTextSearchSuggestion(); - const int maxFetchSize = SuggestionListWorker::getFetchSize() + hasFullText; - m_moreSuggestionsAreAvailable = listSize >= maxFetchSize; - (this->*callback)(start); - }); - connect(suggestionWorker, &SuggestionListWorker::finished, suggestionWorker, &QObject::deleteLater); - suggestionWorker->start(); -} - -QModelIndex SearchBarLineEdit::getDefaulSuggestionIndex() const -{ - const auto firstSuggestionIndex = m_suggestionModel.index(0); - if (!firstSuggestionIndex.isValid()) - return firstSuggestionIndex; - - /* If the first entry matches the typed text, use it as default, otherwise - use the last entry if fulltext search exist. */ - const auto firstSuggestionText = firstSuggestionIndex.data().toString(); - if (this->text().compare(firstSuggestionText, Qt::CaseInsensitive) == 0) - return firstSuggestionIndex; - else if (m_suggestionModel.hasFullTextSearchSuggestion()) - return m_suggestionModel.index(m_suggestionModel.rowCount() - 1); - return QModelIndex(); -} - -/* Line edit does not span the entire searchBar. Completer is displayed - based on line edit, and thus shifting and resizing is needed. -*/ -QRect SearchBarLineEdit::getCompleterRect() const -{ - auto& searchBar = KiwixApp::instance()->getSearchBar(); - const auto& searchGeo = searchBar.geometry(); - const auto& searchLineEditGeo = searchBar.getLineEdit().geometry(); - - const int margin = CSS::SearchBar::margin; - const int border = CSS::SearchBar::border; - const int spaceAround = margin + border; - - /* Border and margin are not accounted in height and width. */ - const int top = searchGeo.height() - 2 * spaceAround; - const int width = searchGeo.width() - 2 * spaceAround; - - /* Shift completer to one of the two laterals of search bar, where which - one it shifted to dependes on whether the line edit is flipped. - */ - int left = -searchLineEditGeo.left(); - - /* When not flipped, left() is relative to within the search bar border, - thus, we shift by spaceAround to match the side of search bar. - - When flipped, the completer starts at the right end of the search bar - We shift it by width to make the completer start at left lateral of - search bar. Since in a flipped state, left() also considered the opposite - side's border, which means we need to shift by a border width in - addition to spaceAround. - */ - left += isRightToLeft() ? -width + spaceAround + border : spaceAround; - - /* Can't set height to 0. Will cause rectangle to be ignored. */ - return QRect(QPoint(left, top), QSize(width, 1)); -} - -SearchBar::SearchBar(QWidget *parent) : - QToolBar(parent), - m_searchBarLineEdit(this), - m_bookmarkButton(this), - m_multiZimButton(this) -{ - QLabel* searchIconLabel = new QLabel; - searchIconLabel->setObjectName("searchIcon"); - searchIconLabel->setPixmap(QIcon(":/icons/search.svg").pixmap(QSize(27, 27))); - - setIconSize(QSize(32, 32)); - - addWidget(searchIconLabel); - addWidget(&m_searchBarLineEdit); - addWidget(&m_bookmarkButton); - addWidget(&m_multiZimButton); - - connect(this, &SearchBar::currentTitleChanged, &m_searchBarLineEdit, - &SearchBarLineEdit::on_currentTitleChanged); - connect(this, &SearchBar::currentTitleChanged, &m_bookmarkButton, - &BookmarkButton::update_display); - connect(KiwixApp::instance()->getContentManager(), - &ContentManager::booksChanged, &m_multiZimButton, - &MultiZimButton::updateDisplay); - connect(this, &SearchBar::currentTitleChanged, &m_multiZimButton, - &MultiZimButton::updateDisplay); -} +#include "searchbar.h" + +#include +#include +#include +#include + +#include "kiwixapp.h" +#include "suggestionlistworker.h" +#include "css_constants.h" +#include "suggestionlistdelegate.h" + +namespace HeaderSectionCSS = CSS::PopupCSS::QHeaderView::section; + +BookmarkButton::BookmarkButton(QWidget *parent) : + QToolButton(parent) +{ + connect(this, &QToolButton::triggered, this, &BookmarkButton::on_buttonClicked); + connect(this, &QToolButton::triggered, this, &BookmarkButton::update_display); + setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::Actions::ToggleAddBookmarkAction)); + + auto library = KiwixApp::instance()->getLibrary(); + connect(library, &Library::bookmarksChanged, this, &BookmarkButton::update_display); +} + +void BookmarkButton::update_display() +{ + auto isBookMarked = KiwixApp::instance()->isCurrentArticleBookmarked(); + auto buttonText = isBookMarked ? gt("remove-bookmark") : gt("add-bookmark"); + defaultAction()->setChecked(isBookMarked); + defaultAction()->setToolTip(buttonText); + defaultAction()->setText(buttonText); +} + +void BookmarkButton::on_buttonClicked() +{ + auto kiwixApp = KiwixApp::instance(); + auto library = kiwixApp->getLibrary(); + auto tabWidget = kiwixApp->getTabWidget(); + if (kiwixApp->isCurrentArticleBookmarked()) { + auto zimid = tabWidget->currentZimId(); + library->removeBookmark( + zimid, tabWidget->currentArticleUrl() + ); + } else { + kiwix::Bookmark bookmark; + auto zimid = tabWidget->currentZimId().toStdString(); + bookmark.setBookId(zimid); + bookmark.setUrl(tabWidget->currentArticleUrl().toStdString()); + bookmark.setTitle(tabWidget->currentArticleTitle().toStdString()); + library->addBookmark(bookmark); + } + library->save(); +} + +SearchBarLineEdit::SearchBarLineEdit(QWidget *parent) : + QLineEdit(parent), + m_suggestionView(new QTreeView), + m_completer(&m_suggestionModel, this) +{ + installEventFilter(this); + setAlignment(KiwixApp::isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft); + mp_typingTimer = new QTimer(this); + mp_typingTimer->setSingleShot(true); + + /* Placeholder does not affect line edit alignment and is aligned to line + edit purely by text direction (LTR leading RTL ending). Thus, we need + directional mask to make it LTR at leading position. + https://stackoverflow.com/questions/66430215/english-and-arabic-mixed-string-not-ordered-correctly-qt + */ + const QString ltrConversionChar(u8"\u200e"); + setPlaceholderText(ltrConversionChar + gt("search")); + setToolTip(gt("search")); + m_completer.setCompletionMode(QCompleter::UnfilteredPopupCompletion); + m_completer.setCaseSensitivity(Qt::CaseInsensitive); + + /* The items should be less than fetch size to enable scrolling. */ + m_completer.setMaxVisibleItems(SuggestionListWorker::getFetchSize() / 2); + m_completer.setWidget(this); + + /* QCompleter's uses default list views, which do not have headers. */ + m_completer.setPopup(m_suggestionView); + + /* The Delegate was overwritten by setPopup(), which is not style-aware */ + m_suggestionView->setItemDelegate(new SuggestionListDelegate(this)); + m_suggestionView->header()->setStretchLastSection(true); + m_suggestionView->setRootIsDecorated(false); + m_suggestionView->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/popup.css")); + + const int contentHeight = HeaderSectionCSS::lineHeight; + m_suggestionView->setIconSize(QSize(contentHeight, contentHeight)); + + /* The suggestionView sizing unfortunately is not aware of headers. We + have to do this manually. We also sized header the same as items. + */ + connect(&m_suggestionModel, &QAbstractListModel::modelReset, [=](){ + /* +1 for header. */ + const int maxItem = m_completer.maxVisibleItems(); + const int count = std::min(m_suggestionModel.rowCount(), maxItem) + 1; + + const int itemHeight = m_suggestionView->sizeHintForRow(0); + + /* Extra space styling above header and below last suggestion item. */ + const int extraMargin = 2 * HeaderSectionCSS::marginTop; + m_suggestionView->setFixedHeight(itemHeight * count + extraMargin); + }); + + connect(m_suggestionView->verticalScrollBar(), &QScrollBar::valueChanged, + this, &SearchBarLineEdit::onScroll); + + qRegisterMetaType>("QList"); + connect(mp_typingTimer, &QTimer::timeout, this, &SearchBarLineEdit::updateCompletion); + + connect(this, &QLineEdit::textEdited, this, + [=](const QString &text) { + m_searchbarInput = text; + m_returnPressed = false; + mp_typingTimer->start(100); + }); + connect(this, &QLineEdit::textChanged, this, + [=](const QString &text) { + Q_UNUSED(text) + if (m_returnPressed) { + this->setText(m_searchbarInput); + } + + /* Empty text is LTR which changes the line edit alignment. + Need to explicitly align right. This is a generalized + solution that aligns text to the direction of the app. */ + bool isSameDirection = text.isRightToLeft() == KiwixApp::isRightToLeft(); + setAlignment(isSameDirection ? Qt::AlignLeft : Qt::AlignRight); + }); + connect(this, &QLineEdit::returnPressed, this, [=]() { + m_returnPressed = true; + + /* Open default index when nothing is selected. + + Key_Return can be pressed during typing, where suggestions no longer + match the text typed. Hence the suggestionsValid check. + */ + const bool suggestionsValid = m_suggestionsAreValidFor == m_searchbarInput; + if (!m_suggestionView->currentIndex().isValid() && suggestionsValid) + openCompletion(getDefaulSuggestionIndex()); + }); + + auto app = KiwixApp::instance(); + connect(app->getAction(KiwixApp::SearchArticleAction), &QAction::triggered, + this, [=]() { + this->selectAll(); + this->setFocus(Qt::ShortcutFocusReason); + }); +} + +void SearchBarLineEdit::hideSuggestions() +{ + m_suggestionView->hide(); +} + +bool SearchBarLineEdit::eventFilter(QObject *, QEvent *event) +{ + if (!(m_aboutToScrollPastEnd && m_moreSuggestionsAreAvailable)) + return false; + + if (const auto e = dynamic_cast(event)) + { + const auto key = e->key(); + const bool isScrollDownKey = key == Qt::Key_Down || key == Qt::Key_PageDown; + const bool noModifiers = e->modifiers().testFlag(Qt::NoModifier); + + if (isScrollDownKey && noModifiers) + { + m_aboutToScrollPastEnd = false; + fetchMoreSuggestions(); + return true; + } + } + return false; +} + +void SearchBarLineEdit::clearSuggestions() +{ + m_suggestionModel.resetSuggestions(); + m_moreSuggestionsAreAvailable = false; +} + +void SearchBarLineEdit::on_currentTitleChanged(const QString& title) +{ + if (this->hasFocus()) { + return; + } + if (!title.startsWith("zim://")) { + setText(title); + } else { + setText(""); + } + m_title = title; +} + +void SearchBarLineEdit::focusInEvent( QFocusEvent* event) +{ + setReadOnly(false); + if (event->reason() == Qt::MouseFocusReason && text() == m_title) { + clear(); + } + if (event->reason() == Qt::ActiveWindowFocusReason || + event->reason() == Qt::MouseFocusReason || + event->reason() == Qt::ShortcutFocusReason || + event->reason() == Qt::PopupFocusReason) { + connect(&m_completer, QOverload::of(&QCompleter::activated), + this, &QLineEdit::setText,Qt::UniqueConnection); + + connect(&m_completer, QOverload::of(&QCompleter::activated), + this, QOverload::of(&SearchBarLineEdit::openCompletion), + Qt::UniqueConnection); + + connect(&m_completer, QOverload::of(&QCompleter::highlighted), this, + [=](const QModelIndex &index){ + setText(index.isValid() ? index.data().toString() : m_searchbarInput); + }, + Qt::UniqueConnection); + } + QLineEdit::focusInEvent(event); +} + +void SearchBarLineEdit::focusOutEvent(QFocusEvent* event) +{ + setReadOnly(true); + if (event->reason() == Qt::MouseFocusReason && text().isEmpty()) { + setText(m_title); + } + deselect(); + disconnect(&m_completer, nullptr, this, nullptr); + return QLineEdit::focusOutEvent(event); +} + +void SearchBarLineEdit::updateCompletion() +{ + mp_typingTimer->stop(); + clearSuggestions(); + const auto& multiZim = KiwixApp::instance()->getSearchBar().getMultiZimButton(); + if (multiZim.getZimIds().isEmpty() || m_searchbarInput.isEmpty()) { + hideSuggestions(); + return; + } + m_token++; + fetchSuggestions(&SearchBarLineEdit::onInitialSuggestions); +} + +void SearchBarLineEdit::fetchMoreSuggestions() +{ + /* TODO: Refactor suggestion worker to re-use zim::SuggestionSearcher for + fetching more suggestion in a single archive. Currently we create a + searcher for every fetch, and discarded after one use. + */ + fetchSuggestions(&SearchBarLineEdit::onAdditionalSuggestions); +} + +void SearchBarLineEdit::onScroll(int value) +{ + if (!m_moreSuggestionsAreAvailable) + { + m_aboutToScrollPastEnd = false; + return; + } + + /* Scrolling using key_down past end will teleport scroller to the top. + We undo this here. Block signal to avoid recursion. We cannot find a way + to intercept the scrolling in eventFilter so, until we find out how, this + code is here to stay. + */ + if (!m_suggestionView->currentIndex().isValid()) + { + const auto old = m_suggestionView->verticalScrollBar()->blockSignals(true); + m_suggestionView->scrollToBottom(); + m_suggestionView->verticalScrollBar()->blockSignals(old); + return; + } + + const auto suggestionScroller = m_suggestionView->verticalScrollBar(); + const auto scrollMin = suggestionScroller->minimum(); + const auto scrollMax = suggestionScroller->maximum(); + const bool scrolledToEnd = value == suggestionScroller->maximum(); + if (m_aboutToScrollPastEnd) + { + if (scrolledToEnd) + { + /* The user's intention to scroll past end has been confirmed */ + fetchMoreSuggestions(); + m_aboutToScrollPastEnd = false; /* Relax until next time */ + } + else + { + /* Scrolling past end did not happen - remove the extra scroll + room created for detecting the intention of scrolling past end + */ + suggestionScroller->setRange(scrollMin, scrollMax - 1); + m_aboutToScrollPastEnd = false; /* ... and relax */ + } + } + else if (scrolledToEnd) + { + /* The user has scrolled to end - monitor for furthur scrolling */ + m_aboutToScrollPastEnd = true; + /* Create some fictitious room for an extra scroll */ + suggestionScroller->setRange(scrollMin, scrollMax + 1); + } +} + +void SearchBarLineEdit::openCompletion(const QModelIndex &index) +{ + if (index.isValid()) + { + const QUrl url = index.data(Qt::UserRole).toUrl(); + const auto app = KiwixApp::instance(); + const bool newTab = app->getTabWidget()->currentWebView() == nullptr; + QTimer::singleShot(0, [=](){KiwixApp::instance()->openUrl(url, newTab);}); + } +} + +void SearchBarLineEdit::onInitialSuggestions(int) +{ + if (m_returnPressed) { + openCompletion(getDefaulSuggestionIndex()); + } else { + m_completer.complete(getCompleterRect()); + + /* Select nothing by default */ + const auto completerSelModel = m_suggestionView->selectionModel(); + completerSelModel->setCurrentIndex(QModelIndex(), QItemSelectionModel::Current); + } +} + +void SearchBarLineEdit::onAdditionalSuggestions(int start) +{ + /* Set selection to be at the last row of the previous list */ + const auto completerStartIdx = m_suggestionView->model()->index(start, 0); + m_suggestionView->setCurrentIndex(completerStartIdx); + m_suggestionView->show(); +} + +void SearchBarLineEdit::fetchSuggestions(NewSuggestionHandlerFuncPtr callback) +{ + const int start = m_suggestionModel.countOfRegularSuggestions(); + const auto searchText = m_searchbarInput; + const auto suggestionWorker = new SuggestionListWorker(searchText, m_token, start, this); + connect(suggestionWorker, &SuggestionListWorker::searchFinished, this, + [=] (const QList& suggestionList, int token) { + if (token != m_token) { + return; + } + + m_suggestionModel.append(suggestionList); + m_suggestionsAreValidFor = searchText; + const int listSize = suggestionList.size(); + const bool hasFullText = listSize > 0 && suggestionList.back().isFullTextSearchSuggestion(); + const int maxFetchSize = SuggestionListWorker::getFetchSize() + hasFullText; + m_moreSuggestionsAreAvailable = listSize >= maxFetchSize; + (this->*callback)(start); + }); + connect(suggestionWorker, &SuggestionListWorker::finished, suggestionWorker, &QObject::deleteLater); + suggestionWorker->start(); +} + +QModelIndex SearchBarLineEdit::getDefaulSuggestionIndex() const +{ + const auto firstSuggestionIndex = m_suggestionModel.index(0); + if (!firstSuggestionIndex.isValid()) + return firstSuggestionIndex; + + /* If the first entry matches the typed text, use it as default, otherwise + use the last entry if fulltext search exist. */ + const auto firstSuggestionText = firstSuggestionIndex.data().toString(); + if (this->text().compare(firstSuggestionText, Qt::CaseInsensitive) == 0) + return firstSuggestionIndex; + else if (m_suggestionModel.hasFullTextSearchSuggestion()) + return m_suggestionModel.index(m_suggestionModel.rowCount() - 1); + return QModelIndex(); +} + +/* Line edit does not span the entire searchBar. Completer is displayed + based on line edit, and thus shifting and resizing is needed. +*/ +QRect SearchBarLineEdit::getCompleterRect() const +{ + auto& searchBar = KiwixApp::instance()->getSearchBar(); + const auto& searchGeo = searchBar.geometry(); + const auto& searchLineEditGeo = searchBar.getLineEdit().geometry(); + + const int margin = CSS::SearchBar::margin; + const int border = CSS::SearchBar::border; + const int spaceAround = margin + border; + + /* Border and margin are not accounted in height and width. */ + const int top = searchGeo.height() - 2 * spaceAround; + const int width = searchGeo.width() - 2 * spaceAround; + + /* Shift completer to one of the two laterals of search bar, where which + one it shifted to dependes on whether the line edit is flipped. + */ + int left = -searchLineEditGeo.left(); + + /* When not flipped, left() is relative to within the search bar border, + thus, we shift by spaceAround to match the side of search bar. + + When flipped, the completer starts at the right end of the search bar + We shift it by width to make the completer start at left lateral of + search bar. Since in a flipped state, left() also considered the opposite + side's border, which means we need to shift by a border width in + addition to spaceAround. + */ + left += isRightToLeft() ? -width + spaceAround + border : spaceAround; + + /* Can't set height to 0. Will cause rectangle to be ignored. */ + return QRect(QPoint(left, top), QSize(width, 1)); +} + +SearchBar::SearchBar(QWidget *parent) : + QToolBar(parent), + m_searchBarLineEdit(this), + m_bookmarkButton(this), + m_multiZimButton(this) +{ + QLabel* searchIconLabel = new QLabel; + searchIconLabel->setObjectName("searchIcon"); + searchIconLabel->setPixmap(QIcon(":/icons/search.svg").pixmap(QSize(27, 27))); + + setIconSize(QSize(32, 32)); + + addWidget(searchIconLabel); + addWidget(&m_searchBarLineEdit); + addWidget(&m_bookmarkButton); + addWidget(&m_multiZimButton); + + connect(this, &SearchBar::currentTitleChanged, &m_searchBarLineEdit, + &SearchBarLineEdit::on_currentTitleChanged); + connect(this, &SearchBar::currentTitleChanged, &m_bookmarkButton, + &BookmarkButton::update_display); + connect(KiwixApp::instance()->getContentManager(), + &ContentManager::booksChanged, &m_multiZimButton, + &MultiZimButton::updateDisplay); + connect(this, &SearchBar::currentTitleChanged, &m_multiZimButton, + &MultiZimButton::updateDisplay); +} diff --git a/src/searchbar.h b/src/searchbar.h index 1a9f65462..1db0d1233 100644 --- a/src/searchbar.h +++ b/src/searchbar.h @@ -1,92 +1,92 @@ -#ifndef SEARCHBAR_H -#define SEARCHBAR_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "suggestionlistmodel.h" -#include "multizimbutton.h" - -class QTreeView; - -class BookmarkButton : public QToolButton { - Q_OBJECT -public: - BookmarkButton(QWidget *parent = nullptr); - -public slots: - void update_display(); - void on_buttonClicked(); -}; - -class SearchBarLineEdit : public QLineEdit -{ - Q_OBJECT - - typedef void (SearchBarLineEdit::*NewSuggestionHandlerFuncPtr)(int start); - -public: - SearchBarLineEdit(QWidget *parent = nullptr); - void hideSuggestions(); - bool eventFilter(QObject *watched, QEvent *event) override; - -public slots: - void on_currentTitleChanged(const QString &title); - void clearSuggestions(); - -protected: - virtual void focusInEvent(QFocusEvent *); - virtual void focusOutEvent(QFocusEvent *); -private: - SuggestionListModel m_suggestionModel; - QTreeView *m_suggestionView; - QCompleter m_completer; - QString m_title; - QString m_searchbarInput; - QString m_suggestionsAreValidFor; - bool m_returnPressed = false; - QTimer* mp_typingTimer; - int m_token; - bool m_moreSuggestionsAreAvailable = false; - - /* We only fetch more suggestions when the user is at the end and tries to - scroll again. This variable is set whenever the user scrolled to the end, - indicating the next scroll should trigger a fetch more action. */ - bool m_aboutToScrollPastEnd = false; - -private slots: - void updateCompletion(); - void fetchMoreSuggestions(); - void onScroll(int value); - void openCompletion(const QModelIndex& index); - void onInitialSuggestions(int); - void onAdditionalSuggestions(int start); - void fetchSuggestions(NewSuggestionHandlerFuncPtr callback); - - QModelIndex getDefaulSuggestionIndex() const; - QRect getCompleterRect() const; -}; - - -class SearchBar : public QToolBar { - Q_OBJECT -public: - SearchBar(QWidget *parent = nullptr); - SearchBarLineEdit& getLineEdit() { return m_searchBarLineEdit; }; - MultiZimButton& getMultiZimButton() { return m_multiZimButton; }; - -signals: - void currentTitleChanged(const QString &title); - -private: - SearchBarLineEdit m_searchBarLineEdit; - BookmarkButton m_bookmarkButton; - MultiZimButton m_multiZimButton; -}; -#endif // SEARCHBAR_H +#ifndef SEARCHBAR_H +#define SEARCHBAR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "suggestionlistmodel.h" +#include "multizimbutton.h" + +class QTreeView; + +class BookmarkButton : public QToolButton { + Q_OBJECT +public: + BookmarkButton(QWidget *parent = nullptr); + +public slots: + void update_display(); + void on_buttonClicked(); +}; + +class SearchBarLineEdit : public QLineEdit +{ + Q_OBJECT + + typedef void (SearchBarLineEdit::*NewSuggestionHandlerFuncPtr)(int start); + +public: + SearchBarLineEdit(QWidget *parent = nullptr); + void hideSuggestions(); + bool eventFilter(QObject *watched, QEvent *event) override; + +public slots: + void on_currentTitleChanged(const QString &title); + void clearSuggestions(); + +protected: + virtual void focusInEvent(QFocusEvent *); + virtual void focusOutEvent(QFocusEvent *); +private: + SuggestionListModel m_suggestionModel; + QTreeView *m_suggestionView; + QCompleter m_completer; + QString m_title; + QString m_searchbarInput; + QString m_suggestionsAreValidFor; + bool m_returnPressed = false; + QTimer* mp_typingTimer; + int m_token; + bool m_moreSuggestionsAreAvailable = false; + + /* We only fetch more suggestions when the user is at the end and tries to + scroll again. This variable is set whenever the user scrolled to the end, + indicating the next scroll should trigger a fetch more action. */ + bool m_aboutToScrollPastEnd = false; + +private slots: + void updateCompletion(); + void fetchMoreSuggestions(); + void onScroll(int value); + void openCompletion(const QModelIndex& index); + void onInitialSuggestions(int); + void onAdditionalSuggestions(int start); + void fetchSuggestions(NewSuggestionHandlerFuncPtr callback); + + QModelIndex getDefaulSuggestionIndex() const; + QRect getCompleterRect() const; +}; + + +class SearchBar : public QToolBar { + Q_OBJECT +public: + SearchBar(QWidget *parent = nullptr); + SearchBarLineEdit& getLineEdit() { return m_searchBarLineEdit; }; + MultiZimButton& getMultiZimButton() { return m_multiZimButton; }; + +signals: + void currentTitleChanged(const QString &title); + +private: + SearchBarLineEdit m_searchBarLineEdit; + BookmarkButton m_bookmarkButton; + MultiZimButton m_multiZimButton; +}; +#endif // SEARCHBAR_H diff --git a/src/settingsmanager.cpp b/src/settingsmanager.cpp index c49893fc1..6ff0c301f 100644 --- a/src/settingsmanager.cpp +++ b/src/settingsmanager.cpp @@ -1,228 +1,228 @@ -#include "settingsmanager.h" -#include "kiwixapp.h" -#include -#include -#include -#include -#include -#include - -QString getDataDirectory() -{ - if (isPortableMode()) { - auto currentDataDir = QString::fromStdString(kiwix::removeLastPathElement(kiwix::getExecutablePath())); - return currentDataDir + QDir::separator() + "data"; - } - - QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - - if (!dataDir.isEmpty() && QDir().mkpath(dataDir)) - return dataDir; - - return QString::fromStdString(kiwix::getCurrentDirectory()); -} - -bool isPortableMode() -{ - auto currentDataDir = QString::fromStdString(kiwix::removeLastPathElement(kiwix::getExecutablePath())); - auto portableFile = QFileInfo(currentDataDir, ".portable"); - - return portableFile.exists(); -} - -namespace { - -QString getSettingsConfPath() -{ - QString confDirectory = isPortableMode() ? - getDataDirectory() : - QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); - - return confDirectory + QDir::separator() + "Kiwix-desktop.conf"; -} - -} - -SettingsManager::SettingsManager(QObject *parent) - : QObject(parent), - m_settings(getSettingsConfPath(), QSettings::IniFormat), - m_view(nullptr) -{ - initSettings(); -} - -SettingsView* SettingsManager::getView() -{ - if (m_view == nullptr) { - auto view = new SettingsView(); - view->init(m_zoomFactor * 100, m_downloadDir, m_monitorDir, - m_moveToTrash, m_reopenTab); - connect(view, &QObject::destroyed, this, [=]() { m_view = nullptr; }); - m_view = view; - } - return m_view; -} - -void SettingsManager::setSettings(const QString &key, const QVariant &value) -{ - m_settings.setValue(key, value); -} - -void SettingsManager::deleteSettings(const QString &key) -{ - m_settings.remove(key); -} - -bool SettingsManager::settingsExists(const QString &key) -{ - return m_settings.contains(key); -} - -QVariant SettingsManager::getSettings(const QString &key) -{ - return m_settings.value(key); -} - -qreal SettingsManager::getZoomFactorByZimId(const QString &id) -{ - auto zoomFactor = m_zoomFactor; - QString key = id + "/zoomFactor"; - if (settingsExists(key)) { - zoomFactor = getSettings(key).toDouble(); - } - return zoomFactor; -} - -void SettingsManager::setKiwixServerPort(int port) -{ - m_kiwixServerPort = port; - m_settings.setValue("localKiwixServer/port", port); - emit(portChanged(port)); -} - -void SettingsManager::setKiwixServerIpAddress(QString ipAddress) -{ - m_kiwixServerIpAddress = ipAddress; - m_settings.setValue("localKiwixServer/ipAddress", ipAddress); -} - -void SettingsManager::setZoomFactor(qreal zoomFactor) -{ - m_zoomFactor = zoomFactor; - m_settings.setValue("view/zoomFactor", zoomFactor); - emit(zoomChanged(zoomFactor)); -} - -void SettingsManager::setDownloadDir(QString downloadDir) -{ - downloadDir = QDir::toNativeSeparators(downloadDir); - m_downloadDir = downloadDir; - m_settings.setValue("download/dir", downloadDir); - emit(downloadDirChanged(downloadDir)); -} - -void SettingsManager::setMonitorDir(QString monitorDir) -{ - monitorDir = QDir::toNativeSeparators(monitorDir); - m_monitorDir = monitorDir; - m_settings.setValue("monitor/dir", monitorDir); - emit(monitorDirChanged(monitorDir)); -} - -void SettingsManager::setMoveToTrash(bool moveToTrash) -{ - m_moveToTrash = moveToTrash; - setSettings("moveToTrash", m_moveToTrash); - emit(moveToTrashChanged(m_moveToTrash)); -} - -void SettingsManager::setReopenTab(bool reopenTab) -{ - m_reopenTab = reopenTab; - setSettings("reopenTab", m_reopenTab); - emit(reopenTabChanged(reopenTab)); -} - -QList SettingsManager::flattenPair(FilterList pairList) -{ - QList res; - for (auto &pair : pairList) { - res.push_back(pair.first+"|"+pair.second); - } - return res; -} - -SettingsManager::FilterList SettingsManager::deducePair(QList variantList) -{ - FilterList pairList; - for (auto &variant : variantList) { - QString str = variant.toString(); - auto pairs = str.split('|'); - pairList.push_back({pairs[0], pairs[1]}); - } - return pairList; -} - -void SettingsManager::setLanguage(FilterList langList) -{ - m_langList = flattenPair(langList); - setSettings("language", m_langList); - emit(languageChanged(m_langList)); -} - -void SettingsManager::setCategory(QStringList categoryList) -{ - m_categoryList = categoryList; - setSettings("category", m_categoryList); - emit(categoryChanged(m_categoryList)); -} - -void SettingsManager::setContentType(FilterList contentTypeList) -{ - m_contentTypeList = flattenPair(contentTypeList); - setSettings("contentType", m_contentTypeList); - emit(contentTypeChanged(m_contentTypeList)); -} - -void SettingsManager::initSettings() -{ - if(isPortableMode()) { - m_downloadDir = m_monitorDir = getDataDirectory(); - } else { - m_downloadDir = m_settings.value("download/dir", getDataDirectory()).toString(); - m_monitorDir = m_settings.value("monitor/dir", QString("")).toString(); - } - m_kiwixServerPort = m_settings.value("localKiwixServer/port", 8080).toInt(); - m_zoomFactor = m_settings.value("view/zoomFactor", 1).toDouble(); - m_kiwixServerIpAddress = m_settings.value("localKiwixServer/ipAddress", QString("0.0.0.0")).toString(); - m_moveToTrash = m_settings.value("moveToTrash", true).toBool(); - m_reopenTab = m_settings.value("reopenTab", false).toBool(); - QString defaultLang = QLocale::languageToString(QLocale().language()) + '|' + QLocale().name().split("_").at(0); - - /* - * Qt5 & Qt6 have slightly different behaviors with regards to initializing QVariant. - * The below approach is specifically chosen to work with both versions. - * m_langList initialized with defaultLang should be of the form: - * - * (QVariant(QString, "English|en")) - * - * and not - * - * QList(QVariant(QChar, 'E'), QVariant(QChar, 'n'), QVariant(QChar, 'g'), ... - */ - QList defaultLangList; // Qt5 QList doesn't support supplying a constructor list - defaultLangList.append(defaultLang); - QVariant defaultLangVariant(defaultLangList); - m_langList = m_settings.value("language", defaultLangVariant).toList(); - - m_categoryList = m_settings.value("category", {}).toStringList(); - - /* Older versions used to save both the category identifier/code and its - * name as shown to the user (separated by the | symbol). Now we store only - * the category identifier from which the name is generated after loading. - * In order to avoid user confusion we drop entries stored in the old format - */ - setCategory(m_categoryList.filter(QRegularExpression(R"(^[^|]*$)"))); - - m_contentTypeList = m_settings.value("contentType", {}).toList(); -} +#include "settingsmanager.h" +#include "kiwixapp.h" +#include +#include +#include +#include +#include +#include + +QString getDataDirectory() +{ + if (isPortableMode()) { + auto currentDataDir = QString::fromStdString(kiwix::removeLastPathElement(kiwix::getExecutablePath())); + return currentDataDir + QDir::separator() + "data"; + } + + QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + + if (!dataDir.isEmpty() && QDir().mkpath(dataDir)) + return dataDir; + + return QString::fromStdString(kiwix::getCurrentDirectory()); +} + +bool isPortableMode() +{ + auto currentDataDir = QString::fromStdString(kiwix::removeLastPathElement(kiwix::getExecutablePath())); + auto portableFile = QFileInfo(currentDataDir, ".portable"); + + return portableFile.exists(); +} + +namespace { + +QString getSettingsConfPath() +{ + QString confDirectory = isPortableMode() ? + getDataDirectory() : + QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + + return confDirectory + QDir::separator() + "Kiwix-desktop.conf"; +} + +} + +SettingsManager::SettingsManager(QObject *parent) + : QObject(parent), + m_settings(getSettingsConfPath(), QSettings::IniFormat), + m_view(nullptr) +{ + initSettings(); +} + +SettingsView* SettingsManager::getView() +{ + if (m_view == nullptr) { + auto view = new SettingsView(); + view->init(m_zoomFactor * 100, m_downloadDir, m_monitorDir, + m_moveToTrash, m_reopenTab); + connect(view, &QObject::destroyed, this, [=]() { m_view = nullptr; }); + m_view = view; + } + return m_view; +} + +void SettingsManager::setSettings(const QString &key, const QVariant &value) +{ + m_settings.setValue(key, value); +} + +void SettingsManager::deleteSettings(const QString &key) +{ + m_settings.remove(key); +} + +bool SettingsManager::settingsExists(const QString &key) +{ + return m_settings.contains(key); +} + +QVariant SettingsManager::getSettings(const QString &key) +{ + return m_settings.value(key); +} + +qreal SettingsManager::getZoomFactorByZimId(const QString &id) +{ + auto zoomFactor = m_zoomFactor; + QString key = id + "/zoomFactor"; + if (settingsExists(key)) { + zoomFactor = getSettings(key).toDouble(); + } + return zoomFactor; +} + +void SettingsManager::setKiwixServerPort(int port) +{ + m_kiwixServerPort = port; + m_settings.setValue("localKiwixServer/port", port); + emit(portChanged(port)); +} + +void SettingsManager::setKiwixServerIpAddress(QString ipAddress) +{ + m_kiwixServerIpAddress = ipAddress; + m_settings.setValue("localKiwixServer/ipAddress", ipAddress); +} + +void SettingsManager::setZoomFactor(qreal zoomFactor) +{ + m_zoomFactor = zoomFactor; + m_settings.setValue("view/zoomFactor", zoomFactor); + emit(zoomChanged(zoomFactor)); +} + +void SettingsManager::setDownloadDir(QString downloadDir) +{ + downloadDir = QDir::toNativeSeparators(downloadDir); + m_downloadDir = downloadDir; + m_settings.setValue("download/dir", downloadDir); + emit(downloadDirChanged(downloadDir)); +} + +void SettingsManager::setMonitorDir(QString monitorDir) +{ + monitorDir = QDir::toNativeSeparators(monitorDir); + m_monitorDir = monitorDir; + m_settings.setValue("monitor/dir", monitorDir); + emit(monitorDirChanged(monitorDir)); +} + +void SettingsManager::setMoveToTrash(bool moveToTrash) +{ + m_moveToTrash = moveToTrash; + setSettings("moveToTrash", m_moveToTrash); + emit(moveToTrashChanged(m_moveToTrash)); +} + +void SettingsManager::setReopenTab(bool reopenTab) +{ + m_reopenTab = reopenTab; + setSettings("reopenTab", m_reopenTab); + emit(reopenTabChanged(reopenTab)); +} + +QList SettingsManager::flattenPair(FilterList pairList) +{ + QList res; + for (auto &pair : pairList) { + res.push_back(pair.first+"|"+pair.second); + } + return res; +} + +SettingsManager::FilterList SettingsManager::deducePair(QList variantList) +{ + FilterList pairList; + for (auto &variant : variantList) { + QString str = variant.toString(); + auto pairs = str.split('|'); + pairList.push_back({pairs[0], pairs[1]}); + } + return pairList; +} + +void SettingsManager::setLanguage(FilterList langList) +{ + m_langList = flattenPair(langList); + setSettings("language", m_langList); + emit(languageChanged(m_langList)); +} + +void SettingsManager::setCategory(QStringList categoryList) +{ + m_categoryList = categoryList; + setSettings("category", m_categoryList); + emit(categoryChanged(m_categoryList)); +} + +void SettingsManager::setContentType(FilterList contentTypeList) +{ + m_contentTypeList = flattenPair(contentTypeList); + setSettings("contentType", m_contentTypeList); + emit(contentTypeChanged(m_contentTypeList)); +} + +void SettingsManager::initSettings() +{ + if(isPortableMode()) { + m_downloadDir = m_monitorDir = getDataDirectory(); + } else { + m_downloadDir = m_settings.value("download/dir", getDataDirectory()).toString(); + m_monitorDir = m_settings.value("monitor/dir", QString("")).toString(); + } + m_kiwixServerPort = m_settings.value("localKiwixServer/port", 8080).toInt(); + m_zoomFactor = m_settings.value("view/zoomFactor", 1).toDouble(); + m_kiwixServerIpAddress = m_settings.value("localKiwixServer/ipAddress", QString("0.0.0.0")).toString(); + m_moveToTrash = m_settings.value("moveToTrash", true).toBool(); + m_reopenTab = m_settings.value("reopenTab", false).toBool(); + QString defaultLang = QLocale::languageToString(QLocale().language()) + '|' + QLocale().name().split("_").at(0); + + /* + * Qt5 & Qt6 have slightly different behaviors with regards to initializing QVariant. + * The below approach is specifically chosen to work with both versions. + * m_langList initialized with defaultLang should be of the form: + * + * (QVariant(QString, "English|en")) + * + * and not + * + * QList(QVariant(QChar, 'E'), QVariant(QChar, 'n'), QVariant(QChar, 'g'), ... + */ + QList defaultLangList; // Qt5 QList doesn't support supplying a constructor list + defaultLangList.append(defaultLang); + QVariant defaultLangVariant(defaultLangList); + m_langList = m_settings.value("language", defaultLangVariant).toList(); + + m_categoryList = m_settings.value("category", {}).toStringList(); + + /* Older versions used to save both the category identifier/code and its + * name as shown to the user (separated by the | symbol). Now we store only + * the category identifier from which the name is generated after loading. + * In order to avoid user confusion we drop entries stored in the old format + */ + setCategory(m_categoryList.filter(QRegularExpression(R"(^[^|]*$)"))); + + m_contentTypeList = m_settings.value("contentType", {}).toList(); +} diff --git a/src/settingsmanager.h b/src/settingsmanager.h index 326b93799..ec4d41c70 100644 --- a/src/settingsmanager.h +++ b/src/settingsmanager.h @@ -1,83 +1,83 @@ -#ifndef SETTINGSMANAGER_H -#define SETTINGSMANAGER_H - -#include -#include -#include "settingsview.h" - -class SettingsManager : public QObject -{ - Q_OBJECT - Q_PROPERTY(int kiwixServerPort READ getKiwixServerPort NOTIFY portChanged) - Q_PROPERTY(qreal zoomFactor MEMBER m_zoomFactor WRITE setZoomFactor NOTIFY zoomChanged) - Q_PROPERTY(QString downloadDir MEMBER m_downloadDir WRITE setDownloadDir NOTIFY downloadDirChanged) - -public: - typedef QList> FilterList; - explicit SettingsManager(QObject *parent = nullptr); - virtual ~SettingsManager() {}; - - SettingsView* getView(); - void setSettings(const QString &key, const QVariant &value); - void deleteSettings(const QString &key); - bool settingsExists(const QString &key); - QVariant getSettings(const QString &key); - qreal getZoomFactorByZimId(const QString &id); - int getKiwixServerPort() const { return m_kiwixServerPort; } - QString getKiwixServerIpAddress() const { return m_kiwixServerIpAddress; } - qreal getZoomFactor() const { return m_zoomFactor; } - QString getDownloadDir() const { return m_downloadDir; } - QString getMonitorDir() const { return m_monitorDir; } - bool getMoveToTrash() const { return m_moveToTrash; } - bool getReopenTab() const { return m_reopenTab; } - FilterList getLanguageList() { return deducePair(m_langList); } - QStringList getCategoryList() { return m_categoryList; } - FilterList getContentType() { return deducePair(m_contentTypeList); } - -public slots: - void setKiwixServerPort(int port); - void setKiwixServerIpAddress(QString ipAddress); - void setZoomFactor(qreal zoomFactor); - void setDownloadDir(QString downloadDir); - void setMonitorDir(QString monitorDir); - void setMoveToTrash(bool moveToTrash); - void setReopenTab(bool reopenTab); - void setLanguage(FilterList langList); - void setCategory(QStringList categoryList); - void setContentType(FilterList contentTypeList); - -private: - void initSettings(); - QList flattenPair(FilterList pairList); - FilterList deducePair(QList); - -signals: - void portChanged(int port); - void zoomChanged(qreal zoomFactor); - void downloadDirChanged(QString downloadDir); - void monitorDirChanged(QString monitorDir); - void moveToTrashChanged(bool moveToTrash); - void reopenTabChanged(bool reopenTab); - void languageChanged(QList langList); - void categoryChanged(QStringList categoryList); - void contentTypeChanged(QList contentTypeList); - -private: - QSettings m_settings; - SettingsView *m_view; - int m_kiwixServerPort; - QString m_kiwixServerIpAddress; - qreal m_zoomFactor; - QString m_downloadDir; - QString m_monitorDir; - bool m_moveToTrash; - bool m_reopenTab; - QList m_langList; - QStringList m_categoryList; - QList m_contentTypeList; -}; - -QString getDataDirectory(); -bool isPortableMode(); - -#endif // SETTINGSMANAGER_H +#ifndef SETTINGSMANAGER_H +#define SETTINGSMANAGER_H + +#include +#include +#include "settingsview.h" + +class SettingsManager : public QObject +{ + Q_OBJECT + Q_PROPERTY(int kiwixServerPort READ getKiwixServerPort NOTIFY portChanged) + Q_PROPERTY(qreal zoomFactor MEMBER m_zoomFactor WRITE setZoomFactor NOTIFY zoomChanged) + Q_PROPERTY(QString downloadDir MEMBER m_downloadDir WRITE setDownloadDir NOTIFY downloadDirChanged) + +public: + typedef QList> FilterList; + explicit SettingsManager(QObject *parent = nullptr); + virtual ~SettingsManager() {}; + + SettingsView* getView(); + void setSettings(const QString &key, const QVariant &value); + void deleteSettings(const QString &key); + bool settingsExists(const QString &key); + QVariant getSettings(const QString &key); + qreal getZoomFactorByZimId(const QString &id); + int getKiwixServerPort() const { return m_kiwixServerPort; } + QString getKiwixServerIpAddress() const { return m_kiwixServerIpAddress; } + qreal getZoomFactor() const { return m_zoomFactor; } + QString getDownloadDir() const { return m_downloadDir; } + QString getMonitorDir() const { return m_monitorDir; } + bool getMoveToTrash() const { return m_moveToTrash; } + bool getReopenTab() const { return m_reopenTab; } + FilterList getLanguageList() { return deducePair(m_langList); } + QStringList getCategoryList() { return m_categoryList; } + FilterList getContentType() { return deducePair(m_contentTypeList); } + +public slots: + void setKiwixServerPort(int port); + void setKiwixServerIpAddress(QString ipAddress); + void setZoomFactor(qreal zoomFactor); + void setDownloadDir(QString downloadDir); + void setMonitorDir(QString monitorDir); + void setMoveToTrash(bool moveToTrash); + void setReopenTab(bool reopenTab); + void setLanguage(FilterList langList); + void setCategory(QStringList categoryList); + void setContentType(FilterList contentTypeList); + +private: + void initSettings(); + QList flattenPair(FilterList pairList); + FilterList deducePair(QList); + +signals: + void portChanged(int port); + void zoomChanged(qreal zoomFactor); + void downloadDirChanged(QString downloadDir); + void monitorDirChanged(QString monitorDir); + void moveToTrashChanged(bool moveToTrash); + void reopenTabChanged(bool reopenTab); + void languageChanged(QList langList); + void categoryChanged(QStringList categoryList); + void contentTypeChanged(QList contentTypeList); + +private: + QSettings m_settings; + SettingsView *m_view; + int m_kiwixServerPort; + QString m_kiwixServerIpAddress; + qreal m_zoomFactor; + QString m_downloadDir; + QString m_monitorDir; + bool m_moveToTrash; + bool m_reopenTab; + QList m_langList; + QStringList m_categoryList; + QList m_contentTypeList; +}; + +QString getDataDirectory(); +bool isPortableMode(); + +#endif // SETTINGSMANAGER_H diff --git a/src/settingsview.cpp b/src/settingsview.cpp index f0d08003c..f48331561 100644 --- a/src/settingsview.cpp +++ b/src/settingsview.cpp @@ -1,232 +1,232 @@ -#include "settingsview.h" -#include "ui_settings.h" -#include "kiwixapp.h" -#include -#include -#include -#include -#include -#include - -namespace -{ - QString formatSettingsDir(const QString& input) { - const int maxLength = 40; - if (input.length() > maxLength) { - QString suffix = input.right(maxLength); - int directoryIndex = suffix.indexOf('/'); - return "..." + suffix.mid(directoryIndex != -1 ? directoryIndex : 0); - } - return input; - } - - void disableInPortableMode(QWidget* w) - { - w->setEnabled(false); - QString disabledStyle = "color: gray;"; - w->setStyleSheet(disabledStyle); - w->setToolTip(gt("portable-disabled-tooltip")); - } -} - -SettingsView::SettingsView(QWidget *parent) - : QScrollArea(parent) - , ui(new Ui::Settings) -{ - SettingsManager *settingsMgr = KiwixApp::instance()->getSettingsManager(); - ui->setupUi(this); - ui->widget->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/_settingsManager.css")); - connect(ui->zoomPercentSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &SettingsView::setZoom); - connect(ui->moveToTrashToggle, &QCheckBox::clicked, this, &SettingsView::setMoveToTrash); - connect(ui->reopenTabToggle, &QCheckBox::clicked, this, &SettingsView::setReopenTab); - connect(ui->browseButton, &QPushButton::clicked, this, &SettingsView::browseDownloadDir); - connect(ui->downloadDirPathCopy, &QPushButton::clicked, [this, settingsMgr]() { - copySettingsPathToClipboard(settingsMgr->getDownloadDir(), ui->downloadDirPathCopy); - }); - connect(ui->monitorDirPathCopy, &QPushButton::clicked, [this, settingsMgr]() { - copySettingsPathToClipboard(settingsMgr->getMonitorDir(), ui->monitorDirPathCopy); - }); - connect(ui->resetButton, &QPushButton::clicked, this, &SettingsView::resetDownloadDir); - connect(ui->monitorBrowse, &QPushButton::clicked, this, &SettingsView::browseMonitorDir); - connect(ui->monitorClear, &QPushButton::clicked, this, &SettingsView::clearMonitorDir); - connect(settingsMgr, &SettingsManager::downloadDirChanged, this, &SettingsView::onDownloadDirChanged); - connect(settingsMgr, &SettingsManager::monitorDirChanged, this, &SettingsView::onMonitorDirChanged); - connect(settingsMgr, &SettingsManager::zoomChanged, this, &SettingsView::onZoomChanged); - connect(settingsMgr, &SettingsManager::moveToTrashChanged, this, &SettingsView::onMoveToTrashChanged); - connect(settingsMgr, &SettingsManager::reopenTabChanged, this, &SettingsView::onReopenTabChanged); - ui->settingsLabel->setText(gt("settings")); - ui->zoomPercentLabel->setText(gt("zoom-level-setting")); - ui->downloadDirLabel->setText(gt("download-directory-setting")); - ui->monitorDirLabel->setText(gt("monitor-directory-setting")); - ui->resetButton->setText(gt("reset")); - ui->browseButton->setText(gt("browse")); - ui->monitorClear->setText(gt("clear")); - ui->monitorBrowse->setText(gt("browse")); - QIcon copyIcon(":/icons/copy.svg"); - ui->downloadDirPathCopy->setIcon(copyIcon); - ui->downloadDirPathCopy->setIconSize(QSize(24, 24)); - ui->monitorDirPathCopy->setIcon(copyIcon); - ui->monitorDirPathCopy->setIconSize(QSize(24, 24)); - ui->monitorHelp->setText("?"); - ui->monitorHelp->setToolTip(gt("monitor-directory-tooltip")); - ui->moveToTrashLabel->setText(gt("move-files-to-trash")); - ui->reopenTabLabel->setText(gt("open-previous-tabs-at-startup")); - if(isPortableMode()) { - disableInPortableMode(ui->browseButton); - disableInPortableMode(ui->resetButton); - disableInPortableMode(ui->monitorBrowse); - disableInPortableMode(ui->monitorClear); - } -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) - ui->line_5->hide(); - ui->moveToTrashLabel->hide(); - ui->moveToTrashToggle->hide(); -#endif - -} - -void SettingsView::init(int zoomPercent, const QString &downloadDir, - const QString &monitorDir, const bool moveToTrash, - bool reopentab) -{ - ui->zoomPercentSpinBox->setValue(zoomPercent); - SettingsView::onDownloadDirChanged(downloadDir); - SettingsView::onMonitorDirChanged(monitorDir); - ui->moveToTrashToggle->setChecked(moveToTrash); - ui->reopenTabToggle->setChecked(reopentab); -} -bool SettingsView::confirmDialog( QString messageText, QString messageTitle) -{ - QMessageBox msgBox( - QMessageBox::Question, //Icon - messageTitle, //Title - messageText, //Text - QMessageBox::Ok | QMessageBox::Cancel //Buttons - ); - msgBox.setDefaultButton(QMessageBox::Ok); - - int ret = msgBox.exec(); - return (ret == QMessageBox::Ok); -} - -bool SettingsView::confirmDialogDownloadDir(const QString &dir) { - auto messageText = gt("download-dir-dialog-msg"); - messageText = messageText.replace("{{DIRECTORY}}", dir); - return confirmDialog(messageText, gt("download-dir-dialog-title")); -} - -bool SettingsView::confirmDialogMonitorDir(const QString &dir) { - auto messageText = gt("monitor-dir-dialog-msg"); - messageText = messageText.replace("{{DIRECTORY}}", dir); - auto messageTitle = gt("monitor-dir-dialog-title"); - return confirmDialog(messageText, messageTitle); -} - -void SettingsView::resetDownloadDir() -{ - auto dir = getDataDirectory(); - const auto &downloadDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir(); - if (dir == downloadDir) { - return; - } - if (confirmDialogDownloadDir(dir)) { - KiwixApp::instance()->getSettingsManager()->setDownloadDir(dir); - } -} - -void SettingsView::browseDownloadDir() -{ - const auto &downloadDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir(); - QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(), - gt("browse-directory"), - downloadDir, - QFileDialog::ShowDirsOnly); - if (dir == downloadDir || dir.isEmpty()) { - return; - } - - if (confirmDialogDownloadDir(dir)) { - KiwixApp::instance()->getSettingsManager()->setDownloadDir(dir); - } -} - -void SettingsView::browseMonitorDir() -{ - const auto &monitorDir = KiwixApp::instance()->getSettingsManager()->getMonitorDir(); - QString previousDir; - if (monitorDir == "") { - previousDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir(); - } else { - previousDir = monitorDir; - } - QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(), - gt("browse-directory"), - previousDir, - QFileDialog::ShowDirsOnly); - if (dir == monitorDir || dir.isEmpty()) { - return; - } - if (confirmDialogMonitorDir(dir)) { - KiwixApp::instance()->setMonitorDir(dir); - } -} - -void SettingsView::clearMonitorDir() -{ - if (confirmDialog(gt("monitor-clear-dir-dialog-msg"), gt("monitor-clear-dir-dialog-title"))) { - KiwixApp::instance()->setMonitorDir(""); - } -} - -void SettingsView::setZoom(int zoomPercent) -{ - qreal zoomFactor = (qreal) zoomPercent/100; - KiwixApp::instance()->getSettingsManager()->setZoomFactor(zoomFactor); -} - -void SettingsView::setMoveToTrash(bool moveToTrash) -{ - KiwixApp::instance()->getSettingsManager()->setMoveToTrash(moveToTrash); -} - -void SettingsView::setReopenTab(bool reopen) -{ - KiwixApp::instance()->getSettingsManager()->setReopenTab(reopen); -} - -void SettingsView::onDownloadDirChanged(const QString &dir) -{ - ui->downloadDirPath->setText(formatSettingsDir(dir)); - ui->downloadDirPath->setToolTip(dir); -} - -void SettingsView::copySettingsPathToClipboard(QString pathToCopy, QPushButton* button) -{ - QApplication::clipboard()->setText(pathToCopy); - QPoint globalPos = button->mapToGlobal(QPoint(0, -button->height())); - QToolTip::showText(globalPos, gt("path-was-copied"), button); -} - -void SettingsView::onMonitorDirChanged(const QString &dir) -{ - ui->monitorClear->setVisible(!dir.isEmpty()); - ui->monitorDirPathCopy->setVisible(!dir.isEmpty()); - ui->monitorDirPath->setText(formatSettingsDir(dir)); - ui->monitorDirPath->setToolTip(dir); -} - -void SettingsView::onZoomChanged(qreal zoomFactor) -{ - qreal zoomPercent = zoomFactor * 100; - ui->zoomPercentSpinBox->setValue(zoomPercent); -} - -void SettingsView::onMoveToTrashChanged(bool moveToTrash) -{ - ui->moveToTrashToggle->setChecked(moveToTrash); -} - -void SettingsView::onReopenTabChanged(bool reopen) -{ - ui->reopenTabToggle->setChecked(reopen); -} +#include "settingsview.h" +#include "ui_settings.h" +#include "kiwixapp.h" +#include +#include +#include +#include +#include +#include + +namespace +{ + QString formatSettingsDir(const QString& input) { + const int maxLength = 40; + if (input.length() > maxLength) { + QString suffix = input.right(maxLength); + int directoryIndex = suffix.indexOf('/'); + return "..." + suffix.mid(directoryIndex != -1 ? directoryIndex : 0); + } + return input; + } + + void disableInPortableMode(QWidget* w) + { + w->setEnabled(false); + QString disabledStyle = "color: gray;"; + w->setStyleSheet(disabledStyle); + w->setToolTip(gt("portable-disabled-tooltip")); + } +} + +SettingsView::SettingsView(QWidget *parent) + : QScrollArea(parent) + , ui(new Ui::Settings) +{ + SettingsManager *settingsMgr = KiwixApp::instance()->getSettingsManager(); + ui->setupUi(this); + ui->widget->setStyleSheet(KiwixApp::instance()->parseStyleFromFile(":/css/_settingsManager.css")); + connect(ui->zoomPercentSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &SettingsView::setZoom); + connect(ui->moveToTrashToggle, &QCheckBox::clicked, this, &SettingsView::setMoveToTrash); + connect(ui->reopenTabToggle, &QCheckBox::clicked, this, &SettingsView::setReopenTab); + connect(ui->browseButton, &QPushButton::clicked, this, &SettingsView::browseDownloadDir); + connect(ui->downloadDirPathCopy, &QPushButton::clicked, [this, settingsMgr]() { + copySettingsPathToClipboard(settingsMgr->getDownloadDir(), ui->downloadDirPathCopy); + }); + connect(ui->monitorDirPathCopy, &QPushButton::clicked, [this, settingsMgr]() { + copySettingsPathToClipboard(settingsMgr->getMonitorDir(), ui->monitorDirPathCopy); + }); + connect(ui->resetButton, &QPushButton::clicked, this, &SettingsView::resetDownloadDir); + connect(ui->monitorBrowse, &QPushButton::clicked, this, &SettingsView::browseMonitorDir); + connect(ui->monitorClear, &QPushButton::clicked, this, &SettingsView::clearMonitorDir); + connect(settingsMgr, &SettingsManager::downloadDirChanged, this, &SettingsView::onDownloadDirChanged); + connect(settingsMgr, &SettingsManager::monitorDirChanged, this, &SettingsView::onMonitorDirChanged); + connect(settingsMgr, &SettingsManager::zoomChanged, this, &SettingsView::onZoomChanged); + connect(settingsMgr, &SettingsManager::moveToTrashChanged, this, &SettingsView::onMoveToTrashChanged); + connect(settingsMgr, &SettingsManager::reopenTabChanged, this, &SettingsView::onReopenTabChanged); + ui->settingsLabel->setText(gt("settings")); + ui->zoomPercentLabel->setText(gt("zoom-level-setting")); + ui->downloadDirLabel->setText(gt("download-directory-setting")); + ui->monitorDirLabel->setText(gt("monitor-directory-setting")); + ui->resetButton->setText(gt("reset")); + ui->browseButton->setText(gt("browse")); + ui->monitorClear->setText(gt("clear")); + ui->monitorBrowse->setText(gt("browse")); + QIcon copyIcon(":/icons/copy.svg"); + ui->downloadDirPathCopy->setIcon(copyIcon); + ui->downloadDirPathCopy->setIconSize(QSize(24, 24)); + ui->monitorDirPathCopy->setIcon(copyIcon); + ui->monitorDirPathCopy->setIconSize(QSize(24, 24)); + ui->monitorHelp->setText("?"); + ui->monitorHelp->setToolTip(gt("monitor-directory-tooltip")); + ui->moveToTrashLabel->setText(gt("move-files-to-trash")); + ui->reopenTabLabel->setText(gt("open-previous-tabs-at-startup")); + if(isPortableMode()) { + disableInPortableMode(ui->browseButton); + disableInPortableMode(ui->resetButton); + disableInPortableMode(ui->monitorBrowse); + disableInPortableMode(ui->monitorClear); + } +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + ui->line_5->hide(); + ui->moveToTrashLabel->hide(); + ui->moveToTrashToggle->hide(); +#endif + +} + +void SettingsView::init(int zoomPercent, const QString &downloadDir, + const QString &monitorDir, const bool moveToTrash, + bool reopentab) +{ + ui->zoomPercentSpinBox->setValue(zoomPercent); + SettingsView::onDownloadDirChanged(downloadDir); + SettingsView::onMonitorDirChanged(monitorDir); + ui->moveToTrashToggle->setChecked(moveToTrash); + ui->reopenTabToggle->setChecked(reopentab); +} +bool SettingsView::confirmDialog( QString messageText, QString messageTitle) +{ + QMessageBox msgBox( + QMessageBox::Question, //Icon + messageTitle, //Title + messageText, //Text + QMessageBox::Ok | QMessageBox::Cancel //Buttons + ); + msgBox.setDefaultButton(QMessageBox::Ok); + + int ret = msgBox.exec(); + return (ret == QMessageBox::Ok); +} + +bool SettingsView::confirmDialogDownloadDir(const QString &dir) { + auto messageText = gt("download-dir-dialog-msg"); + messageText = messageText.replace("{{DIRECTORY}}", dir); + return confirmDialog(messageText, gt("download-dir-dialog-title")); +} + +bool SettingsView::confirmDialogMonitorDir(const QString &dir) { + auto messageText = gt("monitor-dir-dialog-msg"); + messageText = messageText.replace("{{DIRECTORY}}", dir); + auto messageTitle = gt("monitor-dir-dialog-title"); + return confirmDialog(messageText, messageTitle); +} + +void SettingsView::resetDownloadDir() +{ + auto dir = getDataDirectory(); + const auto &downloadDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir(); + if (dir == downloadDir) { + return; + } + if (confirmDialogDownloadDir(dir)) { + KiwixApp::instance()->getSettingsManager()->setDownloadDir(dir); + } +} + +void SettingsView::browseDownloadDir() +{ + const auto &downloadDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir(); + QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(), + gt("browse-directory"), + downloadDir, + QFileDialog::ShowDirsOnly); + if (dir == downloadDir || dir.isEmpty()) { + return; + } + + if (confirmDialogDownloadDir(dir)) { + KiwixApp::instance()->getSettingsManager()->setDownloadDir(dir); + } +} + +void SettingsView::browseMonitorDir() +{ + const auto &monitorDir = KiwixApp::instance()->getSettingsManager()->getMonitorDir(); + QString previousDir; + if (monitorDir == "") { + previousDir = KiwixApp::instance()->getSettingsManager()->getDownloadDir(); + } else { + previousDir = monitorDir; + } + QString dir = QFileDialog::getExistingDirectory(KiwixApp::instance()->getMainWindow(), + gt("browse-directory"), + previousDir, + QFileDialog::ShowDirsOnly); + if (dir == monitorDir || dir.isEmpty()) { + return; + } + if (confirmDialogMonitorDir(dir)) { + KiwixApp::instance()->setMonitorDir(dir); + } +} + +void SettingsView::clearMonitorDir() +{ + if (confirmDialog(gt("monitor-clear-dir-dialog-msg"), gt("monitor-clear-dir-dialog-title"))) { + KiwixApp::instance()->setMonitorDir(""); + } +} + +void SettingsView::setZoom(int zoomPercent) +{ + qreal zoomFactor = (qreal) zoomPercent/100; + KiwixApp::instance()->getSettingsManager()->setZoomFactor(zoomFactor); +} + +void SettingsView::setMoveToTrash(bool moveToTrash) +{ + KiwixApp::instance()->getSettingsManager()->setMoveToTrash(moveToTrash); +} + +void SettingsView::setReopenTab(bool reopen) +{ + KiwixApp::instance()->getSettingsManager()->setReopenTab(reopen); +} + +void SettingsView::onDownloadDirChanged(const QString &dir) +{ + ui->downloadDirPath->setText(formatSettingsDir(dir)); + ui->downloadDirPath->setToolTip(dir); +} + +void SettingsView::copySettingsPathToClipboard(QString pathToCopy, QPushButton* button) +{ + QApplication::clipboard()->setText(pathToCopy); + QPoint globalPos = button->mapToGlobal(QPoint(0, -button->height())); + QToolTip::showText(globalPos, gt("path-was-copied"), button); +} + +void SettingsView::onMonitorDirChanged(const QString &dir) +{ + ui->monitorClear->setVisible(!dir.isEmpty()); + ui->monitorDirPathCopy->setVisible(!dir.isEmpty()); + ui->monitorDirPath->setText(formatSettingsDir(dir)); + ui->monitorDirPath->setToolTip(dir); +} + +void SettingsView::onZoomChanged(qreal zoomFactor) +{ + qreal zoomPercent = zoomFactor * 100; + ui->zoomPercentSpinBox->setValue(zoomPercent); +} + +void SettingsView::onMoveToTrashChanged(bool moveToTrash) +{ + ui->moveToTrashToggle->setChecked(moveToTrash); +} + +void SettingsView::onReopenTabChanged(bool reopen) +{ + ui->reopenTabToggle->setChecked(reopen); +} diff --git a/src/settingsview.h b/src/settingsview.h index 9703ad97f..f61e53be3 100644 --- a/src/settingsview.h +++ b/src/settingsview.h @@ -1,40 +1,40 @@ -#ifndef SETTINGSVIEW_H -#define SETTINGSVIEW_H - -#include -#include -#include -namespace Ui { -class Settings; -} -class SettingsView : public QScrollArea -{ - Q_OBJECT -public: - SettingsView(QWidget *parent = nullptr); - ~SettingsView(){}; - void init(int zoomPercent, const QString &downloadDir, - const QString &monitorDir, const bool moveToTrash, - bool reopentab); - public Q_SLOTS: - void resetDownloadDir(); - void browseDownloadDir(); - void browseMonitorDir(); - void clearMonitorDir(); - void setZoom(int zoomPercent); - void setMoveToTrash(bool moveToTrash); - void setReopenTab(bool reopen); - void onDownloadDirChanged(const QString &dir); - void copySettingsPathToClipboard(QString pathToCopy, QPushButton* button); - void onMonitorDirChanged(const QString &dir); - void onZoomChanged(qreal zoomFactor); - void onMoveToTrashChanged(bool moveToTrash); - void onReopenTabChanged(bool reopen); -private: - bool confirmDialogDownloadDir(const QString& dir); - bool confirmDialog(QString messageText, QString messageTitle); - bool confirmDialogMonitorDir(const QString& dir); - Ui::Settings *ui; -}; - -#endif // SETTINGSVIEW_H +#ifndef SETTINGSVIEW_H +#define SETTINGSVIEW_H + +#include +#include +#include +namespace Ui { +class Settings; +} +class SettingsView : public QScrollArea +{ + Q_OBJECT +public: + SettingsView(QWidget *parent = nullptr); + ~SettingsView(){}; + void init(int zoomPercent, const QString &downloadDir, + const QString &monitorDir, const bool moveToTrash, + bool reopentab); + public Q_SLOTS: + void resetDownloadDir(); + void browseDownloadDir(); + void browseMonitorDir(); + void clearMonitorDir(); + void setZoom(int zoomPercent); + void setMoveToTrash(bool moveToTrash); + void setReopenTab(bool reopen); + void onDownloadDirChanged(const QString &dir); + void copySettingsPathToClipboard(QString pathToCopy, QPushButton* button); + void onMonitorDirChanged(const QString &dir); + void onZoomChanged(qreal zoomFactor); + void onMoveToTrashChanged(bool moveToTrash); + void onReopenTabChanged(bool reopen); +private: + bool confirmDialogDownloadDir(const QString& dir); + bool confirmDialog(QString messageText, QString messageTitle); + bool confirmDialogMonitorDir(const QString& dir); + Ui::Settings *ui; +}; + +#endif // SETTINGSVIEW_H diff --git a/src/suggestionlistdelegate.cpp b/src/suggestionlistdelegate.cpp index 41296627e..e6c4d2b0b 100644 --- a/src/suggestionlistdelegate.cpp +++ b/src/suggestionlistdelegate.cpp @@ -1,104 +1,104 @@ -#include "suggestionlistdelegate.h" -#include "kiwixapp.h" -#include "css_constants.h" - -#include - -namespace HeaderSectionCSS = CSS::PopupCSS::QHeaderView::section; - -void SuggestionListDelegate::paint(QPainter *painter, - const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - /* Paint without text and icon */ - QStyleOptionViewItem opt(option); - QStyledItemDelegate::paint(painter, opt, QModelIndex()); - - paintIcon(painter, opt, index); - paintText(painter, opt, index); -} - -void SuggestionListDelegate::paintIcon(QPainter *p, - const QStyleOptionViewItem &opt, - const QModelIndex &index) const -{ - QRect pixmapRect = opt.rect; - const int lineHeight = HeaderSectionCSS::lineHeight; - const int paddingLeft = HeaderSectionCSS::paddingLeft; - - const QSize mapSize = QSize(lineHeight, lineHeight); - auto pixmap = index.data(Qt::DecorationRole).value().pixmap(mapSize); - - /* Align icon to Header text */ - if (KiwixApp::isRightToLeft()) - { - const int rightEnd = pixmapRect.width() - mapSize.width(); - pixmapRect.setX(pixmapRect.x() + rightEnd - paddingLeft); - } - else - pixmapRect.setX(pixmapRect.x() + paddingLeft); - - /* Align middle */ - pixmapRect.setY(pixmapRect.y() + (pixmapRect.height() - mapSize.height()) / 2); - pixmapRect.setSize(mapSize); - p->drawPixmap(pixmapRect, pixmap); -} - -/** - * @brief Get the elided text using font that can fit inside the length when - * appended with the custom elide text "(...)". - * - * @param font - * @param textRect - * @param text - * @return QString the elided text without any marker. - */ -QString getElidedText(const QFont& font, int length, const QString& text) -{ - const QFontMetrics metrics(font); - const int elideMarkerLength = metrics.tightBoundingRect("(...)").width(); - const int textLength = length - elideMarkerLength; - QString elidedText = metrics.elidedText(text, Qt::ElideRight, textLength); - if (elidedText != text) - return elidedText.isEmpty() ? "" : elidedText.chopped(1); - return text; -} - -void SuggestionListDelegate::paintText(QPainter *p, - const QStyleOptionViewItem &opt, - const QModelIndex &index) const -{ - auto& searchBar = KiwixApp::instance()->getSearchBar(); - const auto& lineEditGeo = searchBar.getLineEdit().geometry(); - - /* Remove border from left() since left is is with respect to border. Detail - reason on how this calculation comes about can be seen in - SearchBarLineEdit::getCompleterRect(); - */ - const int left = lineEditGeo.left() - CSS::SearchBar::border; - QRect textRect = opt.rect; - if (KiwixApp::isRightToLeft()) - { - const auto& searchGeo = searchBar.geometry(); - const int right = searchGeo.width() - left - lineEditGeo.width(); - textRect.setWidth(textRect.width() - right); - } - else - textRect.setX(textRect.x() + left); - - const int flag = {Qt::AlignVCenter | Qt::AlignLeading}; - const QString text = index.data(Qt::DisplayRole).toString(); - - /* Custom text elide. */ - QString elidedText = getElidedText(opt.font, textRect.width(), text); - if (elidedText != text) - { - /* drawText's Align direction determines text direction */ - const bool textDirFlipped = KiwixApp::isRightToLeft() != text.isRightToLeft(); - elidedText = textDirFlipped ? "(...)" + elidedText.trimmed() - : elidedText.trimmed() + "(...)"; - p->drawText(textRect, flag, elidedText); - } - else - p->drawText(textRect, flag, text); -} +#include "suggestionlistdelegate.h" +#include "kiwixapp.h" +#include "css_constants.h" + +#include + +namespace HeaderSectionCSS = CSS::PopupCSS::QHeaderView::section; + +void SuggestionListDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + /* Paint without text and icon */ + QStyleOptionViewItem opt(option); + QStyledItemDelegate::paint(painter, opt, QModelIndex()); + + paintIcon(painter, opt, index); + paintText(painter, opt, index); +} + +void SuggestionListDelegate::paintIcon(QPainter *p, + const QStyleOptionViewItem &opt, + const QModelIndex &index) const +{ + QRect pixmapRect = opt.rect; + const int lineHeight = HeaderSectionCSS::lineHeight; + const int paddingLeft = HeaderSectionCSS::paddingLeft; + + const QSize mapSize = QSize(lineHeight, lineHeight); + auto pixmap = index.data(Qt::DecorationRole).value().pixmap(mapSize); + + /* Align icon to Header text */ + if (KiwixApp::isRightToLeft()) + { + const int rightEnd = pixmapRect.width() - mapSize.width(); + pixmapRect.setX(pixmapRect.x() + rightEnd - paddingLeft); + } + else + pixmapRect.setX(pixmapRect.x() + paddingLeft); + + /* Align middle */ + pixmapRect.setY(pixmapRect.y() + (pixmapRect.height() - mapSize.height()) / 2); + pixmapRect.setSize(mapSize); + p->drawPixmap(pixmapRect, pixmap); +} + +/** + * @brief Get the elided text using font that can fit inside the length when + * appended with the custom elide text "(...)". + * + * @param font + * @param textRect + * @param text + * @return QString the elided text without any marker. + */ +QString getElidedText(const QFont& font, int length, const QString& text) +{ + const QFontMetrics metrics(font); + const int elideMarkerLength = metrics.tightBoundingRect("(...)").width(); + const int textLength = length - elideMarkerLength; + QString elidedText = metrics.elidedText(text, Qt::ElideRight, textLength); + if (elidedText != text) + return elidedText.isEmpty() ? "" : elidedText.chopped(1); + return text; +} + +void SuggestionListDelegate::paintText(QPainter *p, + const QStyleOptionViewItem &opt, + const QModelIndex &index) const +{ + auto& searchBar = KiwixApp::instance()->getSearchBar(); + const auto& lineEditGeo = searchBar.getLineEdit().geometry(); + + /* Remove border from left() since left is is with respect to border. Detail + reason on how this calculation comes about can be seen in + SearchBarLineEdit::getCompleterRect(); + */ + const int left = lineEditGeo.left() - CSS::SearchBar::border; + QRect textRect = opt.rect; + if (KiwixApp::isRightToLeft()) + { + const auto& searchGeo = searchBar.geometry(); + const int right = searchGeo.width() - left - lineEditGeo.width(); + textRect.setWidth(textRect.width() - right); + } + else + textRect.setX(textRect.x() + left); + + const int flag = {Qt::AlignVCenter | Qt::AlignLeading}; + const QString text = index.data(Qt::DisplayRole).toString(); + + /* Custom text elide. */ + QString elidedText = getElidedText(opt.font, textRect.width(), text); + if (elidedText != text) + { + /* drawText's Align direction determines text direction */ + const bool textDirFlipped = KiwixApp::isRightToLeft() != text.isRightToLeft(); + elidedText = textDirFlipped ? "(...)" + elidedText.trimmed() + : elidedText.trimmed() + "(...)"; + p->drawText(textRect, flag, elidedText); + } + else + p->drawText(textRect, flag, text); +} diff --git a/src/suggestionlistdelegate.h b/src/suggestionlistdelegate.h index 426964eb2..6b70fc3fd 100644 --- a/src/suggestionlistdelegate.h +++ b/src/suggestionlistdelegate.h @@ -1,17 +1,17 @@ -#ifndef SUGGESTIONLISTDELEGATE_H -#define SUGGESTIONLISTDELEGATE_H - -#include - -class SuggestionListDelegate : public QStyledItemDelegate -{ -public: - SuggestionListDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}; - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - -private: - void paintIcon(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const; - void paintText(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const; -}; - -#endif // SUGGESTIONLISTDELEGATE_H +#ifndef SUGGESTIONLISTDELEGATE_H +#define SUGGESTIONLISTDELEGATE_H + +#include + +class SuggestionListDelegate : public QStyledItemDelegate +{ +public: + SuggestionListDelegate(QObject *parent = nullptr) : QStyledItemDelegate(parent) {}; + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + +private: + void paintIcon(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const; + void paintText(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const; +}; + +#endif // SUGGESTIONLISTDELEGATE_H diff --git a/src/suggestionlistmodel.cpp b/src/suggestionlistmodel.cpp index 716b0f748..fd59a87ae 100644 --- a/src/suggestionlistmodel.cpp +++ b/src/suggestionlistmodel.cpp @@ -1,107 +1,107 @@ -#include "suggestionlistmodel.h" -#include "kiwixapp.h" -#include "css_constants.h" - -#include - -namespace HeaderSectionCSS = CSS::PopupCSS::QHeaderView::section; - -QString getZimIdFromUrl(QUrl url); - -SuggestionListModel::SuggestionListModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -SuggestionListModel::~SuggestionListModel() -{ -} - -int SuggestionListModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent); - - return m_suggestions.size(); -} - -QVariant SuggestionListModel::data(const QModelIndex &index, int role) const -{ - const int row = index.row(); - if (row < 0 || row >= rowCount()) - return QVariant(); - - switch (role) - { - case Qt::DisplayRole: - case Qt::EditRole: - return m_suggestions.at(row).text; - case Qt::UserRole: - return m_suggestions.at(row).url; - case Qt::DecorationRole: - { - const auto library = KiwixApp::instance()->getLibrary(); - const auto zimId = getZimIdFromUrl(m_suggestions.at(row).url); - return library->getBookIcon(zimId); - } - case Qt::SizeHintRole: - { - /* Padding in css can't change height, we have to achieve padding - by increasing height. - */ - const int padding = HeaderSectionCSS::paddingVertical; - const int lineHeight = HeaderSectionCSS::lineHeight; - return QSize(0, lineHeight + 2 * padding); - } - } - return QVariant(); -} - -QVariant SuggestionListModel::headerData(int section, - Qt::Orientation orientation, - int role) const -{ - if (section != 0 || orientation != Qt::Orientation::Horizontal) - return QVariant(); - - switch (role) - { - case Qt::DisplayRole: - return gt("kiwix-search"); - } - return QVariant(); -} - -void SuggestionListModel::resetSuggestions() -{ - beginResetModel(); - m_suggestions.clear(); - endResetModel(); -} - -void SuggestionListModel::append(const QList &suggestionList) -{ - beginResetModel(); - if (hasFullTextSearchSuggestion()) - m_suggestions.pop_back(); - - for (const auto& suggestion : suggestionList) - m_suggestions.append(suggestion); - endResetModel(); -} - -int SuggestionListModel::countOfRegularSuggestions() const -{ - return hasFullTextSearchSuggestion() - ? rowCount() - 1 - : rowCount(); -} - -bool SuggestionListModel::hasFullTextSearchSuggestion() const -{ - return rowCount() > 0 && m_suggestions.last().isFullTextSearchSuggestion(); -} - -bool SuggestionData::isFullTextSearchSuggestion() const -{ - return url.host().endsWith(".search"); -} +#include "suggestionlistmodel.h" +#include "kiwixapp.h" +#include "css_constants.h" + +#include + +namespace HeaderSectionCSS = CSS::PopupCSS::QHeaderView::section; + +QString getZimIdFromUrl(QUrl url); + +SuggestionListModel::SuggestionListModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +SuggestionListModel::~SuggestionListModel() +{ +} + +int SuggestionListModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + return m_suggestions.size(); +} + +QVariant SuggestionListModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (row < 0 || row >= rowCount()) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return m_suggestions.at(row).text; + case Qt::UserRole: + return m_suggestions.at(row).url; + case Qt::DecorationRole: + { + const auto library = KiwixApp::instance()->getLibrary(); + const auto zimId = getZimIdFromUrl(m_suggestions.at(row).url); + return library->getBookIcon(zimId); + } + case Qt::SizeHintRole: + { + /* Padding in css can't change height, we have to achieve padding + by increasing height. + */ + const int padding = HeaderSectionCSS::paddingVertical; + const int lineHeight = HeaderSectionCSS::lineHeight; + return QSize(0, lineHeight + 2 * padding); + } + } + return QVariant(); +} + +QVariant SuggestionListModel::headerData(int section, + Qt::Orientation orientation, + int role) const +{ + if (section != 0 || orientation != Qt::Orientation::Horizontal) + return QVariant(); + + switch (role) + { + case Qt::DisplayRole: + return gt("kiwix-search"); + } + return QVariant(); +} + +void SuggestionListModel::resetSuggestions() +{ + beginResetModel(); + m_suggestions.clear(); + endResetModel(); +} + +void SuggestionListModel::append(const QList &suggestionList) +{ + beginResetModel(); + if (hasFullTextSearchSuggestion()) + m_suggestions.pop_back(); + + for (const auto& suggestion : suggestionList) + m_suggestions.append(suggestion); + endResetModel(); +} + +int SuggestionListModel::countOfRegularSuggestions() const +{ + return hasFullTextSearchSuggestion() + ? rowCount() - 1 + : rowCount(); +} + +bool SuggestionListModel::hasFullTextSearchSuggestion() const +{ + return rowCount() > 0 && m_suggestions.last().isFullTextSearchSuggestion(); +} + +bool SuggestionData::isFullTextSearchSuggestion() const +{ + return url.host().endsWith(".search"); +} diff --git a/src/suggestionlistmodel.h b/src/suggestionlistmodel.h index 011546158..91b12d89f 100644 --- a/src/suggestionlistmodel.h +++ b/src/suggestionlistmodel.h @@ -1,37 +1,37 @@ -#ifndef SUGGESTIONLISTMODEL_H -#define SUGGESTIONLISTMODEL_H - -#include -#include - -struct SuggestionData -{ - QString text; - QUrl url; - - bool isFullTextSearchSuggestion() const; -}; - -class SuggestionListModel : public QAbstractListModel -{ - Q_OBJECT - -public: - explicit SuggestionListModel(QObject *parent = nullptr); - ~SuggestionListModel(); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - - void resetSuggestions(); - void append(const QList& suggestionList); - - int countOfRegularSuggestions() const; - bool hasFullTextSearchSuggestion() const; - -private: - QList m_suggestions; -}; - -#endif // SUGGESTIONLISTMODEL_H +#ifndef SUGGESTIONLISTMODEL_H +#define SUGGESTIONLISTMODEL_H + +#include +#include + +struct SuggestionData +{ + QString text; + QUrl url; + + bool isFullTextSearchSuggestion() const; +}; + +class SuggestionListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit SuggestionListModel(QObject *parent = nullptr); + ~SuggestionListModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + void resetSuggestions(); + void append(const QList& suggestionList); + + int countOfRegularSuggestions() const; + bool hasFullTextSearchSuggestion() const; + +private: + QList m_suggestions; +}; + +#endif // SUGGESTIONLISTMODEL_H diff --git a/src/suggestionlistworker.cpp b/src/suggestionlistworker.cpp index 5f328b046..eb74fcd27 100644 --- a/src/suggestionlistworker.cpp +++ b/src/suggestionlistworker.cpp @@ -1,59 +1,59 @@ -#include "suggestionlistworker.h" -#include "kiwixapp.h" -#include - -SuggestionListWorker::SuggestionListWorker(const QString& text, int token, int start, QObject *parent) -: QThread(parent), - m_text(text), - m_token(token), - m_start(start) -{ -} - -void SuggestionListWorker::run() -{ - QList suggestionList; - - const auto app = KiwixApp::instance(); - const auto selectedIdList = app->getSearchBar().getMultiZimButton().getZimIds(); - - /* TODO: re-implement this after introducing the actual Multi-Zim. */ - const auto currentZimId = selectedIdList[0]; - try { - const auto archive = app->getLibrary()->getArchive(currentZimId); - QUrl url; - url.setScheme("zim"); - url.setHost(currentZimId + ".zim"); - auto prefix = m_text.toStdString(); - auto suggestionSearcher = zim::SuggestionSearcher(*archive); - auto suggestionSearch = suggestionSearcher.suggest(prefix); - const auto suggestions = suggestionSearch.getResults(m_start, getFetchSize()); - for (auto current : suggestions) { - QString path = QString("/") + QString::fromStdString(current.getPath()); - url.setPath(path); - const auto text = QString::fromStdString(current.getTitle()); - suggestionList.append({text, url}); - } - - // Propose fulltext search - url.setPath(""); - if (archive->hasFulltextIndex()) { - // The host is used to determine the currentZimId - // The content query item is used to know in which zim search (as for kiwix-serve) - url.setHost(currentZimId + ".search"); - QUrlQuery query; - query.addQueryItem("content", currentZimId); - query.addQueryItem("pattern", m_text); - url.setQuery(query); - const auto text = m_text + " (" + gt("fulltext-search") + ")"; - suggestionList.append({text, url}); - } - } catch (std::out_of_range& e) { - // Impossible to find the requested archive (bug ?) - // We could propose a suggestion to do multi-zim search with: - // url.setHost("library.search"); - // but we don't have a correct UI to select on which zim search, how to display results, ... - // So do nothing for now - } - emit(searchFinished(suggestionList, m_token)); -} +#include "suggestionlistworker.h" +#include "kiwixapp.h" +#include + +SuggestionListWorker::SuggestionListWorker(const QString& text, int token, int start, QObject *parent) +: QThread(parent), + m_text(text), + m_token(token), + m_start(start) +{ +} + +void SuggestionListWorker::run() +{ + QList suggestionList; + + const auto app = KiwixApp::instance(); + const auto selectedIdList = app->getSearchBar().getMultiZimButton().getZimIds(); + + /* TODO: re-implement this after introducing the actual Multi-Zim. */ + const auto currentZimId = selectedIdList[0]; + try { + const auto archive = app->getLibrary()->getArchive(currentZimId); + QUrl url; + url.setScheme("zim"); + url.setHost(currentZimId + ".zim"); + auto prefix = m_text.toStdString(); + auto suggestionSearcher = zim::SuggestionSearcher(*archive); + auto suggestionSearch = suggestionSearcher.suggest(prefix); + const auto suggestions = suggestionSearch.getResults(m_start, getFetchSize()); + for (auto current : suggestions) { + QString path = QString("/") + QString::fromStdString(current.getPath()); + url.setPath(path); + const auto text = QString::fromStdString(current.getTitle()); + suggestionList.append({text, url}); + } + + // Propose fulltext search + url.setPath(""); + if (archive->hasFulltextIndex()) { + // The host is used to determine the currentZimId + // The content query item is used to know in which zim search (as for kiwix-serve) + url.setHost(currentZimId + ".search"); + QUrlQuery query; + query.addQueryItem("content", currentZimId); + query.addQueryItem("pattern", m_text); + url.setQuery(query); + const auto text = m_text + " (" + gt("fulltext-search") + ")"; + suggestionList.append({text, url}); + } + } catch (std::out_of_range& e) { + // Impossible to find the requested archive (bug ?) + // We could propose a suggestion to do multi-zim search with: + // url.setHost("library.search"); + // but we don't have a correct UI to select on which zim search, how to display results, ... + // So do nothing for now + } + emit(searchFinished(suggestionList, m_token)); +} diff --git a/src/suggestionlistworker.h b/src/suggestionlistworker.h index 18a80425d..a3e8c7854 100644 --- a/src/suggestionlistworker.h +++ b/src/suggestionlistworker.h @@ -1,25 +1,25 @@ -#ifndef SUGGESTIONLISTWORKER_H -#define SUGGESTIONLISTWORKER_H - -#include -#include "suggestionlistmodel.h" - -class SuggestionListWorker : public QThread -{ - Q_OBJECT -public: - static int getFetchSize() { return 15; }; - - SuggestionListWorker(const QString& text, int token, int start, QObject *parent = nullptr); - void run() override; - -signals: - void searchFinished(const QList& suggestionList, int token); - -private: - QString m_text; - int m_token = 0; - int m_start; -}; - -#endif // SUGGESTIONLISTWORKER_H +#ifndef SUGGESTIONLISTWORKER_H +#define SUGGESTIONLISTWORKER_H + +#include +#include "suggestionlistmodel.h" + +class SuggestionListWorker : public QThread +{ + Q_OBJECT +public: + static int getFetchSize() { return 15; }; + + SuggestionListWorker(const QString& text, int token, int start, QObject *parent = nullptr); + void run() override; + +signals: + void searchFinished(const QList& suggestionList, int token); + +private: + QString m_text; + int m_token = 0; + int m_start; +}; + +#endif // SUGGESTIONLISTWORKER_H diff --git a/src/tabbar.cpp b/src/tabbar.cpp index 3c7632e09..c289650b1 100644 --- a/src/tabbar.cpp +++ b/src/tabbar.cpp @@ -1,551 +1,551 @@ -class QMenu; - -#include "tabbar.h" - -#include "kiwixapp.h" -#include "css_constants.h" -#include -#include -#include -#include -#include -#include -#include -#define QUITIFNULL(VIEW) if (nullptr==(VIEW)) { return; } -#define CURRENTIFNULL(VIEW) if(nullptr==VIEW) { VIEW = currentZimView();} - -namespace -{ - -QAction* getAction(KiwixApp::Actions action) { - return KiwixApp::instance()->getAction(action); -} - -} // unnamed namespace - -TabBar::TabBar(QWidget *parent) : - QTabBar(parent) -{ - QTabBar::setDrawBase(false); - setTabsClosable(true); - setElideMode(Qt::ElideNone); - setDocumentMode(true); - setFocusPolicy(Qt::NoFocus); - setMovable(true); - setIconSize(QSize(30, 30)); - connect(this, &QTabBar::currentChanged, this, &TabBar::onCurrentChanged, Qt::QueuedConnection); - - connect(getAction(KiwixApp::NextTabAction), &QAction::triggered, this, &TabBar::moveToNextTab); - connect(getAction(KiwixApp::PreviousTabAction), &QAction::triggered, this, &TabBar::moveToPreviousTab); - connect(getAction(KiwixApp::CloseCurrentTabAction), &QAction::triggered, - this, [=]() { - this->closeTab(currentIndex()); - }); - connect(getAction(KiwixApp::OpenHomePageAction), &QAction::triggered, - this, &TabBar::openHomePage); - connect(getAction(KiwixApp::SettingAction), &QAction::triggered, - this, &TabBar::openOrSwitchToSettingsTab); - - for (int i = 0 ; i <= 9 ; i++) { - QAction *a = new QAction(this); - a->setShortcut(QKeySequence(Qt::ALT | (Qt::Key_0 + i))); - addAction(a); - connect(a, &QAction::triggered, this, [=](){ - const int tabIndex = i == 0 ? 9 : i - 1; - if (tabIndex < realTabCount()) { - setCurrentIndex(tabIndex); - } - }); - } - - // the slot relies the connection will be direct to reverting back the tab - connect(this, SIGNAL(tabMoved(int,int)), - this, SLOT(onTabMoved(int,int)), Qt::DirectConnection); -} - -void TabBar::openHomePage() { - auto current = this->currentWebView(); - QUITIFNULL(current); - current->setUrl("zim://" + current->zimId() + ".zim/"); -} - -void TabBar::openOrSwitchToSettingsTab() -{ - SettingsView* view = KiwixApp::instance()->getSettingsManager()->getView(); - for (int i = 0 ; i < mp_stackedWidget->count(); i++) { - if (mp_stackedWidget->widget(i) == view) { - setCurrentIndex(i); - return; - } - } - int index = currentIndex() + 1; - mp_stackedWidget->insertWidget(index, view); - emit tabDisplayed(TabType::SettingsTab); - insertTab(index,QIcon(":/icons/settings.svg"), gt("settings")); - setCloseTabButton(index); - setCurrentIndex(index); - KiwixApp::instance()->saveListOfOpenTabs(); -} - -void TabBar::setStackedWidget(QStackedWidget *widget) { - mp_stackedWidget = widget; - connect(this, &QTabBar::currentChanged, - widget, &QStackedWidget::setCurrentIndex); -} - -void TabBar::setContentManagerView(ContentManagerView* view) -{ - qInfo() << "add widget"; - mp_stackedWidget->addWidget(view); - mp_stackedWidget->show(); - int idx = addTab(QIcon(":/icons/kiwix-logo.svg"), ""); - setTabButton(idx, RightSide, nullptr); -} - -void TabBar::setNewTabButton(QAction* newTabAction) -{ - QToolButton *tb = new QToolButton(); - tb->setObjectName("newTabButton"); - tb->setDefaultAction(newTabAction); - tb->setIcon(QIcon(":/icons/new-tab-icon.svg")); - int idx = addTab(""); - setTabEnabled(idx, false); - setTabButton(idx, QTabBar::LeftSide, tb); - tabButton(idx, QTabBar::RightSide)->deleteLater(); - setTabButton(idx, QTabBar::RightSide, Q_NULLPTR); -} - -// Returns the count of real tabs with content (excluding the last pseudo-tab -// that acts as a button for creating new empty tabs; BTW what is the use for -// such empty tabs?) -int TabBar::realTabCount() const -{ - return count() < 1 ? 0 : count() - 1; -} - -void TabBar::moveToNextTab() -{ - const int index = currentIndex(); - setCurrentIndex(index == realTabCount() - 1 ? 0 : index + 1); -} - -void TabBar::moveToPreviousTab() -{ - const int index = currentIndex(); - setCurrentIndex(index <= 0 ? realTabCount() - 1 : index - 1); -} - -void TabBar::scrollNextTab() -{ - const int index = currentIndex(); - setCurrentIndex(index == realTabCount() - 1 ? index : index + 1); -} - -void TabBar::scrollPreviousTab() -{ - const int index = currentIndex(); - setCurrentIndex(index <= 0 ? index : index - 1); -} - -void TabBar::setCloseTabButton(int index) -{ - Q_ASSERT(index > 0 && index < realTabCount()); - - QToolButton *tb = new QToolButton(this); - tb->setObjectName("closeTabButton"); - QAction *a = new QAction(QIcon(":/icons/close.svg"), gt("close-tab"), tb); - a->setToolTip(getAction(KiwixApp::CloseCurrentTabAction)->toolTip()); - tb->setDefaultAction(a); - setTabButton(index, QTabBar::RightSide, tb); - connect(tb, &QToolButton::triggered, this, [=]() { - for ( int i = 0; i < realTabCount(); ++i ) { - if ( tb == tabButton(i, QTabBar::RightSide) ) { - closeTab(i); - return; - } - } - }); -} - -ZimView* TabBar::createNewTab(bool setCurrent, bool nextToCurrentTab) -{ - auto tab = new ZimView(this, this); - const int index = nextToCurrentTab ? currentIndex() + 1 : realTabCount(); - mp_stackedWidget->insertWidget(index, tab); - insertTab(index, ""); - setCloseTabButton(index); - if (setCurrent) { - setCurrentIndex(index); - } - - connect(tab, &ZimView::webActionEnabledChanged, - this, &TabBar::onWebviewHistoryActionChanged); - - KiwixApp::instance()->saveListOfOpenTabs(); - return tab; -} - -void TabBar::openUrl(const QUrl& url, bool newTab) -{ - WebView* webView = currentWebView(); - if (newTab || !webView) { - webView = createNewTab(true, true)->getWebView(); - } - QUITIFNULL(webView); - webView->setUrl(url); -} - -void TabBar::setTitleOf(const QString& title, ZimView* tab) -{ - CURRENTIFNULL(tab); - int idx = mp_stackedWidget->indexOf(tab); - if (idx < 0) - return; - - QString t = title; - - if (title.startsWith("zim://")) { - auto url = QUrl(title); - t = url.path(); - } - - setTabToolTip(idx, t); - - /* we don't use setTabText() because Qt can't draw text with - * fade-out gradient selectively (only for large texts). - * So we just store tab's title, and will draw it later ourselves. - */ - setTabData(idx, QVariant::fromValue(t)); - - // need to initiate repaint - // because setTabData() didn't do it as setTabText() before. - repaint(); -} - -void TabBar::setIconOf(const QIcon &icon, ZimView *tab) -{ - CURRENTIFNULL(tab); - setTabIcon(mp_stackedWidget->indexOf(tab), icon); -} - -QString TabBar::currentZimId() -{ - if (WebView *w = currentWebView()) - return w->zimId(); - return ""; -} - -QString TabBar::currentArticleUrl() -{ - if (WebView *w = currentWebView()) - return w->url().path(); - return ""; -} - -QString TabBar::currentArticleTitle() -{ - if (WebView *w = currentWebView()) - return w->title(); - return ""; -} - -QSize TabBar::tabSizeHint(int index) const -{ - QWidget *w = mp_stackedWidget->widget(index); - - if ((w && qobject_cast(w)) || index >= count() - 1) - return QSize(40, 40); // the "Library" tab is only icon - - return QSize(205, 40); // "Settings" and content tabs have text -} - -void TabBar::openFindInPageBar() -{ - if (ZimView *zv = currentZimView()) - zv->openFindInPageBar(); -} - -void TabBar::triggerWebPageAction(QWebEnginePage::WebAction action, ZimView *widget) -{ - CURRENTIFNULL(widget); - QUITIFNULL(widget); - widget->getWebView()->triggerPageAction(action); - widget->getWebView()->setFocus(); -} - -void TabBar::closeTabsByZimId(const QString &id) -{ - // 0th tab is always (unless this comment becomes outdated by the time you - // read it) the library tab, so iteration could start from 1, however we - // shouldn't try to save CPU cycles at the cost of the code breaking - // should this comment indeed become outdated ;) - for (int i = 0 ; i < realTabCount() ; ++i ) { - auto *zv = qobject_cast(mp_stackedWidget->widget(i)); - if (zv && zv->getWebView()->zimId() == id) { - closeTab(i); - } - } -} - -QStringList TabBar::getTabUrls() const { - QStringList idList; - for (int index = 0; index <= mp_stackedWidget->count(); index++) - { - if (ZimView* zv = qobject_cast(mp_stackedWidget->widget(index))) - idList.push_back(zv->getWebView()->url().url()); - else if (qobject_cast(mp_stackedWidget->widget(index))) - idList.push_back("SettingsTab"); - } - return idList; -} - -QStringList TabBar::getTabZimIds() const -{ - QStringList idList; - for (int index = 0; index <= mp_stackedWidget->count(); index++) - if (ZimView* zv = qobject_cast(mp_stackedWidget->widget(index))) - idList.push_back(zv->getWebView()->zimId()); - return idList; -} - -void TabBar::closeTab(int index) -{ - // The first and last tabs (i.e. the library tab and the + (new tab) button) - // cannot be closed - if (index <= 0 || index >= this->realTabCount()) - return; - - if ( index == currentIndex() ) { - setCurrentIndex(index + 1 == realTabCount() ? index - 1 : index + 1); - } - - QWidget *view = mp_stackedWidget->widget(index); - mp_stackedWidget->removeWidget(view); - view->setParent(nullptr); - removeTab(index); - view->close(); - view->deleteLater(); - - KiwixApp::instance()->saveListOfOpenTabs(); -} - -void TabBar::onCurrentChanged(int index) -{ - if (index == -1) - return; - - // if somehow the last tab (+ button) became active, switch to the previous - if (index >= realTabCount()) { - setCurrentIndex(realTabCount() - 1); - return; - } - - QWidget *w = mp_stackedWidget->widget(index); - - if (qobject_cast(w)) { - emit webActionEnabledChanged(QWebEnginePage::Back, false); - emit webActionEnabledChanged(QWebEnginePage::Forward, false); - emit tabDisplayed(TabType::SettingsTab); - QTimer::singleShot(0, [=](){emit currentTitleChanged("");}); - } else if (auto zv = qobject_cast(w)) { - auto view = zv->getWebView(); - emit webActionEnabledChanged(QWebEnginePage::Back, view->isWebActionEnabled(QWebEnginePage::Back)); - emit webActionEnabledChanged(QWebEnginePage::Forward, view->isWebActionEnabled(QWebEnginePage::Forward)); - emit tabDisplayed(TabType::ZimViewTab); - QTimer::singleShot(0, [=](){emit currentTitleChanged(view->title());}); - } else if (qobject_cast(w)) { - emit webActionEnabledChanged(QWebEnginePage::Back, false); - emit webActionEnabledChanged(QWebEnginePage::Forward, false); - emit tabDisplayed(TabType::LibraryTab); - QTimer::singleShot(0, [=](){emit currentTitleChanged("");}); - } - else { - Q_ASSERT(false); - // In the future, other types of tabs can be added. - // For example, About dialog, or Kiwix Server control panel. - } - - KiwixApp::instance()->saveCurrentTabIndex(); -} - -void TabBar::fullScreenRequested(QWebEngineFullScreenRequest request) -{ - if (request.toggleOn()) { - if (m_fullScreenWindow) - return; - request.accept(); - m_fullScreenWindow.reset(new FullScreenWindow(this->currentWebView())); - } else { - if (!m_fullScreenWindow) - return; - request.accept(); - m_fullScreenWindow.reset(); - } -} - -void TabBar::on_webview_titleChanged(const QString& title) -{ - ZimView* tab = qobject_cast(sender()->parent()); - if (! tab) - return; - - setTitleOf(title, tab); - - if (currentZimView() == tab) - emit currentTitleChanged(title); -} - -void TabBar::onWebviewHistoryActionChanged(QWebEnginePage::WebAction action, bool enabled) -{ - ZimView *zv = qobject_cast(sender()); - - if (!zv || zv != this->currentZimView()) - return; - - emit webActionEnabledChanged(action, enabled); -} - -void TabBar::mousePressEvent(QMouseEvent *event) -{ - if (event->button() == Qt::MiddleButton) { - closeTab(this->tabAt(event->pos())); - } else { - QTabBar::mousePressEvent(event); - } -} - -void TabBar::paintEvent(QPaintEvent *e) -{ - /* first, let Qt draw QTabBar normally. - * QTabbar will leave tabs titles empty because we didn't set these values into QTabBar. - */ - QTabBar::paintEvent(e); - - // Then, for each tab, we draw titles, using fade-out effect when needed. - QPainter p(this); - - for (int i = 0; i < count(); ++i) { - QString tab_title = tabData(i).toString(); - if (tab_title.isEmpty()) - continue; - - /* See the implementation of QTabBar for better understanding this code: - * /5.12.6/Src/qtbase/src/widgets/widgets/qtabbar.cpp - * in particular, QTabBar::initStyleOption() - */ - QStyleOptionTab tabopt; - initStyleOption(&tabopt, i); - - bool need_fade_out = false; - bool textRightToLeft = tab_title.isRightToLeft(); - bool appRightToLeft = QWidget::isRightToLeft(); - - const int padding = CSS::QTabBar::tab::padding; - QRect tabTextRect = style()->subElementRect(QStyle::SE_TabBarTabText, &tabopt, this); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - const int border = CSS::QTabBar::tab::border; - - // Add Padding to left, right. Padding is 4px. Add 5 to account for - // Extra pixel from border. - tabTextRect.setX(tabTextRect.x() + padding + border); - tabTextRect.setWidth(tabTextRect.width() - padding - border); -#else - // Qt6 correctly adds left and right padding but now incorrectly adds - // top or bottom padding. - tabTextRect.setY(tabTextRect.y() - padding); -#endif - QRect fontTextRect = fontMetrics().boundingRect(tab_title); - - if (fontTextRect.width() > tabTextRect.width()) - need_fade_out = true; - - if (need_fade_out) { - // draw the most of tab text extent with the normal color, - // and draw the rest with alpha channel gradient - QColor c0 = tabopt.palette.brush(QPalette::ButtonText).color(); - QColor c1(c0); - - c0.setAlpha(255); // color of font - c1.setAlpha(0); // transparent - - const int mid_Y = tabTextRect.center().y(); - QLinearGradient gr; - - if (textRightToLeft) { - // arabic right-to-left text - gr.setStart(tabTextRect.x(), mid_Y); - gr.setFinalStop(tabTextRect.x() + 0.2 * tabTextRect.width(), mid_Y); - gr.setColorAt(0.0, c1); - gr.setColorAt(1.0, c0); - } - else { - // normal left-to-right text direction - gr.setStart(tabTextRect.x() + 0.8 * tabTextRect.width(), mid_Y); - gr.setFinalStop(tabTextRect.right(), mid_Y); - gr.setColorAt(0.0, c0); - gr.setColorAt(1.0, c1); - } - tabopt.palette.setBrush(QPalette::ButtonText, QBrush(gr)); - } - - int align = Qt::AlignVCenter; - if (need_fade_out) { - /* - We align LTR text in LTR app to the left, while RTL text in RTL app - to the right. Since Qt mirror alignment, we can simply do ALignLeft - for both cases. When text and app direction doesn't match, we flip - the text alignment. - */ - align |= (textRightToLeft == appRightToLeft) ? Qt::AlignLeft : Qt::AlignRight; - } - - style()->drawItemText(&p, tabTextRect, align, - tabopt.palette, true, tab_title, QPalette::ButtonText); - } -} - -void TabBar::tabRemoved(int index) -{ - QTabBar::tabRemoved(index); - emit tabRemovedSignal(index); -} - -void TabBar::tabInserted(int index) -{ - QTabBar::tabInserted(index); - emit tabInsertedSignal(index); -} - -void TabBar::resizeEvent(QResizeEvent *event) -{ - QTabBar::resizeEvent(event); - emit sizeChanged(); -} - -void TabBar::onTabMoved(int from, int to) -{ - // avoid infinitive recursion - static bool reverting = false; - if (reverting) - return; - - // on attempt to move the last tab (+ button) just move it back - // and the library should stick the first (zero) tab - int last = mp_stackedWidget->count(); - if ( - (from == last || to == last) || - (from == 0 || to == 0) - ) { - reverting = true; - moveTab(to, from); - reverting = false; - return; - } - - // swap widgets - QWidget *w_from = mp_stackedWidget->widget(from); - mp_stackedWidget->removeWidget(w_from); - mp_stackedWidget->insertWidget(to, w_from); - - KiwixApp::instance()->saveListOfOpenTabs(); -} +class QMenu; + +#include "tabbar.h" + +#include "kiwixapp.h" +#include "css_constants.h" +#include +#include +#include +#include +#include +#include +#include +#define QUITIFNULL(VIEW) if (nullptr==(VIEW)) { return; } +#define CURRENTIFNULL(VIEW) if(nullptr==VIEW) { VIEW = currentZimView();} + +namespace +{ + +QAction* getAction(KiwixApp::Actions action) { + return KiwixApp::instance()->getAction(action); +} + +} // unnamed namespace + +TabBar::TabBar(QWidget *parent) : + QTabBar(parent) +{ + QTabBar::setDrawBase(false); + setTabsClosable(true); + setElideMode(Qt::ElideNone); + setDocumentMode(true); + setFocusPolicy(Qt::NoFocus); + setMovable(true); + setIconSize(QSize(30, 30)); + connect(this, &QTabBar::currentChanged, this, &TabBar::onCurrentChanged, Qt::QueuedConnection); + + connect(getAction(KiwixApp::NextTabAction), &QAction::triggered, this, &TabBar::moveToNextTab); + connect(getAction(KiwixApp::PreviousTabAction), &QAction::triggered, this, &TabBar::moveToPreviousTab); + connect(getAction(KiwixApp::CloseCurrentTabAction), &QAction::triggered, + this, [=]() { + this->closeTab(currentIndex()); + }); + connect(getAction(KiwixApp::OpenHomePageAction), &QAction::triggered, + this, &TabBar::openHomePage); + connect(getAction(KiwixApp::SettingAction), &QAction::triggered, + this, &TabBar::openOrSwitchToSettingsTab); + + for (int i = 0 ; i <= 9 ; i++) { + QAction *a = new QAction(this); + a->setShortcut(QKeySequence(Qt::ALT | (Qt::Key_0 + i))); + addAction(a); + connect(a, &QAction::triggered, this, [=](){ + const int tabIndex = i == 0 ? 9 : i - 1; + if (tabIndex < realTabCount()) { + setCurrentIndex(tabIndex); + } + }); + } + + // the slot relies the connection will be direct to reverting back the tab + connect(this, SIGNAL(tabMoved(int,int)), + this, SLOT(onTabMoved(int,int)), Qt::DirectConnection); +} + +void TabBar::openHomePage() { + auto current = this->currentWebView(); + QUITIFNULL(current); + current->setUrl("zim://" + current->zimId() + ".zim/"); +} + +void TabBar::openOrSwitchToSettingsTab() +{ + SettingsView* view = KiwixApp::instance()->getSettingsManager()->getView(); + for (int i = 0 ; i < mp_stackedWidget->count(); i++) { + if (mp_stackedWidget->widget(i) == view) { + setCurrentIndex(i); + return; + } + } + int index = currentIndex() + 1; + mp_stackedWidget->insertWidget(index, view); + emit tabDisplayed(TabType::SettingsTab); + insertTab(index,QIcon(":/icons/settings.svg"), gt("settings")); + setCloseTabButton(index); + setCurrentIndex(index); + KiwixApp::instance()->saveListOfOpenTabs(); +} + +void TabBar::setStackedWidget(QStackedWidget *widget) { + mp_stackedWidget = widget; + connect(this, &QTabBar::currentChanged, + widget, &QStackedWidget::setCurrentIndex); +} + +void TabBar::setContentManagerView(ContentManagerView* view) +{ + qInfo() << "add widget"; + mp_stackedWidget->addWidget(view); + mp_stackedWidget->show(); + int idx = addTab(QIcon(":/icons/kiwix-logo.svg"), ""); + setTabButton(idx, RightSide, nullptr); +} + +void TabBar::setNewTabButton(QAction* newTabAction) +{ + QToolButton *tb = new QToolButton(); + tb->setObjectName("newTabButton"); + tb->setDefaultAction(newTabAction); + tb->setIcon(QIcon(":/icons/new-tab-icon.svg")); + int idx = addTab(""); + setTabEnabled(idx, false); + setTabButton(idx, QTabBar::LeftSide, tb); + tabButton(idx, QTabBar::RightSide)->deleteLater(); + setTabButton(idx, QTabBar::RightSide, Q_NULLPTR); +} + +// Returns the count of real tabs with content (excluding the last pseudo-tab +// that acts as a button for creating new empty tabs; BTW what is the use for +// such empty tabs?) +int TabBar::realTabCount() const +{ + return count() < 1 ? 0 : count() - 1; +} + +void TabBar::moveToNextTab() +{ + const int index = currentIndex(); + setCurrentIndex(index == realTabCount() - 1 ? 0 : index + 1); +} + +void TabBar::moveToPreviousTab() +{ + const int index = currentIndex(); + setCurrentIndex(index <= 0 ? realTabCount() - 1 : index - 1); +} + +void TabBar::scrollNextTab() +{ + const int index = currentIndex(); + setCurrentIndex(index == realTabCount() - 1 ? index : index + 1); +} + +void TabBar::scrollPreviousTab() +{ + const int index = currentIndex(); + setCurrentIndex(index <= 0 ? index : index - 1); +} + +void TabBar::setCloseTabButton(int index) +{ + Q_ASSERT(index > 0 && index < realTabCount()); + + QToolButton *tb = new QToolButton(this); + tb->setObjectName("closeTabButton"); + QAction *a = new QAction(QIcon(":/icons/close.svg"), gt("close-tab"), tb); + a->setToolTip(getAction(KiwixApp::CloseCurrentTabAction)->toolTip()); + tb->setDefaultAction(a); + setTabButton(index, QTabBar::RightSide, tb); + connect(tb, &QToolButton::triggered, this, [=]() { + for ( int i = 0; i < realTabCount(); ++i ) { + if ( tb == tabButton(i, QTabBar::RightSide) ) { + closeTab(i); + return; + } + } + }); +} + +ZimView* TabBar::createNewTab(bool setCurrent, bool nextToCurrentTab) +{ + auto tab = new ZimView(this, this); + const int index = nextToCurrentTab ? currentIndex() + 1 : realTabCount(); + mp_stackedWidget->insertWidget(index, tab); + insertTab(index, ""); + setCloseTabButton(index); + if (setCurrent) { + setCurrentIndex(index); + } + + connect(tab, &ZimView::webActionEnabledChanged, + this, &TabBar::onWebviewHistoryActionChanged); + + KiwixApp::instance()->saveListOfOpenTabs(); + return tab; +} + +void TabBar::openUrl(const QUrl& url, bool newTab) +{ + WebView* webView = currentWebView(); + if (newTab || !webView) { + webView = createNewTab(true, true)->getWebView(); + } + QUITIFNULL(webView); + webView->setUrl(url); +} + +void TabBar::setTitleOf(const QString& title, ZimView* tab) +{ + CURRENTIFNULL(tab); + int idx = mp_stackedWidget->indexOf(tab); + if (idx < 0) + return; + + QString t = title; + + if (title.startsWith("zim://")) { + auto url = QUrl(title); + t = url.path(); + } + + setTabToolTip(idx, t); + + /* we don't use setTabText() because Qt can't draw text with + * fade-out gradient selectively (only for large texts). + * So we just store tab's title, and will draw it later ourselves. + */ + setTabData(idx, QVariant::fromValue(t)); + + // need to initiate repaint + // because setTabData() didn't do it as setTabText() before. + repaint(); +} + +void TabBar::setIconOf(const QIcon &icon, ZimView *tab) +{ + CURRENTIFNULL(tab); + setTabIcon(mp_stackedWidget->indexOf(tab), icon); +} + +QString TabBar::currentZimId() +{ + if (WebView *w = currentWebView()) + return w->zimId(); + return ""; +} + +QString TabBar::currentArticleUrl() +{ + if (WebView *w = currentWebView()) + return w->url().path(); + return ""; +} + +QString TabBar::currentArticleTitle() +{ + if (WebView *w = currentWebView()) + return w->title(); + return ""; +} + +QSize TabBar::tabSizeHint(int index) const +{ + QWidget *w = mp_stackedWidget->widget(index); + + if ((w && qobject_cast(w)) || index >= count() - 1) + return QSize(40, 40); // the "Library" tab is only icon + + return QSize(205, 40); // "Settings" and content tabs have text +} + +void TabBar::openFindInPageBar() +{ + if (ZimView *zv = currentZimView()) + zv->openFindInPageBar(); +} + +void TabBar::triggerWebPageAction(QWebEnginePage::WebAction action, ZimView *widget) +{ + CURRENTIFNULL(widget); + QUITIFNULL(widget); + widget->getWebView()->triggerPageAction(action); + widget->getWebView()->setFocus(); +} + +void TabBar::closeTabsByZimId(const QString &id) +{ + // 0th tab is always (unless this comment becomes outdated by the time you + // read it) the library tab, so iteration could start from 1, however we + // shouldn't try to save CPU cycles at the cost of the code breaking + // should this comment indeed become outdated ;) + for (int i = 0 ; i < realTabCount() ; ++i ) { + auto *zv = qobject_cast(mp_stackedWidget->widget(i)); + if (zv && zv->getWebView()->zimId() == id) { + closeTab(i); + } + } +} + +QStringList TabBar::getTabUrls() const { + QStringList idList; + for (int index = 0; index <= mp_stackedWidget->count(); index++) + { + if (ZimView* zv = qobject_cast(mp_stackedWidget->widget(index))) + idList.push_back(zv->getWebView()->url().url()); + else if (qobject_cast(mp_stackedWidget->widget(index))) + idList.push_back("SettingsTab"); + } + return idList; +} + +QStringList TabBar::getTabZimIds() const +{ + QStringList idList; + for (int index = 0; index <= mp_stackedWidget->count(); index++) + if (ZimView* zv = qobject_cast(mp_stackedWidget->widget(index))) + idList.push_back(zv->getWebView()->zimId()); + return idList; +} + +void TabBar::closeTab(int index) +{ + // The first and last tabs (i.e. the library tab and the + (new tab) button) + // cannot be closed + if (index <= 0 || index >= this->realTabCount()) + return; + + if ( index == currentIndex() ) { + setCurrentIndex(index + 1 == realTabCount() ? index - 1 : index + 1); + } + + QWidget *view = mp_stackedWidget->widget(index); + mp_stackedWidget->removeWidget(view); + view->setParent(nullptr); + removeTab(index); + view->close(); + view->deleteLater(); + + KiwixApp::instance()->saveListOfOpenTabs(); +} + +void TabBar::onCurrentChanged(int index) +{ + if (index == -1) + return; + + // if somehow the last tab (+ button) became active, switch to the previous + if (index >= realTabCount()) { + setCurrentIndex(realTabCount() - 1); + return; + } + + QWidget *w = mp_stackedWidget->widget(index); + + if (qobject_cast(w)) { + emit webActionEnabledChanged(QWebEnginePage::Back, false); + emit webActionEnabledChanged(QWebEnginePage::Forward, false); + emit tabDisplayed(TabType::SettingsTab); + QTimer::singleShot(0, [=](){emit currentTitleChanged("");}); + } else if (auto zv = qobject_cast(w)) { + auto view = zv->getWebView(); + emit webActionEnabledChanged(QWebEnginePage::Back, view->isWebActionEnabled(QWebEnginePage::Back)); + emit webActionEnabledChanged(QWebEnginePage::Forward, view->isWebActionEnabled(QWebEnginePage::Forward)); + emit tabDisplayed(TabType::ZimViewTab); + QTimer::singleShot(0, [=](){emit currentTitleChanged(view->title());}); + } else if (qobject_cast(w)) { + emit webActionEnabledChanged(QWebEnginePage::Back, false); + emit webActionEnabledChanged(QWebEnginePage::Forward, false); + emit tabDisplayed(TabType::LibraryTab); + QTimer::singleShot(0, [=](){emit currentTitleChanged("");}); + } + else { + Q_ASSERT(false); + // In the future, other types of tabs can be added. + // For example, About dialog, or Kiwix Server control panel. + } + + KiwixApp::instance()->saveCurrentTabIndex(); +} + +void TabBar::fullScreenRequested(QWebEngineFullScreenRequest request) +{ + if (request.toggleOn()) { + if (m_fullScreenWindow) + return; + request.accept(); + m_fullScreenWindow.reset(new FullScreenWindow(this->currentWebView())); + } else { + if (!m_fullScreenWindow) + return; + request.accept(); + m_fullScreenWindow.reset(); + } +} + +void TabBar::on_webview_titleChanged(const QString& title) +{ + ZimView* tab = qobject_cast(sender()->parent()); + if (! tab) + return; + + setTitleOf(title, tab); + + if (currentZimView() == tab) + emit currentTitleChanged(title); +} + +void TabBar::onWebviewHistoryActionChanged(QWebEnginePage::WebAction action, bool enabled) +{ + ZimView *zv = qobject_cast(sender()); + + if (!zv || zv != this->currentZimView()) + return; + + emit webActionEnabledChanged(action, enabled); +} + +void TabBar::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::MiddleButton) { + closeTab(this->tabAt(event->pos())); + } else { + QTabBar::mousePressEvent(event); + } +} + +void TabBar::paintEvent(QPaintEvent *e) +{ + /* first, let Qt draw QTabBar normally. + * QTabbar will leave tabs titles empty because we didn't set these values into QTabBar. + */ + QTabBar::paintEvent(e); + + // Then, for each tab, we draw titles, using fade-out effect when needed. + QPainter p(this); + + for (int i = 0; i < count(); ++i) { + QString tab_title = tabData(i).toString(); + if (tab_title.isEmpty()) + continue; + + /* See the implementation of QTabBar for better understanding this code: + * /5.12.6/Src/qtbase/src/widgets/widgets/qtabbar.cpp + * in particular, QTabBar::initStyleOption() + */ + QStyleOptionTab tabopt; + initStyleOption(&tabopt, i); + + bool need_fade_out = false; + bool textRightToLeft = tab_title.isRightToLeft(); + bool appRightToLeft = QWidget::isRightToLeft(); + + const int padding = CSS::QTabBar::tab::padding; + QRect tabTextRect = style()->subElementRect(QStyle::SE_TabBarTabText, &tabopt, this); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + const int border = CSS::QTabBar::tab::border; + + // Add Padding to left, right. Padding is 4px. Add 5 to account for + // Extra pixel from border. + tabTextRect.setX(tabTextRect.x() + padding + border); + tabTextRect.setWidth(tabTextRect.width() - padding - border); +#else + // Qt6 correctly adds left and right padding but now incorrectly adds + // top or bottom padding. + tabTextRect.setY(tabTextRect.y() - padding); +#endif + QRect fontTextRect = fontMetrics().boundingRect(tab_title); + + if (fontTextRect.width() > tabTextRect.width()) + need_fade_out = true; + + if (need_fade_out) { + // draw the most of tab text extent with the normal color, + // and draw the rest with alpha channel gradient + QColor c0 = tabopt.palette.brush(QPalette::ButtonText).color(); + QColor c1(c0); + + c0.setAlpha(255); // color of font + c1.setAlpha(0); // transparent + + const int mid_Y = tabTextRect.center().y(); + QLinearGradient gr; + + if (textRightToLeft) { + // arabic right-to-left text + gr.setStart(tabTextRect.x(), mid_Y); + gr.setFinalStop(tabTextRect.x() + 0.2 * tabTextRect.width(), mid_Y); + gr.setColorAt(0.0, c1); + gr.setColorAt(1.0, c0); + } + else { + // normal left-to-right text direction + gr.setStart(tabTextRect.x() + 0.8 * tabTextRect.width(), mid_Y); + gr.setFinalStop(tabTextRect.right(), mid_Y); + gr.setColorAt(0.0, c0); + gr.setColorAt(1.0, c1); + } + tabopt.palette.setBrush(QPalette::ButtonText, QBrush(gr)); + } + + int align = Qt::AlignVCenter; + if (need_fade_out) { + /* + We align LTR text in LTR app to the left, while RTL text in RTL app + to the right. Since Qt mirror alignment, we can simply do ALignLeft + for both cases. When text and app direction doesn't match, we flip + the text alignment. + */ + align |= (textRightToLeft == appRightToLeft) ? Qt::AlignLeft : Qt::AlignRight; + } + + style()->drawItemText(&p, tabTextRect, align, + tabopt.palette, true, tab_title, QPalette::ButtonText); + } +} + +void TabBar::tabRemoved(int index) +{ + QTabBar::tabRemoved(index); + emit tabRemovedSignal(index); +} + +void TabBar::tabInserted(int index) +{ + QTabBar::tabInserted(index); + emit tabInsertedSignal(index); +} + +void TabBar::resizeEvent(QResizeEvent *event) +{ + QTabBar::resizeEvent(event); + emit sizeChanged(); +} + +void TabBar::onTabMoved(int from, int to) +{ + // avoid infinitive recursion + static bool reverting = false; + if (reverting) + return; + + // on attempt to move the last tab (+ button) just move it back + // and the library should stick the first (zero) tab + int last = mp_stackedWidget->count(); + if ( + (from == last || to == last) || + (from == 0 || to == 0) + ) { + reverting = true; + moveTab(to, from); + reverting = false; + return; + } + + // swap widgets + QWidget *w_from = mp_stackedWidget->widget(from); + mp_stackedWidget->removeWidget(w_from); + mp_stackedWidget->insertWidget(to, w_from); + + KiwixApp::instance()->saveListOfOpenTabs(); +} diff --git a/src/tabbar.h b/src/tabbar.h index 605573474..05349cbd5 100644 --- a/src/tabbar.h +++ b/src/tabbar.h @@ -1,98 +1,98 @@ -#ifndef TABWIDGET_H -#define TABWIDGET_H - -#include -#include -#include -#include "webview.h" -#include "zimview.h" -#include "contentmanagerview.h" -#include "fullscreenwindow.h" -#include -#include - -class TabBar : public QTabBar -{ - Q_OBJECT -public: - enum class TabType { - LibraryTab, - ZimViewTab, - SettingsTab - }; - TabBar(QWidget* parent=nullptr); - void setStackedWidget(QStackedWidget* widget); - - void setContentManagerView(ContentManagerView* view); - void setNewTabButton(QAction* newTabAction); - ZimView* createNewTab(bool setCurrent, bool adjacentToCurrentTab); - - ZimView* currentZimView() { - return qobject_cast(mp_stackedWidget->currentWidget()); - } - - WebView* currentWebView() { - if (ZimView *zv = currentZimView()) - return zv->getWebView(); - return nullptr; - } - - void openUrl(const QUrl &url, bool newTab); -// Redirect call to sub-webView - void setTitleOf(const QString& title, ZimView* tab=nullptr); - void setIconOf(const QIcon& icon, ZimView* tab=nullptr); - QString currentZimId(); - - void triggerWebPageAction(QWebEnginePage::WebAction action, ZimView* widget=nullptr); - QString currentArticleUrl(); - QString currentArticleTitle(); - virtual QSize tabSizeHint(int index) const; - void openFindInPageBar(); - void closeTabsByZimId(const QString &id); - QStringList getTabUrls() const; - QStringList getTabZimIds() const; - - // The "+" (new tab) button is implemented as a tab (that is always placed at the end). - // This function returns the count of real tabs. - int realTabCount() const; - -protected: - void mousePressEvent(QMouseEvent *event); - void paintEvent(QPaintEvent *); - void tabRemoved(int index) override; - void tabInserted(int index) override; - void resizeEvent(QResizeEvent *) override; - -signals: - void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); - void tabDisplayed(TabType tabType); - void currentTitleChanged(const QString& title); - void tabRemovedSignal(int index); - void tabInsertedSignal(int index); - void sizeChanged(); - -public slots: - void closeTab(int index); - void openHomePage(); - void openOrSwitchToSettingsTab(); - void fullScreenRequested(QWebEngineFullScreenRequest request); - void on_webview_titleChanged(const QString& title); - void moveToNextTab(); - void moveToPreviousTab(); - void scrollNextTab(); - void scrollPreviousTab(); - -private: - void setCloseTabButton(int index); - -private: - QStackedWidget* mp_stackedWidget; - QScopedPointer m_fullScreenWindow; - -private slots: - void onTabMoved(int from, int to); - void onCurrentChanged(int index); - void onWebviewHistoryActionChanged(QWebEnginePage::WebAction action, bool enabled); -}; - -#endif // TABWIDGET_H +#ifndef TABWIDGET_H +#define TABWIDGET_H + +#include +#include +#include +#include "webview.h" +#include "zimview.h" +#include "contentmanagerview.h" +#include "fullscreenwindow.h" +#include +#include + +class TabBar : public QTabBar +{ + Q_OBJECT +public: + enum class TabType { + LibraryTab, + ZimViewTab, + SettingsTab + }; + TabBar(QWidget* parent=nullptr); + void setStackedWidget(QStackedWidget* widget); + + void setContentManagerView(ContentManagerView* view); + void setNewTabButton(QAction* newTabAction); + ZimView* createNewTab(bool setCurrent, bool adjacentToCurrentTab); + + ZimView* currentZimView() { + return qobject_cast(mp_stackedWidget->currentWidget()); + } + + WebView* currentWebView() { + if (ZimView *zv = currentZimView()) + return zv->getWebView(); + return nullptr; + } + + void openUrl(const QUrl &url, bool newTab); +// Redirect call to sub-webView + void setTitleOf(const QString& title, ZimView* tab=nullptr); + void setIconOf(const QIcon& icon, ZimView* tab=nullptr); + QString currentZimId(); + + void triggerWebPageAction(QWebEnginePage::WebAction action, ZimView* widget=nullptr); + QString currentArticleUrl(); + QString currentArticleTitle(); + virtual QSize tabSizeHint(int index) const; + void openFindInPageBar(); + void closeTabsByZimId(const QString &id); + QStringList getTabUrls() const; + QStringList getTabZimIds() const; + + // The "+" (new tab) button is implemented as a tab (that is always placed at the end). + // This function returns the count of real tabs. + int realTabCount() const; + +protected: + void mousePressEvent(QMouseEvent *event); + void paintEvent(QPaintEvent *); + void tabRemoved(int index) override; + void tabInserted(int index) override; + void resizeEvent(QResizeEvent *) override; + +signals: + void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); + void tabDisplayed(TabType tabType); + void currentTitleChanged(const QString& title); + void tabRemovedSignal(int index); + void tabInsertedSignal(int index); + void sizeChanged(); + +public slots: + void closeTab(int index); + void openHomePage(); + void openOrSwitchToSettingsTab(); + void fullScreenRequested(QWebEngineFullScreenRequest request); + void on_webview_titleChanged(const QString& title); + void moveToNextTab(); + void moveToPreviousTab(); + void scrollNextTab(); + void scrollPreviousTab(); + +private: + void setCloseTabButton(int index); + +private: + QStackedWidget* mp_stackedWidget; + QScopedPointer m_fullScreenWindow; + +private slots: + void onTabMoved(int from, int to); + void onCurrentChanged(int index); + void onWebviewHistoryActionChanged(QWebEnginePage::WebAction action, bool enabled); +}; + +#endif // TABWIDGET_H diff --git a/src/tableofcontentbar.cpp b/src/tableofcontentbar.cpp index 78531db34..2db658568 100644 --- a/src/tableofcontentbar.cpp +++ b/src/tableofcontentbar.cpp @@ -1,101 +1,167 @@ -#include "tableofcontentbar.h" -#include "ui_tableofcontentbar.h" -#include "kiwixapp.h" -#include -#include - -TableOfContentBar::TableOfContentBar(QWidget *parent) : - QFrame(parent), - ui(new Ui::tableofcontentbar) -{ - ui->setupUi(this); - ui->titleLabel->setFont(QFont("Selawik", 18, QFont::Weight::Medium)); - ui->titleLabel->setText(gt("table-of-content")); - ui->hideLabel->setFont(QFont("Selawik", 12)); - ui->hideLabel->setTextFormat(Qt::RichText); - - /* href is needed to make hide clickable, but not used. So Kiwix it is :) */ - ui->hideLabel->setText("" + gt("hide") + ""); - connect(ui->hideLabel, &QLabel::linkActivated, this, [=](){ - KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction)->setChecked(false); - }); - - ui->tree->setRootIsDecorated(false); - ui->tree->setItemsExpandable(false); - connect(ui->tree, &QTreeWidget::itemClicked, this, &TableOfContentBar::onTreeItemActivated); - connect(ui->tree, &QTreeWidget::itemActivated, this, &TableOfContentBar::onTreeItemActivated); -} - -TableOfContentBar::~TableOfContentBar() -{ - delete ui; -} - -void TableOfContentBar::onTreeItemActivated(QTreeWidgetItem *item) -{ - emit navigationRequested(m_url, item->data(0, Qt::UserRole).toString()); -} - -namespace -{ - -QTreeWidgetItem* createChildItem(QTreeWidgetItem* parent, const QString& childNo, const QJsonObject& headerObj) -{ - const auto item = new QTreeWidgetItem(parent); - item->setExpanded(true); - - const auto display = childNo + " " + headerObj["text"].toString(); - item->setToolTip(0, display); - item->setData(0, Qt::DisplayRole, display); - item->setData(0, Qt::FontRole, QFont("Selawik", 12)); - item->setData(0, Qt::UserRole, headerObj["anchor"].toString()); - - return item; -} - -QJsonArray takeDeeperEntries(QJsonArray& headerArr, int level) -{ - QJsonArray result; - while (!headerArr.isEmpty()) - { - const auto& nextHeader = headerArr.first().toObject(); - if (nextHeader["level"].toInt() <= level) - break; - - result.push_back(nextHeader); - headerArr.pop_front(); - } - return result; -} - -void createSubTree(QTreeWidgetItem* parent, QString parentNo, QJsonArray& headerArr) -{ - while (!headerArr.isEmpty()) - { - const auto childHeader = headerArr.takeAt(0).toObject(); - const int childLevel = childHeader["level"].toInt(); - const QString childNo = parentNo + QString::number(parent->childCount() + 1); - QTreeWidgetItem* childItem = createChildItem(parent, childNo, childHeader); - QJsonArray deeperEntries = takeDeeperEntries(headerArr, childLevel); - createSubTree(childItem, childNo + ".", deeperEntries); - } -} - -} - -void TableOfContentBar::setupTree(const QJsonObject& headers) -{ - const auto webView = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!webView) - return; - - const auto headerUrl = headers["url"].toString(); - const auto currentUrl = webView->url().url(QUrl::RemoveFragment); - if (headerUrl != currentUrl) - return; - - m_url = headerUrl; - ui->tree->clear(); - QJsonArray headerArr = headers["headers"].toArray(); - createSubTree(ui->tree->invisibleRootItem(), "", headerArr); -} +#include "tableofcontentbar.h" +#include "ui_tableofcontentbar.h" +#include "kiwixapp.h" +#include +#include +#include + +TableOfContentBar::TableOfContentBar(QWidget *parent) : + QFrame(parent), + ui(new Ui::tableofcontentbar) +{ + ui->setupUi(this); + ui->titleLabel->setFont(QFont("Selawik", 18, QFont::Weight::Medium)); + ui->titleLabel->setText(gt("table-of-content")); + ui->hideLabel->setFont(QFont("Selawik", 12)); + ui->hideLabel->setTextFormat(Qt::RichText); + + /* href is needed to make hide clickable, but not used. So Kiwix it is :) */ + ui->hideLabel->setText("" + gt("hide") + ""); + connect(ui->hideLabel, &QLabel::linkActivated, this, [=](){ + KiwixApp::instance()->getAction(KiwixApp::ToggleTOCAction)->setChecked(false); + }); + + ui->tree->setRootIsDecorated(false); + ui->tree->setItemsExpandable(false); + connect(ui->tree, &QTreeWidget::itemClicked, this, &TableOfContentBar::onTreeItemActivated); + connect(ui->tree, &QTreeWidget::itemActivated, this, &TableOfContentBar::onTreeItemActivated); + + // Setup debounce timer + m_clickDebounceTimer.setSingleShot(true); + m_clickDebounceTimer.setInterval(300); // 300ms debounce +} + +TableOfContentBar::~TableOfContentBar() +{ + delete ui; +} + +void TableOfContentBar::onTreeItemActivated(QTreeWidgetItem *item) +{ + //Safety check + if (!item) { + return; + } + + // Get the anchor from the item + QVariant anchorVariant = item->data(0, Qt::UserRole); + if (!anchorVariant.isValid()) { + return; + } + + QString anchor = anchorVariant.toString(); + if (anchor.isEmpty() || m_url.isEmpty()) { + return; + } + + if (m_isNavigating || (anchor == m_lastAnchor && m_clickDebounceTimer.isActive())) { + return; + } + + m_isNavigating = true; + m_lastAnchor = anchor; + m_clickDebounceTimer.start(); + + QTimer::singleShot(10, this, [this, anchor]() { + emit navigationRequested(m_url, anchor); + + QTimer::singleShot(300, this, [this]() { + m_isNavigating = false; + }); + }); +} + +namespace +{ + +QTreeWidgetItem* createChildItem(QTreeWidgetItem* parent, const QString& childNo, const QJsonObject& headerObj) +{ + const auto item = new QTreeWidgetItem(parent); + item->setExpanded(true); + + const auto display = childNo + " " + headerObj["text"].toString(); + item->setToolTip(0, display); + item->setData(0, Qt::DisplayRole, display); + item->setData(0, Qt::FontRole, QFont("Selawik", 12)); + item->setData(0, Qt::UserRole, headerObj["anchor"].toString()); + + return item; +} + +QJsonArray takeDeeperEntries(QJsonArray& headerArr, int level) +{ + QJsonArray result; + while (!headerArr.isEmpty()) + { + const auto& nextHeader = headerArr.first().toObject(); + if (nextHeader["level"].toInt() <= level) + break; + + result.push_back(nextHeader); + headerArr.pop_front(); + } + return result; +} + +void createSubTree(QTreeWidgetItem* parent, QString parentNo, QJsonArray& headerArr) +{ + while (!headerArr.isEmpty()) + { + const auto childHeader = headerArr.takeAt(0).toObject(); + const int childLevel = childHeader["level"].toInt(); + const QString childNo = parentNo + QString::number(parent->childCount() + 1); + QTreeWidgetItem* childItem = createChildItem(parent, childNo, childHeader); + QJsonArray deeperEntries = takeDeeperEntries(headerArr, childLevel); + createSubTree(childItem, childNo + ".", deeperEntries); + } +} + +} + +void TableOfContentBar::setupTree(const QJsonObject& headers) +{ + const auto webView = KiwixApp::instance()->getTabWidget()->currentWebView(); + if (!webView) + return; + + const auto headerUrl = headers["url"].toString(); + const auto currentUrl = webView->url().url(QUrl::RemoveFragment); + if (headerUrl != currentUrl) + return; + + m_url = headerUrl; + ui->tree->clear(); + QJsonArray headerArr = headers["headers"].toArray(); + createSubTree(ui->tree->invisibleRootItem(), "", headerArr); + + // Update selection based on current URL fragment + updateSelectionFromFragment(webView->url().fragment()); +} + +void TableOfContentBar::updateSelectionFromFragment(const QString& fragment) +{ + if (fragment.isEmpty() || !ui || !ui->tree) { + return; + } + + // Find the item with the matching anchor + QTreeWidgetItemIterator it(ui->tree); + while (*it) { + QVariant anchorVariant = (*it)->data(0, Qt::UserRole); + if (!anchorVariant.isValid()) { + ++it; + continue; + } + + QString anchor = anchorVariant.toString(); + if (anchor == fragment) { + // Select the item without triggering navigation + ui->tree->blockSignals(true); + ui->tree->setCurrentItem(*it); + ui->tree->scrollToItem(*it); + ui->tree->blockSignals(false); + break; + } + ++it; + } +} diff --git a/src/tableofcontentbar.h b/src/tableofcontentbar.h index c2a2356a5..7923c4b63 100644 --- a/src/tableofcontentbar.h +++ b/src/tableofcontentbar.h @@ -1,32 +1,37 @@ -#ifndef TABLEOFCONTENTBAR_H -#define TABLEOFCONTENTBAR_H - -#include - -namespace Ui { -class tableofcontentbar; -} - -class QTreeWidgetItem; - -class TableOfContentBar : public QFrame -{ - Q_OBJECT - -public: - explicit TableOfContentBar(QWidget *parent = nullptr); - ~TableOfContentBar(); - -public slots: - void setupTree(const QJsonObject& headers); - void onTreeItemActivated(QTreeWidgetItem* item); - -signals: - void navigationRequested(const QString& url, const QString& anchor); - -private: - Ui::tableofcontentbar *ui; - QString m_url; -}; - -#endif // TABLEOFCONTENTBAR_H +#ifndef TABLEOFCONTENTBAR_H +#define TABLEOFCONTENTBAR_H + +#include +#include + +namespace Ui { +class tableofcontentbar; +} + +class QTreeWidgetItem; + +class TableOfContentBar : public QFrame +{ + Q_OBJECT + +public: + explicit TableOfContentBar(QWidget *parent = nullptr); + ~TableOfContentBar(); + +public slots: + void setupTree(const QJsonObject& headers); + void onTreeItemActivated(QTreeWidgetItem* item); + void updateSelectionFromFragment(const QString& fragment); + +signals: + void navigationRequested(const QString& url, const QString& anchor); + +private: + Ui::tableofcontentbar *ui; + QString m_url; + QTimer m_clickDebounceTimer; + bool m_isNavigating = false; + QString m_lastAnchor; +}; + +#endif // TABLEOFCONTENTBAR_H diff --git a/src/tableofcontentbar.ui b/src/tableofcontentbar.ui index 8ebf8750e..b310172e9 100644 --- a/src/tableofcontentbar.ui +++ b/src/tableofcontentbar.ui @@ -1,130 +1,130 @@ - - - tableofcontentbar - - - - 0 - 0 - 400 - 300 - - - - - 0 - 0 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 16 - - - - - - - 0 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - 0 - - - - - - - - - Qt::Horizontal - - - - - - - QFrame::NoFrame - - - 0 - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - Qt::ElideRight - - - 30 - - - true - - - true - - - true - - - false - - - - 1 - - - - - - - - - + + + tableofcontentbar + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 16 + + + + + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + + + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + 0 + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + Qt::ElideRight + + + 30 + + + true + + + true + + + true + + + false + + + + 1 + + + + + + + + + diff --git a/src/texttospeechbar.cpp b/src/texttospeechbar.cpp index fe60bd232..46d761090 100644 --- a/src/texttospeechbar.cpp +++ b/src/texttospeechbar.cpp @@ -1,284 +1,284 @@ -#include "texttospeechbar.h" -#include "kiwixapp.h" -#include "ui_texttospeechbar.h" - -TextToSpeechBar::TextToSpeechBar(QWidget *parent) - : QFrame(parent), ui(new Ui::TextToSpeechBar) -{ - ui->setupUi(this); - - connect(&m_speech, &QTextToSpeech::stateChanged, this, - &TextToSpeechBar::onStateChanged); - - ui->stopButton->setText(gt("stop")); - ui->stopButton->setDisabled(true); - - const auto app = KiwixApp::instance(); - connect(app->getAction(KiwixApp::ReadStopAction), &QAction::triggered, - this, &TextToSpeechBar::stop); - connect(ui->stopButton, &QPushButton::pressed, this, - &TextToSpeechBar::stop); - connect(ui->closeButton, &QPushButton::pressed, - this, &TextToSpeechBar::speechClose); - - setupSpeedOptionsComboBox(); - setupVoiceComboBox(); - setupLanguageComboBox(); - languageSelected(ui->langComboBox->currentIndex()); - connect(app->getAction(KiwixApp::ToggleTTSLanguageAction), &QAction::triggered, - this, &TextToSpeechBar::toggleLanguage); - connect(app->getAction(KiwixApp::ToggleTTSVoiceAction), &QAction::triggered, - this, &TextToSpeechBar::toggleVoice); - connect(app->getAction(KiwixApp::IncreaseTTSSpeedAction), &QAction::triggered, - this, &TextToSpeechBar::increaseSpeed); - connect(app->getAction(KiwixApp::DecreaseTTSSpeedAction), &QAction::triggered, - this, &TextToSpeechBar::decreaseSpeed); - connect(ui->speedComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, &TextToSpeechBar::onSpeedChanged); -} - -void TextToSpeechBar::increaseSpeed() -{ - int currentIndex = ui->speedComboBox->currentIndex(); - if (currentIndex < ui->speedComboBox->count() - 1) { - ui->speedComboBox->setCurrentIndex(currentIndex + 1); - } -} - -void TextToSpeechBar::decreaseSpeed() -{ - int currentIndex = ui->speedComboBox->currentIndex(); - if (currentIndex > 0) { - ui->speedComboBox->setCurrentIndex(currentIndex - 1); - } -} - -void TextToSpeechBar::speak(const QString &text) -{ - m_text = text; - m_speech.say(m_text); -} - -void TextToSpeechBar::stop() -{ - m_speech.stop(); -} - -void TextToSpeechBar::setLocale(const QLocale& locale) -{ - for (int i = 0; i < ui->langComboBox->count(); i++) - { - if (ui->langComboBox->itemData(i).toLocale().language() == locale.language()) - { - ui->langComboBox->setCurrentIndex(i); - languageSelected(i); - return; - } - } -} - -void TextToSpeechBar::setupSpeedOptionsComboBox() -{ - ui->speedLabel->setText(gt("speed")); - ui->speedComboBox->setMaxVisibleItems(10); - ui->speedComboBox->setLineEdit(new ComboBoxLineEdit(ui->speedComboBox)); - - QStringList speedOptions = {"0.25","0.50","0.75","1.00","1.25","1.50","1.75","2.00"}; - ui->speedComboBox->addItems(speedOptions); -} - -void TextToSpeechBar::setupLanguageComboBox() -{ - ui->langLabel->setText(gt("language")); - ui->langComboBox->setMaxVisibleItems(10); - ui->langComboBox->setLineEdit(new ComboBoxLineEdit(ui->langComboBox)); - - QLocale current = QLocale::system(); - for (const auto& locale : m_speech.availableLocales()) - { - const QString name(QString("%1 (%2)") - .arg(QLocale::languageToString(locale.language())) - .arg(locale.nativeLanguageName())); - - ui->langComboBox->addItem(name, locale); - if (locale.name() == current.name()) - current = locale; - } - - ui->langComboBox->setCurrentIndex(ui->langComboBox->findData(current)); - connect(ui->langComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, &TextToSpeechBar::languageSelected); -} - -void TextToSpeechBar::setupVoiceComboBox() -{ - ui->voiceLabel->setText(gt("voice")); - ui->voiceComboBox->setMaxVisibleItems(10); - ui->voiceComboBox->setLineEdit(new ComboBoxLineEdit(ui->voiceComboBox)); -} - -void TextToSpeechBar::resetVoiceComboBox() -{ - disconnect(ui->voiceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &TextToSpeechBar::voiceSelected); - ui->voiceComboBox->clear(); - - m_voices = m_speech.availableVoices(); - if ( m_voices.isEmpty() ) - return; - - for (const auto& voice : m_voices) - { - ui->voiceComboBox->addItem(QString("%1 - %2 - %3").arg(voice.name()) - .arg(QVoice::genderName(voice.gender())) - .arg(QVoice::ageName(voice.age()))); - } - - const int voiceIndex = getVoiceIndex(); - ui->voiceComboBox->setCurrentIndex(voiceIndex); - voiceSelected(voiceIndex); - - connect(ui->voiceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &TextToSpeechBar::voiceSelected); -} - -void TextToSpeechBar::resetSpeedComboBox() -{ - double savedSpeed = KiwixApp::instance()->getSavedTtsSpeed(m_speech.locale().name()); - int index = (savedSpeed - 0.25) * 4; - ui->speedComboBox->setCurrentIndex(index); -} - -int TextToSpeechBar::getVoiceIndex() -{ - int voiceIndex = 0; - const QVoice currentVoice = m_speech.voice(); - const QString savedVoiceName = KiwixApp::instance()->getSavedVoiceName(m_speech.locale().name()); - - /* We either stay with default voices or matches with the saved voice. */ - for (int i = 0; i < ui->voiceComboBox->count(); i++) - { - if (m_voices[i].name() == currentVoice.name()) - voiceIndex = i; - if (m_voices[i].name() == savedVoiceName) - { - voiceIndex = i; - break; - } - } - return voiceIndex; -} - -void TextToSpeechBar::speechClose() -{ - /* Prevent webview from scrolling to up to the top after losing focus. */ - const auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!current) - return; - - current->setFocus(); - m_speech.stop(); - close(); -} - -void TextToSpeechBar::speechShow() -{ - show(); - setFocus(); -} - -void TextToSpeechBar::toggleVoice() -{ - const auto zimView = KiwixApp::instance()->getTabWidget()->currentZimView(); - if (!zimView || zimView->getTextToSpeechBar() != this) - return; - - if (isHidden()) - speechShow(); - - ui->voiceComboBox->showPopup(); -} - -void TextToSpeechBar::toggleLanguage() -{ - const auto zimView = KiwixApp::instance()->getTabWidget()->currentZimView(); - if (!zimView || zimView->getTextToSpeechBar() != this) - return; - - if (isHidden()) - speechShow(); - - ui->langComboBox->showPopup(); -} - -void TextToSpeechBar::languageSelected(int index) -{ - const QLocale locale = ui->langComboBox->itemData(index).toLocale(); - m_speech.setLocale(locale); - resetVoiceComboBox(); - resetSpeedComboBox(); -} - -void TextToSpeechBar::voiceSelected(int index) -{ - const auto voice = m_voices.at(index); - const auto currentLang = ui->langComboBox->currentData().toLocale().name(); - KiwixApp::instance()->saveVoiceName(currentLang, voice.name()); - - m_speech.setVoice(voice); - if (m_speech.state() == QTextToSpeech::Speaking) - speak(m_text); -} - -void TextToSpeechBar::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Escape) - { - speechClose(); - return; - } - - QFrame::keyPressEvent(event); -} - -void TextToSpeechBar::onStateChanged(QTextToSpeech::State state) -{ - ui->stopButton->setEnabled(state != QTextToSpeech::Ready); -} - -void TextToSpeechBar::onSpeedChanged(int index) -{ - QString speedText = ui->speedComboBox->itemText(index); - double speed = speedText.toDouble(); - m_speech.setRate(speed - 1); // range:-1,1 - - // Save tts speed for current lang - const auto currentLang = ui->langComboBox->currentData().toLocale().name(); - KiwixApp::instance()->saveTtsSpeed(currentLang, speed); - - // Restarting the speech with new speed set above - if (m_speech.state() == QTextToSpeech::Speaking) - { - m_speech.stop(); - m_speech.say(m_text); - } -} - -ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) : QLineEdit(parent) -{ - setFrame(false); - - /* Work around to both have max visible item and a read-only combobox.*/ - setReadOnly(true); - connect(this, &QLineEdit::selectionChanged, this, &ComboBoxLineEdit::preventSelection); -} - -void ComboBoxLineEdit::preventSelection() -{ - setSelection(0, 0); -} - -void ComboBoxLineEdit::mouseReleaseEvent(QMouseEvent *) -{ - const auto combo = qobject_cast(parent()); - if(combo) - combo->showPopup(); -} +#include "texttospeechbar.h" +#include "kiwixapp.h" +#include "ui_texttospeechbar.h" + +TextToSpeechBar::TextToSpeechBar(QWidget *parent) + : QFrame(parent), ui(new Ui::TextToSpeechBar) +{ + ui->setupUi(this); + + connect(&m_speech, &QTextToSpeech::stateChanged, this, + &TextToSpeechBar::onStateChanged); + + ui->stopButton->setText(gt("stop")); + ui->stopButton->setDisabled(true); + + const auto app = KiwixApp::instance(); + connect(app->getAction(KiwixApp::ReadStopAction), &QAction::triggered, + this, &TextToSpeechBar::stop); + connect(ui->stopButton, &QPushButton::pressed, this, + &TextToSpeechBar::stop); + connect(ui->closeButton, &QPushButton::pressed, + this, &TextToSpeechBar::speechClose); + + setupSpeedOptionsComboBox(); + setupVoiceComboBox(); + setupLanguageComboBox(); + languageSelected(ui->langComboBox->currentIndex()); + connect(app->getAction(KiwixApp::ToggleTTSLanguageAction), &QAction::triggered, + this, &TextToSpeechBar::toggleLanguage); + connect(app->getAction(KiwixApp::ToggleTTSVoiceAction), &QAction::triggered, + this, &TextToSpeechBar::toggleVoice); + connect(app->getAction(KiwixApp::IncreaseTTSSpeedAction), &QAction::triggered, + this, &TextToSpeechBar::increaseSpeed); + connect(app->getAction(KiwixApp::DecreaseTTSSpeedAction), &QAction::triggered, + this, &TextToSpeechBar::decreaseSpeed); + connect(ui->speedComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &TextToSpeechBar::onSpeedChanged); +} + +void TextToSpeechBar::increaseSpeed() +{ + int currentIndex = ui->speedComboBox->currentIndex(); + if (currentIndex < ui->speedComboBox->count() - 1) { + ui->speedComboBox->setCurrentIndex(currentIndex + 1); + } +} + +void TextToSpeechBar::decreaseSpeed() +{ + int currentIndex = ui->speedComboBox->currentIndex(); + if (currentIndex > 0) { + ui->speedComboBox->setCurrentIndex(currentIndex - 1); + } +} + +void TextToSpeechBar::speak(const QString &text) +{ + m_text = text; + m_speech.say(m_text); +} + +void TextToSpeechBar::stop() +{ + m_speech.stop(); +} + +void TextToSpeechBar::setLocale(const QLocale& locale) +{ + for (int i = 0; i < ui->langComboBox->count(); i++) + { + if (ui->langComboBox->itemData(i).toLocale().language() == locale.language()) + { + ui->langComboBox->setCurrentIndex(i); + languageSelected(i); + return; + } + } +} + +void TextToSpeechBar::setupSpeedOptionsComboBox() +{ + ui->speedLabel->setText(gt("speed")); + ui->speedComboBox->setMaxVisibleItems(10); + ui->speedComboBox->setLineEdit(new ComboBoxLineEdit(ui->speedComboBox)); + + QStringList speedOptions = {"0.25","0.50","0.75","1.00","1.25","1.50","1.75","2.00"}; + ui->speedComboBox->addItems(speedOptions); +} + +void TextToSpeechBar::setupLanguageComboBox() +{ + ui->langLabel->setText(gt("language")); + ui->langComboBox->setMaxVisibleItems(10); + ui->langComboBox->setLineEdit(new ComboBoxLineEdit(ui->langComboBox)); + + QLocale current = QLocale::system(); + for (const auto& locale : m_speech.availableLocales()) + { + const QString name(QString("%1 (%2)") + .arg(QLocale::languageToString(locale.language())) + .arg(locale.nativeLanguageName())); + + ui->langComboBox->addItem(name, locale); + if (locale.name() == current.name()) + current = locale; + } + + ui->langComboBox->setCurrentIndex(ui->langComboBox->findData(current)); + connect(ui->langComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, &TextToSpeechBar::languageSelected); +} + +void TextToSpeechBar::setupVoiceComboBox() +{ + ui->voiceLabel->setText(gt("voice")); + ui->voiceComboBox->setMaxVisibleItems(10); + ui->voiceComboBox->setLineEdit(new ComboBoxLineEdit(ui->voiceComboBox)); +} + +void TextToSpeechBar::resetVoiceComboBox() +{ + disconnect(ui->voiceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &TextToSpeechBar::voiceSelected); + ui->voiceComboBox->clear(); + + m_voices = m_speech.availableVoices(); + if ( m_voices.isEmpty() ) + return; + + for (const auto& voice : m_voices) + { + ui->voiceComboBox->addItem(QString("%1 - %2 - %3").arg(voice.name()) + .arg(QVoice::genderName(voice.gender())) + .arg(QVoice::ageName(voice.age()))); + } + + const int voiceIndex = getVoiceIndex(); + ui->voiceComboBox->setCurrentIndex(voiceIndex); + voiceSelected(voiceIndex); + + connect(ui->voiceComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &TextToSpeechBar::voiceSelected); +} + +void TextToSpeechBar::resetSpeedComboBox() +{ + double savedSpeed = KiwixApp::instance()->getSavedTtsSpeed(m_speech.locale().name()); + int index = (savedSpeed - 0.25) * 4; + ui->speedComboBox->setCurrentIndex(index); +} + +int TextToSpeechBar::getVoiceIndex() +{ + int voiceIndex = 0; + const QVoice currentVoice = m_speech.voice(); + const QString savedVoiceName = KiwixApp::instance()->getSavedVoiceName(m_speech.locale().name()); + + /* We either stay with default voices or matches with the saved voice. */ + for (int i = 0; i < ui->voiceComboBox->count(); i++) + { + if (m_voices[i].name() == currentVoice.name()) + voiceIndex = i; + if (m_voices[i].name() == savedVoiceName) + { + voiceIndex = i; + break; + } + } + return voiceIndex; +} + +void TextToSpeechBar::speechClose() +{ + /* Prevent webview from scrolling to up to the top after losing focus. */ + const auto current = KiwixApp::instance()->getTabWidget()->currentWebView(); + if (!current) + return; + + current->setFocus(); + m_speech.stop(); + close(); +} + +void TextToSpeechBar::speechShow() +{ + show(); + setFocus(); +} + +void TextToSpeechBar::toggleVoice() +{ + const auto zimView = KiwixApp::instance()->getTabWidget()->currentZimView(); + if (!zimView || zimView->getTextToSpeechBar() != this) + return; + + if (isHidden()) + speechShow(); + + ui->voiceComboBox->showPopup(); +} + +void TextToSpeechBar::toggleLanguage() +{ + const auto zimView = KiwixApp::instance()->getTabWidget()->currentZimView(); + if (!zimView || zimView->getTextToSpeechBar() != this) + return; + + if (isHidden()) + speechShow(); + + ui->langComboBox->showPopup(); +} + +void TextToSpeechBar::languageSelected(int index) +{ + const QLocale locale = ui->langComboBox->itemData(index).toLocale(); + m_speech.setLocale(locale); + resetVoiceComboBox(); + resetSpeedComboBox(); +} + +void TextToSpeechBar::voiceSelected(int index) +{ + const auto voice = m_voices.at(index); + const auto currentLang = ui->langComboBox->currentData().toLocale().name(); + KiwixApp::instance()->saveVoiceName(currentLang, voice.name()); + + m_speech.setVoice(voice); + if (m_speech.state() == QTextToSpeech::Speaking) + speak(m_text); +} + +void TextToSpeechBar::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Escape) + { + speechClose(); + return; + } + + QFrame::keyPressEvent(event); +} + +void TextToSpeechBar::onStateChanged(QTextToSpeech::State state) +{ + ui->stopButton->setEnabled(state != QTextToSpeech::Ready); +} + +void TextToSpeechBar::onSpeedChanged(int index) +{ + QString speedText = ui->speedComboBox->itemText(index); + double speed = speedText.toDouble(); + m_speech.setRate(speed - 1); // range:-1,1 + + // Save tts speed for current lang + const auto currentLang = ui->langComboBox->currentData().toLocale().name(); + KiwixApp::instance()->saveTtsSpeed(currentLang, speed); + + // Restarting the speech with new speed set above + if (m_speech.state() == QTextToSpeech::Speaking) + { + m_speech.stop(); + m_speech.say(m_text); + } +} + +ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) : QLineEdit(parent) +{ + setFrame(false); + + /* Work around to both have max visible item and a read-only combobox.*/ + setReadOnly(true); + connect(this, &QLineEdit::selectionChanged, this, &ComboBoxLineEdit::preventSelection); +} + +void ComboBoxLineEdit::preventSelection() +{ + setSelection(0, 0); +} + +void ComboBoxLineEdit::mouseReleaseEvent(QMouseEvent *) +{ + const auto combo = qobject_cast(parent()); + if(combo) + combo->showPopup(); +} diff --git a/src/texttospeechbar.h b/src/texttospeechbar.h index 225d15e4b..2ea6ec2c3 100644 --- a/src/texttospeechbar.h +++ b/src/texttospeechbar.h @@ -1,65 +1,65 @@ -#ifndef TEXTTOSPEECHMANAGER_H -#define TEXTTOSPEECHMANAGER_H - -#include -#include -#include - -namespace Ui { -class TextToSpeechBar; -} - -class ComboBoxLineEdit : public QLineEdit -{ - Q_OBJECT -public: - explicit ComboBoxLineEdit(QWidget *parent = 0); - -public slots: - void preventSelection(); - -protected: - void mouseReleaseEvent(QMouseEvent *); -}; - -class TextToSpeechBar : public QFrame -{ - Q_OBJECT -public: - explicit TextToSpeechBar(QWidget *parent = nullptr); - - void speak(const QString& text); - void stop(); - void setLocale(const QLocale& locale); - - void setupLanguageComboBox(); - void setupVoiceComboBox(); - void resetVoiceComboBox(); - void setupSpeedOptionsComboBox(); - void resetSpeedComboBox(); - - int getVoiceIndex(); - -public slots: - void onStateChanged(QTextToSpeech::State state); - void speechClose(); - void speechShow(); - void toggleVoice(); - void toggleLanguage(); - void languageSelected(int index); - void voiceSelected(int index); - void onSpeedChanged(int index); - void increaseSpeed(); - void decreaseSpeed(); - -protected: - void keyPressEvent(QKeyEvent *event); - -private: - QTextToSpeech m_speech; - Ui::TextToSpeechBar *ui; - QVector m_voices; - QString m_text; -}; - -#endif // TEXTTOSPEECHMANAGER_H +#ifndef TEXTTOSPEECHMANAGER_H +#define TEXTTOSPEECHMANAGER_H + +#include +#include +#include + +namespace Ui { +class TextToSpeechBar; +} + +class ComboBoxLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit ComboBoxLineEdit(QWidget *parent = 0); + +public slots: + void preventSelection(); + +protected: + void mouseReleaseEvent(QMouseEvent *); +}; + +class TextToSpeechBar : public QFrame +{ + Q_OBJECT +public: + explicit TextToSpeechBar(QWidget *parent = nullptr); + + void speak(const QString& text); + void stop(); + void setLocale(const QLocale& locale); + + void setupLanguageComboBox(); + void setupVoiceComboBox(); + void resetVoiceComboBox(); + void setupSpeedOptionsComboBox(); + void resetSpeedComboBox(); + + int getVoiceIndex(); + +public slots: + void onStateChanged(QTextToSpeech::State state); + void speechClose(); + void speechShow(); + void toggleVoice(); + void toggleLanguage(); + void languageSelected(int index); + void voiceSelected(int index); + void onSpeedChanged(int index); + void increaseSpeed(); + void decreaseSpeed(); + +protected: + void keyPressEvent(QKeyEvent *event); + +private: + QTextToSpeech m_speech; + Ui::TextToSpeechBar *ui; + QVector m_voices; + QString m_text; +}; + +#endif // TEXTTOSPEECHMANAGER_H diff --git a/src/texttospeechbar.ui b/src/texttospeechbar.ui index fbaf7a230..dccfb97f0 100644 --- a/src/texttospeechbar.ui +++ b/src/texttospeechbar.ui @@ -1,143 +1,143 @@ - - - TextToSpeechBar - - - - 0 - 0 - 833 - 43 - - - - - 0 - 0 - - - - Frame - - - - - - - - - - - - - Qt::LeftToRight - - - true - - - - - - - - - - - - - - true - - - - - - - Speed - - - - - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - true - - - - 0 - 0 - - - - - - - - - - - :/icons/stop-circle.svg:/icons/stop-circle.svg - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - :/icons/close.svg:/icons/close.svg - - - true - - - - - - - - - - + + + TextToSpeechBar + + + + 0 + 0 + 833 + 43 + + + + + 0 + 0 + + + + Frame + + + + + + + + + + + + + Qt::LeftToRight + + + true + + + + + + + + + + + + + + true + + + + + + + Speed + + + + + + + false + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + true + + + + 0 + 0 + + + + + + + + + + + :/icons/stop-circle.svg:/icons/stop-circle.svg + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + :/icons/close.svg:/icons/close.svg + + + true + + + + + + + + + + diff --git a/src/thumbnaildownloader.cpp b/src/thumbnaildownloader.cpp index c7ac39eba..198447879 100644 --- a/src/thumbnaildownloader.cpp +++ b/src/thumbnaildownloader.cpp @@ -1,48 +1,48 @@ -#include "thumbnaildownloader.h" -#include -#include -#include -#include - -ThumbnailDownloader::ThumbnailDownloader() -{ - connect(this, &ThumbnailDownloader::oneThumbnailDownloaded, - this, &ThumbnailDownloader::startNextDownload); -} - -ThumbnailDownloader::~ThumbnailDownloader() -{ -} - -void ThumbnailDownloader::addDownload(QString url, ThumbnailId index) -{ - m_downloadQueue.append({index, url}); - if (!m_isDownloading) - startNextDownload(); -} - -void ThumbnailDownloader::startNextDownload() -{ - if (m_downloadQueue.size() == 0) { - m_isDownloading = false; - return; - } - m_isDownloading = true; - downloadThumbnail(m_downloadQueue.takeFirst()); -} - -void ThumbnailDownloader::downloadThumbnail(ThumbnailInfo thumbnailInfo) -{ - QNetworkRequest req(thumbnailInfo.second); - auto reply = manager.get(req); - connect(reply, &QNetworkReply::finished, this, [=](){ - fileDownloaded(reply, thumbnailInfo); - }); -} - -void ThumbnailDownloader::fileDownloaded(QNetworkReply *pReply, ThumbnailInfo thumbnailInfo) -{ - auto downloadedData = pReply->readAll(); - emit oneThumbnailDownloaded(thumbnailInfo.first, thumbnailInfo.second, downloadedData); - pReply->deleteLater(); -} +#include "thumbnaildownloader.h" +#include +#include +#include +#include + +ThumbnailDownloader::ThumbnailDownloader() +{ + connect(this, &ThumbnailDownloader::oneThumbnailDownloaded, + this, &ThumbnailDownloader::startNextDownload); +} + +ThumbnailDownloader::~ThumbnailDownloader() +{ +} + +void ThumbnailDownloader::addDownload(QString url, ThumbnailId index) +{ + m_downloadQueue.append({index, url}); + if (!m_isDownloading) + startNextDownload(); +} + +void ThumbnailDownloader::startNextDownload() +{ + if (m_downloadQueue.size() == 0) { + m_isDownloading = false; + return; + } + m_isDownloading = true; + downloadThumbnail(m_downloadQueue.takeFirst()); +} + +void ThumbnailDownloader::downloadThumbnail(ThumbnailInfo thumbnailInfo) +{ + QNetworkRequest req(thumbnailInfo.second); + auto reply = manager.get(req); + connect(reply, &QNetworkReply::finished, this, [=](){ + fileDownloaded(reply, thumbnailInfo); + }); +} + +void ThumbnailDownloader::fileDownloaded(QNetworkReply *pReply, ThumbnailInfo thumbnailInfo) +{ + auto downloadedData = pReply->readAll(); + emit oneThumbnailDownloaded(thumbnailInfo.first, thumbnailInfo.second, downloadedData); + pReply->deleteLater(); +} diff --git a/src/thumbnaildownloader.h b/src/thumbnaildownloader.h index 5bb946385..72c14ec49 100644 --- a/src/thumbnaildownloader.h +++ b/src/thumbnaildownloader.h @@ -1,41 +1,41 @@ -#ifndef THUMBNAILDOWNLOADER_H -#define THUMBNAILDOWNLOADER_H - -#include -#include -#include -#include - -class ThumbnailDownloader : public QObject -{ - Q_OBJECT - -public: - typedef QString ThumbnailId; - typedef QPair ThumbnailInfo; - -public: - ThumbnailDownloader(); - ~ThumbnailDownloader(); - - void addDownload(QString url, ThumbnailId index); - void startNextDownload(); - void clearQueue() { m_downloadQueue.clear(); } - -private: - void downloadThumbnail(ThumbnailInfo thumbnailInfo); - -signals: - void oneThumbnailDownloaded(ThumbnailId, QString, QByteArray); - -private: - QQueue m_downloadQueue; - QNetworkAccessManager manager; - bool m_isDownloading = false; - -private slots: - void fileDownloaded(QNetworkReply *pReply, ThumbnailInfo thumbnailInfo); - -}; - -#endif // THUMBNAILDOWNLOADER_H +#ifndef THUMBNAILDOWNLOADER_H +#define THUMBNAILDOWNLOADER_H + +#include +#include +#include +#include + +class ThumbnailDownloader : public QObject +{ + Q_OBJECT + +public: + typedef QString ThumbnailId; + typedef QPair ThumbnailInfo; + +public: + ThumbnailDownloader(); + ~ThumbnailDownloader(); + + void addDownload(QString url, ThumbnailId index); + void startNextDownload(); + void clearQueue() { m_downloadQueue.clear(); } + +private: + void downloadThumbnail(ThumbnailInfo thumbnailInfo); + +signals: + void oneThumbnailDownloaded(ThumbnailId, QString, QByteArray); + +private: + QQueue m_downloadQueue; + QNetworkAccessManager manager; + bool m_isDownloading = false; + +private slots: + void fileDownloaded(QNetworkReply *pReply, ThumbnailInfo thumbnailInfo); + +}; + +#endif // THUMBNAILDOWNLOADER_H diff --git a/src/topwidget.cpp b/src/topwidget.cpp index 9843b0bf9..8af7d054c 100644 --- a/src/topwidget.cpp +++ b/src/topwidget.cpp @@ -1,148 +1,148 @@ -#include "topwidget.h" - -#include "kconstants.h" -#include "kiwixapp.h" -#include "mainmenu.h" -#include "tabbar.h" -#include "portutils.h" - -#include -#include -#include - -TopWidget::TopWidget(QWidget *parent) : - QToolBar(parent) -{ - auto app = KiwixApp::instance(); - - QAction *back = app->getAction(KiwixApp::HistoryBackAction); - connect(back, &QAction::triggered, [app](){ - app->getTabWidget()->triggerWebPageAction(QWebEnginePage::Back); - }); - addAction(back); - - QAction *forward = app->getAction(KiwixApp::HistoryForwardAction); - connect(forward, &QAction::triggered, [app](){ - app->getTabWidget()->triggerWebPageAction(QWebEnginePage::Forward); - }); - addAction(forward); - - QAction *random = app->getAction(KiwixApp::RandomArticleAction); - addAction(random); - - QAction *toc = app->getAction(KiwixApp::ToggleTOCAction); - addAction(toc); - - // For CSS - if (QGuiApplication::isLeftToRight()) { - widgetForAction(back)->setObjectName("leftHistoryButton"); - widgetForAction(forward)->setObjectName("rightHistoryButton"); - } else { - widgetForAction(forward)->setObjectName("leftHistoryButton"); - widgetForAction(back)->setObjectName("rightHistoryButton"); - } - - addSeparator(); - - addAction(app->getAction(KiwixApp::OpenHomePageAction)); - - addWidget(&m_searchEntry); - - addAction(app->getAction(KiwixApp::ToggleReadingListAction)); - - addSeparator(); - - addAction(app->getAction(KiwixApp::OpenFileAction)); - - QMenu* menu = new MainMenu(); - QToolButton *toolButton = new QToolButton(menu); - toolButton->setIcon(QIcon(":/icons/more.svg")); - toolButton->setPopupMode(QToolButton::InstantPopup); - toolButton->setToolTip(gt("main-menu")); - toolButton->setMenu(menu); - addWidget(toolButton); - - setContextMenuPolicy( Qt::PreventContextMenu ); - -#if !SYSTEMTITLEBAR - addAction(QIcon(":/icons/minimize.svg"), "minimize", parent, SLOT(showMinimized())); -#endif - addAction(app->getAction(KiwixApp::ToggleFullscreenAction)); - widgetForAction(app->getAction(KiwixApp::ToggleFullscreenAction))->setObjectName("fullScreenButton"); -#if !SYSTEMTITLEBAR - addAction(QIcon(":/icons/close.svg"), "close", parent, SLOT(close())); -#endif - setMovable(false); -} - -TopWidget::~TopWidget() -{ -} - -void TopWidget::handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled) -{ - auto app = KiwixApp::instance(); - - switch (action) { - case QWebEnginePage::Back: - app->getAction(KiwixApp::HistoryBackAction)->setEnabled(enabled); - break; - case QWebEnginePage::Forward: - app->getAction(KiwixApp::HistoryForwardAction)->setEnabled(enabled); - break; - default: - break; - } -} - - -void TopWidget::mousePressEvent(QMouseEvent *event) { - if (event->button() != Qt::LeftButton) - return; - - QPoint globalPos = portutils::getGlobalPos(*event); - m_cursorPos = globalPos + frameGeometry().topLeft() - parentWidget()->frameGeometry().topLeft(); - m_timestamp = event->timestamp(); - event->accept(); -} - -void TopWidget::mouseMoveEvent(QMouseEvent *event) { - if (event->timestamp() <= m_timestamp) - return; - - QPoint globalPos = portutils::getGlobalPos(*event); - m_timestamp = event->timestamp(); - auto delta = globalPos - m_cursorPos; - parentWidget()->move(delta); - event->accept(); -} - -QToolButton* TopWidget::getBackButton() const -{ - auto app = KiwixApp::instance(); - QAction *back = app->getAction(KiwixApp::HistoryBackAction); - return qobject_cast(widgetForAction(back)); -} - -QToolButton* TopWidget::getForwardButton() const -{ - auto app = KiwixApp::instance(); - QAction *forward = app->getAction(KiwixApp::HistoryForwardAction); - return qobject_cast(widgetForAction(forward)); -} - -void TopWidget::updateBackForwardButtons() -{ - WebView *webview = KiwixApp::instance()->getTabWidget()->currentWebView(); - - if (webview) { - back_menu.reset(webview->getHistoryBackMenu()); - forward_menu.reset(webview->getHistoryForwardMenu()); - } else { - back_menu.reset(); - forward_menu.reset(); - } - - getBackButton()->setMenu(back_menu.data()); - getForwardButton()->setMenu(forward_menu.data()); -} +#include "topwidget.h" + +#include "kconstants.h" +#include "kiwixapp.h" +#include "mainmenu.h" +#include "tabbar.h" +#include "portutils.h" + +#include +#include +#include + +TopWidget::TopWidget(QWidget *parent) : + QToolBar(parent) +{ + auto app = KiwixApp::instance(); + + QAction *back = app->getAction(KiwixApp::HistoryBackAction); + connect(back, &QAction::triggered, [app](){ + app->getTabWidget()->triggerWebPageAction(QWebEnginePage::Back); + }); + addAction(back); + + QAction *forward = app->getAction(KiwixApp::HistoryForwardAction); + connect(forward, &QAction::triggered, [app](){ + app->getTabWidget()->triggerWebPageAction(QWebEnginePage::Forward); + }); + addAction(forward); + + QAction *random = app->getAction(KiwixApp::RandomArticleAction); + addAction(random); + + QAction *toc = app->getAction(KiwixApp::ToggleTOCAction); + addAction(toc); + + // For CSS + if (QGuiApplication::isLeftToRight()) { + widgetForAction(back)->setObjectName("leftHistoryButton"); + widgetForAction(forward)->setObjectName("rightHistoryButton"); + } else { + widgetForAction(forward)->setObjectName("leftHistoryButton"); + widgetForAction(back)->setObjectName("rightHistoryButton"); + } + + addSeparator(); + + addAction(app->getAction(KiwixApp::OpenHomePageAction)); + + addWidget(&m_searchEntry); + + addAction(app->getAction(KiwixApp::ToggleReadingListAction)); + + addSeparator(); + + addAction(app->getAction(KiwixApp::OpenFileAction)); + + QMenu* menu = new MainMenu(); + QToolButton *toolButton = new QToolButton(menu); + toolButton->setIcon(QIcon(":/icons/more.svg")); + toolButton->setPopupMode(QToolButton::InstantPopup); + toolButton->setToolTip(gt("main-menu")); + toolButton->setMenu(menu); + addWidget(toolButton); + + setContextMenuPolicy( Qt::PreventContextMenu ); + +#if !SYSTEMTITLEBAR + addAction(QIcon(":/icons/minimize.svg"), "minimize", parent, SLOT(showMinimized())); +#endif + addAction(app->getAction(KiwixApp::ToggleFullscreenAction)); + widgetForAction(app->getAction(KiwixApp::ToggleFullscreenAction))->setObjectName("fullScreenButton"); +#if !SYSTEMTITLEBAR + addAction(QIcon(":/icons/close.svg"), "close", parent, SLOT(close())); +#endif + setMovable(false); +} + +TopWidget::~TopWidget() +{ +} + +void TopWidget::handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled) +{ + auto app = KiwixApp::instance(); + + switch (action) { + case QWebEnginePage::Back: + app->getAction(KiwixApp::HistoryBackAction)->setEnabled(enabled); + break; + case QWebEnginePage::Forward: + app->getAction(KiwixApp::HistoryForwardAction)->setEnabled(enabled); + break; + default: + break; + } +} + + +void TopWidget::mousePressEvent(QMouseEvent *event) { + if (event->button() != Qt::LeftButton) + return; + + QPoint globalPos = portutils::getGlobalPos(*event); + m_cursorPos = globalPos + frameGeometry().topLeft() - parentWidget()->frameGeometry().topLeft(); + m_timestamp = event->timestamp(); + event->accept(); +} + +void TopWidget::mouseMoveEvent(QMouseEvent *event) { + if (event->timestamp() <= m_timestamp) + return; + + QPoint globalPos = portutils::getGlobalPos(*event); + m_timestamp = event->timestamp(); + auto delta = globalPos - m_cursorPos; + parentWidget()->move(delta); + event->accept(); +} + +QToolButton* TopWidget::getBackButton() const +{ + auto app = KiwixApp::instance(); + QAction *back = app->getAction(KiwixApp::HistoryBackAction); + return qobject_cast(widgetForAction(back)); +} + +QToolButton* TopWidget::getForwardButton() const +{ + auto app = KiwixApp::instance(); + QAction *forward = app->getAction(KiwixApp::HistoryForwardAction); + return qobject_cast(widgetForAction(forward)); +} + +void TopWidget::updateBackForwardButtons() +{ + WebView *webview = KiwixApp::instance()->getTabWidget()->currentWebView(); + + if (webview) { + back_menu.reset(webview->getHistoryBackMenu()); + forward_menu.reset(webview->getHistoryForwardMenu()); + } else { + back_menu.reset(); + forward_menu.reset(); + } + + getBackButton()->setMenu(back_menu.data()); + getForwardButton()->setMenu(forward_menu.data()); +} diff --git a/src/topwidget.h b/src/topwidget.h index e4a6f411a..5af986253 100644 --- a/src/topwidget.h +++ b/src/topwidget.h @@ -1,39 +1,39 @@ -#ifndef TOPWIDGET_H -#define TOPWIDGET_H - -#include -#include -#include -#include - -#include "searchbar.h" - -class TopWidget : public QToolBar -{ - Q_OBJECT -public: - explicit TopWidget(QWidget *parent = nullptr); - virtual ~TopWidget(); - - SearchBar &getSearchBar() { return m_searchEntry; }; - -public slots: - void handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); - void updateBackForwardButtons(); - -protected: - void mousePressEvent(QMouseEvent *event); - void mouseMoveEvent(QMouseEvent *event); - -private: - SearchBar m_searchEntry; - QPoint m_cursorPos; - ulong m_timestamp; - QScopedPointer back_menu; - QScopedPointer forward_menu; - - QToolButton* getBackButton() const; - QToolButton* getForwardButton() const; -}; - -#endif // TOPWIDGET_H +#ifndef TOPWIDGET_H +#define TOPWIDGET_H + +#include +#include +#include +#include + +#include "searchbar.h" + +class TopWidget : public QToolBar +{ + Q_OBJECT +public: + explicit TopWidget(QWidget *parent = nullptr); + virtual ~TopWidget(); + + SearchBar &getSearchBar() { return m_searchEntry; }; + +public slots: + void handleWebActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); + void updateBackForwardButtons(); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + +private: + SearchBar m_searchEntry; + QPoint m_cursorPos; + ulong m_timestamp; + QScopedPointer back_menu; + QScopedPointer forward_menu; + + QToolButton* getBackButton() const; + QToolButton* getForwardButton() const; +}; + +#endif // TOPWIDGET_H diff --git a/src/translation.cpp b/src/translation.cpp index 7a08c2671..863fb6e70 100644 --- a/src/translation.cpp +++ b/src/translation.cpp @@ -1,52 +1,52 @@ -#include "translation.h" -#include -#include -#include -#include -#include - -Translation::Translation() -{ -} - -void Translation::setTranslation(QLocale locale) -{ - auto defaultText = JsonFileToQMap(":/i18n/en.json"); - if (defaultText.isEmpty()) { - throw std::runtime_error("Invalid translation file"); - } - if (locale.bcp47Name() == "en") { - m_translations = defaultText; - return; - } - m_translations = JsonFileToQMap(":/i18n/" + locale.bcp47Name() + ".json"); - for (auto &key : defaultText.keys()) { - if (!m_translations.contains(key) || m_translations.value(key).isEmpty()) { - m_translations.insert(key, defaultText.value(key)); - } - } -} - -QMap Translation::JsonFileToQMap(const QString &filePath) -{ - QMap translations; - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly)) { - return translations; - } - QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); - file.close(); - if (doc.isNull()) { - return translations; - } - auto jsonObj = doc.object(); - for (auto &key : jsonObj.keys()) { - translations.insert(key, jsonObj.value(key).toString()); - } - return translations; -} - -QString Translation::getText(const QString &key) -{ - return (m_translations.contains(key)) ? m_translations.value(key) : key; -} +#include "translation.h" +#include +#include +#include +#include +#include + +Translation::Translation() +{ +} + +void Translation::setTranslation(QLocale locale) +{ + auto defaultText = JsonFileToQMap(":/i18n/en.json"); + if (defaultText.isEmpty()) { + throw std::runtime_error("Invalid translation file"); + } + if (locale.bcp47Name() == "en") { + m_translations = defaultText; + return; + } + m_translations = JsonFileToQMap(":/i18n/" + locale.bcp47Name() + ".json"); + for (auto &key : defaultText.keys()) { + if (!m_translations.contains(key) || m_translations.value(key).isEmpty()) { + m_translations.insert(key, defaultText.value(key)); + } + } +} + +QMap Translation::JsonFileToQMap(const QString &filePath) +{ + QMap translations; + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) { + return translations; + } + QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + file.close(); + if (doc.isNull()) { + return translations; + } + auto jsonObj = doc.object(); + for (auto &key : jsonObj.keys()) { + translations.insert(key, jsonObj.value(key).toString()); + } + return translations; +} + +QString Translation::getText(const QString &key) +{ + return (m_translations.contains(key)) ? m_translations.value(key) : key; +} diff --git a/src/translation.h b/src/translation.h index d7ea1ed1e..4fbadf13b 100644 --- a/src/translation.h +++ b/src/translation.h @@ -1,23 +1,23 @@ -#ifndef TRANSLATION_H -#define TRANSLATION_H - -#include -#include -#include - -class Translation -{ -public: - Translation(); - - void setTranslation(QLocale locale); - QString getText(const QString &key); - -private: - QMap JsonFileToQMap(const QString &filePath); - -private: - QMap m_translations; -}; - -#endif // TRANSLATION_H +#ifndef TRANSLATION_H +#define TRANSLATION_H + +#include +#include +#include + +class Translation +{ +public: + Translation(); + + void setTranslation(QLocale locale); + QString getText(const QString &key); + +private: + QMap JsonFileToQMap(const QString &filePath); + +private: + QMap m_translations; +}; + +#endif // TRANSLATION_H diff --git a/src/urlschemehandler.cpp b/src/urlschemehandler.cpp index a5434e3a4..7a7a338d2 100644 --- a/src/urlschemehandler.cpp +++ b/src/urlschemehandler.cpp @@ -1,278 +1,278 @@ -#include "urlschemehandler.h" -#include "kiwixapp.h" -#include "blobbuffer.h" -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -UrlSchemeHandler::UrlSchemeHandler() -{ -} - -zim::Entry getArchiveEntryFromUrl(const zim::Archive& archive, const QUrl& url) -{ - std::string path = url.path().toUtf8().constData(); - if (path[0] == '/') - path = path.substr(1); - - try { - return archive.getEntryByPath(path); - } catch (zim::EntryNotFound& e) { - if (path.empty() || path == "/") { - return archive.getMainEntry(); - } - } - throw zim::EntryNotFound("Cannot find entry for non empty path"); -} - -void -UrlSchemeHandler::handleContentRequest(QWebEngineUrlRequestJob *request) -{ - auto qurl = request->requestUrl(); - auto library = KiwixApp::instance()->getLibrary(); - auto zim_id = qurl.host(); - zim_id.resize(zim_id.length()-4); - std::shared_ptr archive; - try { - archive = library->getArchive(zim_id); - } catch (std::out_of_range& e) { - replyZimNotFoundPage(request, zim_id); - return; - } - try { - auto entry = getArchiveEntryFromUrl(*archive, qurl); - auto item = entry.getItem(true); - if (entry.isRedirect()) { - auto path = QString("/") + QString::fromStdString(item.getPath()); - qurl.setPath(path); - request->redirect(qurl); - return; - } - - BlobBuffer* buffer = new BlobBuffer(item.getData(0)); - auto mimeType = QByteArray::fromStdString(item.getMimetype()); - mimeType = mimeType.split(';')[0]; - connect(request, &QObject::destroyed, buffer, &QObject::deleteLater); - request->reply(mimeType, buffer); - } catch (zim::EntryNotFound&) { - request->fail(QWebEngineUrlRequestJob::UrlNotFound); - } catch (const zim::ZimFileFormatError&) { - replyBadZimFilePage(request, zim_id); - } -} - -void -UrlSchemeHandler::handleMetaRequest(QWebEngineUrlRequestJob* request) -{ - auto qurl = request->requestUrl(); - auto host = qurl.host(); - auto parts = host.split('.'); - auto zimId = parts[0]; - auto metaName = parts[1]; - - if (metaName == "favicon") { - try { - auto library = KiwixApp::instance()->getLibrary(); - auto book = library->getBookById(zimId); - auto illustration = book.getIllustration(48); - std::string content = illustration->getData(); - std::string mimeType = illustration->mimeType; - QBuffer* buffer = new QBuffer; - buffer->setData(content.data(), content.size()); - connect(request, &QObject::destroyed, buffer, &QObject::deleteLater); - request->reply(QByteArray::fromStdString(mimeType), buffer); - return; - } catch (...) {} - } - request->fail(QWebEngineUrlRequestJob::UrlNotFound); -} - - -class IdNameMapper : public kiwix::NameMapper { - std::string getNameForId(const std::string& id) const { return id + ".zim"; } - std::string getIdForName(const std::string& id) const { return id.substr(0, id.size()-4); } -}; - - -namespace -{ - -struct SearchResultsWithEstimatedMatchCount -{ - std::shared_ptr results; - int estimatedMatchCount = 0; -}; - -SearchResultsWithEstimatedMatchCount getSearchResults(zim::Search& s, int start, int pageLength) -{ - SearchResultsWithEstimatedMatchCount r; - r.estimatedMatchCount = s.getEstimatedMatches(); - r.results = std::make_shared(s.getResults(start, pageLength)); - return r; -} - -} // unnamed namespace - -void -UrlSchemeHandler::handleSearchRequest(QWebEngineUrlRequestJob* request) -{ - auto qurl = request->requestUrl(); - auto app = KiwixApp::instance(); - auto host = qurl.host(); - auto bookId = host.split('.')[0]; - qInfo() << "Handling request" << qurl; - QUrlQuery query(qurl.query()); - if (bookId == "library") { - bookId = query.queryItemValue("content"); - } - auto searchQuery = query.queryItemValue("pattern").toStdString(); - int start = 0; - bool ok; - int temp = query.queryItemValue("start").toInt(&ok); - if (ok) - start = temp; - int pageLength = 25; - temp = query.queryItemValue("pageLength").toInt(&ok); - if (ok) - pageLength = temp; - - std::shared_ptr search; - try { - auto searcher = app->getLibrary()->getSearcher(bookId); - search = std::make_shared(searcher->search(searchQuery)); - } catch(...) { - request->fail(QWebEngineUrlRequestJob::UrlInvalid); - return; - } - - SearchResultsWithEstimatedMatchCount searchResult; - try { - searchResult = getSearchResults(*search, start, pageLength); - } catch (...) { - request->fail(QWebEngineUrlRequestJob::RequestFailed); - return; - } - - kiwix::SearchRenderer renderer( - *searchResult.results, - start, - searchResult.estimatedMatchCount); - renderer.setSearchPattern(searchQuery); - renderer.setSearchBookQuery("content="+bookId.toStdString()); - renderer.setProtocolPrefix("zim://"); - renderer.setSearchProtocolPrefix("zim://" + host.toStdString() + "/"); - renderer.setPageLength(pageLength); - IdNameMapper mapper; - auto content = renderer.getHtml(mapper, nullptr); - QBuffer *buffer = new QBuffer; - buffer->setData(content.data(), content.size()); - connect(request, &QObject::destroyed, buffer, &QObject::deleteLater); - request->reply("text/html", buffer); -} - -namespace -{ - -QString completeHtml(const QString& htmlBodyContent) -{ - const QString htmlHead = R"()"; - const QString fullHtml = "" - + htmlHead - + "" + htmlBodyContent + "" - + ""; - return fullHtml; -} - -void -sendHtmlResponse(QWebEngineUrlRequestJob *request, const QString& htmlBodyContent) -{ - QBuffer *buffer = new QBuffer; - buffer->open(QIODevice::WriteOnly); - buffer->write(completeHtml(htmlBodyContent).toStdString().c_str()); - buffer->close(); - - QObject::connect(request, SIGNAL(destroyed()), buffer, SLOT(deleteLater())); - request->reply("text/html", buffer); -} - -} // unnamed namespace - -void -UrlSchemeHandler::replyZimNotFoundPage(QWebEngineUrlRequestJob *request, - const QString &zimId) -{ - QString path = "N/A", name = "N/A"; - try - { - auto& book = KiwixApp::instance()->getLibrary()->getBookById(zimId); - path = QString::fromStdString(book.getPath()); - name = QString::fromStdString(book.getName()); - } - catch (...) { /* Blank */ } - - QString contentHtml = "
" - "

" + - gt("file-not-found-title") + - "

" - "

" + - gt("file-not-found-text") + - "

" - "

" + - gt("zim-id") + ": " + zimId + - "

" - "

" + - gt("zim-name") + ": " + name + - "

" - "

" + - gt("zim-path") + ": " + path + - "

" - "
"; - - sendHtmlResponse(request, contentHtml); -} - -void -UrlSchemeHandler::replyBadZimFilePage(QWebEngineUrlRequestJob *request, - const QString &zimId) -{ - const auto& book = KiwixApp::instance()->getLibrary()->getBookById(zimId); - const QString path = QString::fromStdString(book.getPath()); - const QString name = QString::fromStdString(book.getName()); - const QString zimEntryPath = request->requestUrl().path(); - - QString contentHtml = "
" - "

" + gt("bad-zim-file-error-page-title") + "

" - "

" + gt("bad-zim-file-error-page-text") + "

" - "

" + gt("zim-id") + ": " + zimId + "

" - "

" + gt("zim-name") + ": " + name + "

" - "

" + gt("zim-path") + ": " + path + "

" - "

" + gt("zim-entry-path") + ": " + zimEntryPath + "

" - "
"; - - sendHtmlResponse(request, contentHtml); -} - -void -UrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request) -{ - auto qurl = request->requestUrl(); - auto host = qurl.host(); - if (host.endsWith(".zim")) { - handleContentRequest(request); - } else if (host.endsWith(".meta")) { - handleMetaRequest(request); - } else if (host.endsWith(".search")) { - handleSearchRequest(request); - } else { - request->fail(QWebEngineUrlRequestJob::UrlNotFound); - } -} +#include "urlschemehandler.h" +#include "kiwixapp.h" +#include "blobbuffer.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +UrlSchemeHandler::UrlSchemeHandler() +{ +} + +zim::Entry getArchiveEntryFromUrl(const zim::Archive& archive, const QUrl& url) +{ + std::string path = url.path().toUtf8().constData(); + if (path[0] == '/') + path = path.substr(1); + + try { + return archive.getEntryByPath(path); + } catch (zim::EntryNotFound& e) { + if (path.empty() || path == "/") { + return archive.getMainEntry(); + } + } + throw zim::EntryNotFound("Cannot find entry for non empty path"); +} + +void +UrlSchemeHandler::handleContentRequest(QWebEngineUrlRequestJob *request) +{ + auto qurl = request->requestUrl(); + auto library = KiwixApp::instance()->getLibrary(); + auto zim_id = qurl.host(); + zim_id.resize(zim_id.length()-4); + std::shared_ptr archive; + try { + archive = library->getArchive(zim_id); + } catch (std::out_of_range& e) { + replyZimNotFoundPage(request, zim_id); + return; + } + try { + auto entry = getArchiveEntryFromUrl(*archive, qurl); + auto item = entry.getItem(true); + if (entry.isRedirect()) { + auto path = QString("/") + QString::fromStdString(item.getPath()); + qurl.setPath(path); + request->redirect(qurl); + return; + } + + BlobBuffer* buffer = new BlobBuffer(item.getData(0)); + auto mimeType = QByteArray::fromStdString(item.getMimetype()); + mimeType = mimeType.split(';')[0]; + connect(request, &QObject::destroyed, buffer, &QObject::deleteLater); + request->reply(mimeType, buffer); + } catch (zim::EntryNotFound&) { + request->fail(QWebEngineUrlRequestJob::UrlNotFound); + } catch (const zim::ZimFileFormatError&) { + replyBadZimFilePage(request, zim_id); + } +} + +void +UrlSchemeHandler::handleMetaRequest(QWebEngineUrlRequestJob* request) +{ + auto qurl = request->requestUrl(); + auto host = qurl.host(); + auto parts = host.split('.'); + auto zimId = parts[0]; + auto metaName = parts[1]; + + if (metaName == "favicon") { + try { + auto library = KiwixApp::instance()->getLibrary(); + auto book = library->getBookById(zimId); + auto illustration = book.getIllustration(48); + std::string content = illustration->getData(); + std::string mimeType = illustration->mimeType; + QBuffer* buffer = new QBuffer; + buffer->setData(content.data(), content.size()); + connect(request, &QObject::destroyed, buffer, &QObject::deleteLater); + request->reply(QByteArray::fromStdString(mimeType), buffer); + return; + } catch (...) {} + } + request->fail(QWebEngineUrlRequestJob::UrlNotFound); +} + + +class IdNameMapper : public kiwix::NameMapper { + std::string getNameForId(const std::string& id) const { return id + ".zim"; } + std::string getIdForName(const std::string& id) const { return id.substr(0, id.size()-4); } +}; + + +namespace +{ + +struct SearchResultsWithEstimatedMatchCount +{ + std::shared_ptr results; + int estimatedMatchCount = 0; +}; + +SearchResultsWithEstimatedMatchCount getSearchResults(zim::Search& s, int start, int pageLength) +{ + SearchResultsWithEstimatedMatchCount r; + r.estimatedMatchCount = s.getEstimatedMatches(); + r.results = std::make_shared(s.getResults(start, pageLength)); + return r; +} + +} // unnamed namespace + +void +UrlSchemeHandler::handleSearchRequest(QWebEngineUrlRequestJob* request) +{ + auto qurl = request->requestUrl(); + auto app = KiwixApp::instance(); + auto host = qurl.host(); + auto bookId = host.split('.')[0]; + qInfo() << "Handling request" << qurl; + QUrlQuery query(qurl.query()); + if (bookId == "library") { + bookId = query.queryItemValue("content"); + } + auto searchQuery = query.queryItemValue("pattern").toStdString(); + int start = 0; + bool ok; + int temp = query.queryItemValue("start").toInt(&ok); + if (ok) + start = temp; + int pageLength = 25; + temp = query.queryItemValue("pageLength").toInt(&ok); + if (ok) + pageLength = temp; + + std::shared_ptr search; + try { + auto searcher = app->getLibrary()->getSearcher(bookId); + search = std::make_shared(searcher->search(searchQuery)); + } catch(...) { + request->fail(QWebEngineUrlRequestJob::UrlInvalid); + return; + } + + SearchResultsWithEstimatedMatchCount searchResult; + try { + searchResult = getSearchResults(*search, start, pageLength); + } catch (...) { + request->fail(QWebEngineUrlRequestJob::RequestFailed); + return; + } + + kiwix::SearchRenderer renderer( + *searchResult.results, + start, + searchResult.estimatedMatchCount); + renderer.setSearchPattern(searchQuery); + renderer.setSearchBookQuery("content="+bookId.toStdString()); + renderer.setProtocolPrefix("zim://"); + renderer.setSearchProtocolPrefix("zim://" + host.toStdString() + "/"); + renderer.setPageLength(pageLength); + IdNameMapper mapper; + auto content = renderer.getHtml(mapper, nullptr); + QBuffer *buffer = new QBuffer; + buffer->setData(content.data(), content.size()); + connect(request, &QObject::destroyed, buffer, &QObject::deleteLater); + request->reply("text/html", buffer); +} + +namespace +{ + +QString completeHtml(const QString& htmlBodyContent) +{ + const QString htmlHead = R"()"; + const QString fullHtml = "" + + htmlHead + + "" + htmlBodyContent + "" + + ""; + return fullHtml; +} + +void +sendHtmlResponse(QWebEngineUrlRequestJob *request, const QString& htmlBodyContent) +{ + QBuffer *buffer = new QBuffer; + buffer->open(QIODevice::WriteOnly); + buffer->write(completeHtml(htmlBodyContent).toStdString().c_str()); + buffer->close(); + + QObject::connect(request, SIGNAL(destroyed()), buffer, SLOT(deleteLater())); + request->reply("text/html", buffer); +} + +} // unnamed namespace + +void +UrlSchemeHandler::replyZimNotFoundPage(QWebEngineUrlRequestJob *request, + const QString &zimId) +{ + QString path = "N/A", name = "N/A"; + try + { + auto& book = KiwixApp::instance()->getLibrary()->getBookById(zimId); + path = QString::fromStdString(book.getPath()); + name = QString::fromStdString(book.getName()); + } + catch (...) { /* Blank */ } + + QString contentHtml = "
" + "

" + + gt("file-not-found-title") + + "

" + "

" + + gt("file-not-found-text") + + "

" + "

" + + gt("zim-id") + ": " + zimId + + "

" + "

" + + gt("zim-name") + ": " + name + + "

" + "

" + + gt("zim-path") + ": " + path + + "

" + "
"; + + sendHtmlResponse(request, contentHtml); +} + +void +UrlSchemeHandler::replyBadZimFilePage(QWebEngineUrlRequestJob *request, + const QString &zimId) +{ + const auto& book = KiwixApp::instance()->getLibrary()->getBookById(zimId); + const QString path = QString::fromStdString(book.getPath()); + const QString name = QString::fromStdString(book.getName()); + const QString zimEntryPath = request->requestUrl().path(); + + QString contentHtml = "
" + "

" + gt("bad-zim-file-error-page-title") + "

" + "

" + gt("bad-zim-file-error-page-text") + "

" + "

" + gt("zim-id") + ": " + zimId + "

" + "

" + gt("zim-name") + ": " + name + "

" + "

" + gt("zim-path") + ": " + path + "

" + "

" + gt("zim-entry-path") + ": " + zimEntryPath + "

" + "
"; + + sendHtmlResponse(request, contentHtml); +} + +void +UrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *request) +{ + auto qurl = request->requestUrl(); + auto host = qurl.host(); + if (host.endsWith(".zim")) { + handleContentRequest(request); + } else if (host.endsWith(".meta")) { + handleMetaRequest(request); + } else if (host.endsWith(".search")) { + handleSearchRequest(request); + } else { + request->fail(QWebEngineUrlRequestJob::UrlNotFound); + } +} diff --git a/src/urlschemehandler.h b/src/urlschemehandler.h index e4955b597..d3194b8da 100644 --- a/src/urlschemehandler.h +++ b/src/urlschemehandler.h @@ -1,21 +1,21 @@ -#ifndef URLSCHEMEHANDLER_H -#define URLSCHEMEHANDLER_H - -#include - -class UrlSchemeHandler : public QWebEngineUrlSchemeHandler -{ - Q_OBJECT -public: - UrlSchemeHandler(); - void requestStarted(QWebEngineUrlRequestJob *request); -private: - void handleMetaRequest(QWebEngineUrlRequestJob *request); - void handleContentRequest(QWebEngineUrlRequestJob *request); - void handleSearchRequest(QWebEngineUrlRequestJob *request); - - void replyZimNotFoundPage(QWebEngineUrlRequestJob *request, const QString& zimId); - void replyBadZimFilePage(QWebEngineUrlRequestJob *request, const QString& zimId); -}; - -#endif // URLSCHEMEHANDLER_H +#ifndef URLSCHEMEHANDLER_H +#define URLSCHEMEHANDLER_H + +#include + +class UrlSchemeHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + UrlSchemeHandler(); + void requestStarted(QWebEngineUrlRequestJob *request); +private: + void handleMetaRequest(QWebEngineUrlRequestJob *request); + void handleContentRequest(QWebEngineUrlRequestJob *request); + void handleSearchRequest(QWebEngineUrlRequestJob *request); + + void replyZimNotFoundPage(QWebEngineUrlRequestJob *request, const QString& zimId); + void replyBadZimFilePage(QWebEngineUrlRequestJob *request, const QString& zimId); +}; + +#endif // URLSCHEMEHANDLER_H diff --git a/src/webpage.cpp b/src/webpage.cpp index 8824710de..2dc9f37c8 100644 --- a/src/webpage.cpp +++ b/src/webpage.cpp @@ -1,24 +1,24 @@ -#include "webpage.h" - -#include -#include -#include -#include "kiwixapp.h" -#include - -WebPage::WebPage(QObject *parent) : - QWebEnginePage(KiwixApp::instance()->getProfile(), parent) -{ - action(QWebEnginePage::SavePage)->setVisible(false); - action(QWebEnginePage::ViewSource)->setVisible(false); - action(QWebEnginePage::Reload)->setVisible(false); -} - -bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) -{ - if (url.scheme() != "zim") { - QDesktopServices::openUrl(url); - return false; - } - return true; -} +#include "webpage.h" + +#include +#include +#include +#include "kiwixapp.h" +#include + +WebPage::WebPage(QObject *parent) : + QWebEnginePage(KiwixApp::instance()->getProfile(), parent) +{ + action(QWebEnginePage::SavePage)->setVisible(false); + action(QWebEnginePage::ViewSource)->setVisible(false); + action(QWebEnginePage::Reload)->setVisible(false); +} + +bool WebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/) +{ + if (url.scheme() != "zim") { + QDesktopServices::openUrl(url); + return false; + } + return true; +} diff --git a/src/webpage.h b/src/webpage.h index d594b0f21..3bec5696b 100644 --- a/src/webpage.h +++ b/src/webpage.h @@ -1,16 +1,16 @@ -#ifndef WEBPAGE_H -#define WEBPAGE_H - -#include - -class WebPage : public QWebEnginePage -{ - Q_OBJECT -public: - explicit WebPage(QObject *parent = nullptr); - -protected: - bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); -}; - -#endif // WEBPAGE_H +#ifndef WEBPAGE_H +#define WEBPAGE_H + +#include + +class WebPage : public QWebEnginePage +{ + Q_OBJECT +public: + explicit WebPage(QObject *parent = nullptr); + +protected: + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); +}; + +#endif // WEBPAGE_H diff --git a/src/webview.cpp b/src/webview.cpp index 638b80a8f..f770589f0 100644 --- a/src/webview.cpp +++ b/src/webview.cpp @@ -1,424 +1,525 @@ -class QMenu; - -#include "webview.h" - -#include -#include -#include -#include "kiwixapp.h" -#include "webpage.h" -#include "css_constants.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "kiwixwebchannelobject.h" -#include "tableofcontentbar.h" - -zim::Entry getArchiveEntryFromUrl(const zim::Archive& archive, const QUrl& url); -QString askForSaveFilePath(const QString& suggestedName); - -void WebViewBackMenu::showEvent(QShowEvent *) -{ - /* In Qt 5.12 CSS options for shifting this menu didn't work. - * In particular: - * - toolbar->setContentsMargins(0,0,0,0); - * - toolbar->layout()->setContentsMargins(0,0,0,0); - * - QToolBar { padding-left: } - * - QToolBar { margin-left; } - * - QToolBar { padding: 5px 12px 5px 12px; } - * - QToolBar::separator:first { width: 10px; } - * (that was attempts to set some spacing on left and right in toolbar - * so back button will be shifted right. - * If in Qt 6.x QToolButton shows its menu in the right position - * this code can be removed. - */ - - QRect geo = geometry(); - - const int marginLeft = CSS::TopWidget::QToolButton::backButton::marginLeft; - geo.moveLeft(geo.left() + marginLeft); - geo.moveTop(geo.top() + 2); - setGeometry(geo); -} - -void WebViewForwardMenu::showEvent(QShowEvent *) -{ - QRect geo = geometry(); - geo.moveTop(geo.top() + 2); - setGeometry(geo); -} - -QString getZimIdFromUrl(QUrl url) -{ - return url.host().split('.')[0]; -} - -QString getResultTypeFromUrl(QUrl url) -{ - return url.host().split('.')[1]; -} - -void WebView::applyCorrectZoomFactor() { - auto url = this->url(); - auto settingsManager = KiwixApp::instance()->getSettingsManager(); - qreal zoomFactor; - const bool isSearchResultsView = QUrlQuery(url).hasQueryItem("pattern") && (getResultTypeFromUrl(url) == "search"); - if (isSearchResultsView) { - zoomFactor = settingsManager->getZoomFactor(); - } else { - auto zimId = getZimIdFromUrl(url); - zoomFactor = settingsManager->getZoomFactorByZimId(zimId); - } - this->setZoomFactor(zoomFactor); -} - -WebView::WebView(QWidget *parent) - : QWebEngineView(parent) -{ - setPage(new WebPage(this)); - QObject::connect(this, &QWebEngineView::urlChanged, this, &WebView::onUrlChanged); - connect(this->page(), &QWebEnginePage::linkHovered, this, [=] (const QString& url) { - m_linkHovered = url; - }); - - /* In Qt 5.12, the zoom factor is not correctly passed after a fulltext search - * Bug Report: https://bugreports.qt.io/browse/QTBUG-51851 - * This rezooms the page to its correct zoom (default/by ZIM ID) after loading is finished. - * If the page is search results, we put the default zoom factor - * If in Qt 6.x, the bug is fixed this code can be removed. - */ -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - connect(this, &QWebEngineView::loadFinished, this, [=] (bool ok) { - if (ok) { - applyCorrectZoomFactor(); - } - }); -#endif - - const auto channel = new QWebChannel(this); - const auto kiwixChannelObj = new KiwixWebChannelObject; - page()->setWebChannel(channel, QWebEngineScript::UserWorld); - channel->registerObject("kiwixChannelObj", kiwixChannelObj); - - const auto tabbar = KiwixApp::instance()->getTabWidget(); - connect(tabbar, &TabBar::currentTitleChanged, this, &WebView::onCurrentTitleChanged); - connect(kiwixChannelObj, &KiwixWebChannelObject::headersChanged, this, &WebView::onHeadersReceived); - - const auto tocbar = KiwixApp::instance()->getMainWindow()->getTableOfContentBar(); - connect(this, &WebView::headersChanged, tocbar, &TableOfContentBar::setupTree); - connect(tocbar, &TableOfContentBar::navigationRequested, this, &WebView::onNavigationRequested); - connect(this, &WebView::navigationRequested, kiwixChannelObj, &KiwixWebChannelObject::navigationRequested); -} - -WebView::~WebView() -{} - -bool WebView::isWebActionEnabled(QWebEnginePage::WebAction webAction) const -{ - return page()->action(webAction)->isEnabled(); -} - -QMenu* WebView::getHistoryBackMenu() const -{ - QWebEngineHistory *h = history(); - - const int cur = h->currentItemIndex(); - if (cur <= 0) { - return Q_NULLPTR; - } - - auto ret = new WebViewBackMenu(); - for (int i = cur - 1 ; i >= 0 ; i--) { - addHistoryItemAction(ret, h->itemAt(i), i); - } - return ret; -} - -QMenu* WebView::getHistoryForwardMenu() const -{ - QWebEngineHistory *h = history(); - - const int cur = h->currentItemIndex(); - if (cur + 1 >= h->count()) { - return Q_NULLPTR; - } - - auto ret = new WebViewForwardMenu(); - for (int i = cur + 1 ; i < h->count() ; i++) { - addHistoryItemAction(ret, h->itemAt(i), i); - } - return ret; -} - -namespace -{ - -/** - * @brief Get the Zim Item object corresponding to the given url. - * - * @param url QUrl - * @return zim::Item - * - * @exception throws exception if zimId is invalid, archive doesn't exist, - * entry is invalid or not found, or entry is redirect. - */ -zim::Item getZimItem(const QUrl& url) -{ - const auto app = KiwixApp::instance(); - const auto library = app->getLibrary(); - const auto archive = library->getArchive(getZimIdFromUrl(url)); - const auto entry = getArchiveEntryFromUrl(*archive, url); - return entry.getItem(true); -} - -bool isHTMLContent(const zim::Item& item) -{ - auto mimeType = QByteArray::fromStdString(item.getMimetype()); - mimeType = mimeType.split(';')[0]; - return mimeType == "text/html"; -} - -} - -void WebView::saveViewContent() -{ - try { - const auto item = getZimItem(url()); - - /* We have to sanitize here, as parsing will start once we pass the file - name to either save or download method. - */ - const QString suggestedFileName = QString::fromStdString(kiwix::getSlugifiedFileName(item.getTitle())); - if (isHTMLContent(item)) - { - const QString fileName = askForSaveFilePath(suggestedFileName + ".pdf"); - if (!fileName.isEmpty()) - page()->printToPdf(fileName); - } - else - page()->download(this->url(), suggestedFileName); - } - catch (...) { /* Blank */} -} - -void WebView::onCurrentTitleChanged() -{ - const auto tabbar = KiwixApp::instance()->getTabWidget(); - const auto noAnchorUrl = url().url(QUrl::RemoveFragment); - const auto headersValid = m_headers["url"].toString() == noAnchorUrl; - - /* If headers not valid for this webview, then we are loading and the emit - will be handled by KiwixWebChannelObject::headersChanged. - */ - if (tabbar->currentWebView() == this && headersValid) - emit headersChanged(m_headers); -} - -void WebView::onHeadersReceived(const QString& headersJSONStr) -{ - const auto tabbar = KiwixApp::instance()->getTabWidget(); - m_headers = QJsonDocument::fromJson(headersJSONStr.toUtf8()).object(); - - if (tabbar->currentWebView() == this) - emit headersChanged(m_headers); -} - -void WebView::onNavigationRequested(const QString &url, const QString &anchor) -{ - const auto tabbar = KiwixApp::instance()->getTabWidget(); - if (tabbar->currentWebView() == this) - emit navigationRequested(url, anchor); -} - -void WebView::addHistoryItemAction(QMenu *menu, - const QWebEngineHistoryItem &item, - int n) const -{ - QAction *a = menu->addAction(item.title()); - a->setData(QVariant::fromValue(n)); - connect(a, &QAction::triggered, this, &WebView::gotoTriggeredHistoryItemAction); -} - -void WebView::gotoTriggeredHistoryItemAction() -{ - QAction *a = qobject_cast(QObject::sender()); - if (!a) - return; - - int n = a->data().toInt(); - QWebEngineHistory *h = history(); - if (n < 0 || n >= h->count()) - return; - - h->goToItem(h->itemAt(n)); -} - - -QWebEngineView* WebView::createWindow(QWebEnginePage::WebWindowType type) -{ - if ( type==QWebEnginePage::WebBrowserBackgroundTab - || type==QWebEnginePage::WebBrowserTab ) - { - auto tabWidget = KiwixApp::instance()->getTabWidget(); - return tabWidget->createNewTab(false, true)->getWebView(); - } - return nullptr; -} - -void WebView::onUrlChanged(const QUrl& url) { - auto zimId = getZimIdFromUrl(url); - auto app = KiwixApp::instance(); - app->saveListOfOpenTabs(); - if (m_currentZimId == zimId ) { - return; - } - m_currentZimId = zimId; - emit zimIdChanged(m_currentZimId); - m_icon = app->getLibrary()->getBookIcon(m_currentZimId); - auto zoomFactor = app->getSettingsManager()->getZoomFactorByZimId(zimId); - this->setZoomFactor(zoomFactor); - emit iconChanged(m_icon); -} - -void WebView::wheelEvent(QWheelEvent *event) { - if ((event->modifiers() & Qt::ControlModifier) != 0) - { - if (event->angleDelta().y() > 0) { - KiwixApp::instance()->getAction(KiwixApp::ZoomInAction)->activate(QAction::Trigger); - } else if (event->angleDelta().y() < 0) { - KiwixApp::instance()->getAction(KiwixApp::ZoomOutAction)->activate(QAction::Trigger); - } - } -} - - -void WebView::contextMenuEvent(QContextMenuEvent *event) -{ - QMenu* menu; - if (m_linkHovered.isEmpty()) { - menu = createStandardContextMenu(); - } else { - menu = createLinkContextMenu(); - } - -#if defined(QT_TEXTTOSPEECH_LIB) - const auto app = KiwixApp::instance(); - menu->addAction(app->getAction(KiwixApp::ReadArticleAction)); - if (page()->hasSelection()) - menu->addAction(app->getAction(KiwixApp::ReadTextAction)); -#endif - - menu->exec(event->globalPos()); -} - - -QMenu* WebView::createStandardContextMenu() { - auto app = KiwixApp::instance(); - - QMenu* menu = new QMenu(this); - auto backAction = new QAction(gt("back")); - backAction->setEnabled(app->getAction(KiwixApp::HistoryBackAction)->isEnabled()); - backAction->setIcon(app->getAction(KiwixApp::HistoryBackAction)->icon()); - menu->addAction(backAction); - connect(menu, &QObject::destroyed, backAction, &QObject::deleteLater); - connect(backAction, &QAction::triggered, this, [=](bool checked) { - Q_UNUSED(checked); - KiwixApp::instance()->getTabWidget()->triggerWebPageAction(QWebEnginePage::Back); - }); - - auto forwardAction = new QAction(gt("forward")); - forwardAction->setEnabled(app->getAction(KiwixApp::HistoryForwardAction)->isEnabled()); - forwardAction->setIcon(app->getAction(KiwixApp::HistoryForwardAction)->icon()); - menu->addAction(forwardAction); - connect(menu, &QObject::destroyed, forwardAction, &QObject::deleteLater); - connect(forwardAction, &QAction::triggered, this, [=](bool checked) { - Q_UNUSED(checked); - KiwixApp::instance()->getTabWidget()->triggerWebPageAction(QWebEnginePage::Forward); - }); - - menu->addAction(app->getAction(KiwixApp::SavePageAsAction)); - return menu; -} - - -QMenu* WebView::createLinkContextMenu() { - QMenu* menu = new QMenu(this); - - if (!m_linkHovered.startsWith("zim://")) { - auto openLinkInWebBrowserAction = new QAction(gt("open-link-in-web-browser")); - menu->addAction(openLinkInWebBrowserAction); - connect(menu, &QObject::destroyed, openLinkInWebBrowserAction, &QObject::deleteLater); - connect(openLinkInWebBrowserAction, &QAction::triggered, this, [=](bool checked) { - Q_UNUSED(checked); - QDesktopServices::openUrl(m_linkHovered); - }); - } else { - auto openLinkNewTab = new QAction(gt("open-link-new-tab")); - openLinkNewTab->setIcon(QIcon(":/icons/new-tab-icon.svg")); - menu->addAction(openLinkNewTab); - connect(menu, &QObject::destroyed, openLinkNewTab, &QObject::deleteLater); - connect(openLinkNewTab, &QAction::triggered, this, [=](bool checked) { - Q_UNUSED(checked); - KiwixApp::instance()->openUrl(m_linkHovered, true); - }); - } - - return menu; -} - - -bool WebView::eventFilter(QObject *src, QEvent *e) -{ - Q_UNUSED(src) - // work around QTBUG-43602 - if (e->type() == QEvent::Wheel) { - auto we = static_cast(e); - if (we->modifiers() == Qt::ControlModifier) - return true; - } - if (e->type() == QEvent::MouseButtonRelease) { - auto me = static_cast(e); - if (!m_linkHovered.startsWith("zim://") - && (me->modifiers() == Qt::ControlModifier || me->button() == Qt::MiddleButton)) - { - QDesktopServices::openUrl(m_linkHovered); - return true; - } - if (me->button() == Qt::BackButton) - { - back(); - return true; - } - if (me->button() == Qt::ForwardButton) - { - forward(); - return true; - } - } - return false; -} - -bool WebView::event(QEvent *event) -{ - // work around QTBUG-43602 - if (event->type() == QEvent::ChildAdded) { - auto ce = static_cast(event); - ce->child()->installEventFilter(this); - } else if (event->type() == QEvent::ChildRemoved) { - auto ce = static_cast(event); - ce->child()->removeEventFilter(this); - } - if (event->type() == QEvent::ToolTip) { - return true; - } else { - return QWebEngineView::event(event); - } - return true; -} +class QMenu; + +#include "webview.h" + +#include +#include +#include +#include "kiwixapp.h" +#include "webpage.h" +#include "css_constants.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "kiwixwebchannelobject.h" +#include "tableofcontentbar.h" +#include + +zim::Entry getArchiveEntryFromUrl(const zim::Archive& archive, const QUrl& url); +QString askForSaveFilePath(const QString& suggestedName); + +void WebViewBackMenu::showEvent(QShowEvent *) +{ + /* In Qt 5.12 CSS options for shifting this menu didn't work. + * In particular: + * - toolbar->setContentsMargins(0,0,0,0); + * - toolbar->layout()->setContentsMargins(0,0,0,0); + * - QToolBar { padding-left: } + * - QToolBar { margin-left; } + * - QToolBar { padding: 5px 12px 5px 12px; } + * - QToolBar::separator:first { width: 10px; } + * (that was attempts to set some spacing on left and right in toolbar + * so back button will be shifted right. + * If in Qt 6.x QToolButton shows its menu in the right position + * this code can be removed. + */ + + QRect geo = geometry(); + + const int marginLeft = CSS::TopWidget::QToolButton::backButton::marginLeft; + geo.moveLeft(geo.left() + marginLeft); + geo.moveTop(geo.top() + 2); + setGeometry(geo); +} + +void WebViewForwardMenu::showEvent(QShowEvent *) +{ + QRect geo = geometry(); + geo.moveTop(geo.top() + 2); + setGeometry(geo); +} + +QString getZimIdFromUrl(QUrl url) +{ + return url.host().split('.')[0]; +} + +QString getResultTypeFromUrl(QUrl url) +{ + return url.host().split('.')[1]; +} + +void WebView::applyCorrectZoomFactor() { + auto url = this->url(); + auto settingsManager = KiwixApp::instance()->getSettingsManager(); + qreal zoomFactor; + const bool isSearchResultsView = QUrlQuery(url).hasQueryItem("pattern") && (getResultTypeFromUrl(url) == "search"); + if (isSearchResultsView) { + zoomFactor = settingsManager->getZoomFactor(); + } else { + auto zimId = getZimIdFromUrl(url); + zoomFactor = settingsManager->getZoomFactorByZimId(zimId); + } + this->setZoomFactor(zoomFactor); +} + +WebView::WebView(QWidget *parent) + : QWebEngineView(parent) +{ + setPage(new WebPage(this)); + QObject::connect(this, &QWebEngineView::urlChanged, this, &WebView::onUrlChanged); + QObject::connect(this, &QWebEngineView::urlChanged, this, &WebView::handleTocHistoryNavigation); + connect(this->page(), &QWebEnginePage::linkHovered, this, [=] (const QString& url) { + m_linkHovered = url; + }); + + /* In Qt 5.12, the zoom factor is not correctly passed after a fulltext search + * Bug Report: https://bugreports.qt.io/browse/QTBUG-51851 + * This rezooms the page to its correct zoom (default/by ZIM ID) after loading is finished. + * If the page is search results, we put the default zoom factor + * If in Qt 6.x, the bug is fixed this code can be removed. + */ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + connect(this, &QWebEngineView::loadFinished, this, [=] (bool ok) { + if (ok) { + applyCorrectZoomFactor(); + } + }); +#endif + + const auto channel = new QWebChannel(this); + const auto kiwixChannelObj = new KiwixWebChannelObject; + page()->setWebChannel(channel, QWebEngineScript::UserWorld); + channel->registerObject("kiwixChannelObj", kiwixChannelObj); + + const auto tabbar = KiwixApp::instance()->getTabWidget(); + connect(tabbar, &TabBar::currentTitleChanged, this, &WebView::onCurrentTitleChanged); + connect(kiwixChannelObj, &KiwixWebChannelObject::headersChanged, this, &WebView::onHeadersReceived); + connect(kiwixChannelObj, &KiwixWebChannelObject::navigationRequested, + this, &WebView::onNavigationRequested); + connect(kiwixChannelObj, &KiwixWebChannelObject::consoleMessageReceived, + this, &WebView::onConsoleMessageReceived); + + const auto tocbar = KiwixApp::instance()->getMainWindow()->getTableOfContentBar(); + connect(this, &WebView::headersChanged, tocbar, &TableOfContentBar::setupTree); + connect(tocbar, &TableOfContentBar::navigationRequested, this, &WebView::onNavigationRequested); + connect(this, &WebView::navigationRequested, kiwixChannelObj, &KiwixWebChannelObject::navigationRequested); +} + +WebView::~WebView() +{} + +bool WebView::isWebActionEnabled(QWebEnginePage::WebAction webAction) const +{ + return page()->action(webAction)->isEnabled(); +} + +QMenu* WebView::getHistoryBackMenu() const +{ + QWebEngineHistory *h = history(); + + const int cur = h->currentItemIndex(); + if (cur <= 0) { + return Q_NULLPTR; + } + + auto ret = new WebViewBackMenu(); + for (int i = cur - 1 ; i >= 0 ; i--) { + addHistoryItemAction(ret, h->itemAt(i), i); + } + return ret; +} + +QMenu* WebView::getHistoryForwardMenu() const +{ + QWebEngineHistory *h = history(); + + const int cur = h->currentItemIndex(); + if (cur + 1 >= h->count()) { + return Q_NULLPTR; + } + + auto ret = new WebViewForwardMenu(); + for (int i = cur + 1 ; i < h->count() ; i++) { + addHistoryItemAction(ret, h->itemAt(i), i); + } + return ret; +} + +namespace +{ + +/** + * @brief Get the Zim Item object corresponding to the given url. + * + * @param url QUrl + * @return zim::Item + * + * @exception throws exception if zimId is invalid, archive doesn't exist, + * entry is invalid or not found, or entry is redirect. + */ +zim::Item getZimItem(const QUrl& url) +{ + const auto app = KiwixApp::instance(); + const auto library = app->getLibrary(); + const auto archive = library->getArchive(getZimIdFromUrl(url)); + const auto entry = getArchiveEntryFromUrl(*archive, url); + return entry.getItem(true); +} + +bool isHTMLContent(const zim::Item& item) +{ + auto mimeType = QByteArray::fromStdString(item.getMimetype()); + mimeType = mimeType.split(';')[0]; + return mimeType == "text/html"; +} + +} + +void WebView::saveViewContent() +{ + try { + const auto item = getZimItem(url()); + + /* We have to sanitize here, as parsing will start once we pass the file + name to either save or download method. + */ + const QString suggestedFileName = QString::fromStdString(kiwix::getSlugifiedFileName(item.getTitle())); + if (isHTMLContent(item)) + { + const QString fileName = askForSaveFilePath(suggestedFileName + ".pdf"); + if (!fileName.isEmpty()) + page()->printToPdf(fileName); + } + else + page()->download(this->url(), suggestedFileName); + } + catch (...) { /* Blank */} +} + +void WebView::onCurrentTitleChanged() +{ + const auto tabbar = KiwixApp::instance()->getTabWidget(); + const auto noAnchorUrl = url().url(QUrl::RemoveFragment); + const auto headersValid = m_headers["url"].toString() == noAnchorUrl; + + /* If headers not valid for this webview, then we are loading and the emit + will be handled by KiwixWebChannelObject::headersChanged. + */ + if (tabbar->currentWebView() == this && headersValid) + emit headersChanged(m_headers); +} + +void WebView::onHeadersReceived(const QString& headersJSONStr) +{ + QJsonDocument doc = QJsonDocument::fromJson(headersJSONStr.toUtf8()); + if (!doc.isObject()) + return; + + m_headers = doc.object(); + if (KiwixApp::instance()->getTabWidget()->currentWebView() == this) + emit headersChanged(m_headers); +} + +void WebView::onConsoleMessageReceived(const QString& message) +{ + // Parse the JSON message + QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8()); + if (!doc.isObject()) + return; + + QJsonObject obj = doc.object(); + QString type = obj["type"].toString(); + QString msg = obj["message"].toString(); + + // Log to console for debugging + qDebug() << "JS Console [" << type << "]: " << msg; +} + +void WebView::onNavigationRequested(const QString &url, const QString &anchor) +{ + // Safety checks + const auto tabbar = KiwixApp::instance()->getTabWidget(); + if (!tabbar || tabbar->currentWebView() != this) { + return; + } + + // Check if we're already at this anchor + if (this->url().hasFragment() && this->url().fragment() == anchor) { + // Already at this anchor, no need to navigate + return; + } + + // Create a QUrl with fragment for history + QUrl historyUrl(url); + historyUrl.setFragment(anchor); + + // Properly escape the URL and anchor for JavaScript + QString escapedUrl = url.toHtmlEscaped(); + QString escapedAnchor = anchor.toHtmlEscaped(); + + // Use a simpler approach to avoid navigation loops + QString js = QString( + "if (window.history && window.history.pushState) {" + " if (document.getElementById('%1')) {" + " window.history.pushState({anchor: '%1'}, '', '%2#%1');" + " console.log('History updated for anchor: %1');" + " } else {" + " console.error('Anchor not found: %1');" + " }" + "}" + ).arg(escapedAnchor).arg(escapedUrl); + + // Execute JavaScript safely with a callback + page()->runJavaScript(js, [this, url, anchor](const QVariant &result) { + Q_UNUSED(result); + // Emit the navigation signal to the JavaScript after the history is updated + // Use a small delay to prevent navigation loops + QTimer::singleShot(50, this, [this, url, anchor]() { + emit navigationRequested(url, anchor); + }); + }); +} + +// Add a method to handle history navigation for TOC entries +void WebView::handleTocHistoryNavigation(const QUrl &url) +{ + // Safety check + if (!url.isValid()) { + return; + } + + // If the URL has a fragment and the base URL matches the current page + if (url.hasFragment() && url.url(QUrl::RemoveFragment) == this->url().url(QUrl::RemoveFragment)) { + // Extract the anchor from the fragment + QString anchor = url.fragment(); + + // Safety check for empty anchor + if (anchor.isEmpty()) { + return; + } + + // Check if we're already at this anchor to prevent loops + if (this->url().hasFragment() && this->url().fragment() == anchor) { + return; + } + + // Use a small delay to prevent navigation loops + QTimer::singleShot(50, this, [this, url, anchor]() { + // Emit navigation signal instead of loading the page + emit navigationRequested(url.url(QUrl::RemoveFragment), anchor); + }); + + return; + } +} + +void WebView::addHistoryItemAction(QMenu *menu, + const QWebEngineHistoryItem &item, + int n) const +{ + QAction *a = menu->addAction(item.title()); + a->setData(QVariant::fromValue(n)); + connect(a, &QAction::triggered, this, &WebView::gotoTriggeredHistoryItemAction); +} + +void WebView::gotoTriggeredHistoryItemAction() +{ + QAction *a = qobject_cast(QObject::sender()); + if (!a) + return; + + int n = a->data().toInt(); + QWebEngineHistory *h = history(); + if (n < 0 || n >= h->count()) + return; + + h->goToItem(h->itemAt(n)); +} + + +QWebEngineView* WebView::createWindow(QWebEnginePage::WebWindowType type) +{ + if ( type==QWebEnginePage::WebBrowserBackgroundTab + || type==QWebEnginePage::WebBrowserTab ) + { + auto tabWidget = KiwixApp::instance()->getTabWidget(); + return tabWidget->createNewTab(false, true)->getWebView(); + } + return nullptr; +} + +void WebView::onUrlChanged(const QUrl& url) { + auto zimId = getZimIdFromUrl(url); + auto app = KiwixApp::instance(); + app->saveListOfOpenTabs(); + if (m_currentZimId == zimId ) { + // Even if the ZIM ID hasn't changed, we still need to update TOC selection + if (url.hasFragment()) { + auto tocBar = app->getMainWindow()->getTableOfContentBar(); + if (tocBar) { + tocBar->updateSelectionFromFragment(url.fragment()); + } + } + return; + } + m_currentZimId = zimId; + emit zimIdChanged(m_currentZimId); + m_icon = app->getLibrary()->getBookIcon(m_currentZimId); + auto zoomFactor = app->getSettingsManager()->getZoomFactorByZimId(zimId); + this->setZoomFactor(zoomFactor); + emit iconChanged(m_icon); +} + +void WebView::wheelEvent(QWheelEvent *event) { + if ((event->modifiers() & Qt::ControlModifier) != 0) + { + if (event->angleDelta().y() > 0) { + KiwixApp::instance()->getAction(KiwixApp::ZoomInAction)->activate(QAction::Trigger); + } else if (event->angleDelta().y() < 0) { + KiwixApp::instance()->getAction(KiwixApp::ZoomOutAction)->activate(QAction::Trigger); + } + } +} + + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu* menu; + if (m_linkHovered.isEmpty()) { + menu = createStandardContextMenu(); + } else { + menu = createLinkContextMenu(); + } + +#if defined(QT_TEXTTOSPEECH_LIB) + const auto app = KiwixApp::instance(); + menu->addAction(app->getAction(KiwixApp::ReadArticleAction)); + if (page()->hasSelection()) + menu->addAction(app->getAction(KiwixApp::ReadTextAction)); +#endif + + menu->exec(event->globalPos()); +} + + +QMenu* WebView::createStandardContextMenu() { + auto app = KiwixApp::instance(); + + QMenu* menu = new QMenu(this); + auto backAction = new QAction(gt("back")); + backAction->setEnabled(app->getAction(KiwixApp::HistoryBackAction)->isEnabled()); + backAction->setIcon(app->getAction(KiwixApp::HistoryBackAction)->icon()); + menu->addAction(backAction); + connect(menu, &QObject::destroyed, backAction, &QObject::deleteLater); + connect(backAction, &QAction::triggered, this, [=](bool checked) { + Q_UNUSED(checked); + KiwixApp::instance()->getTabWidget()->triggerWebPageAction(QWebEnginePage::Back); + }); + + auto forwardAction = new QAction(gt("forward")); + forwardAction->setEnabled(app->getAction(KiwixApp::HistoryForwardAction)->isEnabled()); + forwardAction->setIcon(app->getAction(KiwixApp::HistoryForwardAction)->icon()); + menu->addAction(forwardAction); + connect(menu, &QObject::destroyed, forwardAction, &QObject::deleteLater); + connect(forwardAction, &QAction::triggered, this, [=](bool checked) { + Q_UNUSED(checked); + KiwixApp::instance()->getTabWidget()->triggerWebPageAction(QWebEnginePage::Forward); + }); + + menu->addAction(app->getAction(KiwixApp::SavePageAsAction)); + return menu; +} + + +QMenu* WebView::createLinkContextMenu() { + QMenu* menu = new QMenu(this); + + if (!m_linkHovered.startsWith("zim://")) { + auto openLinkInWebBrowserAction = new QAction(gt("open-link-in-web-browser")); + menu->addAction(openLinkInWebBrowserAction); + connect(menu, &QObject::destroyed, openLinkInWebBrowserAction, &QObject::deleteLater); + connect(openLinkInWebBrowserAction, &QAction::triggered, this, [=](bool checked) { + Q_UNUSED(checked); + QDesktopServices::openUrl(m_linkHovered); + }); + } else { + auto openLinkNewTab = new QAction(gt("open-link-new-tab")); + openLinkNewTab->setIcon(QIcon(":/icons/new-tab-icon.svg")); + menu->addAction(openLinkNewTab); + connect(menu, &QObject::destroyed, openLinkNewTab, &QObject::deleteLater); + connect(openLinkNewTab, &QAction::triggered, this, [=](bool checked) { + Q_UNUSED(checked); + KiwixApp::instance()->openUrl(m_linkHovered, true); + }); + } + + return menu; +} + + +bool WebView::eventFilter(QObject *src, QEvent *e) +{ + Q_UNUSED(src) + // work around QTBUG-43602 + if (e->type() == QEvent::Wheel) { + auto we = static_cast(e); + if (we->modifiers() == Qt::ControlModifier) + return true; + } + if (e->type() == QEvent::MouseButtonRelease) { + auto me = static_cast(e); + if (!m_linkHovered.startsWith("zim://") + && (me->modifiers() == Qt::ControlModifier || me->button() == Qt::MiddleButton)) + { + QDesktopServices::openUrl(m_linkHovered); + return true; + } + if (me->button() == Qt::BackButton) + { + back(); + return true; + } + if (me->button() == Qt::ForwardButton) + { + forward(); + return true; + } + } + return false; +} + +bool WebView::event(QEvent *event) +{ + // work around QTBUG-43602 + if (event->type() == QEvent::ChildAdded) { + auto ce = static_cast(event); + ce->child()->installEventFilter(this); + } else if (event->type() == QEvent::ChildRemoved) { + auto ce = static_cast(event); + ce->child()->removeEventFilter(this); + } + if (event->type() == QEvent::ToolTip) { + return true; + } else { + return QWebEngineView::event(event); + } + return true; +} diff --git a/src/webview.h b/src/webview.h index d65639207..725a37f19 100644 --- a/src/webview.h +++ b/src/webview.h @@ -1,85 +1,87 @@ -#ifndef WEBVIEW_H -#define WEBVIEW_H - -#include -#include -#include -#include -#include - -#include "findinpagebar.h" - -class QWebEngineHistoryItem; - - -class WebViewBackMenu : public QMenu -{ - Q_OBJECT -public: - WebViewBackMenu(QWidget* parent=nullptr) : QMenu(parent) {} - void showEvent(QShowEvent *); -}; - -class WebViewForwardMenu : public QMenu -{ - Q_OBJECT -public: - WebViewForwardMenu(QWidget* parent=nullptr) : QMenu(parent) {} - void showEvent(QShowEvent *); -}; - - -class WebView : public QWebEngineView -{ - Q_OBJECT - Q_PROPERTY(const QIcon icon READ icon NOTIFY iconChanged) - Q_PROPERTY(QString zimId READ zimId NOTIFY zimIdChanged) - -public: - WebView(QWidget *parent = Q_NULLPTR); - virtual ~WebView(); - - bool isWebActionEnabled(QWebEnginePage::WebAction webAction) const; - const QIcon &icon() { return m_icon; } - const QString &zimId() { return m_currentZimId; } - - QMenu* getHistoryBackMenu() const; - QMenu* getHistoryForwardMenu() const; - - void saveViewContent(); - -public slots: - void onUrlChanged(const QUrl& url); - -signals: - void iconChanged(const QIcon& icon); - void zimIdChanged(const QString& zimId); - void headersChanged(const QJsonObject& headers); - void navigationRequested(const QString& url, const QString& anchor); - -protected: - virtual QWebEngineView* createWindow(QWebEnginePage::WebWindowType type); - void wheelEvent(QWheelEvent *event); - bool event(QEvent *event); - bool eventFilter(QObject *src, QEvent *e); - void contextMenuEvent(QContextMenuEvent *event); - - QString m_currentZimId; - QIcon m_icon; - QString m_linkHovered; - -private slots: - void gotoTriggeredHistoryItemAction(); - void onCurrentTitleChanged(); - void onHeadersReceived(const QString& headersJSONStr); - void onNavigationRequested(const QString& url, const QString& anchor); - -private: - void addHistoryItemAction(QMenu *menu, const QWebEngineHistoryItem &item, int n) const; - void applyCorrectZoomFactor(); - QMenu* createStandardContextMenu(); - QMenu* createLinkContextMenu(); - QJsonObject m_headers; -}; - -#endif // WEBVIEW_H +#ifndef WEBVIEW_H +#define WEBVIEW_H + +#include +#include +#include +#include +#include + +#include "findinpagebar.h" + +class QWebEngineHistoryItem; + + +class WebViewBackMenu : public QMenu +{ + Q_OBJECT +public: + WebViewBackMenu(QWidget* parent=nullptr) : QMenu(parent) {} + void showEvent(QShowEvent *); +}; + +class WebViewForwardMenu : public QMenu +{ + Q_OBJECT +public: + WebViewForwardMenu(QWidget* parent=nullptr) : QMenu(parent) {} + void showEvent(QShowEvent *); +}; + + +class WebView : public QWebEngineView +{ + Q_OBJECT + Q_PROPERTY(const QIcon icon READ icon NOTIFY iconChanged) + Q_PROPERTY(QString zimId READ zimId NOTIFY zimIdChanged) + +public: + WebView(QWidget *parent = Q_NULLPTR); + virtual ~WebView(); + + bool isWebActionEnabled(QWebEnginePage::WebAction webAction) const; + const QIcon &icon() { return m_icon; } + const QString &zimId() { return m_currentZimId; } + + QMenu* getHistoryBackMenu() const; + QMenu* getHistoryForwardMenu() const; + + void saveViewContent(); + +public slots: + void onUrlChanged(const QUrl& url); + +signals: + void iconChanged(const QIcon& icon); + void zimIdChanged(const QString& zimId); + void headersChanged(const QJsonObject& headers); + void navigationRequested(const QString& url, const QString& anchor); + +protected: + virtual QWebEngineView* createWindow(QWebEnginePage::WebWindowType type); + void wheelEvent(QWheelEvent *event); + bool event(QEvent *event); + bool eventFilter(QObject *src, QEvent *e); + void contextMenuEvent(QContextMenuEvent *event); + + QString m_currentZimId; + QIcon m_icon; + QString m_linkHovered; + +private slots: + void gotoTriggeredHistoryItemAction(); + void onCurrentTitleChanged(); + void onHeadersReceived(const QString& headersJSONStr); + void onConsoleMessageReceived(const QString& message); + void onNavigationRequested(const QString& url, const QString& anchor); + void handleTocHistoryNavigation(const QUrl& url); + +private: + void addHistoryItemAction(QMenu *menu, const QWebEngineHistoryItem &item, int n) const; + void applyCorrectZoomFactor(); + QMenu* createStandardContextMenu(); + QMenu* createLinkContextMenu(); + QJsonObject m_headers; +}; + +#endif // WEBVIEW_H diff --git a/src/zimview.cpp b/src/zimview.cpp index d1dd74c7c..3e2e223fb 100644 --- a/src/zimview.cpp +++ b/src/zimview.cpp @@ -1,155 +1,155 @@ -#include "zimview.h" -#include "kiwixapp.h" -#include -#include -#include - -#if defined(QT_TEXTTOSPEECH_LIB) -#include "texttospeechbar.h" -#endif - -ZimView::ZimView(TabBar *tabBar, QWidget *parent) - : QWidget(parent), - mp_tabBar(tabBar), - mp_findInPageBar(new FindInPageBar(this)) -{ - mp_webView = new WebView(); - QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(mp_webView); - layout->addWidget(mp_findInPageBar); - layout->setContentsMargins(0,0,0,0); - layout->setSpacing(0); - setLayout(layout); // now 'mp_webView' has 'this' as the parent QObject - mp_findInPageBar->hide(); - auto app = KiwixApp::instance(); - -#if defined(QT_TEXTTOSPEECH_LIB) - mp_ttsBar = new TextToSpeechBar(this); - layout->addWidget(mp_ttsBar); - mp_ttsBar->hide(); - connect(mp_webView, &WebView::zimIdChanged, this, &ZimView::setSpeechLocaleByZimId); - connect(app->getAction(KiwixApp::ReadArticleAction), &QAction::triggered, this, &ZimView::readArticle); - connect(app->getAction(KiwixApp::ReadTextAction), &QAction::triggered, this, &ZimView::readSelectedText); -#endif - - connect(app->getAction(KiwixApp::ZoomInAction), &QAction::triggered, - this, [=]() { - if (mp_tabBar->currentZimView() != this) - return; - auto zoomFactor = mp_webView->zoomFactor(); - zoomFactor += 0.1; - zoomFactor = std::max(std::min(zoomFactor, 5.0), 0.25); - mp_webView->setZoomFactor(zoomFactor); - auto key = mp_webView->zimId() + "/zoomFactor"; - KiwixApp::instance()->getSettingsManager()->setSettings(key, zoomFactor); - }); - connect(app->getAction(KiwixApp::ZoomOutAction), &QAction::triggered, - this, [=]() { - if (mp_tabBar->currentZimView() != this) - return; - auto zoomFactor = mp_webView->zoomFactor(); - zoomFactor -= 0.1; - zoomFactor = std::max(std::min(zoomFactor, 5.0), 0.25); - mp_webView->setZoomFactor(zoomFactor); - auto key = mp_webView->zimId() + "/zoomFactor"; - KiwixApp::instance()->getSettingsManager()->setSettings(key, zoomFactor); - }); - connect(app->getAction(KiwixApp::ZoomResetAction), &QAction::triggered, - this, [=]() { - if (mp_tabBar->currentZimView() != this) - return; - auto settingsManager = KiwixApp::instance()->getSettingsManager(); - mp_webView->setZoomFactor(settingsManager->getZoomFactor()); - auto key = mp_webView->zimId() + "/zoomFactor"; - settingsManager->deleteSettings(key); - }); - connect(app->getAction(KiwixApp::SavePageAsAction), &QAction::triggered, - this, [=](){ - if (mp_tabBar->currentZimView() == this) - mp_webView->saveViewContent(); - }); - connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::zoomChanged, this, [=]() { - auto key = mp_webView->zimId() + "/zoomFactor"; - auto zimZoomFactor = KiwixApp::instance()->getSettingsManager()->getSettings(key); - if(!zimZoomFactor.toBool()) { - mp_webView->setZoomFactor(KiwixApp::instance()->getSettingsManager()->getZoomFactor()); - } - }); - connect(mp_webView->page(), &QWebEnginePage::fullScreenRequested, mp_tabBar, &TabBar::fullScreenRequested); - connect(mp_webView, &WebView::titleChanged, mp_tabBar, &TabBar::on_webview_titleChanged); - connect(mp_webView, &WebView::iconChanged, this, - [=](const QIcon& icon) { mp_tabBar->setIconOf(icon, this); }); - - connect(mp_webView->page()->action(QWebEnginePage::Back), &QAction::changed, - [this]() { - emit webActionEnabledChanged(QWebEnginePage::Back, this->mp_webView->isWebActionEnabled(QWebEnginePage::Back)); - }); - connect(mp_webView->page()->action(QWebEnginePage::Forward), &QAction::changed, - [this]() { - emit webActionEnabledChanged(QWebEnginePage::Forward, this->mp_webView->isWebActionEnabled(QWebEnginePage::Forward)); - }); - connect(mp_webView->page(), &QWebEnginePage::linkHovered, this, - [=](const QString& url) { - if (mp_tabBar->currentIndex() == 0) { - return; - } - if (url.isEmpty()) { - QToolTip::hideText(); - } else { - auto link = url; - if (url.startsWith("zim://")) { - link = QUrl(url).path(); - } - - /* because we use QWebEnginePage::linkHovered signal, - * we can be sure the current tab is a web page - * (mp_tabBar->currentWebView() is not nullptr) - */ - auto pos = mp_tabBar->mapToGlobal(QPoint(-3, mp_tabBar->currentWebView()->height() - mp_tabBar->PdmHeightMM + 2)); - QToolTip::showText(pos, link); - } - }); -} - -void ZimView::openFindInPageBar() -{ - mp_findInPageBar->show(); - mp_findInPageBar->getFindLineEdit()->setFocus(); -} - -#if defined(QT_TEXTTOSPEECH_LIB) -void ZimView::readArticle() -{ - if (mp_tabBar->currentZimView() != this) - return; - - mp_webView->page()->toPlainText([=](const QString& articleText){ - mp_ttsBar->speak(articleText); - mp_ttsBar->speechShow(); - }); -} - -void ZimView::readSelectedText() -{ - if (mp_tabBar->currentZimView() != this || !mp_webView->page()->hasSelection()) - return; - - mp_ttsBar->speak(mp_webView->page()->selectedText()); - mp_ttsBar->speechShow(); -} - -void ZimView::setSpeechLocaleByZimId(const QString& zimId) -{ - try - { - const auto book = KiwixApp::instance()->getLibrary()->getBookById(zimId); - const auto iso3 = QString::fromStdString(book.getLanguages().at(0)); - const auto iso2 = iso3.chopped(1); - - /* Try both 3 letter and 2 letter name. */ - const auto iso2Locale = QLocale(iso2); - const bool isValidISO2 = iso2Locale.language() != QLocale::C; - mp_ttsBar->setLocale(isValidISO2 ? iso2Locale : QLocale(iso3)); - } catch (...) { /* Blank */ } -} -#endif +#include "zimview.h" +#include "kiwixapp.h" +#include +#include +#include + +#if defined(QT_TEXTTOSPEECH_LIB) +#include "texttospeechbar.h" +#endif + +ZimView::ZimView(TabBar *tabBar, QWidget *parent) + : QWidget(parent), + mp_tabBar(tabBar), + mp_findInPageBar(new FindInPageBar(this)) +{ + mp_webView = new WebView(); + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(mp_webView); + layout->addWidget(mp_findInPageBar); + layout->setContentsMargins(0,0,0,0); + layout->setSpacing(0); + setLayout(layout); // now 'mp_webView' has 'this' as the parent QObject + mp_findInPageBar->hide(); + auto app = KiwixApp::instance(); + +#if defined(QT_TEXTTOSPEECH_LIB) + mp_ttsBar = new TextToSpeechBar(this); + layout->addWidget(mp_ttsBar); + mp_ttsBar->hide(); + connect(mp_webView, &WebView::zimIdChanged, this, &ZimView::setSpeechLocaleByZimId); + connect(app->getAction(KiwixApp::ReadArticleAction), &QAction::triggered, this, &ZimView::readArticle); + connect(app->getAction(KiwixApp::ReadTextAction), &QAction::triggered, this, &ZimView::readSelectedText); +#endif + + connect(app->getAction(KiwixApp::ZoomInAction), &QAction::triggered, + this, [=]() { + if (mp_tabBar->currentZimView() != this) + return; + auto zoomFactor = mp_webView->zoomFactor(); + zoomFactor += 0.1; + zoomFactor = std::max(std::min(zoomFactor, 5.0), 0.25); + mp_webView->setZoomFactor(zoomFactor); + auto key = mp_webView->zimId() + "/zoomFactor"; + KiwixApp::instance()->getSettingsManager()->setSettings(key, zoomFactor); + }); + connect(app->getAction(KiwixApp::ZoomOutAction), &QAction::triggered, + this, [=]() { + if (mp_tabBar->currentZimView() != this) + return; + auto zoomFactor = mp_webView->zoomFactor(); + zoomFactor -= 0.1; + zoomFactor = std::max(std::min(zoomFactor, 5.0), 0.25); + mp_webView->setZoomFactor(zoomFactor); + auto key = mp_webView->zimId() + "/zoomFactor"; + KiwixApp::instance()->getSettingsManager()->setSettings(key, zoomFactor); + }); + connect(app->getAction(KiwixApp::ZoomResetAction), &QAction::triggered, + this, [=]() { + if (mp_tabBar->currentZimView() != this) + return; + auto settingsManager = KiwixApp::instance()->getSettingsManager(); + mp_webView->setZoomFactor(settingsManager->getZoomFactor()); + auto key = mp_webView->zimId() + "/zoomFactor"; + settingsManager->deleteSettings(key); + }); + connect(app->getAction(KiwixApp::SavePageAsAction), &QAction::triggered, + this, [=](){ + if (mp_tabBar->currentZimView() == this) + mp_webView->saveViewContent(); + }); + connect(KiwixApp::instance()->getSettingsManager(), &SettingsManager::zoomChanged, this, [=]() { + auto key = mp_webView->zimId() + "/zoomFactor"; + auto zimZoomFactor = KiwixApp::instance()->getSettingsManager()->getSettings(key); + if(!zimZoomFactor.toBool()) { + mp_webView->setZoomFactor(KiwixApp::instance()->getSettingsManager()->getZoomFactor()); + } + }); + connect(mp_webView->page(), &QWebEnginePage::fullScreenRequested, mp_tabBar, &TabBar::fullScreenRequested); + connect(mp_webView, &WebView::titleChanged, mp_tabBar, &TabBar::on_webview_titleChanged); + connect(mp_webView, &WebView::iconChanged, this, + [=](const QIcon& icon) { mp_tabBar->setIconOf(icon, this); }); + + connect(mp_webView->page()->action(QWebEnginePage::Back), &QAction::changed, + [this]() { + emit webActionEnabledChanged(QWebEnginePage::Back, this->mp_webView->isWebActionEnabled(QWebEnginePage::Back)); + }); + connect(mp_webView->page()->action(QWebEnginePage::Forward), &QAction::changed, + [this]() { + emit webActionEnabledChanged(QWebEnginePage::Forward, this->mp_webView->isWebActionEnabled(QWebEnginePage::Forward)); + }); + connect(mp_webView->page(), &QWebEnginePage::linkHovered, this, + [=](const QString& url) { + if (mp_tabBar->currentIndex() == 0) { + return; + } + if (url.isEmpty()) { + QToolTip::hideText(); + } else { + auto link = url; + if (url.startsWith("zim://")) { + link = QUrl(url).path(); + } + + /* because we use QWebEnginePage::linkHovered signal, + * we can be sure the current tab is a web page + * (mp_tabBar->currentWebView() is not nullptr) + */ + auto pos = mp_tabBar->mapToGlobal(QPoint(-3, mp_tabBar->currentWebView()->height() - mp_tabBar->PdmHeightMM + 2)); + QToolTip::showText(pos, link); + } + }); +} + +void ZimView::openFindInPageBar() +{ + mp_findInPageBar->show(); + mp_findInPageBar->getFindLineEdit()->setFocus(); +} + +#if defined(QT_TEXTTOSPEECH_LIB) +void ZimView::readArticle() +{ + if (mp_tabBar->currentZimView() != this) + return; + + mp_webView->page()->toPlainText([=](const QString& articleText){ + mp_ttsBar->speak(articleText); + mp_ttsBar->speechShow(); + }); +} + +void ZimView::readSelectedText() +{ + if (mp_tabBar->currentZimView() != this || !mp_webView->page()->hasSelection()) + return; + + mp_ttsBar->speak(mp_webView->page()->selectedText()); + mp_ttsBar->speechShow(); +} + +void ZimView::setSpeechLocaleByZimId(const QString& zimId) +{ + try + { + const auto book = KiwixApp::instance()->getLibrary()->getBookById(zimId); + const auto iso3 = QString::fromStdString(book.getLanguages().at(0)); + const auto iso2 = iso3.chopped(1); + + /* Try both 3 letter and 2 letter name. */ + const auto iso2Locale = QLocale(iso2); + const bool isValidISO2 = iso2Locale.language() != QLocale::C; + mp_ttsBar->setLocale(isValidISO2 ? iso2Locale : QLocale(iso3)); + } catch (...) { /* Blank */ } +} +#endif diff --git a/src/zimview.h b/src/zimview.h index 6bd84f69c..8ed9e30fd 100644 --- a/src/zimview.h +++ b/src/zimview.h @@ -1,48 +1,48 @@ -#ifndef ZIMVIEW_H -#define ZIMVIEW_H - -#include -#include - -class FindInPageBar; -class TabBar; -class WebView; - -#if defined(QT_TEXTTOSPEECH_LIB) -class TextToSpeechBar; -#endif - -class ZimView : public QWidget -{ - Q_OBJECT -public: - explicit ZimView(TabBar* tabBar, QWidget *parent = nullptr); - - WebView *getWebView() { return mp_webView; } - FindInPageBar *getFindInPageBar() { return mp_findInPageBar; } -#if defined(QT_TEXTTOSPEECH_LIB) - TextToSpeechBar *getTextToSpeechBar() { return mp_ttsBar; } -#endif - void openFindInPageBar(); - -public slots: -#if defined(QT_TEXTTOSPEECH_LIB) - void readArticle(); - void readSelectedText(); - void setSpeechLocaleByZimId(const QString& zimId); -#endif - -signals: - void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); - -private: - WebView *mp_webView; - TabBar *mp_tabBar; - FindInPageBar *mp_findInPageBar; - -#if defined(QT_TEXTTOSPEECH_LIB) - TextToSpeechBar *mp_ttsBar; -#endif -}; - -#endif // ZIMVIEW_H +#ifndef ZIMVIEW_H +#define ZIMVIEW_H + +#include +#include + +class FindInPageBar; +class TabBar; +class WebView; + +#if defined(QT_TEXTTOSPEECH_LIB) +class TextToSpeechBar; +#endif + +class ZimView : public QWidget +{ + Q_OBJECT +public: + explicit ZimView(TabBar* tabBar, QWidget *parent = nullptr); + + WebView *getWebView() { return mp_webView; } + FindInPageBar *getFindInPageBar() { return mp_findInPageBar; } +#if defined(QT_TEXTTOSPEECH_LIB) + TextToSpeechBar *getTextToSpeechBar() { return mp_ttsBar; } +#endif + void openFindInPageBar(); + +public slots: +#if defined(QT_TEXTTOSPEECH_LIB) + void readArticle(); + void readSelectedText(); + void setSpeechLocaleByZimId(const QString& zimId); +#endif + +signals: + void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled); + +private: + WebView *mp_webView; + TabBar *mp_tabBar; + FindInPageBar *mp_findInPageBar; + +#if defined(QT_TEXTTOSPEECH_LIB) + TextToSpeechBar *mp_ttsBar; +#endif +}; + +#endif // ZIMVIEW_H diff --git a/subprojects/QtSingleApplication/common.pri b/subprojects/QtSingleApplication/common.pri index 924c57c88..73bdc2758 100644 --- a/subprojects/QtSingleApplication/common.pri +++ b/subprojects/QtSingleApplication/common.pri @@ -1,14 +1,14 @@ -exists(config.pri):infile(config.pri, SOLUTIONS_LIBRARY, yes): CONFIG += qtsingleapplication-uselib - -TEMPLATE += fakelib -greaterThan(QT_MAJOR_VERSION, 5)|\ - if(equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 4))|\ - if(equals(QT_MAJOR_VERSION, 5):equals(QT_MINOR_VERSION, 4):greaterThan(QT_PATCH_VERSION, 1)) { - QTSINGLEAPPLICATION_LIBNAME = $$qt5LibraryTarget(QtSolutions_SingleApplication-head) -} else { - QTSINGLEAPPLICATION_LIBNAME = $$qtLibraryTarget(QtSolutions_SingleApplication-head) -} -TEMPLATE -= fakelib - -QTSINGLEAPPLICATION_LIBDIR = $$PWD/lib -unix:qtsingleapplication-uselib:!qtsingleapplication-buildlib:QMAKE_RPATHDIR += $$QTSINGLEAPPLICATION_LIBDIR +exists(config.pri):infile(config.pri, SOLUTIONS_LIBRARY, yes): CONFIG += qtsingleapplication-uselib + +TEMPLATE += fakelib +greaterThan(QT_MAJOR_VERSION, 5)|\ + if(equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 4))|\ + if(equals(QT_MAJOR_VERSION, 5):equals(QT_MINOR_VERSION, 4):greaterThan(QT_PATCH_VERSION, 1)) { + QTSINGLEAPPLICATION_LIBNAME = $$qt5LibraryTarget(QtSolutions_SingleApplication-head) +} else { + QTSINGLEAPPLICATION_LIBNAME = $$qtLibraryTarget(QtSolutions_SingleApplication-head) +} +TEMPLATE -= fakelib + +QTSINGLEAPPLICATION_LIBDIR = $$PWD/lib +unix:qtsingleapplication-uselib:!qtsingleapplication-buildlib:QMAKE_RPATHDIR += $$QTSINGLEAPPLICATION_LIBDIR diff --git a/subprojects/QtSingleApplication/src/QtLockedFile b/subprojects/QtSingleApplication/src/QtLockedFile index 16b48ba9d..642afda07 100644 --- a/subprojects/QtSingleApplication/src/QtLockedFile +++ b/subprojects/QtSingleApplication/src/QtLockedFile @@ -1 +1 @@ -#include "qtlockedfile.h" +#include "qtlockedfile.h" diff --git a/subprojects/QtSingleApplication/src/QtSingleApplication b/subprojects/QtSingleApplication/src/QtSingleApplication index d111bf72d..baa6ea94a 100644 --- a/subprojects/QtSingleApplication/src/QtSingleApplication +++ b/subprojects/QtSingleApplication/src/QtSingleApplication @@ -1 +1 @@ -#include "qtsingleapplication.h" +#include "qtsingleapplication.h" diff --git a/subprojects/QtSingleApplication/src/qtlocalpeer.cpp b/subprojects/QtSingleApplication/src/qtlocalpeer.cpp index 254668559..776897a8e 100644 --- a/subprojects/QtSingleApplication/src/qtlocalpeer.cpp +++ b/subprojects/QtSingleApplication/src/qtlocalpeer.cpp @@ -1,223 +1,223 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtlocalpeer.h" -#include -#include -#include -#include - -#if defined(Q_OS_WIN) -#include -#include -typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); -static PProcessIdToSessionId pProcessIdToSessionId = 0; -#endif -#if defined(Q_OS_UNIX) -#include -#include -#include -#endif - -namespace QtLP_Private { -#include "qtlockedfile.cpp" -#if defined(Q_OS_WIN) -#include "qtlockedfile_win.cpp" -#else -#include "qtlockedfile_unix.cpp" -#endif -} - -const char* QtLocalPeer::ack = "ack"; - -QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) - : QObject(parent), id(appId) -{ - QString prefix = id; - if (id.isEmpty()) { - id = QCoreApplication::applicationFilePath(); -#if defined(Q_OS_WIN) - id = id.toLower(); -#endif - prefix = id.section(u'/', -1); - } -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - prefix.remove(QRegExp("[^a-zA-Z]")); -#else - prefix.remove(QRegularExpression("[^a-zA-Z]")); -#endif - prefix.truncate(6); - - QByteArray idc = id.toUtf8(); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - quint16 idNum = qChecksum(idc.constData(), idc.size()); -#else - QByteArrayView v = QByteArrayView(idc); - quint16 idNum = qChecksum(v); -#endif - - socketName = QLatin1String("qtsingleapp-") + prefix - + u'-' + QString::number(idNum, 16); - -#if defined(Q_OS_WIN) - if (!pProcessIdToSessionId) { - QLibrary lib("kernel32"); - pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); - } - if (pProcessIdToSessionId) { - DWORD sessionId = 0; - pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); - socketName += QLatin1Char('-') + QString::number(sessionId, 16); - } -#else - socketName += u'-' + QString::number(::getuid(), 16); -#endif - - server = new QLocalServer(this); - QString lockName = QDir(QDir::tempPath()).absolutePath() - + u'/' + socketName - + QLatin1String("-lockfile"); - lockFile.setFileName(lockName); - lockFile.open(QIODevice::ReadWrite); -} - - - -bool QtLocalPeer::isClient() -{ - if (lockFile.isLocked()) - return false; - - if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) - return true; - - bool res = server->listen(socketName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) - // ### Workaround - if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { - QFile::remove(QDir::cleanPath(QDir::tempPath())+u'/'+socketName); - res = server->listen(socketName); - } -#endif - if (!res) - qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); - QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); - return false; -} - - -bool QtLocalPeer::sendMessage(const QString &message, int timeout) -{ - if (!isClient()) - return false; - - QLocalSocket socket; - bool connOk = false; - for(int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(timeout/2); - if (connOk || i) - break; - int ms = 250; -#if defined(Q_OS_WIN) - Sleep(DWORD(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, NULL); -#endif - } - if (!connOk) - return false; - - QByteArray uMsg(message.toUtf8()); - QDataStream ds(&socket); - ds.writeBytes(uMsg.constData(), uMsg.size()); - bool res = socket.waitForBytesWritten(timeout); - if (res) { - res &= socket.waitForReadyRead(timeout); // wait for ack - if (res) - res &= (socket.read(qstrlen(ack)) == ack); - } - return res; -} - - -void QtLocalPeer::receiveConnection() -{ - QLocalSocket* socket = server->nextPendingConnection(); - if (!socket) - return; - - while (true) { - if (socket->state() == QLocalSocket::UnconnectedState) { - qWarning("QtLocalPeer: Peer disconnected"); - delete socket; - return; - } - if (socket->bytesAvailable() >= qint64(sizeof(quint32))) - break; - socket->waitForReadyRead(); - } - - QDataStream ds(socket); - QByteArray uMsg; - quint32 remaining; - ds >> remaining; - uMsg.resize(remaining); - int got = 0; - char* uMsgBuf = uMsg.data(); - do { - got = ds.readRawData(uMsgBuf, remaining); - remaining -= got; - uMsgBuf += got; - } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); - if (got < 0) { - qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); - delete socket; - return; - } - QString message(QString::fromUtf8(uMsg)); - socket->write(ack, qstrlen(ack)); - socket->waitForBytesWritten(1000); - socket->waitForDisconnected(1000); // make sure client reads ack - delete socket; - emit messageReceived(message); //### (might take a long time to return) -} +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtlocalpeer.h" +#include +#include +#include +#include + +#if defined(Q_OS_WIN) +#include +#include +typedef BOOL(WINAPI*PProcessIdToSessionId)(DWORD,DWORD*); +static PProcessIdToSessionId pProcessIdToSessionId = 0; +#endif +#if defined(Q_OS_UNIX) +#include +#include +#include +#endif + +namespace QtLP_Private { +#include "qtlockedfile.cpp" +#if defined(Q_OS_WIN) +#include "qtlockedfile_win.cpp" +#else +#include "qtlockedfile_unix.cpp" +#endif +} + +const char* QtLocalPeer::ack = "ack"; + +QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId) + : QObject(parent), id(appId) +{ + QString prefix = id; + if (id.isEmpty()) { + id = QCoreApplication::applicationFilePath(); +#if defined(Q_OS_WIN) + id = id.toLower(); +#endif + prefix = id.section(u'/', -1); + } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + prefix.remove(QRegExp("[^a-zA-Z]")); +#else + prefix.remove(QRegularExpression("[^a-zA-Z]")); +#endif + prefix.truncate(6); + + QByteArray idc = id.toUtf8(); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + quint16 idNum = qChecksum(idc.constData(), idc.size()); +#else + QByteArrayView v = QByteArrayView(idc); + quint16 idNum = qChecksum(v); +#endif + + socketName = QLatin1String("qtsingleapp-") + prefix + + u'-' + QString::number(idNum, 16); + +#if defined(Q_OS_WIN) + if (!pProcessIdToSessionId) { + QLibrary lib("kernel32"); + pProcessIdToSessionId = (PProcessIdToSessionId)lib.resolve("ProcessIdToSessionId"); + } + if (pProcessIdToSessionId) { + DWORD sessionId = 0; + pProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + socketName += QLatin1Char('-') + QString::number(sessionId, 16); + } +#else + socketName += u'-' + QString::number(::getuid(), 16); +#endif + + server = new QLocalServer(this); + QString lockName = QDir(QDir::tempPath()).absolutePath() + + u'/' + socketName + + QLatin1String("-lockfile"); + lockFile.setFileName(lockName); + lockFile.open(QIODevice::ReadWrite); +} + + + +bool QtLocalPeer::isClient() +{ + if (lockFile.isLocked()) + return false; + + if (!lockFile.lock(QtLP_Private::QtLockedFile::WriteLock, false)) + return true; + + bool res = server->listen(socketName); +#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) + // ### Workaround + if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(QDir::cleanPath(QDir::tempPath())+u'/'+socketName); + res = server->listen(socketName); + } +#endif + if (!res) + qWarning("QtSingleCoreApplication: listen on local socket failed, %s", qPrintable(server->errorString())); + QObject::connect(server, SIGNAL(newConnection()), SLOT(receiveConnection())); + return false; +} + + +bool QtLocalPeer::sendMessage(const QString &message, int timeout) +{ + if (!isClient()) + return false; + + QLocalSocket socket; + bool connOk = false; + for(int i = 0; i < 2; i++) { + // Try twice, in case the other instance is just starting up + socket.connectToServer(socketName); + connOk = socket.waitForConnected(timeout/2); + if (connOk || i) + break; + int ms = 250; +#if defined(Q_OS_WIN) + Sleep(DWORD(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif + } + if (!connOk) + return false; + + QByteArray uMsg(message.toUtf8()); + QDataStream ds(&socket); + ds.writeBytes(uMsg.constData(), uMsg.size()); + bool res = socket.waitForBytesWritten(timeout); + if (res) { + res &= socket.waitForReadyRead(timeout); // wait for ack + if (res) + res &= (socket.read(qstrlen(ack)) == ack); + } + return res; +} + + +void QtLocalPeer::receiveConnection() +{ + QLocalSocket* socket = server->nextPendingConnection(); + if (!socket) + return; + + while (true) { + if (socket->state() == QLocalSocket::UnconnectedState) { + qWarning("QtLocalPeer: Peer disconnected"); + delete socket; + return; + } + if (socket->bytesAvailable() >= qint64(sizeof(quint32))) + break; + socket->waitForReadyRead(); + } + + QDataStream ds(socket); + QByteArray uMsg; + quint32 remaining; + ds >> remaining; + uMsg.resize(remaining); + int got = 0; + char* uMsgBuf = uMsg.data(); + do { + got = ds.readRawData(uMsgBuf, remaining); + remaining -= got; + uMsgBuf += got; + } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); + if (got < 0) { + qWarning("QtLocalPeer: Message reception failed %s", socket->errorString().toLatin1().constData()); + delete socket; + return; + } + QString message(QString::fromUtf8(uMsg)); + socket->write(ack, qstrlen(ack)); + socket->waitForBytesWritten(1000); + socket->waitForDisconnected(1000); // make sure client reads ack + delete socket; + emit messageReceived(message); //### (might take a long time to return) +} diff --git a/subprojects/QtSingleApplication/src/qtlocalpeer.h b/subprojects/QtSingleApplication/src/qtlocalpeer.h index 1b533b1ab..555beb23c 100644 --- a/subprojects/QtSingleApplication/src/qtlocalpeer.h +++ b/subprojects/QtSingleApplication/src/qtlocalpeer.h @@ -1,77 +1,77 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTLOCALPEER_H -#define QTLOCALPEER_H - -#include -#include -#include - -#include "qtlockedfile.h" - -class QtLocalPeer : public QObject -{ - Q_OBJECT - -public: - QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); - bool isClient(); - bool sendMessage(const QString &message, int timeout); - QString applicationId() const - { return id; } - -Q_SIGNALS: - void messageReceived(const QString &message); - -protected Q_SLOTS: - void receiveConnection(); - -protected: - QString id; - QString socketName; - QLocalServer* server; - QtLP_Private::QtLockedFile lockFile; - -private: - static const char* ack; -}; - -#endif // QTLOCALPEER_H +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLOCALPEER_H +#define QTLOCALPEER_H + +#include +#include +#include + +#include "qtlockedfile.h" + +class QtLocalPeer : public QObject +{ + Q_OBJECT + +public: + QtLocalPeer(QObject *parent = 0, const QString &appId = QString()); + bool isClient(); + bool sendMessage(const QString &message, int timeout); + QString applicationId() const + { return id; } + +Q_SIGNALS: + void messageReceived(const QString &message); + +protected Q_SLOTS: + void receiveConnection(); + +protected: + QString id; + QString socketName; + QLocalServer* server; + QtLP_Private::QtLockedFile lockFile; + +private: + static const char* ack; +}; + +#endif // QTLOCALPEER_H diff --git a/subprojects/QtSingleApplication/src/qtlockedfile.cpp b/subprojects/QtSingleApplication/src/qtlockedfile.cpp index c142a863a..1fd2f73fc 100644 --- a/subprojects/QtSingleApplication/src/qtlockedfile.cpp +++ b/subprojects/QtSingleApplication/src/qtlockedfile.cpp @@ -1,193 +1,193 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtlockedfile.h" - -/*! - \class QtLockedFile - - \brief The QtLockedFile class extends QFile with advisory locking - functions. - - A file may be locked in read or write mode. Multiple instances of - \e QtLockedFile, created in multiple processes running on the same - machine, may have a file locked in read mode. Exactly one instance - may have it locked in write mode. A read and a write lock cannot - exist simultaneously on the same file. - - The file locks are advisory. This means that nothing prevents - another process from manipulating a locked file using QFile or - file system functions offered by the OS. Serialization is only - guaranteed if all processes that access the file use - QLockedFile. Also, while holding a lock on a file, a process - must not open the same file again (through any API), or locks - can be unexpectedly lost. - - The lock provided by an instance of \e QtLockedFile is released - whenever the program terminates. This is true even when the - program crashes and no destructors are called. -*/ - -/*! \enum QtLockedFile::LockMode - - This enum describes the available lock modes. - - \value ReadLock A read lock. - \value WriteLock A write lock. - \value NoLock Neither a read lock nor a write lock. -*/ - -/*! - Constructs an unlocked \e QtLockedFile object. This constructor - behaves in the same way as \e QFile::QFile(). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile() - : QFile() -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Constructs an unlocked QtLockedFile object with file \a name. This - constructor behaves in the same way as \e QFile::QFile(const - QString&). - - \sa QFile::QFile() -*/ -QtLockedFile::QtLockedFile(const QString &name) - : QFile(name) -{ -#ifdef Q_OS_WIN - wmutex = 0; - rmutex = 0; -#endif - m_lock_mode = NoLock; -} - -/*! - Opens the file in OpenMode \a mode. - - This is identical to QFile::open(), with the one exception that the - Truncate mode flag is disallowed. Truncation would conflict with the - advisory file locking, since the file would be modified before the - write lock is obtained. If truncation is required, use resize(0) - after obtaining the write lock. - - Returns true if successful; otherwise false. - - \sa QFile::open(), QFile::resize() -*/ -bool QtLockedFile::open(OpenMode mode) -{ - if (mode & QIODevice::Truncate) { - qWarning("QtLockedFile::open(): Truncate mode not allowed."); - return false; - } - return QFile::open(mode); -} - -/*! - Returns \e true if this object has a in read or write lock; - otherwise returns \e false. - - \sa lockMode() -*/ -bool QtLockedFile::isLocked() const -{ - return m_lock_mode != NoLock; -} - -/*! - Returns the type of lock currently held by this object, or \e - QtLockedFile::NoLock. - - \sa isLocked() -*/ -QtLockedFile::LockMode QtLockedFile::lockMode() const -{ - return m_lock_mode; -} - -/*! - \fn bool QtLockedFile::lock(LockMode mode, bool block = true) - - Obtains a lock of type \a mode. The file must be opened before it - can be locked. - - If \a block is true, this function will block until the lock is - aquired. If \a block is false, this function returns \e false - immediately if the lock cannot be aquired. - - If this object already has a lock of type \a mode, this function - returns \e true immediately. If this object has a lock of a - different type than \a mode, the lock is first released and then a - new lock is obtained. - - This function returns \e true if, after it executes, the file is - locked by this object, and \e false otherwise. - - \sa unlock(), isLocked(), lockMode() -*/ - -/*! - \fn bool QtLockedFile::unlock() - - Releases a lock. - - If the object has no lock, this function returns immediately. - - This function returns \e true if, after it executes, the file is - not locked by this object, and \e false otherwise. - - \sa lock(), isLocked(), lockMode() -*/ - -/*! - \fn QtLockedFile::~QtLockedFile() - - Destroys the \e QtLockedFile object. If any locks were held, they - are released. -*/ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtlockedfile.h" + +/*! + \class QtLockedFile + + \brief The QtLockedFile class extends QFile with advisory locking + functions. + + A file may be locked in read or write mode. Multiple instances of + \e QtLockedFile, created in multiple processes running on the same + machine, may have a file locked in read mode. Exactly one instance + may have it locked in write mode. A read and a write lock cannot + exist simultaneously on the same file. + + The file locks are advisory. This means that nothing prevents + another process from manipulating a locked file using QFile or + file system functions offered by the OS. Serialization is only + guaranteed if all processes that access the file use + QLockedFile. Also, while holding a lock on a file, a process + must not open the same file again (through any API), or locks + can be unexpectedly lost. + + The lock provided by an instance of \e QtLockedFile is released + whenever the program terminates. This is true even when the + program crashes and no destructors are called. +*/ + +/*! \enum QtLockedFile::LockMode + + This enum describes the available lock modes. + + \value ReadLock A read lock. + \value WriteLock A write lock. + \value NoLock Neither a read lock nor a write lock. +*/ + +/*! + Constructs an unlocked \e QtLockedFile object. This constructor + behaves in the same way as \e QFile::QFile(). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile() + : QFile() +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Constructs an unlocked QtLockedFile object with file \a name. This + constructor behaves in the same way as \e QFile::QFile(const + QString&). + + \sa QFile::QFile() +*/ +QtLockedFile::QtLockedFile(const QString &name) + : QFile(name) +{ +#ifdef Q_OS_WIN + wmutex = 0; + rmutex = 0; +#endif + m_lock_mode = NoLock; +} + +/*! + Opens the file in OpenMode \a mode. + + This is identical to QFile::open(), with the one exception that the + Truncate mode flag is disallowed. Truncation would conflict with the + advisory file locking, since the file would be modified before the + write lock is obtained. If truncation is required, use resize(0) + after obtaining the write lock. + + Returns true if successful; otherwise false. + + \sa QFile::open(), QFile::resize() +*/ +bool QtLockedFile::open(OpenMode mode) +{ + if (mode & QIODevice::Truncate) { + qWarning("QtLockedFile::open(): Truncate mode not allowed."); + return false; + } + return QFile::open(mode); +} + +/*! + Returns \e true if this object has a in read or write lock; + otherwise returns \e false. + + \sa lockMode() +*/ +bool QtLockedFile::isLocked() const +{ + return m_lock_mode != NoLock; +} + +/*! + Returns the type of lock currently held by this object, or \e + QtLockedFile::NoLock. + + \sa isLocked() +*/ +QtLockedFile::LockMode QtLockedFile::lockMode() const +{ + return m_lock_mode; +} + +/*! + \fn bool QtLockedFile::lock(LockMode mode, bool block = true) + + Obtains a lock of type \a mode. The file must be opened before it + can be locked. + + If \a block is true, this function will block until the lock is + aquired. If \a block is false, this function returns \e false + immediately if the lock cannot be aquired. + + If this object already has a lock of type \a mode, this function + returns \e true immediately. If this object has a lock of a + different type than \a mode, the lock is first released and then a + new lock is obtained. + + This function returns \e true if, after it executes, the file is + locked by this object, and \e false otherwise. + + \sa unlock(), isLocked(), lockMode() +*/ + +/*! + \fn bool QtLockedFile::unlock() + + Releases a lock. + + If the object has no lock, this function returns immediately. + + This function returns \e true if, after it executes, the file is + not locked by this object, and \e false otherwise. + + \sa lock(), isLocked(), lockMode() +*/ + +/*! + \fn QtLockedFile::~QtLockedFile() + + Destroys the \e QtLockedFile object. If any locks were held, they + are released. +*/ diff --git a/subprojects/QtSingleApplication/src/qtlockedfile.h b/subprojects/QtSingleApplication/src/qtlockedfile.h index 84c18e5c9..64c225b8a 100644 --- a/subprojects/QtSingleApplication/src/qtlockedfile.h +++ b/subprojects/QtSingleApplication/src/qtlockedfile.h @@ -1,97 +1,97 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTLOCKEDFILE_H -#define QTLOCKEDFILE_H - -#include -#ifdef Q_OS_WIN -#include -#endif - -#if defined(Q_OS_WIN) -# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) -# define QT_QTLOCKEDFILE_EXPORT -# elif defined(QT_QTLOCKEDFILE_IMPORT) -# if defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# endif -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) -# elif defined(QT_QTLOCKEDFILE_EXPORT) -# undef QT_QTLOCKEDFILE_EXPORT -# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTLOCKEDFILE_EXPORT -#endif - -namespace QtLP_Private { - -class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile -{ -public: - enum LockMode { NoLock = 0, ReadLock, WriteLock }; - - QtLockedFile(); - QtLockedFile(const QString &name); - ~QtLockedFile(); - - bool open(OpenMode mode); - - bool lock(LockMode mode, bool block = true); - bool unlock(); - bool isLocked() const; - LockMode lockMode() const; - -private: -#ifdef Q_OS_WIN - Qt::HANDLE wmutex; - Qt::HANDLE rmutex; - QVector rmutexes; - QString mutexname; - - Qt::HANDLE getMutexHandle(int idx, bool doCreate); - bool waitMutex(Qt::HANDLE mutex, bool doBlock); - -#endif - LockMode m_lock_mode; -}; -} -#endif +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTLOCKEDFILE_H +#define QTLOCKEDFILE_H + +#include +#ifdef Q_OS_WIN +#include +#endif + +#if defined(Q_OS_WIN) +# if !defined(QT_QTLOCKEDFILE_EXPORT) && !defined(QT_QTLOCKEDFILE_IMPORT) +# define QT_QTLOCKEDFILE_EXPORT +# elif defined(QT_QTLOCKEDFILE_IMPORT) +# if defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# endif +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllimport) +# elif defined(QT_QTLOCKEDFILE_EXPORT) +# undef QT_QTLOCKEDFILE_EXPORT +# define QT_QTLOCKEDFILE_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTLOCKEDFILE_EXPORT +#endif + +namespace QtLP_Private { + +class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile +{ +public: + enum LockMode { NoLock = 0, ReadLock, WriteLock }; + + QtLockedFile(); + QtLockedFile(const QString &name); + ~QtLockedFile(); + + bool open(OpenMode mode); + + bool lock(LockMode mode, bool block = true); + bool unlock(); + bool isLocked() const; + LockMode lockMode() const; + +private: +#ifdef Q_OS_WIN + Qt::HANDLE wmutex; + Qt::HANDLE rmutex; + QVector rmutexes; + QString mutexname; + + Qt::HANDLE getMutexHandle(int idx, bool doCreate); + bool waitMutex(Qt::HANDLE mutex, bool doBlock); + +#endif + LockMode m_lock_mode; +}; +} +#endif diff --git a/subprojects/QtSingleApplication/src/qtlockedfile_unix.cpp b/subprojects/QtSingleApplication/src/qtlockedfile_unix.cpp index 976c1b9ee..c16d28959 100644 --- a/subprojects/QtSingleApplication/src/qtlockedfile_unix.cpp +++ b/subprojects/QtSingleApplication/src/qtlockedfile_unix.cpp @@ -1,115 +1,115 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include - -#include "qtlockedfile.h" - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; - int cmd = block ? F_SETLKW : F_SETLK; - int ret = fcntl(handle(), cmd, &fl); - - if (ret == -1) { - if (errno != EINTR && errno != EAGAIN) - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - - m_lock_mode = mode; - return true; -} - - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - struct flock fl; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - fl.l_type = F_UNLCK; - int ret = fcntl(handle(), F_SETLKW, &fl); - - if (ret == -1) { - qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); - return false; - } - - m_lock_mode = NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); -} - +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qtlockedfile.h" + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = (mode == ReadLock) ? F_RDLCK : F_WRLCK; + int cmd = block ? F_SETLKW : F_SETLK; + int ret = fcntl(handle(), cmd, &fl); + + if (ret == -1) { + if (errno != EINTR && errno != EAGAIN) + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + + m_lock_mode = mode; + return true; +} + + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + struct flock fl; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; + fl.l_type = F_UNLCK; + int ret = fcntl(handle(), F_SETLKW, &fl); + + if (ret == -1) { + qWarning("QtLockedFile::lock(): fcntl: %s", strerror(errno)); + return false; + } + + m_lock_mode = NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); +} + diff --git a/subprojects/QtSingleApplication/src/qtlockedfile_win.cpp b/subprojects/QtSingleApplication/src/qtlockedfile_win.cpp index 5e2126204..c61337f89 100644 --- a/subprojects/QtSingleApplication/src/qtlockedfile_win.cpp +++ b/subprojects/QtSingleApplication/src/qtlockedfile_win.cpp @@ -1,211 +1,211 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qtlockedfile.h" -#include -#include - -#define MUTEX_PREFIX "QtLockedFile mutex " -// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS -#define MAX_READERS MAXIMUM_WAIT_OBJECTS - -#if QT_VERSION >= 0x050000 -#define QT_WA(unicode, ansi) unicode -#endif - -Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) -{ - if (mutexname.isEmpty()) { - QFileInfo fi(*this); - mutexname = QString::fromLatin1(MUTEX_PREFIX) - + fi.absoluteFilePath().toLower(); - } - QString mname(mutexname); - if (idx >= 0) - mname += QString::number(idx); - - Qt::HANDLE mutex; - if (doCreate) { - QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); - return 0; - } - } - else { - QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, - { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); - if (!mutex) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) - qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); - return 0; - } - } - return mutex; -} - -bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) -{ - Q_ASSERT(mutex); - DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); - switch (res) { - case WAIT_OBJECT_0: - case WAIT_ABANDONED: - return true; - break; - case WAIT_TIMEOUT: - break; - default: - qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); - } - return false; -} - - - -bool QtLockedFile::lock(LockMode mode, bool block) -{ - if (!isOpen()) { - qWarning("QtLockedFile::lock(): file is not opened"); - return false; - } - - if (mode == NoLock) - return unlock(); - - if (mode == m_lock_mode) - return true; - - if (m_lock_mode != NoLock) - unlock(); - - if (!wmutex && !(wmutex = getMutexHandle(-1, true))) - return false; - - if (!waitMutex(wmutex, block)) - return false; - - if (mode == ReadLock) { - int idx = 0; - for (; idx < MAX_READERS; idx++) { - rmutex = getMutexHandle(idx, false); - if (!rmutex || waitMutex(rmutex, false)) - break; - CloseHandle(rmutex); - } - bool ok = true; - if (idx >= MAX_READERS) { - qWarning("QtLockedFile::lock(): too many readers"); - rmutex = 0; - ok = false; - } - else if (!rmutex) { - rmutex = getMutexHandle(idx, true); - if (!rmutex || !waitMutex(rmutex, false)) - ok = false; - } - if (!ok && rmutex) { - CloseHandle(rmutex); - rmutex = 0; - } - ReleaseMutex(wmutex); - if (!ok) - return false; - } - else { - Q_ASSERT(rmutexes.isEmpty()); - for (int i = 0; i < MAX_READERS; i++) { - Qt::HANDLE mutex = getMutexHandle(i, false); - if (mutex) - rmutexes.append(mutex); - } - if (rmutexes.size()) { - DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), - TRUE, block ? INFINITE : 0); - if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { - if (res != WAIT_TIMEOUT) - qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); - m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky - unlock(); - return false; - } - } - } - - m_lock_mode = mode; - return true; -} - -bool QtLockedFile::unlock() -{ - if (!isOpen()) { - qWarning("QtLockedFile::unlock(): file is not opened"); - return false; - } - - if (!isLocked()) - return true; - - if (m_lock_mode == ReadLock) { - ReleaseMutex(rmutex); - CloseHandle(rmutex); - rmutex = 0; - } - else { - foreach(Qt::HANDLE mutex, rmutexes) { - ReleaseMutex(mutex); - CloseHandle(mutex); - } - rmutexes.clear(); - ReleaseMutex(wmutex); - } - - m_lock_mode = QtLockedFile::NoLock; - return true; -} - -QtLockedFile::~QtLockedFile() -{ - if (isOpen()) - unlock(); - if (wmutex) - CloseHandle(wmutex); -} +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtlockedfile.h" +#include +#include + +#define MUTEX_PREFIX "QtLockedFile mutex " +// Maximum number of concurrent read locks. Must not be greater than MAXIMUM_WAIT_OBJECTS +#define MAX_READERS MAXIMUM_WAIT_OBJECTS + +#if QT_VERSION >= 0x050000 +#define QT_WA(unicode, ansi) unicode +#endif + +Qt::HANDLE QtLockedFile::getMutexHandle(int idx, bool doCreate) +{ + if (mutexname.isEmpty()) { + QFileInfo fi(*this); + mutexname = QString::fromLatin1(MUTEX_PREFIX) + + fi.absoluteFilePath().toLower(); + } + QString mname(mutexname); + if (idx >= 0) + mname += QString::number(idx); + + Qt::HANDLE mutex; + if (doCreate) { + QT_WA( { mutex = CreateMutexW(NULL, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = CreateMutexA(NULL, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + qErrnoWarning("QtLockedFile::lock(): CreateMutex failed"); + return 0; + } + } + else { + QT_WA( { mutex = OpenMutexW(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, (TCHAR*)mname.utf16()); }, + { mutex = OpenMutexA(SYNCHRONIZE | MUTEX_MODIFY_STATE, FALSE, mname.toLocal8Bit().constData()); } ); + if (!mutex) { + if (GetLastError() != ERROR_FILE_NOT_FOUND) + qErrnoWarning("QtLockedFile::lock(): OpenMutex failed"); + return 0; + } + } + return mutex; +} + +bool QtLockedFile::waitMutex(Qt::HANDLE mutex, bool doBlock) +{ + Q_ASSERT(mutex); + DWORD res = WaitForSingleObject(mutex, doBlock ? INFINITE : 0); + switch (res) { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + return true; + break; + case WAIT_TIMEOUT: + break; + default: + qErrnoWarning("QtLockedFile::lock(): WaitForSingleObject failed"); + } + return false; +} + + + +bool QtLockedFile::lock(LockMode mode, bool block) +{ + if (!isOpen()) { + qWarning("QtLockedFile::lock(): file is not opened"); + return false; + } + + if (mode == NoLock) + return unlock(); + + if (mode == m_lock_mode) + return true; + + if (m_lock_mode != NoLock) + unlock(); + + if (!wmutex && !(wmutex = getMutexHandle(-1, true))) + return false; + + if (!waitMutex(wmutex, block)) + return false; + + if (mode == ReadLock) { + int idx = 0; + for (; idx < MAX_READERS; idx++) { + rmutex = getMutexHandle(idx, false); + if (!rmutex || waitMutex(rmutex, false)) + break; + CloseHandle(rmutex); + } + bool ok = true; + if (idx >= MAX_READERS) { + qWarning("QtLockedFile::lock(): too many readers"); + rmutex = 0; + ok = false; + } + else if (!rmutex) { + rmutex = getMutexHandle(idx, true); + if (!rmutex || !waitMutex(rmutex, false)) + ok = false; + } + if (!ok && rmutex) { + CloseHandle(rmutex); + rmutex = 0; + } + ReleaseMutex(wmutex); + if (!ok) + return false; + } + else { + Q_ASSERT(rmutexes.isEmpty()); + for (int i = 0; i < MAX_READERS; i++) { + Qt::HANDLE mutex = getMutexHandle(i, false); + if (mutex) + rmutexes.append(mutex); + } + if (rmutexes.size()) { + DWORD res = WaitForMultipleObjects(rmutexes.size(), rmutexes.constData(), + TRUE, block ? INFINITE : 0); + if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) { + if (res != WAIT_TIMEOUT) + qErrnoWarning("QtLockedFile::lock(): WaitForMultipleObjects failed"); + m_lock_mode = WriteLock; // trick unlock() to clean up - semiyucky + unlock(); + return false; + } + } + } + + m_lock_mode = mode; + return true; +} + +bool QtLockedFile::unlock() +{ + if (!isOpen()) { + qWarning("QtLockedFile::unlock(): file is not opened"); + return false; + } + + if (!isLocked()) + return true; + + if (m_lock_mode == ReadLock) { + ReleaseMutex(rmutex); + CloseHandle(rmutex); + rmutex = 0; + } + else { + foreach(Qt::HANDLE mutex, rmutexes) { + ReleaseMutex(mutex); + CloseHandle(mutex); + } + rmutexes.clear(); + ReleaseMutex(wmutex); + } + + m_lock_mode = QtLockedFile::NoLock; + return true; +} + +QtLockedFile::~QtLockedFile() +{ + if (isOpen()) + unlock(); + if (wmutex) + CloseHandle(wmutex); +} diff --git a/subprojects/QtSingleApplication/src/qtsingleapplication.cpp b/subprojects/QtSingleApplication/src/qtsingleapplication.cpp index d0fb15d76..71b31d58f 100644 --- a/subprojects/QtSingleApplication/src/qtsingleapplication.cpp +++ b/subprojects/QtSingleApplication/src/qtsingleapplication.cpp @@ -1,347 +1,347 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtsingleapplication.h" -#include "qtlocalpeer.h" -#include - - -/*! - \class QtSingleApplication qtsingleapplication.h - \brief The QtSingleApplication class provides an API to detect and - communicate with running instances of an application. - - This class allows you to create applications where only one - instance should be running at a time. I.e., if the user tries to - launch another instance, the already running instance will be - activated instead. Another usecase is a client-server system, - where the first started instance will assume the role of server, - and the later instances will act as clients of that server. - - By default, the full path of the executable file is used to - determine whether two processes are instances of the same - application. You can also provide an explicit identifier string - that will be compared instead. - - The application should create the QtSingleApplication object early - in the startup phase, and call isRunning() to find out if another - instance of this application is already running. If isRunning() - returns false, it means that no other instance is running, and - this instance has assumed the role as the running instance. In - this case, the application should continue with the initialization - of the application user interface before entering the event loop - with exec(), as normal. - - The messageReceived() signal will be emitted when the running - application receives messages from another instance of the same - application. When a message is received it might be helpful to the - user to raise the application so that it becomes visible. To - facilitate this, QtSingleApplication provides the - setActivationWindow() function and the activateWindow() slot. - - If isRunning() returns true, another instance is already - running. It may be alerted to the fact that another instance has - started by using the sendMessage() function. Also data such as - startup parameters (e.g. the name of the file the user wanted this - new instance to open) can be passed to the running instance with - this function. Then, the application should terminate (or enter - client mode). - - If isRunning() returns true, but sendMessage() fails, that is an - indication that the running instance is frozen. - - Here's an example that shows how to convert an existing - application to use QtSingleApplication. It is very simple and does - not make use of all QtSingleApplication's functionality (see the - examples for that). - - \code - // Original - int main(int argc, char **argv) - { - QApplication app(argc, argv); - - MyMainWidget mmw; - mmw.show(); - return app.exec(); - } - - // Single instance - int main(int argc, char **argv) - { - QtSingleApplication app(argc, argv); - - if (app.isRunning()) - return !app.sendMessage(someDataString); - - MyMainWidget mmw; - app.setActivationWindow(&mmw); - mmw.show(); - return app.exec(); - } - \endcode - - Once this QtSingleApplication instance is destroyed (normally when - the process exits or crashes), when the user next attempts to run the - application this instance will not, of course, be encountered. The - next instance to call isRunning() or sendMessage() will assume the - role as the new running instance. - - For console (non-GUI) applications, QtSingleCoreApplication may be - used instead of this class, to avoid the dependency on the QtGui - library. - - \sa QtSingleCoreApplication -*/ - - -void QtSingleApplication::sysInit(const QString &appId) -{ - actWin = 0; - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a GUIenabled are passed on to the QAppliation constructor. - - If you are creating a console application (i.e. setting \a - GUIenabled to false), you may consider using - QtSingleCoreApplication instead. -*/ - -QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) - : QApplication(argc, argv, GUIenabled) -{ - sysInit(); -} - - -/*! - Creates a QtSingleApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QAppliation constructor. -*/ - -QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) - : QApplication(argc, argv) -{ - sysInit(appId); -} - -#if QT_VERSION < 0x050000 - -/*! - Creates a QtSingleApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc, \a - argv, and \a type are passed on to the QAppliation constructor. -*/ -QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) - : QApplication(argc, argv, type) -{ - sysInit(); -} - - -# if defined(Q_WS_X11) -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, - and \a cmap are passed on to the QApplication constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(); -} - -/*! - Special constructor for X11, ref. the documentation of - QApplication's corresponding constructor. The application identifier - will be \a appId. \a dpy, \a argc, \a - argv, \a visual, and \a cmap are passed on to the QApplication - constructor. -*/ -QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) - : QApplication(dpy, argc, argv, visual, cmap) -{ - sysInit(appId); -} -# endif // Q_WS_X11 -#endif // QT_VERSION < 0x050000 - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ -bool QtSingleApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ -QString QtSingleApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - Sets the activation window of this application to \a aw. The - activation window is the widget that will be activated by - activateWindow(). This is typically the application's main window. - - If \a activateOnMessage is true (the default), the window will be - activated automatically every time a message is received, just prior - to the messageReceived() signal being emitted. - - \sa activateWindow(), messageReceived() -*/ - -void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) -{ - actWin = aw; - if (activateOnMessage) - connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); - else - disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); -} - - -/*! - Returns the applications activation window if one has been set by - calling setActivationWindow(), otherwise returns 0. - - \sa setActivationWindow() -*/ -QWidget* QtSingleApplication::activationWindow() const -{ - return actWin; -} - - -/*! - De-minimizes, raises, and activates this application's activation window. - This function does nothing if no activation window has been set. - - This is a convenience function to show the user that this - application instance has been activated when he has tried to start - another instance. - - This function should typically be called in response to the - messageReceived() signal. By default, that will happen - automatically, if an activation window has been set. - - \sa setActivationWindow(), messageReceived(), initialize() -*/ -void QtSingleApplication::activateWindow() -{ - if (actWin) { - actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); - actWin->raise(); - actWin->activateWindow(); - } -} - - -/*! - \fn void QtSingleApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage(), setActivationWindow(), activateWindow() -*/ - - -/*! - \fn void QtSingleApplication::initialize(bool dummy = true) - - \obsolete -*/ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qtsingleapplication.h" +#include "qtlocalpeer.h" +#include + + +/*! + \class QtSingleApplication qtsingleapplication.h + \brief The QtSingleApplication class provides an API to detect and + communicate with running instances of an application. + + This class allows you to create applications where only one + instance should be running at a time. I.e., if the user tries to + launch another instance, the already running instance will be + activated instead. Another usecase is a client-server system, + where the first started instance will assume the role of server, + and the later instances will act as clients of that server. + + By default, the full path of the executable file is used to + determine whether two processes are instances of the same + application. You can also provide an explicit identifier string + that will be compared instead. + + The application should create the QtSingleApplication object early + in the startup phase, and call isRunning() to find out if another + instance of this application is already running. If isRunning() + returns false, it means that no other instance is running, and + this instance has assumed the role as the running instance. In + this case, the application should continue with the initialization + of the application user interface before entering the event loop + with exec(), as normal. + + The messageReceived() signal will be emitted when the running + application receives messages from another instance of the same + application. When a message is received it might be helpful to the + user to raise the application so that it becomes visible. To + facilitate this, QtSingleApplication provides the + setActivationWindow() function and the activateWindow() slot. + + If isRunning() returns true, another instance is already + running. It may be alerted to the fact that another instance has + started by using the sendMessage() function. Also data such as + startup parameters (e.g. the name of the file the user wanted this + new instance to open) can be passed to the running instance with + this function. Then, the application should terminate (or enter + client mode). + + If isRunning() returns true, but sendMessage() fails, that is an + indication that the running instance is frozen. + + Here's an example that shows how to convert an existing + application to use QtSingleApplication. It is very simple and does + not make use of all QtSingleApplication's functionality (see the + examples for that). + + \code + // Original + int main(int argc, char **argv) + { + QApplication app(argc, argv); + + MyMainWidget mmw; + mmw.show(); + return app.exec(); + } + + // Single instance + int main(int argc, char **argv) + { + QtSingleApplication app(argc, argv); + + if (app.isRunning()) + return !app.sendMessage(someDataString); + + MyMainWidget mmw; + app.setActivationWindow(&mmw); + mmw.show(); + return app.exec(); + } + \endcode + + Once this QtSingleApplication instance is destroyed (normally when + the process exits or crashes), when the user next attempts to run the + application this instance will not, of course, be encountered. The + next instance to call isRunning() or sendMessage() will assume the + role as the new running instance. + + For console (non-GUI) applications, QtSingleCoreApplication may be + used instead of this class, to avoid the dependency on the QtGui + library. + + \sa QtSingleCoreApplication +*/ + + +void QtSingleApplication::sysInit(const QString &appId) +{ + actWin = 0; + peer = new QtLocalPeer(this, appId); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); +} + + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a GUIenabled are passed on to the QAppliation constructor. + + If you are creating a console application (i.e. setting \a + GUIenabled to false), you may consider using + QtSingleCoreApplication instead. +*/ + +QtSingleApplication::QtSingleApplication(int &argc, char **argv, bool GUIenabled) + : QApplication(argc, argv, GUIenabled) +{ + sysInit(); +} + + +/*! + Creates a QtSingleApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QAppliation constructor. +*/ + +QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char **argv) + : QApplication(argc, argv) +{ + sysInit(appId); +} + +#if QT_VERSION < 0x050000 + +/*! + Creates a QtSingleApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc, \a + argv, and \a type are passed on to the QAppliation constructor. +*/ +QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type) + : QApplication(argc, argv, type) +{ + sysInit(); +} + + +# if defined(Q_WS_X11) +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a visual, + and \a cmap are passed on to the QApplication constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, visual, cmap) +{ + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be QCoreApplication::applicationFilePath(). \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) +{ + sysInit(); +} + +/*! + Special constructor for X11, ref. the documentation of + QApplication's corresponding constructor. The application identifier + will be \a appId. \a dpy, \a argc, \a + argv, \a visual, and \a cmap are passed on to the QApplication + constructor. +*/ +QtSingleApplication::QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual, Qt::HANDLE cmap) + : QApplication(dpy, argc, argv, visual, cmap) +{ + sysInit(appId); +} +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleApplication::isRunning() +{ + return peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ +bool QtSingleApplication::sendMessage(const QString &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ +QString QtSingleApplication::id() const +{ + return peer->applicationId(); +} + + +/*! + Sets the activation window of this application to \a aw. The + activation window is the widget that will be activated by + activateWindow(). This is typically the application's main window. + + If \a activateOnMessage is true (the default), the window will be + activated automatically every time a message is received, just prior + to the messageReceived() signal being emitted. + + \sa activateWindow(), messageReceived() +*/ + +void QtSingleApplication::setActivationWindow(QWidget* aw, bool activateOnMessage) +{ + actWin = aw; + if (activateOnMessage) + connect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); + else + disconnect(peer, SIGNAL(messageReceived(const QString&)), this, SLOT(activateWindow())); +} + + +/*! + Returns the applications activation window if one has been set by + calling setActivationWindow(), otherwise returns 0. + + \sa setActivationWindow() +*/ +QWidget* QtSingleApplication::activationWindow() const +{ + return actWin; +} + + +/*! + De-minimizes, raises, and activates this application's activation window. + This function does nothing if no activation window has been set. + + This is a convenience function to show the user that this + application instance has been activated when he has tried to start + another instance. + + This function should typically be called in response to the + messageReceived() signal. By default, that will happen + automatically, if an activation window has been set. + + \sa setActivationWindow(), messageReceived(), initialize() +*/ +void QtSingleApplication::activateWindow() +{ + if (actWin) { + actWin->setWindowState(actWin->windowState() & ~Qt::WindowMinimized); + actWin->raise(); + actWin->activateWindow(); + } +} + + +/*! + \fn void QtSingleApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage(), setActivationWindow(), activateWindow() +*/ + + +/*! + \fn void QtSingleApplication::initialize(bool dummy = true) + + \obsolete +*/ diff --git a/subprojects/QtSingleApplication/src/qtsingleapplication.h b/subprojects/QtSingleApplication/src/qtsingleapplication.h index 049406f72..464801054 100644 --- a/subprojects/QtSingleApplication/src/qtsingleapplication.h +++ b/subprojects/QtSingleApplication/src/qtsingleapplication.h @@ -1,105 +1,105 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTSINGLEAPPLICATION_H -#define QTSINGLEAPPLICATION_H - -#include - -class QtLocalPeer; - -#if defined(Q_OS_WIN) -# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) -# define QT_QTSINGLEAPPLICATION_EXPORT -# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) -# if defined(QT_QTSINGLEAPPLICATION_EXPORT) -# undef QT_QTSINGLEAPPLICATION_EXPORT -# endif -# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) -# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) -# undef QT_QTSINGLEAPPLICATION_EXPORT -# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) -# endif -#else -# define QT_QTSINGLEAPPLICATION_EXPORT -#endif - -class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication -{ - Q_OBJECT - -public: - QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); - QtSingleApplication(const QString &id, int &argc, char **argv); -#if QT_VERSION < 0x050000 - QtSingleApplication(int &argc, char **argv, Type type); -# if defined(Q_WS_X11) - QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); - QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); - QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); -# endif // Q_WS_X11 -#endif // QT_VERSION < 0x050000 - - bool isRunning(); - QString id() const; - - void setActivationWindow(QWidget* aw, bool activateOnMessage = true); - QWidget* activationWindow() const; - - // Obsolete: - void initialize(bool dummy = true) - { isRunning(); Q_UNUSED(dummy) } - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - void activateWindow(); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - void sysInit(const QString &appId = QString()); - QtLocalPeer *peer; - QWidget *actWin; -}; - -#endif // QTSINGLEAPPLICATION_H +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSINGLEAPPLICATION_H +#define QTSINGLEAPPLICATION_H + +#include + +class QtLocalPeer; + +#if defined(Q_OS_WIN) +# if !defined(QT_QTSINGLEAPPLICATION_EXPORT) && !defined(QT_QTSINGLEAPPLICATION_IMPORT) +# define QT_QTSINGLEAPPLICATION_EXPORT +# elif defined(QT_QTSINGLEAPPLICATION_IMPORT) +# if defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# endif +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllimport) +# elif defined(QT_QTSINGLEAPPLICATION_EXPORT) +# undef QT_QTSINGLEAPPLICATION_EXPORT +# define QT_QTSINGLEAPPLICATION_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTSINGLEAPPLICATION_EXPORT +#endif + +class QT_QTSINGLEAPPLICATION_EXPORT QtSingleApplication : public QApplication +{ + Q_OBJECT + +public: + QtSingleApplication(int &argc, char **argv, bool GUIenabled = true); + QtSingleApplication(const QString &id, int &argc, char **argv); +#if QT_VERSION < 0x050000 + QtSingleApplication(int &argc, char **argv, Type type); +# if defined(Q_WS_X11) + QtSingleApplication(Display* dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); + QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap= 0); + QtSingleApplication(Display* dpy, const QString &appId, int argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0); +# endif // Q_WS_X11 +#endif // QT_VERSION < 0x050000 + + bool isRunning(); + QString id() const; + + void setActivationWindow(QWidget* aw, bool activateOnMessage = true); + QWidget* activationWindow() const; + + // Obsolete: + void initialize(bool dummy = true) + { isRunning(); Q_UNUSED(dummy) } + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + void activateWindow(); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + void sysInit(const QString &appId = QString()); + QtLocalPeer *peer; + QWidget *actWin; +}; + +#endif // QTSINGLEAPPLICATION_H diff --git a/subprojects/QtSingleApplication/src/qtsingleapplication.pri b/subprojects/QtSingleApplication/src/qtsingleapplication.pri index 6f2bced94..4d39ddbbd 100644 --- a/subprojects/QtSingleApplication/src/qtsingleapplication.pri +++ b/subprojects/QtSingleApplication/src/qtsingleapplication.pri @@ -1,17 +1,17 @@ -include(../common.pri) -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD -QT *= network -greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets - -qtsingleapplication-uselib:!qtsingleapplication-buildlib { - LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME -} else { - SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp - HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h -} - -win32 { - contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT - else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT -} +include(../common.pri) +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +QT *= network +greaterThan(QT_MAJOR_VERSION, 4): QT *= widgets + +qtsingleapplication-uselib:!qtsingleapplication-buildlib { + LIBS += -L$$QTSINGLEAPPLICATION_LIBDIR -l$$QTSINGLEAPPLICATION_LIBNAME +} else { + SOURCES += $$PWD/qtsingleapplication.cpp $$PWD/qtlocalpeer.cpp + HEADERS += $$PWD/qtsingleapplication.h $$PWD/qtlocalpeer.h +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSINGLEAPPLICATION_EXPORT + else:qtsingleapplication-uselib:DEFINES += QT_QTSINGLEAPPLICATION_IMPORT +} diff --git a/subprojects/QtSingleApplication/src/qtsinglecoreapplication.cpp b/subprojects/QtSingleApplication/src/qtsinglecoreapplication.cpp index 563453732..c66ba06de 100644 --- a/subprojects/QtSingleApplication/src/qtsinglecoreapplication.cpp +++ b/subprojects/QtSingleApplication/src/qtsinglecoreapplication.cpp @@ -1,149 +1,149 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qtsinglecoreapplication.h" -#include "qtlocalpeer.h" - -/*! - \class QtSingleCoreApplication qtsinglecoreapplication.h - \brief A variant of the QtSingleApplication class for non-GUI applications. - - This class is a variant of QtSingleApplication suited for use in - console (non-GUI) applications. It is an extension of - QCoreApplication (instead of QApplication). It does not require - the QtGui library. - - The API and usage is identical to QtSingleApplication, except that - functions relating to the "activation window" are not present, for - obvious reasons. Please refer to the QtSingleApplication - documentation for explanation of the usage. - - A QtSingleCoreApplication instance can communicate to a - QtSingleApplication instance if they share the same application - id. Hence, this class can be used to create a light-weight - command-line tool that sends commands to a GUI application. - - \sa QtSingleApplication -*/ - -/*! - Creates a QtSingleCoreApplication object. The application identifier - will be QCoreApplication::applicationFilePath(). \a argc and \a - argv are passed on to the QCoreAppliation constructor. -*/ - -QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Creates a QtSingleCoreApplication object with the application - identifier \a appId. \a argc and \a argv are passed on to the - QCoreAppliation constructor. -*/ -QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) - : QCoreApplication(argc, argv) -{ - peer = new QtLocalPeer(this, appId); - connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); -} - - -/*! - Returns true if another instance of this application is running; - otherwise false. - - This function does not find instances of this application that are - being run by a different user (on Windows: that are running in - another session). - - \sa sendMessage() -*/ - -bool QtSingleCoreApplication::isRunning() -{ - return peer->isClient(); -} - - -/*! - Tries to send the text \a message to the currently running - instance. The QtSingleCoreApplication object in the running instance - will emit the messageReceived() signal when it receives the - message. - - This function returns true if the message has been sent to, and - processed by, the current instance. If there is no instance - currently running, or if the running instance fails to process the - message within \a timeout milliseconds, this function return false. - - \sa isRunning(), messageReceived() -*/ - -bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) -{ - return peer->sendMessage(message, timeout); -} - - -/*! - Returns the application identifier. Two processes with the same - identifier will be regarded as instances of the same application. -*/ - -QString QtSingleCoreApplication::id() const -{ - return peer->applicationId(); -} - - -/*! - \fn void QtSingleCoreApplication::messageReceived(const QString& message) - - This signal is emitted when the current instance receives a \a - message from another instance of this application. - - \sa sendMessage() -*/ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qtsinglecoreapplication.h" +#include "qtlocalpeer.h" + +/*! + \class QtSingleCoreApplication qtsinglecoreapplication.h + \brief A variant of the QtSingleApplication class for non-GUI applications. + + This class is a variant of QtSingleApplication suited for use in + console (non-GUI) applications. It is an extension of + QCoreApplication (instead of QApplication). It does not require + the QtGui library. + + The API and usage is identical to QtSingleApplication, except that + functions relating to the "activation window" are not present, for + obvious reasons. Please refer to the QtSingleApplication + documentation for explanation of the usage. + + A QtSingleCoreApplication instance can communicate to a + QtSingleApplication instance if they share the same application + id. Hence, this class can be used to create a light-weight + command-line tool that sends commands to a GUI application. + + \sa QtSingleApplication +*/ + +/*! + Creates a QtSingleCoreApplication object. The application identifier + will be QCoreApplication::applicationFilePath(). \a argc and \a + argv are passed on to the QCoreAppliation constructor. +*/ + +QtSingleCoreApplication::QtSingleCoreApplication(int &argc, char **argv) + : QCoreApplication(argc, argv) +{ + peer = new QtLocalPeer(this); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); +} + + +/*! + Creates a QtSingleCoreApplication object with the application + identifier \a appId. \a argc and \a argv are passed on to the + QCoreAppliation constructor. +*/ +QtSingleCoreApplication::QtSingleCoreApplication(const QString &appId, int &argc, char **argv) + : QCoreApplication(argc, argv) +{ + peer = new QtLocalPeer(this, appId); + connect(peer, SIGNAL(messageReceived(const QString&)), SIGNAL(messageReceived(const QString&))); +} + + +/*! + Returns true if another instance of this application is running; + otherwise false. + + This function does not find instances of this application that are + being run by a different user (on Windows: that are running in + another session). + + \sa sendMessage() +*/ + +bool QtSingleCoreApplication::isRunning() +{ + return peer->isClient(); +} + + +/*! + Tries to send the text \a message to the currently running + instance. The QtSingleCoreApplication object in the running instance + will emit the messageReceived() signal when it receives the + message. + + This function returns true if the message has been sent to, and + processed by, the current instance. If there is no instance + currently running, or if the running instance fails to process the + message within \a timeout milliseconds, this function return false. + + \sa isRunning(), messageReceived() +*/ + +bool QtSingleCoreApplication::sendMessage(const QString &message, int timeout) +{ + return peer->sendMessage(message, timeout); +} + + +/*! + Returns the application identifier. Two processes with the same + identifier will be regarded as instances of the same application. +*/ + +QString QtSingleCoreApplication::id() const +{ + return peer->applicationId(); +} + + +/*! + \fn void QtSingleCoreApplication::messageReceived(const QString& message) + + This signal is emitted when the current instance receives a \a + message from another instance of this application. + + \sa sendMessage() +*/ diff --git a/subprojects/QtSingleApplication/src/qtsinglecoreapplication.h b/subprojects/QtSingleApplication/src/qtsinglecoreapplication.h index b87fffe41..459d529af 100644 --- a/subprojects/QtSingleApplication/src/qtsinglecoreapplication.h +++ b/subprojects/QtSingleApplication/src/qtsinglecoreapplication.h @@ -1,71 +1,71 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Solutions component. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTSINGLECOREAPPLICATION_H -#define QTSINGLECOREAPPLICATION_H - -#include - -class QtLocalPeer; - -class QtSingleCoreApplication : public QCoreApplication -{ - Q_OBJECT - -public: - QtSingleCoreApplication(int &argc, char **argv); - QtSingleCoreApplication(const QString &id, int &argc, char **argv); - - bool isRunning(); - QString id() const; - -public Q_SLOTS: - bool sendMessage(const QString &message, int timeout = 5000); - - -Q_SIGNALS: - void messageReceived(const QString &message); - - -private: - QtLocalPeer* peer; -}; - -#endif // QTSINGLECOREAPPLICATION_H +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Solutions component. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTSINGLECOREAPPLICATION_H +#define QTSINGLECOREAPPLICATION_H + +#include + +class QtLocalPeer; + +class QtSingleCoreApplication : public QCoreApplication +{ + Q_OBJECT + +public: + QtSingleCoreApplication(int &argc, char **argv); + QtSingleCoreApplication(const QString &id, int &argc, char **argv); + + bool isRunning(); + QString id() const; + +public Q_SLOTS: + bool sendMessage(const QString &message, int timeout = 5000); + + +Q_SIGNALS: + void messageReceived(const QString &message); + + +private: + QtLocalPeer* peer; +}; + +#endif // QTSINGLECOREAPPLICATION_H diff --git a/subprojects/QtSingleApplication/src/qtsinglecoreapplication.pri b/subprojects/QtSingleApplication/src/qtsinglecoreapplication.pri index d2d6cc3e1..ca96c587f 100644 --- a/subprojects/QtSingleApplication/src/qtsinglecoreapplication.pri +++ b/subprojects/QtSingleApplication/src/qtsinglecoreapplication.pri @@ -1,10 +1,10 @@ -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD -HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h -SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp - -QT *= network - -win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { - DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) -} +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +HEADERS += $$PWD/qtsinglecoreapplication.h $$PWD/qtlocalpeer.h +SOURCES += $$PWD/qtsinglecoreapplication.cpp $$PWD/qtlocalpeer.cpp + +QT *= network + +win32:contains(TEMPLATE, lib):contains(CONFIG, shared) { + DEFINES += QT_QTSINGLECOREAPPLICATION_EXPORT=__declspec(dllexport) +} diff --git a/ui/about.ui b/ui/about.ui index f8cc38cf1..31b52dfb4 100644 --- a/ui/about.ui +++ b/ui/about.ui @@ -1,81 +1,81 @@ - - - AboutDialog - - - - 0 - 0 - 850 - 650 - - - - - 1 - 1 - - - - About - - - false - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - QAbstractScrollArea::AdjustToContents - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - qrc:/texts/about.html - - - - true - - - - - - - - + + + AboutDialog + + + + 0 + 0 + 850 + 650 + + + + + 1 + 1 + + + + About + + + false + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + qrc:/texts/about.html + + + + true + + + + + + + + diff --git a/ui/kiwixchoicebox.ui b/ui/kiwixchoicebox.ui index 4d78a3a79..05daa7e79 100644 --- a/ui/kiwixchoicebox.ui +++ b/ui/kiwixchoicebox.ui @@ -1,115 +1,115 @@ - - - kiwixchoicebox - - - - 0 - 0 - 268 - 54 - - - - - 0 - 0 - - - - - 0 - 0 - - - - Form - - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - TextLabel - - - - - - - - 50 - 16777215 - - - - Clear - - - - - - - - - - - 0 - 0 - - - - - 250 - 0 - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 5 - - - - - - - - - + + + kiwixchoicebox + + + + 0 + 0 + 268 + 54 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + TextLabel + + + + + + + + 50 + 16777215 + + + + Clear + + + + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 5 + + + + + + + + + diff --git a/ui/kiwixmessagebox.ui b/ui/kiwixmessagebox.ui index 957b2e3a8..c17d153ea 100644 --- a/ui/kiwixmessagebox.ui +++ b/ui/kiwixmessagebox.ui @@ -1,205 +1,205 @@ - - - kiwixmessagebox - - - - 0 - 0 - 368 - 166 - - - - - 0 - 0 - - - - - 474 - 16777215 - - - - Dialog - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 250 - 20 - - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - PointingHandCursor - - - Yes - - - true - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - PointingHandCursor - - - Ok - - - true - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - PointingHandCursor - - - No - - - true - - - - - - - - - - 0 - 0 - - - - - 600 - 16777215 - - - - Would you like to confirm doing xyz? - - - - - - - - - - 0 - 0 - - - - Confirm title - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 20 - 16777215 - - - - PointingHandCursor - - - - - - - :/icons/close.svg:/icons/close.svg - - - - - - - - - - - - + + + kiwixmessagebox + + + + 0 + 0 + 368 + 166 + + + + + 0 + 0 + + + + + 474 + 16777215 + + + + Dialog + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 250 + 20 + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + PointingHandCursor + + + Yes + + + true + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + PointingHandCursor + + + Ok + + + true + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + PointingHandCursor + + + No + + + true + + + + + + + + + + 0 + 0 + + + + + 600 + 16777215 + + + + Would you like to confirm doing xyz? + + + + + + + + + + 0 + 0 + + + + Confirm title + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 20 + 16777215 + + + + PointingHandCursor + + + + + + + :/icons/close.svg:/icons/close.svg + + + + + + + + + + + + diff --git a/ui/localkiwixserver.ui b/ui/localkiwixserver.ui index 04891927b..03b270c9a 100644 --- a/ui/localkiwixserver.ui +++ b/ui/localkiwixserver.ui @@ -1,218 +1,218 @@ - - - LocalKiwixServer - - - - 0 - 0 - 594 - 271 - - - - - 0 - 0 - - - - - 0 - 0 - - - - Local Kiwix Server Settings - Kiwix - - - false - - - true - - - - QLayout::SetFixedSize - - - - - - 12 - 75 - true - - - - Local Kiwix Server - - - - - - - - 11 - 75 - true - - - - PointingHandCursor - - - Close - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 11 - 75 - true - - - - PointingHandCursor - - - false - - - Start Kiwix Server - - - false - - - true - - - - - - - - 0 - 0 - - - - - 50 - 50 - - - - 0 - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - - - - - - - - - true - - - - 11 - 75 - true - - - - PointingHandCursor - - - Open in browser - - - true - - - - - - - - - - - - 0 - 0 - - - - - 450 - 0 - - - - - 12 - - - - TextLabel - - - true - - - - - - - - + + + LocalKiwixServer + + + + 0 + 0 + 594 + 271 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Local Kiwix Server Settings - Kiwix + + + false + + + true + + + + QLayout::SetFixedSize + + + + + + 12 + 75 + true + + + + Local Kiwix Server + + + + + + + + 11 + 75 + true + + + + PointingHandCursor + + + Close + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 11 + 75 + true + + + + PointingHandCursor + + + false + + + Start Kiwix Server + + + false + + + true + + + + + + + + 0 + 0 + + + + + 50 + 50 + + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + true + + + + 11 + 75 + true + + + + PointingHandCursor + + + Open in browser + + + true + + + + + + + + + + + + 0 + 0 + + + + + 450 + 0 + + + + + 12 + + + + TextLabel + + + true + + + + + + + + diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 269b91bf2..a7b428a5b 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1,216 +1,216 @@ - - - MainWindow - - - - 0 - 0 - 968 - 576 - - - - Kiwix - - - true - - - QMainWindow::AnimatedDocks - - - true - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 0 - - - - - 0 - - - - - - 40 - 40 - - - - - - - - 24 - 24 - - - - - - - - - - - - 40 - 40 - - - - - - - - :/icons/new-tab-icon.svg:/icons/new-tab-icon.svg - - - - 16 - 16 - - - - - - - - - 40 - 40 - - - - - - - - 24 - 24 - - - - - - - - - - - - true - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - - 0 - 0 - - - - - - - - - - - background-color: rgb(255, 255, 255); - - - - - - - - - - - - false - - - false - - - TopToolBarArea - - - false - - - - - - - TopWidget - QToolBar -
src/topwidget.h
-
- - TabBar - QWidget -
src/tabbar.h
- 1 -
- - ContentManagerSide - QWidget -
src/contentmanagerside.h
- 1 -
- - ReadingListBar - QWidget -
src/readinglistbar.h
- 1 -
- - TableOfContentBar - QWidget -
src/tableofcontentbar.h
- 1 -
-
- - - - -
+ + + MainWindow + + + + 0 + 0 + 968 + 576 + + + + Kiwix + + + true + + + QMainWindow::AnimatedDocks + + + true + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + 0 + + + + + + 40 + 40 + + + + + + + + 24 + 24 + + + + + + + + + + + + 40 + 40 + + + + + + + + :/icons/new-tab-icon.svg:/icons/new-tab-icon.svg + + + + 16 + 16 + + + + + + + + + 40 + 40 + + + + + + + + 24 + 24 + + + + + + + + + + + + true + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + + 0 + 0 + + + + + + + + + + + background-color: rgb(255, 255, 255); + + + + + + + + + + + + false + + + false + + + TopToolBarArea + + + false + + + + + + + TopWidget + QToolBar +
src/topwidget.h
+
+ + TabBar + QWidget +
src/tabbar.h
+ 1 +
+ + ContentManagerSide + QWidget +
src/contentmanagerside.h
+ 1 +
+ + ReadingListBar + QWidget +
src/readinglistbar.h
+ 1 +
+ + TableOfContentBar + QWidget +
src/tableofcontentbar.h
+ 1 +
+
+ + + + +
diff --git a/ui/settings.ui b/ui/settings.ui index 60077495d..bc2f8f07a 100644 --- a/ui/settings.ui +++ b/ui/settings.ui @@ -1,441 +1,441 @@ - - - Settings - - - -0 -0 -1029 -580 - - - - true - - - - - 0 - 0 - 1029 - 580 - - - - - Qt::AlignCenter - - - - - - 0 - 0 - 1029 - 580 - - - - - 0 - 0 - - - - - 600 - 450 - - - - Form - - - - - - - 16777215 - 16777215 - - - - - - - - - - 0 - 0 - - - - Settings - - - - - - - Qt::Horizontal - - - - - - - - - Zoom level: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - % - - - 30 - - - 500 - - - 10 - - - 100 - - - - - - - - - QFrame::Plain - - - Qt::Horizontal - - - - - - - - - Download directory: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - true - - - - - - Reset - - - true - - - true - - - - - - - true - - - - - - Browse - - - true - - - - - - - - - Qt::Horizontal - - - - - - - - - TextLabel - - - - - - - ? - - - Qt::AutoText - - - Qt::AlignCenter - - - -5 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - TextLabel - - - - - - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Reset - - - - - - - Browse - - - - - - - - - Qt::Horizontal - - - - - - - 0 - - - - - Move deleted files to trash - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - true - - - - - - - - - Qt::Horizontal - - - - - - - 0 - - - 0 - - - - - Re-open closed tabs - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - + + + Settings + + + +0 +0 +1029 +580 + + + + true + + + + + 0 + 0 + 1029 + 580 + + + + + Qt::AlignCenter + + + + + + 0 + 0 + 1029 + 580 + + + + + 0 + 0 + + + + + 600 + 450 + + + + Form + + + + + + + 16777215 + 16777215 + + + + + + + + + + 0 + 0 + + + + Settings + + + + + + + Qt::Horizontal + + + + + + + + + Zoom level: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + % + + + 30 + + + 500 + + + 10 + + + 100 + + + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + + + Download directory: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + true + + + + + + Reset + + + true + + + true + + + + + + + true + + + + + + Browse + + + true + + + + + + + + + Qt::Horizontal + + + + + + + + + TextLabel + + + + + + + ? + + + Qt::AutoText + + + Qt::AlignCenter + + + -5 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + TextLabel + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Reset + + + + + + + Browse + + + + + + + + + Qt::Horizontal + + + + + + + 0 + + + + + Move deleted files to trash + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + true + + + + + + + + + Qt::Horizontal + + + + + + + 0 + + + 0 + + + + + Re-open closed tabs + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + +