Compare commits

..

1 Commits

Author SHA1 Message Date
Veloman Yunkan
ebf0fe8b8f More predictable Downloader::startDownload()
Before this change `Downloader::startDownload()` might avoid starting a
new download when a download with the specified URI was already present
in its cache. This might be confusing for the following reasons:

1. uri is not the only parameter of `Downloader::startDownload()` - a
   target download directory may also be specified through the second
   `options` parameter. Thus calling `Downloader::startDownload()` twice
   with the same URI but different download directories would not save
   files into the second directory.

2. Files of a completed download may be removed, whereupon downloading the same
   files again won't be a no-op. However in such a situation
   `Downloader` refuses to actually repeat a previous download.
2024-02-13 18:42:15 +04:00
143 changed files with 1307 additions and 6310 deletions

View File

@@ -11,148 +11,101 @@ jobs:
strategy:
fail-fast: false
matrix:
os:
- macos-13
target:
- macos-aarch64-dyn
- macos-x86_64-dyn
- ios-arm64-dyn
- ios-x86_64-dyn
include:
- target: macos-aarch64-dyn
arch_name: arm64-apple-macos
run_test: true
- target: macos-x86_64-dyn
arch_name: x86_64-apple-darwin
run_test: true
- target: ios-arm64-dyn
arch_name: aarch64-apple-ios
run_test: false
- target: ios-x86_64-dyn
arch_name: x86-apple-ios-simulator
run_test: false
runs-on: macos-15
- native_dyn
- iOS_arm64
- iOS_x86_64
runs-on: ${{ matrix.os }}
env:
HOME: /Users/runner
steps:
- name: Retrieve source code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install packages
run: |
brew update
brew install ninja meson
brew unlink python3
# upgrade from python@3.11.2_1 to python@3.11.3 fails to overwrite those
rm -f /usr/local/bin/2to3 /usr/local/bin/2to3-3.11 /usr/local/bin/idle3 /usr/local/bin/idle3.11 /usr/local/bin/pydoc3 /usr/local/bin/pydoc3.11 /usr/local/bin/python3 /usr/local/bin/python3-config /usr/local/bin/python3.11 /usr/local/bin/python3.11-config
brew install pkg-config ninja meson
env:
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
- name: Install dependencies
uses: kiwix/kiwix-build/actions/dl_deps_archive@main
with:
os_name: macos
target_platform: ${{ matrix.target }}
- name: Compile
env:
PKG_CONFIG_PATH: ${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib/pkgconfig
CPPFLAGS: -I${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/include
PKG_CONFIG_PATH: ${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/lib/pkgconfig
CPPFLAGS: -I${{env.HOME}}/BUILD_native_dyn/INSTALL/include
MESON_OPTION: --default-library=shared -Db_coverage=true
MESON_CROSSFILE: ${{env.HOME}}/BUILD_${{matrix.arch_name}}/meson_cross_file.txt
MESON_CROSSFILE: ${{env.HOME}}/BUILD_${{matrix.target}}/meson_cross_file.txt
shell: bash
run: |
if [ -e $MESON_CROSSFILE ]; then
if [[ ! "${{matrix.target}}" =~ native_.* ]]; then
MESON_OPTION="$MESON_OPTION --cross-file $MESON_CROSSFILE -Dstatic-linkage=true"
fi
meson . build ${MESON_OPTION}
ninja -C build
- name: Test libkiwix
if: matrix.run_test
if: startsWith(matrix.target, 'native_')
env:
SKIP_BIG_MEMORY_TEST: 1
LD_LIBRARY_PATH: ${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib:${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib64
LD_LIBRARY_PATH: ${{env.HOME}}/BUILD_native_dyn/INSTALL/lib:${{env.HOME}}/BUILD_native_dyn/INSTALL/lib64
run: meson test -C build --verbose
Windows:
runs-on: windows-2022
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup python 3.10
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install packages
run:
choco install pkgconfiglite ninja
- name: Install python modules
run: pip3 install meson
- 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-static
- name: Compile
shell: cmd
run: |
set PKG_CONFIG_PATH=%cd%\BUILD_win-amd64\INSTALL\lib\pkgconfig
set CPPFLAGS=-I%cd%\BUILD_win-amd64\INSTALL\include
meson.exe setup . build -Dwerror=false --default-library=static --buildtype=release
cd build
ninja.exe
- name: Test
shell: cmd
run: |
cd build
meson.exe test --verbose
env:
WAIT_TIME_FACTOR_TEST: 10
Linux:
strategy:
fail-fast: false
matrix:
target:
- linux-x86_64-static
- linux-x86_64-dyn
- android-arm
- android-arm64
image_variant: ['jammy']
name:
- native_static
- native_dyn
- android_arm
- android_arm64
- win32_static
- win32_dyn
include:
- target: linux-x86_64-static
- name: native_static
target: native_static
image_variant: focal
lib_postfix: '/x86_64-linux-gnu'
arch_name: linux-x86_64
run_test: true
coverage: true
- target: linux-x86_64-dyn
- name: native_dyn
target: native_dyn
image_variant: focal
lib_postfix: '/x86_64-linux-gnu'
arch_name: linux-x86_64
run_test: true
coverage: true
- target: android-arm
- name: android_arm
target: android_arm
image_variant: focal
lib_postfix: '/arm-linux-androideabi'
arch_name: arm-linux-androideabi
run_test: false
coverage: false
- target: android-arm64
- name: android_arm64
target: android_arm64
image_variant: focal
lib_postfix: '/aarch64-linux-android'
arch_name: aarch64-linux-android
run_test: false
coverage: false
- name: win32_static
target: win32_static
image_variant: f35
lib_postfix: '64'
- name: win32_dyn
target: win32_dyn
image_variant: f35
lib_postfix: '64'
env:
HOME: /home/runner
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
container:
image: "ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:2025-06-07"
image: "ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:38"
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Install dependencies
uses: kiwix/kiwix-build/actions/dl_deps_archive@main
with:
@@ -161,40 +114,38 @@ jobs:
shell: bash
run: |
meson --version
if [[ "${{matrix.target}}" =~ .*-dyn ]]; then
if [[ "${{matrix.target}}" =~ .*_dyn ]]; then
MESON_OPTION="--default-library=shared"
else
MESON_OPTION="--default-library=static"
fi
if [ -e "${{env.HOME}}/BUILD_${{matrix.arch_name}}/meson_cross_file.txt" ]; then
MESON_OPTION="$MESON_OPTION --cross-file ${{env.HOME}}/BUILD_${{matrix.arch_name}}/meson_cross_file.txt"
else
if [[ "${{matrix.target}}" =~ native_.* ]]; then
MESON_OPTION="$MESON_OPTION -Db_coverage=true"
else
MESON_OPTION="$MESON_OPTION --cross-file $HOME/BUILD_${{matrix.target}}/meson_cross_file.txt"
fi
if [[ "${{matrix.target}}" =~ android-.* ]]; then
if [[ "${{matrix.target}}" =~ android_.* ]]; then
MESON_OPTION="$MESON_OPTION -Dstatic-linkage=true"
fi
meson . build ${MESON_OPTION}
cd build
ninja
env:
PKG_CONFIG_PATH: "/home/runner/BUILD_${{matrix.arch_name}}/INSTALL/lib/pkgconfig:/home/runner/BUILD_${{matrix.arch_name}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
CPPFLAGS: "-I/home/runner/BUILD_${{matrix.arch_name}}/INSTALL/include"
PKG_CONFIG_PATH: "/home/runner/BUILD_${{matrix.target}}/INSTALL/lib/pkgconfig:/home/runner/BUILD_${{matrix.target}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
CPPFLAGS: "-I/home/runner/BUILD_${{matrix.target}}/INSTALL/include"
- name: Test
if: matrix.run_test
if: startsWith(matrix.target, 'native_')
shell: bash
run: |
cd build
meson test --verbose
if [[ "${{matrix.coverage}}" = "true" ]]; then
ninja coverage
fi
ninja coverage
env:
LD_LIBRARY_PATH: "/home/runner/BUILD_${{matrix.arch_name}}/INSTALL/lib:/home/runner/BUILD_${{matrix.arch_name}}/INSTALL/lib${{matrix.lib_postfix}}"
LD_LIBRARY_PATH: "/home/runner/BUILD_${{matrix.target}}/INSTALL/lib:/home/runner/BUILD_${{matrix.target}}/INSTALL/lib${{matrix.lib_postfix}}"
SKIP_BIG_MEMORY_TEST: 1
- name: Publish coverage
if: matrix.coverage
if: startsWith(matrix.target, 'native_')
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}

View File

@@ -15,15 +15,11 @@ jobs:
fail-fast: false
matrix:
distro:
# - debian-unstable
# - debian-trixie
# - debian-bookworm
# - debian-bullseye
- ubuntu-noble
- debian-unstable
- ubuntu-jammy
- ubuntu-focal
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
# Determine which PPA we should upload to
- name: PPA
@@ -31,54 +27,25 @@ jobs:
run: |
if [[ $REF == refs/tags* ]]
then
echo "ppa=kiwixteam/release" >> $GITHUB_OUTPUT
echo "::set-output name=ppa::kiwixteam/release"
else
echo "ppa=kiwixteam/dev" >> $GITHUB_OUTPUT
echo "::set-output name=ppa::kiwixteam/dev"
fi
env:
REF: ${{ github.ref }}
- uses: legoktm/gh-action-auto-dch@main
- uses: legoktm/gh-action-auto-dch@master
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
- 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
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: legoktm/gh-action-build-deb@ubuntu-jammy
if: matrix.distro == 'ubuntu-jammy'
@@ -88,12 +55,20 @@ jobs:
args: --no-sign
ppa: ${{ steps.ppa.outputs.ppa }}
- uses: actions/upload-artifact@v4
- 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@v3
with:
name: Packages for ${{ matrix.distro }}
path: output
- uses: legoktm/gh-action-dput@main
- uses: legoktm/gh-action-dput@master
name: Upload dev package
# Only upload on pushes to main
if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' && startswith(matrix.distro, 'ubuntu-')
@@ -102,7 +77,7 @@ jobs:
repository: ppa:kiwixteam/dev
packages: output/*_source.changes
- uses: legoktm/gh-action-dput@main
- uses: legoktm/gh-action-dput@master
name: Upload release package
if: github.event_name == 'release' && startswith(matrix.distro, 'ubuntu-')
with:

View File

@@ -1,70 +1,3 @@
libkiwix 14.1.1
===============
* Server:
- Fix regression for kiwix-serve --nosearchbar (@veloman-yunkan #1250)
- Avoid results content interpretation... crash in fulltext search (@vighnesh-sawant #1241)
- Fix for intermittent /content/blank.html errors (@veloman-yunkan #1249)
libkiwix 14.1.0
===============
* Server:
- Viewer detects & tracks intrapage navigation anchors too (@veloman-yunkan #1213)
- Add support for catalog only mode (@veloman-yunkan #1219)
- Add API which returns server access url (@vighnesh-sawant #1234)
- Fix chrome searchbar placeholder text overflow (@aditii2712 #1185)
- Fix magnet link queryStyring (@rgaudin #1160)
- Improve chrome printing stylesheet (@kelson42 #1202)
- Default white background (@kelson42 #1205)
* Other:
- Switched to the new libzim illustrations API (@veloman-yunkan #1226)
- Stop building Windows with DEBUG symbols in CI (@kelson42 #1165)
- Update many things in the CI/CD (@kelson42 #1203 #1194 #1209 #1207 #1235)
- Requires now libzim 9.4.0 (@kelson42 #1231)
- Fix compilation for FreeBSD (@OICe2 #1173 #1174)
- Wait up to 1s to let aria2c to start before complaining (@kelson42 #1169)
libkiwix 14.0.0
===============
* Server:
- Support of IPv6 (@veloman-yunkan @aryanA101a #1074 #1093)
- Better public IP configuration/detection (@sgourdas #1132)
- Fix API errors in catalog searches if Xapian keyword in used (@veloman-yunkan #1137)
- Clearly define which Web browsers are supported (@kelson42 @rgaudin @jaifroid @benoit74 #1132)
- Improve welcome page download buttons (@veloman-yunkan #1094)
- Better handling of external (non-HTTP) links (@veloman-yunkan #1123)
- Fix book illustration size on welcome page to 48x48 pixels (@veloman-yunkan #1127)
- Remove "Multiple Languages" in language filter (@veloman-yunkan #1098)
- Stop transforming tags casing (@kelson42 @veloman-yunkan #1079 #1121)
- ZIM file size consistently advertised in MiB (@harsha-mangena #1132)
- Few new supported languages in the filter (@kelson42 #1080)
- Improve accesskeys (@kelson42 #1075)
- Add OpenSearch <link> to head of pages (@kelson42 #1070)
* Compilation/Packaging:
- Multiple fixes around deb packaging (@kelson42 #1108 #1114 #1135)
- Generating of libkiwix.pc via Meson (@veloman-yunkan #1133)
- Native Windows CI/CD (@mgautierfr @kelson42 #1113 #1125)
- Better check (maximum) libzim version (@kelson42 #1124)
- Multiple automated tests improvements (@veloman-yunkan #1068 #1067)
* Other:
- Deleted supported env. variable `$KIWIX_DATA_DIR` and `kiwix::getDataDirectory()` (@sgourdas #1107)
- New string slugification for filenames (@shaopenglin #1105)
- Multiple improvements around aria2c download mgmt. (@veloman-yunkan #1097)
libkiwix 13.1.0
===============
* Server:
- Properly translated error pages (@veloman-yunkan #1032)
- Properly translated search result page (@veloman-yunkan #1046)
- Default UI language is resolved in frontend (@veloman-yunkan #1044)
- Better support of older Web browsers by polyfilling replaceAll() (@veloman-yunkan #1054)
* New API to migrate bookmarks between books (@mgautierfr #1043)
* Fixed compilation on Haiku OS (@Begasus #1048)
libkiwix 13.0.0
===============
@@ -120,6 +53,8 @@ libkiwix 12.1.0
* Remove libkiwix android publisher from the repository (@kelson42 #884)
* Various fixes of meson and CI. (@mgautierfr @kelson42)
libkiwix 12.0.0
===============
@@ -159,6 +94,7 @@ libkiwix 12.0.0
* Fix documentation (@kelson42 #816)
* Udpate translation (#787 #839 #847)
libkiwix 11.0.0
===============

View File

@@ -7,7 +7,7 @@ GNU/Linux, macOS, Android, iOS, ...).
[![Release](https://img.shields.io/github/v/tag/kiwix/libkiwix?label=release&sort=semver)](https://download.kiwix.org/release/libkiwix/)
[![Repositories](https://img.shields.io/repology/repositories/libkiwix?label=repositories)](https://github.com/kiwix/libkiwix/wiki/Repology)
[![Build Status](https://github.com/kiwix/libkiwix/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/kiwix/libkiwix/actions?query=branch%3Amain)
[![Build Status](https://github.com/kiwix/libkiwix/workflows/CI/badge.svg?query=branch%3Amain)](https://github.com/kiwix/libkiwix/actions?query=branch%3Amain)
[![Doc](https://readthedocs.org/projects/libkiwix/badge/?style=flat)](https://libkiwix.readthedocs.org/en/latest/?badge=latest)
[![CodeFactor](https://www.codefactor.io/repository/github/kiwix/libkiwix/badge)](https://www.codefactor.io/repository/github/kiwix/libkiwix)
[![Codecov](https://codecov.io/gh/kiwix/libkiwix/branch/main/graph/badge.svg)](https://codecov.io/gh/kiwix/libkiwix)

17
debian/control vendored
View File

@@ -3,12 +3,13 @@ Priority: optional
Maintainer: Kiwix team <kiwix@kiwix.org>
Build-Depends: debhelper-compat (= 13),
meson,
pkgconf,
libzim-dev (>= 9.0), libzim-dev (<< 10.0),
pkg-config,
libzim-dev (>= 7.2.0~),
libcurl4-gnutls-dev,
libicu-dev,
libgtest-dev,
libkainjow-mustache-dev,
liblzma-dev,
libmicrohttpd-dev,
libpugixml-dev,
zlib1g-dev
@@ -21,13 +22,12 @@ Package: libkiwix-dev
Section: libdevel
Architecture: any
Multi-Arch: same
Depends: libkiwix14 (= ${binary:Version}), ${misc:Depends}, python3,
libzim-dev (>= 9.0), libzim-dev (<< 10.0),
Depends: libkiwix10 (= ${binary:Version}), ${misc:Depends}, python3,
libzim-dev (>= 7.2.0~),
libicu-dev,
libpugixml-dev,
libcurl4-gnutls-dev,
libmicrohttpd-dev,
zlib1g-dev
libmicrohttpd-dev
Description: library of common code for Kiwix (development)
Kiwix is an offline Wikipedia reader. libkiwix provides the
software core for Kiwix, and contains the code shared by all
@@ -35,12 +35,11 @@ Description: library of common code for Kiwix (development)
.
This package contains development files.
Package: libkiwix14
Package: libkiwix10
Architecture: any
Multi-Arch: same
Depends: ${shlibs:Depends}, ${misc:Depends}, aria2
Conflicts: libkiwix0, libkiwix3, libkiwix9, libkiwix10, libkiwix11, libkiwix12, libkiwix13
Replaces: libkiwix0, libkiwix3, libkiwix9, libkiwix10, libkiwix11, libkiwix12, libkiwix13
Conflicts: libkiwix0, libkiwix3, libkiwix9
Description: library of common code for Kiwix
Kiwix is an offline Wikipedia reader. libkiwix provides the
software core for Kiwix, and contains the code shared by all

View File

View File

@@ -29,33 +29,19 @@ class xml_node;
namespace kiwix
{
class Book;
/**
* A class to store information about a bookmark (an article in a book)
*/
class Bookmark
{
public:
/**
* Create an empty bookmark.
*
* Bookmark must be populated with `set*` methods
*/
Bookmark();
/**
* Create a bookmark given a Book, a path and a title.
*/
Bookmark(const Book& book, const std::string& path, const std::string& title);
~Bookmark();
void updateFromXml(const pugi::xml_node& node);
const std::string& getBookId() const { return m_bookId; }
const std::string& getBookTitle() const { return m_bookTitle; }
const std::string& getBookName() const { return m_bookName; }
const std::string& getBookFlavour() const { return m_bookFlavour; }
const std::string& getUrl() const { return m_url; }
const std::string& getTitle() const { return m_title; }
const std::string& getLanguage() const { return m_language; }
@@ -63,8 +49,6 @@ class Bookmark
void setBookId(const std::string& bookId) { m_bookId = bookId; }
void setBookTitle(const std::string& bookTitle) { m_bookTitle = bookTitle; }
void setBookName(const std::string& bookName) { m_bookName = bookName; }
void setBookFlavour(const std::string& bookFlavour) { m_bookFlavour = bookFlavour; }
void setUrl(const std::string& url) { m_url = url; }
void setTitle(const std::string& title) { m_title = title; }
void setLanguage(const std::string& language) { m_language = language; }
@@ -73,8 +57,6 @@ class Bookmark
protected:
std::string m_bookId;
std::string m_bookTitle;
std::string m_bookName;
std::string m_bookFlavour;
std::string m_url;
std::string m_title;
std::string m_language;

View File

@@ -16,7 +16,6 @@
namespace kiwix {
enum class IpMode { IPV4, IPV6, ALL, AUTO }; // AUTO: Server decides the protocol
typedef zim::size_type size_type;
typedef zim::offset_type offset_type;

View File

@@ -168,16 +168,8 @@ class Download {
*/
class Downloader
{
public: // types
typedef std::vector<std::pair<std::string, std::string>> Options;
public: // functions
/*
* Create a new Downloader object.
*
* @param sessionFileDir: The directory where aria2 will store its session file.
*/
explicit Downloader(std::string sessionFileDir);
public:
Downloader();
virtual ~Downloader();
void close();
@@ -185,22 +177,14 @@ class Downloader
/**
* Start a new download.
*
* This method is thread safe and returns a pointer to a newly created
* `Download` or an existing one with a matching URI. In the latter case
* the options parameter is ignored, which can lead to surprising results.
* For example, if the old and new download requests (sharing the same URI)
* have different values for the download directory or output file name
* options, after the download is reported to be complete the downloaded file
* will be present only at the location specified for the first request.
*
* This method is thread safe and return a pointer to a newly created `Download`.
* User should call `update` on the returned `Download` to have an accurate status.
*
* @param uri: The uri of the thing to download.
* @param downloadDir: The download directory where the thing should be stored (takes precedence over any "dir" in `options`).
* @param options: A series of pair <option_name, option_value> to pass to aria.
* @return: The newly created Download.
*/
std::shared_ptr<Download> startDownload(const std::string& uri, const std::string& downloadDir, Options options = {});
std::shared_ptr<Download> startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options = {});
/**
* Get a download corrsponding to a download id (did)
@@ -222,7 +206,7 @@ class Downloader
*/
std::vector<std::string> getDownloadIds() const;
private: // data
private:
mutable std::mutex m_lock;
std::map<std::string, std::shared_ptr<Download>> m_knownDownloads;
std::shared_ptr<Aria2> mp_aria;

View File

@@ -55,22 +55,6 @@ enum supportedListMode {
NOVALID = 1 << 5
};
enum MigrationMode {
/** When migrating bookmarks, do not allow to migrate to an older book than the currently pointed one
* (or date stored in the bookmark if book is invalid)
*
* If no newer books are found, no upgrade is made.
*/
UPGRADE_ONLY = 0,
/** Try hard to do a migration. This mostly does:
* - Try to find a newer book.
* - If book is invalid: find a best book, potentially older.
* Older book will never be returned if current book is a valid one.
*/
ALLOW_DOWNGRADE = 1,
};
class Filter {
public: // types
using Tags = std::vector<std::string>;
@@ -87,7 +71,6 @@ class Filter {
std::string _query;
bool _queryIsPartial;
std::string _name;
std::string _flavour;
public: // functions
Filter();
@@ -147,7 +130,6 @@ class Filter {
Filter& maxSize(size_t size);
Filter& query(std::string query, bool partial=true);
Filter& name(std::string name);
Filter& flavour(std::string flavour);
Filter& clearLang();
Filter& clearCategory();
@@ -170,9 +152,6 @@ class Filter {
bool hasCreator() const;
const std::string& getCreator() const { return _creator; }
bool hasFlavour() const;
const std::string& getFlavour() const { return _flavour; }
const Tags& getAcceptTags() const { return _acceptTags; }
const Tags& getRejectTags() const { return _rejectTags; }
@@ -271,7 +250,7 @@ class Library: public std::enable_shared_from_this<Library>
void addBookmark(const Bookmark& bookmark);
/**
* Remove a bookmark
* Remove a bookmarkk
*
* @param zimId The zimId of the bookmark.
* @param url The url of the bookmark.
@@ -279,66 +258,6 @@ class Library: public std::enable_shared_from_this<Library>
*/
bool removeBookmark(const std::string& zimId, const std::string& url);
/**
* Migrate all invalid bookmarks.
*
* All invalid bookmarks (ie pointing to unknown books, no check is made on bookmark pointing to
* invalid articles of valid book) will be migrated (if possible) to a better book.
* "Better book", will be determined using method `getBestTargetBookId`.
*
* @return A tuple<int, int>: <The number of bookmarks updated>, <Number of invalid bookmarks before migration was performed>.
*/
std::tuple<int, int> migrateBookmarks(MigrationMode migrationMode = ALLOW_DOWNGRADE);
/**
* Migrate all bookmarks associated to a specific book.
*
* All bookmarks associated to `sourceBookId` book will be migrated to a better book.
* "Better book", will be determined using method `getBestTargetBookId`.
*
* @param sourceBookId the source bookId of the bookmarks to migrate.
* @param migrationMode how we will find the best book.
* @return The number of bookmarks updated.
*/
int migrateBookmarks(const std::string& sourceBookId, MigrationMode migrationMode = UPGRADE_ONLY);
/**
* Migrate bookmarks
*
* Migrate all bookmarks pointing to `source` to `destination`.
*
* @param sourceBookId the source bookId of the bookmarks to migrate.
* @param targetBookId the destination bookId to migrate the bookmarks to.
* @return The number of bookmarks updated.
*/
int migrateBookmarks(const std::string& sourceBookId, const std::string& targetBookId);
/**
* Get the best available bookId for a bookmark.
*
* Given a bookmark, return the best available bookId.
* "best available bookId" is determined using heuristitcs based on book name, flavour and date.
*
* @param bookmark The bookmark to search the bookId for.
* @param migrationMode The migration mode to use.
* @return A bookId. Potentially empty string if no suitable book found.
*/
std::string getBestTargetBookId(const Bookmark& bookmark, MigrationMode migrationMode) const;
/**
* Get the best bookId for a combination of book's name, flavour and date.
*
* Given a bookName (mandatory), try to find the best book.
* If preferedFlavour is given, will try to find a book with the same flavour. If not found, return a book with a different flavour.
* If minDate is given, return a book newer than minDate. If not found, return a empty bookId.
*
* @param bookName The name of the book
* @param preferedFlavour The prefered flavour.
* @param minDate the minimal book date acceptable. Must be a string in the format "YYYY-MM-DD".
* @return A bookId corresponding to the query, or empty string if not found.
*/
std::string getBestTargetBookId(const std::string& bookName, const std::string& preferedFlavour="", const std::string& minDate="") const;
// XXX: This is a non-thread-safe operation
const Book& getBookById(const std::string& id) const;
// XXX: This is a non-thread-safe operation
@@ -484,13 +403,12 @@ private: // functions
AttributeCounts getBookAttributeCounts(BookStrPropMemFn p) const;
std::vector<std::string> getBookPropValueSet(BookStrPropMemFn p) const;
BookIdCollection filterViaBookDB(const Filter& filter) const;
std::string getBestFromBookCollection(BookIdCollection books, const Bookmark& bookmark, MigrationMode migrationMode) const;
unsigned int getBookCount_not_protected(const bool localBooks, const bool remoteBooks) const;
void updateBookDB(const Book& book);
void dropCache(const std::string& bookId);
private: //data
mutable std::recursive_mutex m_mutex;
mutable std::mutex m_mutex;
Library::Revision m_revision;
std::map<std::string, Entry> m_books;
using ArchiveCache = ConcurrentCache<std::string, std::shared_ptr<zim::Archive>>;

View File

@@ -155,15 +155,6 @@ class Manager
const std::string& url = "",
const bool checkMetaData = false);
/**
* Add all books from the directory tree into the library.
*
* @param path The path of the directory to scan.
* @param verboseFlag Verbose logs flag.
*/
void addBooksFromDirectory(const std::string& path,
const bool verboseFlag = false);
std::string writableLibraryPath;
bool m_hasSearchResult = false;

View File

@@ -7,12 +7,10 @@ headers = [
'downloader.h',
'search_renderer.h',
'server.h',
'spelling_correction.h',
'kiwixserve.h',
'name_mapper.h',
'tools.h',
'version.h',
'i18n.h'
'version.h'
]
install_headers(headers, subdir:'kiwix')

View File

@@ -50,13 +50,10 @@ class HumanReadableNameMapper : public NameMapper {
std::map<std::string, std::string> m_nameToId;
public:
HumanReadableNameMapper(const kiwix::Library& library, bool withAlias);
HumanReadableNameMapper(kiwix::Library& library, bool withAlias);
virtual ~HumanReadableNameMapper() = default;
virtual std::string getNameForId(const std::string& id) const;
virtual std::string getIdForName(const std::string& name) const;
private:
void mapName(const kiwix::Library& lib, std::string name, std::string id);
};
class UpdatableNameMapper : public NameMapper {

View File

@@ -22,7 +22,6 @@
#include <string>
#include <memory>
#include "tools.h"
namespace kiwix
{
@@ -52,7 +51,7 @@ namespace kiwix
void stop();
void setRoot(const std::string& root);
void setAddress(const std::string& addr);
void setAddress(const std::string& addr) { m_addr = addr; }
void setPort(int port) { m_port = port; }
void setNbThreads(int threads) { m_nbThreads = threads; }
void setMultiZimSearchLimit(unsigned int limit) { m_multizimSearchLimit = limit; }
@@ -63,19 +62,14 @@ namespace kiwix
{ m_withTaskbar = withTaskbar; m_withLibraryButton = withLibraryButton; }
void setBlockExternalLinks(bool blockExternalLinks)
{ m_blockExternalLinks = blockExternalLinks; }
void setCatalogOnlyMode(bool enable) { m_catalogOnlyMode = enable; }
void setContentServerUrl(std::string url) { m_contentServerUrl = url; }
void setIpMode(IpMode mode) { m_ipMode = mode; }
int getPort() const;
IpAddress getAddress() const;
IpMode getIpMode() const;
std::vector<std::string> getServerAccessUrls() const;
int getPort();
std::string getAddress();
protected:
std::shared_ptr<Library> mp_library;
std::shared_ptr<NameMapper> mp_nameMapper;
std::string m_root = "";
IpAddress m_addr;
std::string m_addr = "";
std::string m_indexTemplateString = "";
int m_port = 80;
int m_nbThreads = 1;
@@ -84,10 +78,7 @@ namespace kiwix
bool m_withTaskbar = true;
bool m_withLibraryButton = true;
bool m_blockExternalLinks = false;
IpMode m_ipMode = IpMode::AUTO;
int m_ipConnectionLimit = 0;
bool m_catalogOnlyMode = false;
std::string m_contentServerUrl;
std::unique_ptr<InternalServer> mp_server;
};
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 2025 Veloman Yunkan
*
* 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
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef KIWIX_SPELLING_CORRECTION_H
#define KIWIX_SPELLING_CORRECTION_H
#include <filesystem>
#include <memory>
#include <string>
#include <vector>
namespace zim
{
class Archive;
}
namespace Xapian
{
class Database;
}
namespace kiwix
{
class SpellingsDB
{
public: // functions
SpellingsDB(const zim::Archive& archive, std::filesystem::path cacheDirPath);
~SpellingsDB();
SpellingsDB(const SpellingsDB& ) = delete;
void operator=(const SpellingsDB& ) = delete;
std::vector<std::string> getSpellingCorrections(const std::string& word, uint32_t maxCount) const;
private: // data
std::unique_ptr<Xapian::Database> impl_;
};
} // namespace kiwix
#endif // KIWIX_SPELLING_CORRECTION_H

View File

@@ -24,17 +24,8 @@
#include <vector>
#include <map>
#include <cstdint>
#include "common.h"
namespace kiwix
{
struct IpAddress
{
std::string addr; // IPv4 address
std::string addr6; // IPv6 address
};
namespace kiwix {
typedef std::pair<std::string, std::string> LangNameCodePair;
typedef std::vector<LangNameCodePair> FeedLanguages;
typedef std::vector<std::string> FeedCategories;
@@ -46,6 +37,26 @@ typedef std::vector<std::string> FeedCategories;
*/
std::string getCurrentDirectory();
/**
* Return the data directory.
*
* The data directory is a directory where to put data (zim files, ...)
* It depends of the platform and it may be changed by user using environment variable.
*
* The resolution order is :
* - `KIWIX_DATA_DIR` env variable (if set).
* - On Windows :
* . `$APPDATA/kiwix` if $APPDATA is set
* . `$USERPROFILE/kiwix` if $USERPROFILE is set
* - Else :
* . `$XDG_DATA_HOME/kiwix`if $XDG_DATA_HOME is set
* . `$HOME/.local/share/kiwx` if $HOWE is set
* - current directory
*
* @return the path of the data directory (utf8 encoded)
*/
std::string getDataDirectory();
/** Return the path of the executable
*
* Some application may be packaged in auto extractible archive (Appimage) and the
@@ -199,30 +210,13 @@ bool fileReadable(const std::string& path);
std::string getMimeTypeForFile(const std::string& filename);
/** Provides all available network interfaces
*
* This function provides the available IPv4 and IPv6 network interfaces
* as a map from the interface name to its IPv4 and/or IPv6 address(es).
*/
std::map<std::string, IpAddress> getNetworkInterfacesIPv4Or6();
/** Provides all available IPv4 network interfaces
*
* This function provides the available IPv4 network interfaces
* as a map from the interface name to its IPv4 address.
*
* Provided for backward compatibility with libkiwix v13.1.0.
*/
std::map<std::string, std::string> getNetworkInterfaces();
/** Provides the best IP address
* This function provides the best IP addresses for both ipv4 and ipv6 protocols,
* in an IpAddress struct, based on the list given by getNetworkInterfacesIPv4Or6()
*/
IpAddress getBestPublicIps();
/** Provides the best IPv4 adddress
* Equivalent to getBestPublicIp(false). Provided for backward compatibility
* with libkiwix v13.1.0.
* This function provides the best IP address from the list given by getNetworkInterfaces
*/
std::string getBestPublicIp();
@@ -237,15 +231,15 @@ std::string beautifyFileSize(uint64_t number);
/**
* Load languages stored in an OPDS stream.
*
*
* @param content the OPDS stream.
* @return vector containing pairs of language code and their corresponding full language name.
* @return vector containing pairs of language code and their corresponding full language name.
*/
FeedLanguages readLanguagesFromFeed(const std::string& content);
/**
* Load categories stored in an OPDS stream .
*
*
* @param content the OPDS stream.
* @return vector containing category strings.
*/
@@ -253,19 +247,10 @@ FeedCategories readCategoriesFromFeed(const std::string& content);
/**
* Retrieve the full language name associated with a given ISO 639-3 language code.
*
*
* @param lang ISO 639-3 language code.
* @return full language name.
*/
std::string getLanguageSelfName(const std::string& lang);
/**
* Slugifies the filename by converting any characters reserved by the operating
* system to '_'. Note filename is only the file name and not a path.
*
* @param filename Valid UTF-8 encoded file name string.
* @return slugified string.
*/
std::string getSlugifiedFileName(const std::string& filename);
}
#endif // KIWIX_TOOLS_H

10
kiwix.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@prefix@
libdir=${prefix}/lib64
includedir=${prefix}/include
Name: libkiwix
Description: A library that contains a lot of things used by used by other kiwix programs
Version: @version@
Requires: @requires@
Libs: -L${libdir} -lkiwix @extra_libs@
Cflags: -I${includedir}/ @extra_cflags@

View File

@@ -1,61 +1,26 @@
project('libkiwix', 'cpp',
version : '14.1.1',
version : '13.0.0',
license : 'GPLv3+',
default_options : ['c_std=c11', 'cpp_std=c++17', 'werror=true'])
compiler = meson.get_compiler('cpp')
static_deps = get_option('static-linkage') or get_option('default_library') == 'static'
extra_libs = []
# Atomics as compiled by GCC or clang can lead to external references to
# functions depending on the type size and the platform. LLVM provides them in
# 'libcompiler_rt', which clang normally automatically links in, while GNU
# provides them in 'libatomic', which GCC *does not* link in automatically (but
# this is probably going to change, see
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81358). Regardless of the setup
# of the compiler driver itself (GCC or clang), we can thus assume that if some
# atomic references can't be resolved, then 'libatomic' is missing.
atomics_program = '''
#include <atomic>
#include <cstdint>
using namespace std;
int main() {
volatile atomic_bool a_b(true);
volatile atomic_ullong a_ull(-1);
// Next two lines are to cover atomic<socket_t> from 'httplib.h'.
volatile atomic<uint32_t> a_u32(-1);
volatile atomic<uint64_t> a_u64(-1);
return atomic_load(&a_b) == false && atomic_load(&a_ull) == 0 &&
atomic_load(&a_u32) == 0 && atomic_load(&a_u64) == 0;
}
'''
if not compiler.links(atomics_program,
name: 'compiler driver readily supports atomics')
libatomic = compiler.find_library('atomic')
compiler.links(atomics_program, name: 'atomics work with libatomic',
dependencies: libatomic, required: true)
extra_libs += ['-latomic']
# See https://github.com/kiwix/libkiwix/issues/371
if ['arm', 'mips', 'm68k', 'ppc', 'sh4'].contains(host_machine.cpu_family())
extra_libs = ['-latomic']
else
extra_libs = []
endif
# C++ std::thread is implemented using pthread on Linux by GCC, and on FreeBSD
# for both GCC and LLVM.
if (host_machine.system() == 'linux' and compiler.get_id() == 'gcc') or \
host_machine.system() == 'freebsd'
if (compiler.get_id() == 'gcc' and build_machine.system() == 'linux') or host_machine.system() == 'freebsd'
# C++ std::thread is implemented using pthread on linux by gcc
thread_dep = dependency('threads')
else
thread_dep = dependency('', required:false)
endif
libicu_dep = dependency('icu-i18n', static:static_deps)
if libicu_dep.version().version_compare('>= 76')
libicu_deps = [libicu_dep, dependency('icu-uc', static:static_deps)]
else
libicu_deps = [libicu_dep]
endif
pugixml_dep = dependency('pugixml', static:static_deps)
libcurl_dep = dependency('libcurl', static:static_deps)
microhttpd_dep = dependency('libmicrohttpd', static:static_deps)
@@ -70,10 +35,9 @@ else
error('Cannot found header mustache.hpp')
endif
libzim_dep = dependency('libzim', version:['>=9.4.0', '<10.0.0'], static:static_deps)
libzim_dep = dependency('libzim', version : '>=8.1.0', static:static_deps)
if not compiler.has_header_symbol('zim/zim.h', 'LIBZIM_WITH_XAPIAN', dependencies: libzim_dep)
error('Libzim seems to be compiled without Xapian. Xapian support is mandatory.')
error('Libzim seems to be compiled without xapian. Xapian support is mandatory.')
endif
@@ -85,25 +49,21 @@ endif
if host_machine.system() == 'windows'
add_project_arguments('-DNOMINMAX', language: 'cpp')
extra_libs += ['-liphlpapi']
endif
if build_machine.system() == 'windows'
extra_libs += ['-lshlwapi', '-lwinmm']
endif
# Dependencies as string
all_deps = [thread_dep, libzim_dep, pugixml_dep, libcurl_dep, microhttpd_dep, zlib_dep, xapian_dep]
# Dependencies as array
all_deps += libicu_deps
all_deps = [thread_dep, libicu_dep, libzim_dep, pugixml_dep, libcurl_dep, microhttpd_dep, zlib_dep, xapian_dep]
inc = include_directories('include', extra_include)
conf = configuration_data()
conf.set('LIBKIWIX_VERSION', '"@0@"'.format(meson.project_version()))
if build_machine.system() == 'windows'
extra_link_args = ['-lshlwapi', '-lwinmm']
else
extra_link_args = []
endif
subdir('include')
subdir('scripts')
subdir('static')
@@ -113,10 +73,17 @@ if get_option('doc')
subdir('docs')
endif
pkg_mod = import('pkgconfig')
pkg_mod.generate(libraries : [libkiwix] + extra_libs,
version : meson.project_version(),
name : 'libkiwix',
filebase : 'libkiwix',
description : 'A library that contains useful primitives that Kiwix readers have in common',
extra_cflags: extra_cflags)
pkg_requires = ['libzim', 'icu-i18n', 'pugixml', 'libcurl', 'libmicrohttpd', 'xapian-core']
pkg_conf = configuration_data()
pkg_conf.set('prefix', get_option('prefix'))
pkg_conf.set('requires', ' '.join(pkg_requires))
pkg_conf.set('extra_libs', ' '.join(extra_libs))
pkg_conf.set('extra_cflags', extra_cflags)
pkg_conf.set('version', meson.project_version())
configure_file(output : 'kiwix.pc',
configuration : pkg_conf,
input : 'kiwix.pc.in',
install_dir: get_option('libdir')+'/pkgconfig'
)

View File

@@ -61,7 +61,7 @@ lang_table_entry_cxx_template = '''
cxxfile_template = '''// This file is automatically generated. Do not modify it.
#include "server/i18n_utils.h"
#include "server/i18n.h"
namespace kiwix {
namespace i18n {

View File

@@ -61,32 +61,6 @@ resource_decl_template = """{namespaces_open}
extern const std::string {identifier};
{namespaces_close}"""
BINARY_RESOURCE_EXTENSIONS = {'.ico', '.png', '.ttf'}
TEXT_RESOURCE_EXTENSIONS = {
'.css',
'.html',
'.js',
'.json',
'.svg',
'.tmpl',
'.webmanifest',
'.xml',
}
if not BINARY_RESOURCE_EXTENSIONS.isdisjoint(TEXT_RESOURCE_EXTENSIONS):
raise RuntimeError(f"The following file type extensions are declared to be both binary and text: {BINARY_RESOURCE_EXTENSIONS.intersection(TEXT_RESOURCE_EXTENSIONS)}")
def is_binary_resource(filename):
_, extension = os.path.splitext(filename)
is_binary = extension in BINARY_RESOURCE_EXTENSIONS
is_text = extension in TEXT_RESOURCE_EXTENSIONS
if not is_binary and not is_text:
# all file type extensions of static resources must be listed
# in either BINARY_RESOURCE_EXTENSIONS or TEXT_RESOURCE_EXTENSIONS
raise RuntimeError(f"Unknown file type extension: {extension}")
return is_binary
class Resource:
def __init__(self, base_dirs, filename, cacheid=None):
filename = filename
@@ -98,8 +72,6 @@ class Resource:
try:
with open(os.path.join(base_dir, filename), 'rb') as f:
self.data = f.read()
if not is_binary_resource(filename):
self.data = self.data.replace(b"\r\n", b"\n")
found = True
break
except FileNotFoundError:

View File

@@ -4,7 +4,6 @@
#include "xmlrpc.h"
#include <iostream>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <thread>
#include <chrono>
@@ -30,41 +29,18 @@
namespace kiwix {
namespace {
void pauseAnyActiveDownloads(const std::string& ariaSessionFilePath)
{
std::ifstream inputFile(ariaSessionFilePath);
if ( !inputFile )
return;
std::ostringstream ss;
std::string line;
while ( std::getline(inputFile, line) ) {
if ( !startsWith(line, " pause=") ) {
ss << line << "\n";
}
if ( !line.empty() && line[0] != ' ' && line[0] != '#' ) {
ss << " pause=true\n";
}
}
std::ofstream outputFile(ariaSessionFilePath);
outputFile << ss.str();
}
} // unnamed namespace
Aria2::Aria2(std::string sessionFileDir):
Aria2::Aria2():
mp_aria(nullptr),
m_port(42042),
m_secret(getNewRpcSecret())
{
m_downloadDir = getDataDirectory();
makeDirectory(m_downloadDir);
std::vector<const char*> callCmd;
std::string rpc_port = "--rpc-listen-port=" + to_string(m_port);
std::string session_file = appendToDirectory(sessionFileDir, "kiwix.session");
pauseAnyActiveDownloads(session_file);
std::string download_dir = "--dir=" + getDataDirectory();
std::string session_file = appendToDirectory(getDataDirectory(), "kiwix.session");
std::string session = "--save-session=" + session_file;
std::string inputFile = "--input-file=" + session_file;
// std::string log_dir = "--log=\"" + logDir + "\"";
@@ -91,6 +67,7 @@ Aria2::Aria2(std::string sessionFileDir):
callCmd.push_back("--enable-rpc");
callCmd.push_back(rpc_secret.c_str());
callCmd.push_back(rpc_port.c_str());
callCmd.push_back(download_dir.c_str());
if (fileReadable(session_file)) {
callCmd.push_back(inputFile.c_str());
}
@@ -120,30 +97,20 @@ Aria2::Aria2(std::string sessionFileDir):
curl_easy_setopt(p_curl, CURLOPT_PORT, m_port);
curl_easy_setopt(p_curl, CURLOPT_POST, 1L);
curl_easy_setopt(p_curl, CURLOPT_ERRORBUFFER, curlErrorBuffer);
curl_easy_setopt(p_curl, CURLOPT_TIMEOUT_MS, 100);
typedef std::chrono::duration<double> Seconds;
const double MAX_WAITING_TIME_SECONDS = 1;
const auto t0 = std::chrono::steady_clock::now();
bool maxWaitingTimeWasExceeded = false;
CURLcode res = CURLE_OK;
while ( !maxWaitingTimeWasExceeded ) {
int watchdog = 50;
while(--watchdog) {
sleep(10);
curlErrorBuffer[0] = 0;
res = curl_easy_perform(p_curl);
auto res = curl_easy_perform(p_curl);
if (res == CURLE_OK) {
break;
} else if (watchdog == 1) {
LOG_ARIA_ERROR();
}
const auto dt = std::chrono::steady_clock::now() - t0;
const double elapsedTime = std::chrono::duration_cast<Seconds>(dt).count();
maxWaitingTimeWasExceeded = elapsedTime > MAX_WAITING_TIME_SECONDS;
}
curl_easy_cleanup(p_curl);
if ( maxWaitingTimeWasExceeded ) {
LOG_ARIA_ERROR();
if (!watchdog) {
throw std::runtime_error("Cannot connect to aria2c rpc. Aria2c launch cmd : " + launchCmd);
}
}

View File

@@ -22,10 +22,11 @@ class Aria2
std::unique_ptr<Subprocess> mp_aria;
int m_port;
std::string m_secret;
std::string m_downloadDir;
std::string doRequest(const MethodCall& methodCall);
public:
explicit Aria2(std::string sessionFileDir);
Aria2();
virtual ~Aria2() = default;
void close();

View File

@@ -82,11 +82,10 @@ void Book::update(const zim::Archive& archive) {
m_size = static_cast<uint64_t>(getArchiveFileSize(archive)) << 10;
m_illustrations.clear();
for ( const auto& illustrationInfo : archive.getIllustrationInfos() ) {
for ( const auto illustrationSize : archive.getIllustrationSizes() ) {
const auto illustration = std::make_shared<Illustration>();
const zim::Item illustrationItem = archive.getIllustrationItem(illustrationInfo);
illustration->width = illustrationInfo.width;
illustration->height = illustrationInfo.height;
const zim::Item illustrationItem = archive.getIllustrationItem(illustrationSize);
illustration->width = illustration->height = illustrationSize;
illustration->mimeType = illustrationItem.getMimetype();
illustration->data = illustrationItem.getData();
// NOTE: illustration->url is left uninitialized

View File

@@ -18,7 +18,6 @@
*/
#include "bookmark.h"
#include "book.h"
#include <pugixml.hpp>
@@ -29,17 +28,6 @@ Bookmark::Bookmark()
{
}
Bookmark::Bookmark(const Book& book, const std::string& path, const std::string& title):
m_bookId(book.getId()),
m_bookTitle(book.getTitle()),
m_bookName(book.getName()),
m_bookFlavour(book.getFlavour()),
m_url(path),
m_title(title),
m_language(book.getCommaSeparatedLanguages()),
m_date(book.getDate())
{}
/* Destructor */
Bookmark::~Bookmark()
{
@@ -50,8 +38,6 @@ void Bookmark::updateFromXml(const pugi::xml_node& node)
auto bookNode = node.child("book");
m_bookId = bookNode.child("id").child_value();
m_bookTitle = bookNode.child("title").child_value();
m_bookName = bookNode.child("name").child_value();
m_bookFlavour = bookNode.child("flavour").child_value();
m_language = bookNode.child("language").child_value();
m_date = bookNode.child("date").child_value();
m_title = node.child("title").child_value();

View File

@@ -18,7 +18,6 @@
*/
#include "downloader.h"
#include "tools.h"
#include "tools/pathTools.h"
#include "tools/stringTools.h"
@@ -125,8 +124,8 @@ void Download::cancelDownload()
}
/* Constructor */
Downloader::Downloader(std::string sessionFileDir) :
mp_aria(new Aria2(sessionFileDir))
Downloader::Downloader() :
mp_aria(new Aria2())
{
try {
for (auto gid : mp_aria->tellWaiting()) {
@@ -151,20 +150,11 @@ Downloader::Downloader(std::string sessionFileDir) :
/* Destructor */
Downloader::~Downloader()
{
close();
}
void Downloader::close()
{
if ( mp_aria ) {
try {
mp_aria->close();
} catch (const std::exception& err) {
std::cerr << "ERROR: Failed to save the downloader state: "
<< err.what() << std::endl;
}
mp_aria.reset();
}
mp_aria->close();
}
std::vector<std::string> Downloader::getDownloadIds() const {
@@ -176,51 +166,9 @@ std::vector<std::string> Downloader::getDownloadIds() const {
return ret;
}
namespace
{
bool downloadCanBeReused(const Download& d,
const std::string& uri,
const Downloader::Options& /*options*/)
{
const auto& uris = d.getUris();
const bool sameURI = std::find(uris.begin(), uris.end(), uri) != uris.end();
if ( !sameURI )
return false;
switch ( d.getStatus() ) {
case Download::K_ERROR:
case Download::K_UNKNOWN:
case Download::K_REMOVED:
return false;
case Download::K_ACTIVE:
case Download::K_WAITING:
case Download::K_PAUSED:
return true; // XXX: what if options are different?
case Download::K_COMPLETE:
return fileExists(d.getPath()); // XXX: what if options are different?
}
return false;
}
} // unnamed namespace
std::shared_ptr<Download> Downloader::startDownload(const std::string& uri, const std::string& downloadDir, Options options)
std::shared_ptr<Download> Downloader::startDownload(const std::string& uri, const std::vector<std::pair<std::string, std::string>>& options)
{
std::unique_lock<std::mutex> lock(m_lock);
options.erase(std::remove_if(options.begin(), options.end(), [](const auto& option) {
return option.first == "dir";
}), options.end());
options.push_back({"dir", downloadDir});
for (auto& p: m_knownDownloads) {
auto& d = p.second;
if ( downloadCanBeReused(*d, uri, options) )
return d;
}
std::vector<std::string> uris = {uri};
auto gid = mp_aria->addUri(uris, options);
m_knownDownloads[gid] = std::make_shared<Download>(mp_aria, gid);

View File

@@ -3,7 +3,7 @@
#include "tools/otherTools.h"
#include "tools.h"
#include "tools/regexTools.h"
#include "server/i18n_utils.h"
#include "server/i18n.h"
namespace kiwix
{
@@ -27,30 +27,6 @@ std::string humanFriendlyTitle(std::string title)
return humanFriendlyString;
}
kainjow::mustache::object getLangTag(const std::vector<std::string>& bookLanguages) {
std::string langShortString = "";
std::string langFullString = "???";
//if more than 1 languages then show "mul" else show the language
if(bookLanguages.size() > 1) {
std::vector<std::string> mulLanguages;
langShortString = "mul";
for (const auto& lang : bookLanguages) {
const std::string fullLang = getLanguageSelfName(lang);
mulLanguages.push_back(fullLang);
}
langFullString = kiwix::join(mulLanguages, ",");
} else if(bookLanguages.size() == 1) {
langShortString = bookLanguages[0];
langFullString = getLanguageSelfName(langShortString);
}
kainjow::mustache::object langTag;
langTag["langShortString"] = langShortString;
langTag["langFullString"] = langFullString;
return langTag;
}
kainjow::mustache::list getTagList(std::string tags)
{
const auto tagsList = kiwix::split(tags, ";", true, false);
@@ -96,16 +72,17 @@ std::string HTMLDumper::dumpPlainHTML(kiwix::Filter filter) const
contentId = urlEncode(nameMapper->getNameForId(bookId));
} catch (...) {}
const auto bookDescription = bookObj.getDescription();
const auto langCode = bookObj.getCommaSeparatedLanguages();
const auto bookIconUrl = rootLocation + "/catalog/v2/illustration/" + bookId + "/?size=48";
const auto tags = bookObj.getTags();
const auto downloadAvailable = (bookObj.getUrl() != "");
const auto langTagObj = getLangTag(bookObj.getLanguages());
std::string faviconAttr = "style=background-image:url(" + bookIconUrl + ")";
booksData.push_back(kainjow::mustache::object{
{"id", contentId},
{"title", bookTitle},
{"description", bookDescription},
{"langTag", langTagObj},
{"langCode", langCode},
{"faviconAttr", faviconAttr},
{"tagList", getTagList(tags)},
{"downloadAvailable", downloadAvailable}
@@ -130,7 +107,6 @@ std::string HTMLDumper::dumpPlainHTML(kiwix::Filter filter) const
RESOURCE::templates::no_js_library_page_html,
kainjow::mustache::object{
{"root", rootLocation},
{"contentAccessUrl", onlyAsNonEmptyMustacheValue(contentAccessUrl)},
{"books", booksData },
{"searchQuery", searchQuery},
{"languages", languages},

View File

@@ -110,7 +110,7 @@ Library::~Library() = default;
bool Library::addBook(const Book& book)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
++m_revision;
/* Try to find it */
updateBookDB(book);
@@ -141,13 +141,13 @@ bool Library::addBook(const Book& book)
void Library::addBookmark(const Bookmark& bookmark)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
m_bookmarks.push_back(bookmark);
}
bool Library::removeBookmark(const std::string& zimId, const std::string& url)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for(auto it=m_bookmarks.begin(); it!=m_bookmarks.end(); it++) {
if (it->getBookId() == zimId && it->getUrl() == url) {
m_bookmarks.erase(it);
@@ -157,159 +157,6 @@ bool Library::removeBookmark(const std::string& zimId, const std::string& url)
return false;
}
std::tuple<int, int> Library::migrateBookmarks(MigrationMode migrationMode) {
std::set<std::string> sourceBooks;
int invalidBookmarks = 0;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
for(auto& bookmark:m_bookmarks) {
if (m_books.find(bookmark.getBookId()) == m_books.end()) {
invalidBookmarks += 1;
sourceBooks.insert(bookmark.getBookId());
}
}
}
int changed = 0;
for(auto& sourceBook:sourceBooks) {
changed += migrateBookmarks(sourceBook, migrationMode);
}
return std::make_tuple(changed, invalidBookmarks);
}
std::string Library::getBestFromBookCollection(BookIdCollection books, const Bookmark& bookmark, MigrationMode migrationMode) const {
// This function try to get the best book for a bookmark from a book collection.
// It assumes that all books in the collection are "acceptable".
// (this definiton is not clear but for now it is book's name is equal to bookmark's bookName)
//
// The algorithm first sort the colletion by "flavour equality" and date.
// "flavour equality" is if book's flavour is same that bookmark's flavour (let's say "flavourA" here)
// So we have the sorted collection:
// - flavourA, date 5
// - flavourA, date 4
// - flavourB, date 6
// - flavourC, date 5
// - flavourB, date 3
//
// Then, depending of migrationMode:
// - If ALLOW_DOWNGRADE => take the first one
// - If UPGRADE_ONLY => loop on books until we find a book newer than bookmark.
// So if bookmark date is 5 => flavourB, date 6
// if bookmark date is 4 => flavourA, date 5
// if bookmark date is 7 => No book
if (books.empty()) {
return "";
}
sort(books, DATE, false);
stable_sort(books.begin(), books.end(), [&](const std::string& bookId1, const std::string& bookId2) {
const auto& book1 = getBookById(bookId1);
const auto& book2 = getBookById(bookId2);
bool same_flavour1 = book1.getFlavour() == bookmark.getBookFlavour();
bool same_flavour2 = book2.getFlavour() == bookmark.getBookFlavour();
// return True if bookId1 is before bookId2, ie if same_flavour1 and not same_flavour2
return same_flavour1 > same_flavour2;
});
if (migrationMode == ALLOW_DOWNGRADE) {
return books[0];
} else {
for (const auto& bookId: books) {
const auto& book = getBookById(bookId);
if (book.getDate() >= bookmark.getDate()) {
return bookId;
}
}
}
return "";
}
std::string remove_quote(std::string input) {
std::replace(input.begin(), input.end(), '"', ' ');
return input;
}
std::string Library::getBestTargetBookId(const std::string& bookName, const std::string& preferedFlavour, const std::string& minDate) const {
// Let's reuse our algorithm based on bookmark.
MigrationMode migrationMode = UPGRADE_ONLY;
auto bookmark = Bookmark();
bookmark.setBookName(bookName);
bookmark.setBookFlavour(preferedFlavour);
if (minDate.empty()) {
migrationMode = ALLOW_DOWNGRADE;
} else {
bookmark.setDate(minDate);
}
return getBestTargetBookId(bookmark, migrationMode);
}
std::string Library::getBestTargetBookId(const Bookmark& bookmark, MigrationMode migrationMode) const {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
// Search for a existing book with the same name
auto book_filter = Filter();
if (!bookmark.getBookName().empty()) {
book_filter.name(bookmark.getBookName());
} else {
// We don't have a name stored (older bookmarks)
// Fallback on title (All bookmarks should have one, but let's be safe against wrongly filled bookmark)
if (bookmark.getBookTitle().empty()) {
// No bookName nor bookTitle, no way to find target book.
return "";
}
book_filter.query("title:\"" + remove_quote(bookmark.getBookTitle()) + "\"");
}
auto targetBooks = filter(book_filter);
auto bestBook = getBestFromBookCollection(targetBooks, bookmark, migrationMode);
if (bestBook.empty()) {
try {
getBookById(bookmark.getBookId());
return bookmark.getBookId();
} catch (std::out_of_range&) {}
}
return bestBook;
}
int Library::migrateBookmarks(const std::string& sourceBookId, MigrationMode migrationMode) {
std::lock_guard<std::recursive_mutex> lock(m_mutex);
Bookmark firstBookmarkToChange;
for(auto& bookmark:m_bookmarks) {
if (bookmark.getBookId() == sourceBookId) {
firstBookmarkToChange = bookmark;
break;
}
}
if (firstBookmarkToChange.getBookId().empty()) {
return 0;
}
std::string betterBook = getBestTargetBookId(firstBookmarkToChange, migrationMode);
if (betterBook.empty()) {
return 0;
}
return migrateBookmarks(sourceBookId, betterBook);
}
int Library::migrateBookmarks(const std::string& sourceBookId, const std::string& targetBookId) {
if (sourceBookId == targetBookId) {
return 0;
}
int changed = 0;
for (auto& bookmark:m_bookmarks) {
if (bookmark.getBookId() == sourceBookId) {
bookmark.setBookId(targetBookId);
changed +=1;
}
}
return changed;
}
void Library::dropCache(const std::string& id)
{
@@ -319,7 +166,7 @@ void Library::dropCache(const std::string& id)
bool Library::removeBookById(const std::string& id)
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
m_bookDB->delete_document("Q" + id);
dropCache(id);
// We do not change the cache size here
@@ -337,7 +184,7 @@ bool Library::removeBookById(const std::string& id)
Library::Revision Library::getRevision() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
return m_revision;
}
@@ -345,7 +192,7 @@ uint32_t Library::removeBooksNotUpdatedSince(Revision libraryRevision)
{
BookIdCollection booksToRemove;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for ( const auto& entry : m_books) {
if ( entry.second.lastUpdatedRevision <= libraryRevision ) {
booksToRemove.push_back(entry.first);
@@ -370,7 +217,7 @@ const Book& Library::getBookById(const std::string& id) const
Book Library::getBookByIdThreadSafe(const std::string& id) const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
return getBookById(id);
}
@@ -428,7 +275,7 @@ std::shared_ptr<ZimSearcher> Library::getSearcherByIds(const BookIdSet& ids)
unsigned int Library::getBookCount(const bool localBooks,
const bool remoteBooks) const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
return getBookCount_not_protected(localBooks, remoteBooks);
}
@@ -441,7 +288,7 @@ bool Library::writeToFile(const std::string& path) const
dumper.setBaseDir(baseDir);
std::string xml;
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
xml = dumper.dumpLibXMLContent(allBookIds);
};
return writeTextFile(path, xml);
@@ -457,7 +304,7 @@ bool Library::writeBookmarksToFile(const std::string& path) const
Library::AttributeCounts Library::getBookAttributeCounts(BookStrPropMemFn p) const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
AttributeCounts propValueCounts;
for (const auto& pair: m_books) {
@@ -489,7 +336,7 @@ std::vector<std::string> Library::getBooksLanguages() const
Library::AttributeCounts Library::getBooksLanguagesWithCounts() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
AttributeCounts langsWithCounts;
for (const auto& pair: m_books) {
@@ -505,7 +352,7 @@ Library::AttributeCounts Library::getBooksLanguagesWithCounts() const
std::vector<std::string> Library::getBooksCategories() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
std::set<std::string> categories;
for (const auto& pair: m_books) {
@@ -536,7 +383,7 @@ const std::vector<kiwix::Bookmark> Library::getBookmarks(bool onlyValidBookmarks
}
std::vector<kiwix::Bookmark> validBookmarks;
auto booksId = getBooksIds();
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for(auto& bookmark:m_bookmarks) {
if (std::find(booksId.begin(), booksId.end(), bookmark.getBookId()) != booksId.end()) {
validBookmarks.push_back(bookmark);
@@ -547,7 +394,7 @@ const std::vector<kiwix::Bookmark> Library::getBookmarks(bool onlyValidBookmarks
Library::BookIdCollection Library::getBooksIds() const
{
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
BookIdCollection bookIds;
for (auto& pair: m_books) {
@@ -590,7 +437,6 @@ void Library::updateBookDB(const Book& book)
indexer.index_text(normalizeText(book.getCreator()), 1, "A");
indexer.index_text(normalizeText(book.getPublisher()), 1, "XP");
doc.add_term("XN"+normalizeText(book.getName()));
indexer.index_text(normalizeText(book.getFlavour()), 1, "XF");
indexer.index_text(normalizeText(book.getCategory()), 1, "XC");
for ( const auto& tag : split(normalizeText(book.getTags()), ";") ) {
@@ -631,7 +477,6 @@ Xapian::Query buildXapianQueryFromFilterQuery(const Filter& filter)
queryParser.add_prefix("title", "S");
queryParser.add_prefix("description", "XD");
queryParser.add_prefix("name", "XN");
queryParser.add_prefix("flavour", "XF");
queryParser.add_prefix("category", "XC");
queryParser.add_prefix("lang", "L");
queryParser.add_prefix("publisher", "XP");
@@ -645,6 +490,8 @@ Xapian::Query buildXapianQueryFromFilterQuery(const Filter& filter)
//queryParser.set_stemmer(Xapian::Stem(iso639_3ToXapian(???)));
//queryParser.set_stemming_strategy(Xapian::QueryParser::STEM_SOME);
const auto flags = Xapian::QueryParser::FLAG_PHRASE
| Xapian::QueryParser::FLAG_BOOLEAN
| Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE
| Xapian::QueryParser::FLAG_LOVEHATE
| Xapian::QueryParser::FLAG_WILDCARD
| partialQueryFlag;
@@ -656,11 +503,6 @@ Xapian::Query nameQuery(const std::string& name)
return Xapian::Query("XN" + normalizeText(name));
}
Xapian::Query flavourQuery(const std::string& name)
{
return Xapian::Query("XF" + normalizeText(name));
}
Xapian::Query multipleParamQuery(const std::string& commaSeparatedList, const std::string& prefix)
{
Xapian::Query q;
@@ -728,9 +570,6 @@ Xapian::Query buildXapianQuery(const Filter& filter)
if ( filter.hasName() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, nameQuery(filter.getName()));
}
if ( filter.hasFlavour() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, flavourQuery(filter.getFlavour()));
}
if ( filter.hasCategory() ) {
q = Xapian::Query(Xapian::Query::OP_AND, q, categoryQuery(filter.getCategory()));
}
@@ -761,7 +600,7 @@ Library::BookIdCollection Library::filterViaBookDB(const Filter& filter) const
BookIdCollection bookIds;
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
Xapian::Enquire enquire(*m_bookDB);
enquire.set_query(query);
const auto results = enquire.get_mset(0, m_books.size());
@@ -776,7 +615,7 @@ Library::BookIdCollection Library::filter(const Filter& filter) const
{
BookIdCollection result;
const auto preliminaryResult = filterViaBookDB(filter);
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
for(auto id : preliminaryResult) {
if(filter.accept(m_books.at(id))) {
result.push_back(id);
@@ -850,7 +689,7 @@ void Library::sort(BookIdCollection& bookIds, supportedListSortBy sort, bool asc
// NOTE: for the entire duration of the sort. Will need to obtain (under a
// NOTE: lock) the required atributes from the books once, and then the
// NOTE: sorting will run on a copy of data without locking.
std::lock_guard<std::recursive_mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock(m_mutex);
switch(sort) {
case TITLE:
std::sort(bookIds.begin(), bookIds.end(), Comparator<TITLE>(this, ascending));
@@ -896,7 +735,6 @@ enum filterTypes {
QUERY = FLAG(12),
NAME = FLAG(13),
CATEGORY = FLAG(14),
FLAVOUR = FLAG(15),
};
Filter& Filter::local(bool accept)
@@ -998,13 +836,6 @@ Filter& Filter::name(std::string name)
activeFilters |= NAME;
return *this;
}
Filter& Filter::flavour(std::string flavour)
{
_flavour = flavour;
activeFilters |= FLAVOUR;
return *this;
}
Filter& Filter::clearLang()
{
@@ -1050,12 +881,6 @@ bool Filter::hasCreator() const
return ACTIVE(_CREATOR);
}
bool Filter::hasFlavour() const
{
return ACTIVE(FLAVOUR);
}
bool Filter::accept(const Book& book) const
{
auto local = !book.getPath().empty();

View File

@@ -50,13 +50,6 @@ class LibraryDumper
*/
void setRootLocation(const std::string& rootLocation) { this->rootLocation = rootLocation; }
/**
* Set the URL for accessing book content
*
* @param url the URL of the /content endpoint of the content server
*/
void setContentAccessUrl(const std::string& url) { this->contentAccessUrl = url; }
/**
* Set some informations about the search results.
*
@@ -65,10 +58,10 @@ class LibraryDumper
* @param count the number of result of the current set (or page).
*/
void setOpenSearchInfo(int totalResult, int startIndex, int count);
/**
* Sets user default language
*
*
* @param userLang the user language to be set
*/
void setUserLanguage(std::string userLang) { this->m_userLang = userLang; }
@@ -88,7 +81,6 @@ class LibraryDumper
const kiwix::NameMapper* const nameMapper;
std::string libraryId;
std::string rootLocation;
std::string contentAccessUrl;
std::string m_userLang;
int m_totalResults;
int m_startIndex;

View File

@@ -97,15 +97,11 @@ void LibXMLDumper::handleBookmark(Bookmark bookmark, pugi::xml_node root_node) {
auto book = library->getBookByIdThreadSafe(bookmark.getBookId());
ADD_TEXT_ENTRY(book_node, "id", book.getId());
ADD_TEXT_ENTRY(book_node, "title", book.getTitle());
ADD_TEXT_ENTRY(book_node, "name", book.getName());
ADD_TEXT_ENTRY(book_node, "flavour", book.getFlavour());
ADD_TEXT_ENTRY(book_node, "language", book.getCommaSeparatedLanguages());
ADD_TEXT_ENTRY(book_node, "date", book.getDate());
} catch (...) {
ADD_TEXT_ENTRY(book_node, "id", bookmark.getBookId());
ADD_TEXT_ENTRY(book_node, "title", bookmark.getBookTitle());
ADD_TEXT_ENTRY(book_node, "name", bookmark.getBookName());
ADD_TEXT_ENTRY(book_node, "flavour", bookmark.getBookFlavour());
ADD_TEXT_ENTRY(book_node, "language", bookmark.getLanguage());
ADD_TEXT_ENTRY(book_node, "date", bookmark.getDate());
}
@@ -139,7 +135,7 @@ std::string LibXMLDumper::dumpLibXMLBookmark()
pugi::xml_node bookmarksNode = doc.append_child("bookmarks");
if (library) {
for (auto& bookmark: library->getBookmarks(false)) {
for (auto& bookmark: library->getBookmarks()) {
handleBookmark(bookmark, bookmarksNode);
}
}

View File

@@ -23,14 +23,6 @@
#include "tools/pathTools.h"
#include <pugixml.hpp>
#include <filesystem>
#include <iostream>
#include <set>
#include <queue>
#include <cctype>
#include <algorithm>
namespace fs = std::filesystem;
namespace kiwix
{
@@ -259,58 +251,6 @@ bool Manager::addBookFromPath(const std::string& pathToOpen,
.empty());
}
void Manager::addBooksFromDirectory(const std::string& path,
const bool verboseFlag)
{
std::set<std::string> iteratedDirs;
std::queue<std::string> dirQueue;
dirQueue.push(fs::absolute(path).u8string());
int totalBooksAdded = 0;
if (verboseFlag)
std::cout << "Adding books from the directory tree: " << dirQueue.front() << std::endl;
while (!dirQueue.empty()) {
const auto currentPath = dirQueue.front();
dirQueue.pop();
if (verboseFlag)
std::cout << "Visiting directory: " << currentPath << std::endl;
for (const auto& dirEntry : fs::directory_iterator(currentPath)) {
auto resolvedPath = dirEntry.path();
if (fs::is_symlink(dirEntry)) {
try {
resolvedPath = fs::canonical(dirEntry.path());
} catch (const std::exception& e) {
std::cerr << "Could not resolve symlink " << resolvedPath.u8string() << " to a valid path. Skipping..." << std::endl;
continue;
}
}
const std::string pathString = resolvedPath.u8string();
std::string resolvedPathExtension = resolvedPath.extension();
std::transform(resolvedPathExtension.begin(), resolvedPathExtension.end(), resolvedPathExtension.begin(),
[](unsigned char c){ return std::tolower(c); });
if (fs::is_directory(resolvedPath)) {
if (iteratedDirs.find(pathString) == iteratedDirs.end())
dirQueue.push(pathString);
else if (verboseFlag)
std::cout << "Already iterated over " << pathString << ". Skipping..." << std::endl;
} else if (resolvedPathExtension == ".zim" || resolvedPathExtension == ".zimaa") {
if (!this->addBookFromPath(pathString, pathString, "", false)) {
std::cerr << "Could not add " << pathString << " into the library." << std::endl;
} else if (verboseFlag) {
std::cout << "Added " << pathString << " into the library." << std::endl;
totalBooksAdded++;
}
} else if (verboseFlag) {
std::cout << "Skipped " << pathString << " - unsupported file type or permission denied." << std::endl;
}
}
iteratedDirs.insert(currentPath);
}
if (verboseFlag)
std::cout << "Traversal completed. Total books added: " << totalBooksAdded << std::endl;
}
bool Manager::readBookFromPath(const std::string& path, kiwix::Book* book)
{
std::string tmp_path = path;

View File

@@ -31,7 +31,6 @@ kiwix_sources = [
'server/internalServer_catalog.cpp',
'server/i18n.cpp',
'opds_catalog.cpp',
'spelling_correction.cpp',
'version.cpp'
]
kiwix_sources += lib_resources

View File

@@ -24,34 +24,30 @@
namespace kiwix {
HumanReadableNameMapper::HumanReadableNameMapper(const kiwix::Library& library, bool withAlias) {
for (auto& bookId: library.filter(kiwix::Filter())) {
HumanReadableNameMapper::HumanReadableNameMapper(kiwix::Library& library, bool withAlias) {
for (auto& bookId: library.filter(kiwix::Filter().local(true).valid(true))) {
auto& currentBook = library.getBookById(bookId);
auto bookName = currentBook.getHumanReadableIdFromPath();
m_idToName[bookId] = bookName;
mapName(library, bookName, bookId);
m_nameToId[bookName] = bookId;
if (!withAlias)
continue;
auto aliasName = replaceRegex(bookName, "", "_[[:digit:]]{4}-[[:digit:]]{2}$");
if (aliasName != bookName) {
mapName(library, aliasName, bookId);
if (aliasName == bookName) {
continue;
}
if (m_nameToId.find(aliasName) == m_nameToId.end()) {
m_nameToId[aliasName] = bookId;
} else {
auto alreadyPresentPath = library.getBookById(m_nameToId[aliasName]).getPath();
std::cerr << "Path collision: " << alreadyPresentPath
<< " and " << currentBook.getPath()
<< " can't share the same URL path '" << aliasName << "'."
<< " Therefore, only " << alreadyPresentPath
<< " will be served." << std::endl;
}
}
}
void HumanReadableNameMapper::mapName(const Library& library, std::string name, std::string bookId) {
if (m_nameToId.find(name) == m_nameToId.end()) {
m_nameToId[name] = bookId;
} else {
const auto& currentBook = library.getBookById(bookId);
auto alreadyPresentPath = library.getBookById(m_nameToId[name]).getPath();
std::cerr << "Path collision: '" << alreadyPresentPath
<< "' and '" << currentBook.getPath()
<< "' can't share the same URL path '" << name << "'."
<< " Therefore, only '" << alreadyPresentPath
<< "' will be served." << std::endl;
}
}

View File

@@ -51,26 +51,24 @@ typedef kainjow::mustache::list IllustrationInfo;
IllustrationInfo getBookIllustrationInfo(const Book& book)
{
kainjow::mustache::list illustrations;
for ( const auto& illustration : book.getIllustrations() ) {
// For now, we are handling only sizexsize@1 illustration.
// So we can simply pass one size to mustache.
illustrations.push_back(kainjow::mustache::object{
{"icon_size", to_string(illustration->width)},
{"icon_mimetype", illustration->mimeType}
});
if ( book.isPathValid() ) {
for ( const auto& illustration : book.getIllustrations() ) {
// For now, we are handling only sizexsize@1 illustration.
// So we can simply pass one size to mustache.
illustrations.push_back(kainjow::mustache::object{
{"icon_size", to_string(illustration->width)},
{"icon_mimetype", illustration->mimeType}
});
}
}
return illustrations;
}
std::string fullEntryXML(const Book& book,
const std::string& rootLocation,
const std::string& contentAccessUrl,
const std::string& contentId)
std::string fullEntryXML(const Book& book, const std::string& rootLocation, const std::string& contentId)
{
const auto bookDate = book.getDate() + "T00:00:00Z";
const kainjow::mustache::object data{
{"root", rootLocation},
{"contentAccessUrl", onlyAsNonEmptyMustacheValue(contentAccessUrl)},
{"id", book.getId()},
{"name", book.getName()},
{"title", book.getTitle()},
@@ -107,12 +105,7 @@ std::string partialEntryXML(const Book& book, const std::string& rootLocation)
return render_template(xmlTemplate, data);
}
BooksData getBooksData(const Library* library,
const NameMapper* nameMapper,
const std::vector<std::string>& bookIds,
const std::string& rootLocation,
const std::string& contentAccessUrl,
bool partial)
BooksData getBooksData(const Library* library, const NameMapper* nameMapper, const std::vector<std::string>& bookIds, const std::string& rootLocation, bool partial)
{
BooksData booksData;
for ( const auto& bookId : bookIds ) {
@@ -121,7 +114,7 @@ BooksData getBooksData(const Library* library,
const std::string contentId = nameMapper->getNameForId(bookId);
const auto entryXML = partial
? partialEntryXML(book, rootLocation)
: fullEntryXML(book, rootLocation, contentAccessUrl, contentId);
: fullEntryXML(book, rootLocation, contentId);
booksData.push_back(kainjow::mustache::object{ {"entry", entryXML} });
} catch ( const std::out_of_range& ) {
// the book was removed from the library since its id was obtained
@@ -136,7 +129,7 @@ BooksData getBooksData(const Library* library,
string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const std::string& query) const
{
const auto booksData = getBooksData(library, nameMapper, bookIds, rootLocation, contentAccessUrl, false);
const auto booksData = getBooksData(library, nameMapper, bookIds, rootLocation, false);
const kainjow::mustache::object template_data{
{"date", gen_date_str()},
{"root", rootLocation},
@@ -154,7 +147,7 @@ string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const s
string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const std::string& query, bool partial) const
{
const auto endpointRoot = rootLocation + "/catalog/v2";
const auto booksData = getBooksData(library, nameMapper, bookIds, rootLocation, contentAccessUrl, partial);
const auto booksData = getBooksData(library, nameMapper, bookIds, rootLocation, partial);
const char* const endpoint = partial ? "/partial_entries" : "/entries";
const std::string url = endpoint + (query.empty() ? "" : "?" + query);
@@ -179,7 +172,7 @@ std::string OPDSDumper::dumpOPDSCompleteEntry(const std::string& bookId) const
const std::string contentId = nameMapper->getNameForId(bookId);
return XML_HEADER
+ "\n"
+ fullEntryXML(book, rootLocation, contentAccessUrl, contentId);
+ fullEntryXML(book, rootLocation, contentId);
}
std::string OPDSDumper::categoriesOPDSFeed() const

View File

@@ -32,7 +32,7 @@
#include "libkiwix-resources.h"
#include "tools/stringTools.h"
#include "server/i18n_utils.h"
#include "server/i18n.h"
namespace kiwix
{
@@ -58,7 +58,7 @@ ParameterizedMessage searchResultsPageHeaderMsg(const std::string& searchPattern
return ParameterizedMessage("search-results-page-header",
{
{"SEARCH_PATTERN", searchPattern},
{"START", r.get("startLabel")->string_value()},
{"START", r.get("start")->string_value()},
{"END", r.get("end") ->string_value()},
{"COUNT", r.get("count")->string_value()},
}
@@ -225,8 +225,7 @@ std::string SearchRenderer::renderTemplate(const std::string& tmpl_str, const Na
results.set("items", items);
results.set("count", kiwix::beautifyInteger(estimatedResultCount));
results.set("start", kiwix::beautifyInteger(resultStart));
results.set("startLabel", kiwix::beautifyInteger(resultStart+1));
results.set("end", kiwix::beautifyInteger(std::min(resultStart+pageLength, estimatedResultCount)));
results.set("end", kiwix::beautifyInteger(std::min(resultStart+pageLength-1, estimatedResultCount)));
// pagination
auto pagination = buildPagination(

View File

@@ -29,22 +29,6 @@
namespace kiwix {
namespace
{
std::string makeServerUrl(std::string host, int port, std::string root)
{
const int httpDefaultPort = 80;
if (port == httpDefaultPort) {
return "http://" + host + root;
} else {
return "http://" + host + ":" + std::to_string(port) + root;
}
}
} // unnamed namespace
Server::Server(LibraryPtr library, std::shared_ptr<NameMapper> nameMapper) :
mp_library(library),
mp_nameMapper(nameMapper),
@@ -67,18 +51,9 @@ bool Server::start() {
m_withTaskbar,
m_withLibraryButton,
m_blockExternalLinks,
m_ipMode,
m_indexTemplateString,
m_ipConnectionLimit,
m_catalogOnlyMode,
m_contentServerUrl));
if (mp_server->start()) {
// this syncs m_addr of InternalServer and Server as they may diverge
m_addr = mp_server->getAddress();
return true;
} else {
return false;
}
m_ipConnectionLimit));
return mp_server->start();
}
void Server::stop() {
@@ -91,53 +66,22 @@ void Server::stop() {
void Server::setRoot(const std::string& root)
{
m_root = root;
while (!m_root.empty() && m_root.back() == '/')
m_root.pop_back();
while (!m_root.empty() && m_root.front() == '/')
m_root = m_root.substr(1);
m_root = m_root.empty() ? m_root : "/" + m_root;
}
void Server::setAddress(const std::string& addr)
{
m_addr.addr.clear();
m_addr.addr6.clear();
if (addr.empty()) return;
if (addr.find(':') != std::string::npos) { // IPv6
m_addr.addr6 = (addr[0] == '[') ? addr.substr(1, addr.length() - 2) : addr; // Remove brackets if any
} else {
m_addr.addr = addr;
if (m_root[0] != '/') {
m_root = "/" + m_root;
}
if (m_root.back() == '/') {
m_root.erase(m_root.size() - 1);
}
}
int Server::getPort() const
int Server::getPort()
{
return m_port;
return mp_server->getPort();
}
IpAddress Server::getAddress() const
std::string Server::getAddress()
{
return m_addr;
}
IpMode Server::getIpMode() const
{
return mp_server->getIpMode();
}
std::vector<std::string> Server::getServerAccessUrls() const
{
std::vector<std::string> result;
if (!m_addr.addr.empty()) {
result.push_back(makeServerUrl(m_addr.addr, m_port, m_root));
}
if (!m_addr.addr6.empty()) {
result.push_back(makeServerUrl("[" + m_addr.addr6 + "]", m_port, m_root));
}
return result;
return mp_server->getAddress();
}
}

View File

@@ -17,7 +17,7 @@
* MA 02110-1301, USA.
*/
#include "i18n_utils.h"
#include "i18n.h"
#include "tools/otherTools.h"
@@ -193,13 +193,4 @@ std::string selectMostSuitableLanguage(const UserLangPreferences& prefs)
return bestLangSoFar;
}
std::string translateBookCategory(const std::string& lang, const std::string& category)
{
try {
return getTranslatedString(lang, "book-category." + category);
} catch (...) {
return category;
}
}
} // namespace kiwix

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2024 Veloman Yunkan <veloman.yunkan@gmail.com>
* Copyright 2022 Veloman Yunkan <veloman.yunkan@gmail.com>
*
* 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
@@ -17,15 +17,29 @@
* MA 02110-1301, USA.
*/
#ifndef KIWIX_I18N
#define KIWIX_I18N
#ifndef KIWIX_SERVER_I18N
#define KIWIX_SERVER_I18N
#include <map>
#include <string>
#include <mustache.hpp>
namespace kiwix
{
struct I18nString {
const char* const key;
const char* const value;
};
struct I18nStringTable {
const char* const lang;
const size_t entryCount;
const I18nString* const entries;
const char* get(const std::string& key) const;
};
std::string getTranslatedString(const std::string& lang, const std::string& key);
namespace i18n
@@ -56,6 +70,28 @@ private:
const std::string m_lang;
};
class GetTranslatedStringWithMsgId
{
typedef kainjow::mustache::basic_data<std::string> MustacheString;
typedef std::pair<std::string, MustacheString> MsgIdAndTranslation;
public:
explicit GetTranslatedStringWithMsgId(const std::string& lang) : m_lang(lang) {}
MsgIdAndTranslation operator()(const std::string& key) const
{
return {key, getTranslatedString(m_lang, key)};
}
MsgIdAndTranslation operator()(const std::string& key, const Parameters& params) const
{
return {key, expandParameterizedString(m_lang, key, params)};
}
private:
const std::string m_lang;
};
} // namespace i18n
class ParameterizedMessage
@@ -85,8 +121,18 @@ inline ParameterizedMessage nonParameterizedMessage(const std::string& msgId)
return ParameterizedMessage(msgId, noParams);
}
std::string translateBookCategory(const std::string& lang, const std::string& category);
struct LangPreference
{
const std::string lang;
const float preference;
};
typedef std::vector<LangPreference> UserLangPreferences;
UserLangPreferences parseUserLanguagePreferences(const std::string& s);
std::string selectMostSuitableLanguage(const UserLangPreferences& prefs);
} // namespace kiwix
#endif // KIWIX_I18N
#endif // KIWIX_SERVER_I18N

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2022 Veloman Yunkan <veloman.yunkan@gmail.com>
*
* 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
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#ifndef KIWIX_SERVER_I18N_UTILS
#define KIWIX_SERVER_I18N_UTILS
#include "i18n.h"
#include <mustache.hpp>
namespace kiwix
{
struct I18nString {
const char* const key;
const char* const value;
};
struct I18nStringTable {
const char* const lang;
const size_t entryCount;
const I18nString* const entries;
const char* get(const std::string& key) const;
};
namespace i18n
{
class GetTranslatedStringWithMsgId
{
typedef kainjow::mustache::basic_data<std::string> MustacheString;
typedef std::pair<std::string, MustacheString> MsgIdAndTranslation;
public:
explicit GetTranslatedStringWithMsgId(const std::string& lang) : m_lang(lang) {}
MsgIdAndTranslation operator()(const std::string& key) const
{
return {key, getTranslatedString(m_lang, key)};
}
MsgIdAndTranslation operator()(const std::string& key, const Parameters& params) const
{
return {key, expandParameterizedString(m_lang, key, params)};
}
private:
const std::string m_lang;
};
} // namespace i18n
struct LangPreference
{
const std::string lang;
const float preference;
};
typedef std::vector<LangPreference> UserLangPreferences;
UserLangPreferences parseUserLanguagePreferences(const std::string& s);
std::string selectMostSuitableLanguage(const UserLangPreferences& prefs);
} // namespace kiwix
#endif // KIWIX_SERVER_I18N_UTILS

View File

@@ -54,7 +54,7 @@ extern "C" {
#include "search_renderer.h"
#include "opds_dumper.h"
#include "html_dumper.h"
#include "i18n_utils.h"
#include "i18n.h"
#include <zim/uuid.h>
#include <zim/error.h>
@@ -85,18 +85,14 @@ namespace kiwix {
namespace
{
bool ipAvailable(const std::string addr)
inline std::string normalizeRootUrl(std::string rootUrl)
{
auto interfaces = kiwix::getNetworkInterfacesIPv4Or6();
while ( !rootUrl.empty() && rootUrl.back() == '/' )
rootUrl.pop_back();
for (const auto& kv : interfaces) {
const auto& interfaceIps = kv.second;
if ((interfaceIps.addr == addr) || (interfaceIps.addr6 == addr)) {
return true;
}
}
return false;
while ( !rootUrl.empty() && rootUrl.front() == '/' )
rootUrl = rootUrl.substr(1);
return rootUrl.empty() ? rootUrl : "/" + rootUrl;
}
std::string
@@ -115,12 +111,9 @@ std::string getSearchComponent(const RequestContext& request)
return query.empty() ? query : "?" + query;
}
Filter get_search_filter(const RequestContext& request, const std::string& prefix="", bool catalogOnlyMode = false)
Filter get_search_filter(const RequestContext& request, const std::string& prefix="")
{
auto filter = kiwix::Filter();
if ( !catalogOnlyMode ) {
filter.valid(true).local(true);
}
auto filter = kiwix::Filter().valid(true).local(true);
try {
filter.query(request.get_argument(prefix+"q"));
} catch (const std::out_of_range&) {}
@@ -414,7 +407,7 @@ public:
InternalServer::InternalServer(LibraryPtr library,
std::shared_ptr<NameMapper> nameMapper,
IpAddress addr,
std::string addr,
int port,
std::string root,
int nbThreads,
@@ -423,14 +416,11 @@ InternalServer::InternalServer(LibraryPtr library,
bool withTaskbar,
bool withLibraryButton,
bool blockExternalLinks,
IpMode ipMode,
std::string indexTemplateString,
int ipConnectionLimit,
bool catalogOnlyMode,
std::string contentServerUrl) :
int ipConnectionLimit) :
m_addr(addr),
m_port(port),
m_root(root),
m_root(normalizeRootUrl(root)),
m_rootPrefixOfDecodedURL(m_root),
m_nbThreads(nbThreads),
m_multizimSearchLimit(multizimSearchLimit),
@@ -438,7 +428,6 @@ InternalServer::InternalServer(LibraryPtr library,
m_withTaskbar(withTaskbar),
m_withLibraryButton(withLibraryButton),
m_blockExternalLinks(blockExternalLinks),
m_ipMode(ipMode),
m_indexTemplateString(indexTemplateString.empty() ? RESOURCE::templates::index_html : indexTemplateString),
m_ipConnectionLimit(ipConnectionLimit),
mp_daemon(nullptr),
@@ -446,9 +435,7 @@ InternalServer::InternalServer(LibraryPtr library,
mp_nameMapper(nameMapper ? nameMapper : std::shared_ptr<NameMapper>(&defaultNameMapper, NoDelete())),
searchCache(getEnvVar<int>("KIWIX_SEARCH_CACHE_SIZE", DEFAULT_CACHE_SIZE)),
suggestionSearcherCache(getEnvVar<int>("KIWIX_SUGGESTION_SEARCHER_CACHE_SIZE", std::max((unsigned int) (mp_library->getBookCount(true, true)*0.1), 1U))),
m_customizedResources(new CustomizedResources),
m_catalogOnlyMode(catalogOnlyMode),
m_contentServerUrl(contentServerUrl)
m_customizedResources(new CustomizedResources)
{
m_root = urlEncode(m_root);
}
@@ -464,62 +451,28 @@ bool InternalServer::start() {
if (m_verbose.load())
flags |= MHD_USE_DEBUG;
struct sockaddr_in sockAddr4={0};
sockAddr4.sin_family = AF_INET;
sockAddr4.sin_port = htons(m_port);
struct sockaddr_in6 sockAddr6={0};
sockAddr6.sin6_family = AF_INET6;
sockAddr6.sin6_port = htons(m_port);
if (m_addr.addr.empty() && m_addr.addr6.empty()) { // No ip address provided
if (m_ipMode == IpMode::AUTO) m_ipMode = IpMode::ALL;
sockAddr6.sin6_addr = in6addr_any;
sockAddr4.sin_addr.s_addr = htonl(INADDR_ANY);
IpAddress bestIps = kiwix::getBestPublicIps();
if (m_ipMode == IpMode::IPV4 || m_ipMode == IpMode::ALL) m_addr.addr = bestIps.addr;
if (m_ipMode == IpMode::IPV6 || m_ipMode == IpMode::ALL) m_addr.addr6 = bestIps.addr6;
struct sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(m_port);
if (m_addr.empty()) {
if (0 != INADDR_ANY) {
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
m_addr = kiwix::getBestPublicIp();
} else {
const std::string addr = !m_addr.addr.empty() ? m_addr.addr : m_addr.addr6;
if (m_ipMode != kiwix::IpMode::AUTO) {
std::cerr << "ERROR: When an IP address is provided the IP mode must not be set" << std::endl;
if (inet_pton(AF_INET, m_addr.c_str(), &(sockAddr.sin_addr.s_addr)) == 0) {
std::cerr << "Ip address " << m_addr << " is not a valid ip address" << std::endl;
return false;
}
bool validV4 = inet_pton(AF_INET, m_addr.addr.c_str(), &(sockAddr4.sin_addr.s_addr)) == 1;
bool validV6 = inet_pton(AF_INET6, m_addr.addr6.c_str(), &(sockAddr6.sin6_addr.s6_addr)) == 1;
if (!validV4 && !validV6) {
std::cerr << "ERROR: invalid IP address: " << addr << std::endl;
return false;
}
if (!ipAvailable(addr)) {
std::cerr << "ERROR: IP address is not available on this system: " << addr << std::endl;
return false;
}
m_ipMode = !m_addr.addr.empty() ? IpMode::IPV4 : IpMode::IPV6;
}
if (m_ipMode == IpMode::ALL) {
flags|=MHD_USE_DUAL_STACK;
} else if (m_ipMode == IpMode::IPV6) {
flags|=MHD_USE_IPv6;
}
struct sockaddr* sockaddr = (m_ipMode==IpMode::ALL || m_ipMode==IpMode::IPV6)
? (struct sockaddr*)&sockAddr6
: (struct sockaddr*)&sockAddr4;
mp_daemon = MHD_start_daemon(flags,
m_port,
NULL,
NULL,
&staticHandlerCallback,
this,
MHD_OPTION_SOCK_ADDR, sockaddr,
MHD_OPTION_SOCK_ADDR, &sockAddr,
MHD_OPTION_THREAD_POOL_SIZE, m_nbThreads,
MHD_OPTION_PER_IP_CONNECTION_LIMIT, m_ipConnectionLimit,
MHD_OPTION_END);
@@ -706,10 +659,12 @@ std::unique_ptr<Response> InternalServer::handle_request(const RequestContext& r
return Response::build_redirect(contentUrl + query);
} catch (std::exception& e) {
fprintf(stderr, "===== Unhandled error : %s\n", e.what());
return HTTP500Response(request, m_root, request.get_full_url(), e.what());
return HTTP500Response(request)
+ ParameterizedMessage("non-translated-text", {{"MSG", e.what()}});
} catch (...) {
fprintf(stderr, "===== Unhandled unknown error\n");
return HTTP500Response(request, m_root, request.get_full_url());
return HTTP500Response(request)
+ nonParameterizedMessage("unknown-error");
}
}
@@ -840,15 +795,6 @@ std::string InternalServer::getNoJSDownloadPageHTML(const std::string& bookId, c
);
}
void InternalServer::setContentAccessUrl(LibraryDumper& libDumper) const
{
if ( !m_contentServerUrl.empty() ) {
libDumper.setContentAccessUrl(m_contentServerUrl + "/content");
} else if ( !m_catalogOnlyMode ) {
libDumper.setContentAccessUrl(m_root + "/content");
}
}
std::unique_ptr<Response> InternalServer::handle_no_js(const RequestContext& request)
{
const auto url = request.get_url();
@@ -856,13 +802,12 @@ std::unique_ptr<Response> InternalServer::handle_no_js(const RequestContext& req
HTMLDumper htmlDumper(mp_library.get(), mp_nameMapper.get());
htmlDumper.setRootLocation(m_root);
htmlDumper.setLibraryId(getLibraryId());
setContentAccessUrl(htmlDumper);
auto userLang = request.get_user_language();
htmlDumper.setUserLanguage(userLang);
std::string content;
if (urlParts.size() == 1) {
auto filter = get_search_filter(request, "", m_catalogOnlyMode);
auto filter = get_search_filter(request);
try {
if (request.get_argument("category") == "") {
filter.clearCategory();
@@ -992,7 +937,7 @@ std::unique_ptr<Response> InternalServer::handle_search_request(const RequestCon
} catch(std::runtime_error& e) {
// Searcher->search will throw a runtime error if there is no valid xapian database to do the search.
// (in case of zim file not containing a index)
const auto cssUrl = renderUrl(m_root, RESOURCE::templates::url_of_search_results_css_tmpl);
const auto cssUrl = renderUrl(m_root, RESOURCE::templates::url_of_search_results_css);
HTTPErrorResponse response(request, MHD_HTTP_NOT_FOUND,
"fulltext-search-unavailable",
"404-page-heading",
@@ -1011,11 +956,11 @@ std::unique_ptr<Response> InternalServer::handle_search_request(const RequestCon
return response;
}
const auto start = max(0u, request.get_optional_param("start", 0u));
const auto start = max(1u, request.get_optional_param("start", 1u));
const auto pageLength = getSearchPageSize(request);
/* Get the results */
SearchRenderer renderer(search->getResults(start, pageLength), start,
SearchRenderer renderer(search->getResults(start-1, pageLength), start,
search->getEstimatedMatches());
renderer.setSearchPattern(searchInfo.pattern);
renderer.setSearchBookQuery(searchInfo.bookFilterQuery);
@@ -1090,7 +1035,9 @@ std::unique_ptr<Response> InternalServer::handle_captured_external(const Request
return UrlNotFoundResponse(request);
}
return BlockExternalLinkResponse(request, m_root, source);
auto data = get_default_data();
data.set("source", source);
return ContentResponse::build(RESOURCE::templates::captured_external_html, data, "text/html; charset=utf-8");
}
std::unique_ptr<Response> InternalServer::handle_catch(const RequestContext& request)
@@ -1110,7 +1057,7 @@ std::vector<std::string>
InternalServer::search_catalog(const RequestContext& request,
kiwix::OPDSDumper& opdsDumper)
{
const auto filter = get_search_filter(request, "", m_catalogOnlyMode);
const auto filter = get_search_filter(request);
std::vector<std::string> bookIdsToDump = mp_library->filter(filter);
const auto totalResults = bookIdsToDump.size();
const long count = request.get_optional_param("count", 10L);
@@ -1124,6 +1071,15 @@ InternalServer::search_catalog(const RequestContext& request,
namespace
{
ParameterizedMessage suggestSearchMsg(const std::string& searchURL, const std::string& pattern)
{
return ParameterizedMessage("suggest-search",
{
{ "PATTERN", pattern },
{ "SEARCH_URL", searchURL }
});
}
///////////////////////////////////////////////////////////////////////////////
// The content security policy below is set on responses to the /content
// endpoint in order to prevent the ZIM content from interfering with the
@@ -1141,7 +1097,6 @@ const std::string CONTENT_CSP_HEADER =
"allow-same-origin "
"allow-modals "
"allow-popups "
"allow-popups-to-escape-sandbox "
"allow-forms "
"allow-downloads;";
@@ -1178,7 +1133,9 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
} catch (const std::out_of_range& e) {}
if (archive == nullptr) {
return NewHTTP404Response(request, m_root, m_root + url);
const std::string searchURL = m_root + "/search?pattern=" + kiwix::urlEncode(pattern);
return UrlNotFoundResponse(request)
+ suggestSearchMsg(searchURL, kiwix::urlDecode(pattern));
}
const std::string archiveUuid(archive->getUuid());
@@ -1223,7 +1180,9 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
if (m_verbose.load())
printf("Failed to find %s\n", urlStr.c_str());
return NewHTTP404Response(request, m_root, m_root + url);
std::string searchURL = m_root + "/search?content=" + bookName + "&pattern=" + kiwix::urlEncode(pattern);
return UrlNotFoundResponse(request)
+ suggestSearchMsg(searchURL, kiwix::urlDecode(pattern));
}
}

View File

@@ -27,7 +27,6 @@ extern "C" {
#include "library.h"
#include "name_mapper.h"
#include "tools.h"
#include <zim/search.h>
#include <zim/suggestion.h>
@@ -90,13 +89,12 @@ class SearchInfo {
typedef kainjow::mustache::data MustacheData;
class OPDSDumper;
class LibraryDumper;
class InternalServer {
public:
InternalServer(LibraryPtr library,
std::shared_ptr<NameMapper> nameMapper,
IpAddress addr,
std::string addr,
int port,
std::string root,
int nbThreads,
@@ -105,11 +103,8 @@ class InternalServer {
bool withTaskbar,
bool withLibraryButton,
bool blockExternalLinks,
IpMode ipMode,
std::string indexTemplateString,
int ipConnectionLimit,
bool catalogOnlyMode,
std::string zimViewerURL);
int ipConnectionLimit);
virtual ~InternalServer();
MHD_Result handlerCallback(struct MHD_Connection* connection,
@@ -121,9 +116,8 @@ class InternalServer {
void** cont_cls);
bool start();
void stop();
IpAddress getAddress() const { return m_addr; }
int getPort() const { return m_port; }
IpMode getIpMode() const { return m_ipMode; }
std::string getAddress() { return m_addr; }
int getPort() { return m_port; }
private: // functions
std::unique_ptr<Response> handle_request(const RequestContext& request);
@@ -163,8 +157,6 @@ class InternalServer {
std::string getLibraryId() const;
std::string getNoJSDownloadPageHTML(const std::string& bookId, const std::string& userLang) const;
OPDSDumper getOPDSDumper() const;
void setContentAccessUrl(LibraryDumper& libDumper) const;
private: // types
class LockableSuggestionSearcher;
@@ -172,7 +164,7 @@ class InternalServer {
typedef ConcurrentCache<std::string, std::shared_ptr<LockableSuggestionSearcher>> SuggestionSearcherCache;
private: // data
IpAddress m_addr;
std::string m_addr;
int m_port;
std::string m_root; // URI-encoded
std::string m_rootPrefixOfDecodedURL; // URI-decoded
@@ -182,7 +174,6 @@ class InternalServer {
bool m_withTaskbar;
bool m_withLibraryButton;
bool m_blockExternalLinks;
IpMode m_ipMode;
std::string m_indexTemplateString;
int m_ipConnectionLimit;
struct MHD_Daemon* mp_daemon;
@@ -197,9 +188,6 @@ class InternalServer {
class CustomizedResources;
std::unique_ptr<CustomizedResources> m_customizedResources;
const bool m_catalogOnlyMode;
const std::string m_contentServerUrl;
};
}

View File

@@ -51,15 +51,6 @@ const std::string opdsMimeType[] = {
} // unnamed namespace
OPDSDumper InternalServer::getOPDSDumper() const
{
kiwix::OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
setContentAccessUrl(opdsDumper);
return opdsDumper;
}
std::unique_ptr<Response> InternalServer::handle_catalog(const RequestContext& request)
{
if (m_verbose.load()) {
@@ -89,7 +80,9 @@ std::unique_ptr<Response> InternalServer::handle_catalog(const RequestContext& r
}
zim::Uuid uuid;
kiwix::OPDSDumper opdsDumper = getOPDSDumper();
kiwix::OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
std::vector<std::string> bookIdsToDump;
if (url == "root.xml") {
uuid = zim::Uuid::generate(host);
@@ -165,7 +158,9 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_root(const RequestCo
std::unique_ptr<Response> InternalServer::handle_catalog_v2_entries(const RequestContext& request, bool partial)
{
kiwix::OPDSDumper opdsDumper = getOPDSDumper();
OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
const auto bookIds = search_catalog(request, opdsDumper);
const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query(), partial);
return ContentResponse::build(
@@ -182,7 +177,9 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_complete_entry(const
return UrlNotFoundResponse(request);
}
kiwix::OPDSDumper opdsDumper = getOPDSDumper();
OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
const auto opdsFeed = opdsDumper.dumpOPDSCompleteEntry(entryId);
return ContentResponse::build(
opdsFeed,
@@ -192,7 +189,9 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_complete_entry(const
std::unique_ptr<Response> InternalServer::handle_catalog_v2_categories(const RequestContext& request)
{
kiwix::OPDSDumper opdsDumper = getOPDSDumper();
OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
return ContentResponse::build(
opdsDumper.categoriesOPDSFeed(),
opdsMimeType[OPDS_NAVIGATION_FEED]
@@ -201,7 +200,9 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_categories(const Req
std::unique_ptr<Response> InternalServer::handle_catalog_v2_languages(const RequestContext& request)
{
kiwix::OPDSDumper opdsDumper = getOPDSDumper();
OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
return ContentResponse::build(
opdsDumper.languagesOPDSFeed(),
opdsMimeType[OPDS_NAVIGATION_FEED]

View File

@@ -28,7 +28,7 @@
#include <cctype>
#include "tools/stringTools.h"
#include "i18n_utils.h"
#include "i18n.h"
namespace kiwix {

View File

@@ -243,23 +243,6 @@ public:
};
}
static Data fromMsgId(const std::string& nonParameterizedMsgId)
{
return from(nonParameterizedMessage(nonParameterizedMsgId));
}
static Data staticMultiParagraphText(const std::string& msgIdPrefix, size_t n)
{
Object paragraphs;
for ( size_t i = 1; i <= n; ++i ) {
std::ostringstream oss;
oss << "p" << i;
const std::string pId = oss.str();
paragraphs[pId] = fromMsgId(msgIdPrefix + "." + pId);
}
return paragraphs;
}
std::string asJSON() const;
void dumpJSON(std::ostream& os) const;
@@ -385,45 +368,6 @@ std::unique_ptr<ContentResponse> ContentResponseBlueprint::generateResponseObjec
return r;
}
NewHTTP404Response::NewHTTP404Response(const RequestContext& request,
const std::string& root,
const std::string& urlPath)
: ContentResponseBlueprint(&request,
MHD_HTTP_NOT_FOUND,
"text/html; charset=utf-8",
RESOURCE::templates::sexy404_html,
/*includeKiwixResponseData=*/true)
{
*this->m_data = Data(Data::Object{
{"root", root },
{"url_path", urlPath},
{"PAGE_TITLE", Data::fromMsgId("new-404-page-title")},
{"PAGE_HEADING", Data::fromMsgId("new-404-page-heading")},
{"404_img_text", Data::fromMsgId("404-img-text")},
{"path_was_not_found_msg", Data::fromMsgId("path-was-not-found")},
{"advice", Data::staticMultiParagraphText("404-advice", 5)},
});
}
BlockExternalLinkResponse::BlockExternalLinkResponse(const RequestContext& request,
const std::string& root,
const std::string& externalUrl)
: ContentResponseBlueprint(&request,
MHD_HTTP_OK,
"text/html; charset=utf-8",
RESOURCE::templates::captured_external_html,
/*includeKiwixResponseData=*/true)
{
*this->m_data = Data(Data::Object{
{"root", root },
{"external_link_detected", Data::fromMsgId("external-link-detected") },
{"url", externalUrl },
{"caution_warning", Data::fromMsgId("caution-warning") },
{"external_link_intro", Data::fromMsgId("external-link-intro") },
{"advice", Data::staticMultiParagraphText("external-link-advice", 3)},
});
}
HTTPErrorResponse::HTTPErrorResponse(const RequestContext& request,
int httpStatusCode,
const std::string& pageTitleMsgId,
@@ -439,8 +383,8 @@ HTTPErrorResponse::HTTPErrorResponse(const RequestContext& request,
Data::List emptyList;
*this->m_data = Data(Data::Object{
{"CSS_URL", Data::onlyAsNonEmptyValue(cssUrl) },
{"PAGE_TITLE", Data::fromMsgId(pageTitleMsgId)},
{"PAGE_HEADING", Data::fromMsgId(headingMsgId)},
{"PAGE_TITLE", Data::from(nonParameterizedMessage(pageTitleMsgId))},
{"PAGE_HEADING", Data::from(nonParameterizedMessage(headingMsgId))},
{"details", emptyList}
});
}
@@ -491,30 +435,15 @@ HTTP400Response::HTTP400Response(const RequestContext& request)
*this += ParameterizedMessage("invalid-request", {{"url", requestUrl}});
}
HTTP500Response::HTTP500Response(const RequestContext& request,
const std::string& root,
const std::string& urlPath,
const std::string& errorText)
: ContentResponseBlueprint(&request,
MHD_HTTP_INTERNAL_SERVER_ERROR,
"text/html; charset=utf-8",
RESOURCE::templates::sexy500_html,
/*includeKiwixResponseData=*/true)
HTTP500Response::HTTP500Response(const RequestContext& request)
: HTTPErrorResponse(request,
MHD_HTTP_INTERNAL_SERVER_ERROR,
"500-page-title",
"500-page-heading",
std::string(),
/*includeKiwixResponseData=*/true)
{
auto pageParams = Data::Object{
{"root", root },
{"url_path", urlPath},
{"PAGE_TITLE", Data::fromMsgId("500-page-title")},
{"PAGE_HEADING", Data::fromMsgId("500-page-heading")},
{"PAGE_TEXT", Data::fromMsgId("500-page-text")},
{"500_img_text", Data::fromMsgId("500-img-text")},
};
if ( !errorText.empty() ) {
pageParams["error"] = errorText;
}
*this->m_data = Data(pageParams);
*this += nonParameterizedMessage("500-page-text");
}
std::unique_ptr<Response> Response::build_416(size_t resourceLength)

View File

@@ -27,7 +27,7 @@
#include <mustache.hpp>
#include "byte_range.h"
#include "etag.h"
#include "i18n_utils.h"
#include "i18n.h"
#include <zim/item.h>
@@ -145,13 +145,6 @@ protected: //data
std::unique_ptr<Data> m_data;
};
struct NewHTTP404Response : ContentResponseBlueprint
{
NewHTTP404Response(const RequestContext& request,
const std::string& root,
const std::string& urlPath);
};
struct HTTPErrorResponse : ContentResponseBlueprint
{
HTTPErrorResponse(const RequestContext& request,
@@ -180,12 +173,9 @@ struct HTTP400Response : HTTPErrorResponse
explicit HTTP400Response(const RequestContext& request);
};
struct HTTP500Response : ContentResponseBlueprint
struct HTTP500Response : HTTPErrorResponse
{
HTTP500Response(const RequestContext& request,
const std::string& root,
const std::string& urlPath,
const std::string& error = "");
explicit HTTP500Response(const RequestContext& request);
};
class ItemResponse : public Response {
@@ -200,13 +190,6 @@ class ItemResponse : public Response {
std::string m_mimeType;
};
struct BlockExternalLinkResponse : ContentResponseBlueprint
{
BlockExternalLinkResponse(const RequestContext& request,
const std::string& root,
const std::string& externalUrl);
};
}
#endif //KIWIXLIB_SERVER_RESPONSE_H

View File

@@ -1,108 +0,0 @@
/*
* Copyright (C) 2025 Veloman Yunkan
*
* 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
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "spelling_correction.h"
#include "zim/archive.h"
#include <sstream>
#include <stdexcept>
#include <xapian.h>
namespace kiwix
{
namespace
{
std::vector<std::string> getAllTitles(const zim::Archive& a)
{
std::vector<std::string> result;
for (const auto& entry : a.iterByPath() ) {
result.push_back(entry.getTitle());
}
return result;
}
void createXapianDB(std::string path, const zim::Archive& archive)
{
const int flags = Xapian::DB_BACKEND_GLASS|Xapian::DB_CREATE;
const auto tmpDbPath = path + ".tmp";
Xapian::WritableDatabase db(tmpDbPath, flags);
for (const auto& t : getAllTitles(archive)) {
db.add_spelling(t);
}
db.commit();
db.compact(path, Xapian::DBCOMPACT_SINGLE_FILE);
db.close();
std::filesystem::remove_all(tmpDbPath);
}
std::string spellingsDBPathForZIMArchive(std::filesystem::path cacheDirPath, const zim::Archive& a)
{
// The version of spellings DB must be updated each time an important change
// to the implementation is made that renders using the previous version
// impossible or undesirable.
const char SPELLINGS_DB_VERSION[] = "0.1";
std::ostringstream filename;
filename << a.getUuid() << ".spellingsdb.v" << SPELLINGS_DB_VERSION;
return (cacheDirPath / filename.str()).string();
}
std::unique_ptr<Xapian::Database> openOrCreateXapianDB(std::filesystem::path cacheDirPath, const zim::Archive& archive)
{
const auto path = spellingsDBPathForZIMArchive(cacheDirPath, archive);
try
{
return std::make_unique<Xapian::Database>(path);
}
catch (const Xapian::DatabaseOpeningError& )
{
createXapianDB(path, archive);
return std::make_unique<Xapian::Database>(path);
}
}
} // unnamed namespace
SpellingsDB::SpellingsDB(const zim::Archive& archive, std::filesystem::path cacheDirPath)
: impl_(openOrCreateXapianDB(cacheDirPath, archive))
{
}
SpellingsDB::~SpellingsDB()
{
}
std::vector<std::string> SpellingsDB::getSpellingCorrections(const std::string& word, uint32_t maxCount) const
{
if ( maxCount > 1 ) {
throw std::runtime_error("More than one spelling correction was requested");
}
std::vector<std::string> result;
const auto term = impl_->get_spelling_suggestion(word, 3);
if ( !term.empty() ) {
result.push_back(term);
}
return result;
}
} // namespace kiwix

View File

@@ -10,12 +10,10 @@ namespace
// These mappings are not provided by the ICU library, any such mappings can be manually added here
std::map<std::string, std::string> iso639_3 = {
{"ami", "Amis"},
{"atj", "atikamekw"},
{"azb", "آذربایجان دیلی"},
{"bcl", "central bikol"},
{"bgs", "tagabawa"},
{"blk", "ပအိုဝ်ႏ"},
{"bxr", "буряад хэлэн"},
{"cbk", "chavacano"},
{"cdo", "閩東語"},
@@ -25,16 +23,13 @@ std::map<std::string, std::string> iso639_3 = {
{"eml", "emiliân-rumagnōl"},
{"fbs", "српскохрватски"},
{"fon", "fɔ̀ngbè"},
{"gcr", "Kriyòl gwiyannen"},
{"guw", "Gungbe"},
{"hbs", "srpskohrvatski"},
{"hyw", "հայերէն/հայերեն"},
{"ido", "ido"},
{"kbp", "kabɩ"},
{"kld", "Gamilaraay"},
{"lbe", "лакку маз"},
{"lbj", "ལ་དྭགས་སྐད་"},
{"lld", "ladin"},
{"map", "Austronesian"},
{"mhr", "марий йылме"},
{"mnw", "ဘာသာမန်"},
@@ -46,15 +41,10 @@ std::map<std::string, std::string> iso639_3 = {
{"olo", "livvi"},
{"pih", "Pitcairn-Norfolk"},
{"pnb", "Western Panjabi"},
{"pwn", "Pinayuanan"},
{"rmr", "Caló"},
{"rmy", "romani shib"},
{"roa", "romance languages"},
{"skr", "سرائیکی"},
{"szy", "Sakizaya"},
{"tay", "Tayal"},
{"tgl", "Wikang Tagalog"},
{"twi", "Akwapem Twi"},
{"twi", "twi"},
// ICU for Ubuntu versions <= focal (20.04) returns "" for the language code ""
// unlike the later versions - which returns "und". We map this value to "Undetermined" for a common ground.
{"", "Undetermined"},
@@ -68,7 +58,6 @@ void fillLanguagesMap()
const kiwix::ICULanguageInfo lang(*icuLangPtr);
iso639_3.insert({lang.iso3Code(), lang.selfName()});
}
iso639_3.erase("mul");
}
} // unnamed namespace

View File

@@ -19,7 +19,6 @@
*/
#include "tools.h"
#include "stringTools.h"
#include <tools/networkTools.h>
#include <stdio.h>
@@ -33,16 +32,13 @@
#include <stdexcept>
#ifdef _WIN32
#include <iphlpapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#else
#include <unistd.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#endif
@@ -51,12 +47,6 @@
#include <sys/sockio.h>
#endif
namespace kiwix
{
namespace
{
size_t write_callback_to_iss(char* ptr, size_t size, size_t nmemb, void* userdata)
{
auto str = static_cast<std::stringstream*>(userdata);
@@ -64,15 +54,7 @@ size_t write_callback_to_iss(char* ptr, size_t size, size_t nmemb, void* userdat
return nmemb;
}
void updatePublicIpAddress(IpAddress& publicIpAddr, const IpAddress& interfaceIpAddr)
{
if (publicIpAddr.addr.empty()) publicIpAddr.addr = interfaceIpAddr.addr;
if (publicIpAddr.addr6.empty()) publicIpAddr.addr6 = interfaceIpAddr.addr6;
}
} // unnamed namespace
std::string download(const std::string& url) {
std::string kiwix::download(const std::string& url) {
auto curl = curl_easy_init();
std::stringstream ss;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
@@ -93,161 +75,103 @@ std::string download(const std::string& url) {
return ss.str();
}
namespace
{
std::map<std::string, std::string> kiwix::getNetworkInterfaces() {
std::map<std::string, std::string> interfaces;
#ifdef _WIN32
std::map<std::string, IpAddress> getNetworkInterfacesWin() {
std::map<std::string, IpAddress> interfaces;
const int working_buffer_size = 15000;
const int max_tries = 3;
ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
// default to unspecified address family (both)
ULONG family = AF_UNSPEC;
ULONG outBufLen = working_buffer_size;
ULONG Iterations = 0;
DWORD dwRetVal = 0;
PIP_ADAPTER_ADDRESSES interfacesHead = NULL;
// Successively allocate the required memory until GetAdaptersAddresses does not
// results in ERROR_BUFFER_OVERFLOW for a maximum of max_tries
do{
interfacesHead = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
if (interfacesHead == NULL) {
std::cerr << "Memory allocation failed for IP_ADAPTER_ADDRESSES struct" << std::endl;
return interfaces;
}
dwRetVal = GetAdaptersAddresses(family, flags, NULL, interfacesHead, &outBufLen);
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < max_tries));
if (dwRetVal == NO_ERROR) {
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
unsigned int i = 0;
for (PIP_ADAPTER_ADDRESSES temp = interfacesHead; temp != NULL;
temp = temp->Next) {
pUnicast = temp->FirstUnicastAddress;
if (pUnicast != NULL) {
for (i = 0; pUnicast != NULL; i++){
if (pUnicast->Address.lpSockaddr->sa_family == AF_INET)
{
sockaddr_in *si = (sockaddr_in *)(pUnicast->Address.lpSockaddr);
char host[INET_ADDRSTRLEN]={0};
inet_ntop(AF_INET, &(si->sin_addr), host, sizeof(host));
interfaces[temp->AdapterName].addr=host;
}
else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6)
{
sockaddr_in6 *si = (sockaddr_in6 *)(pUnicast->Address.lpSockaddr);
char host[INET6_ADDRSTRLEN]={0};
inet_ntop(AF_INET6, &(si->sin6_addr), host, sizeof(host));
if (!IN6_IS_ADDR_LINKLOCAL(&(si->sin6_addr)))
interfaces[temp->AdapterName].addr6=host;
}
pUnicast = pUnicast->Next;
}
}
}
} else {
std::cerr << "Call to GetAdaptersAddresses failed with error: "<< dwRetVal << std::endl;
SOCKET sd = WSASocket(AF_INET, SOCK_DGRAM, 0, 0, 0, 0);
if (sd == INVALID_SOCKET) {
std::cerr << "Failed to get a socket. Error " << WSAGetLastError() << std::endl;
return interfaces;
}
if (interfacesHead) free(interfacesHead);
INTERFACE_INFO InterfaceList[20];
unsigned long nBytesReturned;
if (WSAIoctl(sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList,
sizeof(InterfaceList), &nBytesReturned, 0, 0) == SOCKET_ERROR) {
std::cerr << "Failed calling WSAIoctl: error " << WSAGetLastError() << std::endl;
return interfaces;
}
int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
for (int i = 0; i < nNumInterfaces; ++i) {
sockaddr_in *pAddress;
pAddress = (sockaddr_in *) & (InterfaceList[i].iiAddress.AddressIn);
if(pAddress->sin_family == AF_INET) {
/* Add to the map */
std::string interfaceName = std::string(inet_ntoa(pAddress->sin_addr));
interfaces[interfaceName] = interfaceName;
}
}
#else
/* Get Network interfaces information */
char buf[16384];
struct ifconf ifconf;
int fd = socket(PF_INET, SOCK_DGRAM, 0); /* Only IPV4 */
ifconf.ifc_len = sizeof(buf);
ifconf.ifc_buf=buf;
if(ioctl(fd, SIOCGIFCONF, &ifconf)!=0) {
perror("ioctl(SIOCGIFCONF)");
}
/* Go through each interface */
struct ifreq *ifreq;
ifreq = ifconf.ifc_req;
for (int i = 0; i < ifconf.ifc_len; ) {
if (ifreq->ifr_addr.sa_family == AF_INET) {
/* Get the network interface ip */
char host[128] = { 0 };
const int error = getnameinfo(&(ifreq->ifr_addr), sizeof(ifreq->ifr_addr),
host, sizeof(host),
0, 0, NI_NUMERICHOST);
if (!error) {
std::string interfaceName = std::string(ifreq->ifr_name);
std::string interfaceIp = std::string(host);
/* Add to the map */
interfaces[interfaceName] = interfaceIp;
} else {
perror("getnameinfo()");
}
}
/* some systems have ifr_addr.sa_len and adjust the length that
* way, but not mine. weird */
size_t len;
#ifndef __linux__
len = IFNAMSIZ + ifreq->ifr_addr.sa_len;
#else
len = sizeof(*ifreq);
#endif
ifreq = (struct ifreq*)((char*)ifreq+len);
i += len;
}
#endif
return interfaces;
}
#else
std::string kiwix::getBestPublicIp() {
auto interfaces = getNetworkInterfaces();
std::map<std::string, IpAddress> getNetworkInterfacesPosix() {
std::map<std::string, IpAddress> interfaces;
struct ifaddrs *interfacesHead;
if (getifaddrs(&interfacesHead) == -1) {
perror("getifaddrs");
}
for (ifaddrs *temp = interfacesHead; temp != NULL; temp = temp->ifa_next) {
if (temp->ifa_addr == NULL) continue;
if (temp->ifa_addr->sa_family == AF_INET) {
sockaddr_in *si = (sockaddr_in *)(temp->ifa_addr);
char host[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, &(si->sin_addr), host, sizeof(host));
interfaces[temp->ifa_name].addr=host;
} else if (temp->ifa_addr->sa_family == AF_INET6) {
sockaddr_in6 *si = (sockaddr_in6 *)(temp->ifa_addr);
char host[INET6_ADDRSTRLEN] = {0};
inet_ntop(AF_INET6, &(si->sin6_addr), host, sizeof(host));
if (!IN6_IS_ADDR_LINKLOCAL(&(si->sin6_addr)))
interfaces[temp->ifa_name].addr6=host;
}
}
freeifaddrs(interfacesHead);
return interfaces;
}
#endif
} // unnamed namespace
std::map<std::string, IpAddress> getNetworkInterfacesIPv4Or6() {
#ifdef _WIN32
return getNetworkInterfacesWin();
#else
return getNetworkInterfacesPosix();
#endif
}
std::map<std::string, std::string> getNetworkInterfaces() {
std::map<std::string, std::string> result;
for ( const auto& kv : getNetworkInterfacesIPv4Or6() ) {
const std::string& interfaceName = kv.first;
const auto& ipAddresses = kv.second;
if ( !ipAddresses.addr.empty() ) {
result[interfaceName] = ipAddresses.addr;
}
}
return result;
}
IpAddress getBestPublicIps() {
IpAddress bestPublicIps;
std::map<std::string, IpAddress> interfaces = getNetworkInterfacesIPv4Or6();
#ifndef _WIN32
const char* const prioritizedNames[] = { "eth0", "eth1", "wlan0", "wlan1", "en0", "en1" };
for (const auto& name : prioritizedNames) {
const auto it = interfaces.find(name);
if (it != interfaces.end()) {
updatePublicIpAddress(bestPublicIps, it->second);
const char* const prioritizedNames[] =
{ "eth0", "eth1", "wlan0", "wlan1", "en0", "en1" };
for(auto name: prioritizedNames) {
auto it = interfaces.find(name);
if(it != interfaces.end()) {
return it->second;
}
}
#endif
const char* const v4prefixes[] = { "192.168", "172.16", "10.0" };
for (const auto& prefix : v4prefixes) {
for (const auto& kv : interfaces) {
const auto& interfaceIps = kv.second;
if (kiwix::startsWith(interfaceIps.addr, prefix)) {
updatePublicIpAddress(bestPublicIps, interfaceIps);
const char* const prefixes[] = { "192.168", "172.16.", "10.0" };
for(auto prefix : prefixes){
for(auto& itr : interfaces) {
auto interfaceIp = itr.second;
if (interfaceIp.find(prefix) == 0) {
return interfaceIp;
}
}
}
updatePublicIpAddress(bestPublicIps, {"127.0.0.1", "::1"});
return bestPublicIps;
return "127.0.0.1";
}
std::string getBestPublicIp()
{
return getBestPublicIps().addr;
}
} // namespace kiwix

View File

@@ -32,7 +32,7 @@
#endif
#include "tools/stringTools.h"
#include "server/i18n_utils.h"
#include "server/i18n.h"
#include "libkiwix-resources.h"
#include <map>

View File

@@ -320,6 +320,16 @@ bool kiwix::fileReadable(const std::string& path)
#endif
}
bool makeDirectory(const std::string& path)
{
#ifdef _WIN32
int status = _wmkdir(Utf8ToWide(path).c_str());
#else
int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
return status == 0;
}
std::string makeTmpDirectory()
{
#ifdef _WIN32
@@ -428,6 +438,52 @@ std::string kiwix::getCurrentDirectory()
return ret;
}
std::string kiwix::getDataDirectory()
{
// Try to get the dataDir from the `KIWIX_DATA_DIR` env var
#ifdef _WIN32
wchar_t* cDataDir = ::_wgetenv(L"KIWIX_DATA_DIR");
if (cDataDir != nullptr) {
return WideToUtf8(cDataDir);
}
#else
char* cDataDir = ::getenv("KIWIX_DATA_DIR");
if (cDataDir != nullptr) {
return cDataDir;
}
#endif
// Compute the dataDir from the user directory.
std::string dataDir;
#ifdef _WIN32
cDataDir = ::_wgetenv(L"APPDATA");
if (cDataDir == nullptr)
cDataDir = ::_wgetenv(L"USERPROFILE");
if (cDataDir != nullptr)
dataDir = WideToUtf8(cDataDir);
#else
cDataDir = ::getenv("XDG_DATA_HOME");
if (cDataDir != nullptr) {
dataDir = cDataDir;
} else {
cDataDir = ::getenv("HOME");
if (cDataDir != nullptr) {
dataDir = cDataDir;
dataDir = appendToDirectory(dataDir, ".local");
dataDir = appendToDirectory(dataDir, "share");
}
}
#endif
if (!dataDir.empty()) {
dataDir = appendToDirectory(dataDir, "kiwix");
makeDirectory(dataDir);
return dataDir;
}
// Let's use the currentDirectory
return getCurrentDirectory();
}
static std::map<std::string, std::string> extMimeTypes = {
{ "html", "text/html"},
{ "htm", "text/html"},

View File

@@ -29,6 +29,7 @@ std::wstring Utf8ToWide(const std::string& str);
unsigned int getFileSize(const std::string& path);
std::string getFileSizeAsString(const std::string& path);
bool makeDirectory(const std::string& path);
std::string makeTmpDirectory();
bool copyFile(const std::string& sourcePath, const std::string& destPath);
bool writeTextFile(const std::string& path, const std::string& content);

View File

@@ -32,7 +32,6 @@
#include <iostream>
#include <iomanip>
#include <regex>
/* tell ICU where to find its dat file (tables) */
void kiwix::loadICUExternalTables()
@@ -257,7 +256,7 @@ std::string kiwix::urlDecode(const std::string& value, bool component)
// If there aren't enough characters left for this to be a
// valid escape code, just use the character and move on
if (value.end() - it < 3) {
if (it > value.end() - 3) {
os << *it;
continue;
}
@@ -440,13 +439,3 @@ template<>
std::string kiwix::extractFromString(const std::string& str) {
return str;
}
std::string kiwix::getSlugifiedFileName(const std::string& filename)
{
#ifdef _WIN32
const std::regex reservedCharsReg(R"([<>:"/\\|?*])");
#else
const std::regex reservedCharsReg("/");
#endif
return std::regex_replace(filename, reservedCharsReg, "_");
}

View File

@@ -66,9 +66,6 @@ std::string ucAll(const std::string& word);
std::string lcAll(const std::string& word);
std::string ucFirst(const std::string& word);
std::string lcFirst(const std::string& word);
/* This function is broken, related Github issue
* https://github.com/kiwix/libkiwix/issues/1188 */
std::string toTitle(const std::string& word);
std::string normalize(const std::string& word);

View File

@@ -1,8 +1,6 @@
skin/i18n/ar.json
skin/i18n/bn.json
skin/i18n/br.json
skin/i18n/cs.json
skin/i18n/dag.json
skin/i18n/de.json
skin/i18n/dga.json
skin/i18n/el.json
@@ -10,14 +8,10 @@ skin/i18n/en.json
skin/i18n/es.json
skin/i18n/fi.json
skin/i18n/fr.json
skin/i18n/ha.json
skin/i18n/he.json
skin/i18n/hi.json
skin/i18n/hu.json
skin/i18n/hy.json
skin/i18n/ia.json
skin/i18n/id.json
skin/i18n/ig.json
skin/i18n/it.json
skin/i18n/ja.json
skin/i18n/ko.json
@@ -25,14 +19,10 @@ skin/i18n/ku-latn.json
skin/i18n/lb.json
skin/i18n/mk.json
skin/i18n/ms.json
skin/i18n/nb.json
skin/i18n/nl.json
skin/i18n/nqo.json
skin/i18n/or.json
skin/i18n/pl.json
skin/i18n/pt-br.json
skin/i18n/pt.json
skin/i18n/ro.json
skin/i18n/ru.json
skin/i18n/sc.json
skin/i18n/sk.json
@@ -40,7 +30,6 @@ skin/i18n/skr-arab.json
skin/i18n/sl.json
skin/i18n/sq.json
skin/i18n/sv.json
skin/i18n/sw.json
skin/i18n/te.json
skin/i18n/test.json
skin/i18n/tr.json

View File

@@ -1,30 +1,22 @@
skin/caret.png
skin/bittorrent.png
skin/magnet.png
skin/404.svg
skin/500.svg
skin/blocklink.svg
skin/feed.svg
skin/langSelector.svg
skin/download.png
skin/download-white.svg
skin/hash.png
skin/search-icon.svg
skin/iso6391To3.js
skin/isotope.pkgd.min.js
skin/index.js
skin/autoComplete/autoComplete.min.js
skin/error.css
skin/print.css
skin/kiwix.css
skin/taskbar.css
skin/index.css
skin/fonts/DMSans-Regular.ttf
skin/fonts/Poppins.ttf
skin/fonts/Roboto.ttf
skin/search_results.css
skin/blank.html
skin/polyfills.js
skin/viewer.js
skin/i18n.js
skin/languages.js
@@ -44,12 +36,10 @@ templates/catalog_v2_entry.xml
templates/catalog_v2_partial_entry.xml
templates/catalog_v2_categories.xml
templates/catalog_v2_languages.xml
templates/url_of_search_results_css.tmpl
templates/url_of_search_results_css
templates/viewer_settings.js
templates/no_js_library_page.html
templates/no_js_download.html
templates/sexy404.html
templates/sexy500.html
opensearchdescription.xml
ft_opensearchdescription.xml
catalog_v2_searchdescription.xml

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.4 KiB

View File

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -23,9 +23,6 @@
.autoComplete_wrapper > ul {
position: absolute;
min-width: 100%;
width: fit-content;
max-width: 600px;
max-height: 226px;
overflow-y: scroll;
top: 100%;
@@ -63,11 +60,6 @@
transition: all 0.2s ease;
}
.autoComplete_wrapper > ul > li > a {
overflow: hidden;
text-overflow: ellipsis;
}
.autoComplete_wrapper > ul > li::selection {
color: rgba(#ffffff, 0);
background-color: rgba(#ffffff, 0);

View File

@@ -1 +0,0 @@
<svg viewBox="0 0 1742.79 1984.21" xmlns="http://www.w3.org/2000/svg"><ellipse cx="667.97" cy="1872.93" fill="#c7c8ca" rx="649.71" ry="111.28"/><path d="m933.76 1775.81c0-29.5-23.92-53.42-53.42-53.42h-163.28c-24.37 0-45.97-15.91-52.98-39.26l-105.96-347.65 18.1-9.63c251.03-105.38 519.58 82.75 706.54-97.93l1.17-1.17c23.21-21.02 46.27-42.47 114.72.29 73.56 46.12 236 166.53 309.71 372.32 0 0 44.66-15.91 32.25-70.49-12.41-54.73-102.89-212.36-287.96-352.18-33.86-25.69-64.36-47-89.76-64.07 36.93-158.21-155.14-349.11-342.69-259.21-134.57-126.54-284.6-178.2-422.67-176.16-365.6 5.11-647.58 386.04-344.3 749.45l.58.58c4.67 5.55 9.49 11.09 14.3 16.64 19.7 23.64 32.69 44.51 43.93 81.29l90.34 296.57h-31.96c-29.48 0-53.42 23.94-53.42 53.42h121.43l204.04 96.47c12.55-26.71 1.17-58.53-25.4-71.08l-53.56-25.25h153.83c0-29.48-23.94-53.42-53.42-53.42h-139.38c-24.37 0-45.97-15.91-52.98-39.26l-71.66-235.42c-17.81-61.88 23.21-102.31 61.74-107.13 35.17-4.38 52.83 18.39 69.47 73.27l63.63 208.85h-31.96c-29.48 0-53.42 23.94-53.42 53.42h121.43l204.04 96.47c12.55-26.71 1.17-58.53-25.4-71.08l-53.56-25.25h177.88z"/><path d="m545.68 2.53h52.19v1441.85h-52.19z" fill="#f89a16" transform="matrix(.9855856 -.16917749 .16917749 .9855856 -114.16 107.17)"/><path d="m581.21 624.21-55.8-17.21-93.34-544.54 53.51 3.6z" fill="#da7c2b"/><path d="m981.62 279.44c-30.85 1.91-83.72 3.58-83.72 3.58l79.01-31.06-43.25-251.96-933.66 160.24 56.63 330 68.11 13.66s-38.47 19-60.89 28.37l22.52 131.23 933.66-160.24z" fill="#f89a16"/><circle cx="1144.16" cy="1031.93" fill="#fff" r="82.26" transform="matrix(.70710678 -.70710678 .70710678 .70710678 -394.57 1111.29)"/><circle cx="1124.15" cy="1004.52" r="52.63" transform="matrix(.41786707 -.90850818 .90850818 .41786707 -258.21 1606.05)"/><g fill="#fff"><path d="m387.43 559.95-69.59-57.58 107.75-360.2 93.69-15.45 226.75 308.17-45.64 75.04-312.97 50.02zm-11.05-75.33 25.78 21.33 266.91-42.65 15.63-25.7-187.97-255.46-31.41 5.18-88.94 297.31z"/><path d="m526.16 300.48 5.44 89.61-20.72 3.32-28.24-85.96-9.08-47.73 43.52-6.98 9.08 47.73zm18.46 103.04 7.02 36.88-41.43 6.64-7.02-36.88z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,6 +0,0 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <g id="Interface / Download"> <path id="Vector" d="M6 21H18M12 3V17M12 17L17 12M12 17L7 12" stroke="#f6f5f4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g> </g>
</svg>

Before

Width:  |  Height:  |  Size: 546 B

View File

@@ -1,159 +0,0 @@
@font-face {
font-family:"DM Sans";
font-style: normal;
font-weight: 400;
src : url('../skin/fonts/DMSans-Regular.ttf?KIWIXCACHEID');
}
@font-face {
font-family:"DM Sans Bold";
font-style: normal;
font-weight: 700;
src : url('../skin/fonts/DMSans-Regular.ttf?KIWIXCACHEID');
}
body {
background: linear-gradient(to bottom right, #ffffff, #e6e6e6);
background-repeat: no-repeat;
background-attachment: fixed;
}
header {
width: 100%;
margin: auto;
text-align: center;
margin-top: 15%;
margin-bottom: 15%;
}
header img {
width: 60%;
min-width: 200px;
max-width: 500px;
max-height: 300px;
}
section {
display: flex;
flex-direction: column;
align-items: center;
}
header, .intro {
font-family: "DM Sans";
}
.intro {
font-size: 1em;
padding: 0 10%;
line-height: 1.2em;
text-align: center;
}
.intro h1 {
line-height: 1.1em;
font-family: "DM Sans Bold";
font-size: 1.2em;
}
.intro code {
font-family: monospace;
font-size: 1.1em;
word-break: break-all;
}
.intro a, .intro a:active, .intro a:visited {
color: #00b4e4;
text-decoration: none;
word-break: break-all;
}
.advice {
width: 80%;
margin: auto;
margin-bottom: 15%;
margin-top: 5em;
background-color: #ffffff;
border-radius: 1rem;
border: 1px solid #b7b7b7;
padding: 2em;
font-family: "DM Sans";
font-size: .9em;
box-sizing: border-box;
align-items: normal;
}
.advice p {
margin-bottom: 1em;
}
.advice p:first-child {
margin-top: 0;
}
.advice p.list-intro {
margin: 0;
}
.advice ul {
list-style-type: square;
margin: 0;
padding: 0 1em;
}
.advice ul li {
line-height: 2em;
}
.advice p:last-child {
margin-bottom: 0;
}
/* sm: 640px+ */
@media (width >= 40rem) {
header {
margin-bottom: 1em;
margin-top: 5em;
}
header img {
width: 50%;
}
.intro h1 {
font-size: 2em;
}
.advice {
width: 50%;
}
}
/* xl: 1280px+ */
@media (width >= 80rem) {
.intro h1 {
font-size: 3.4em;
}
}
/* 2xl: 1536px+ */
@media (width >= 96rem) {
header img {
width: 25%;
min-width: 200px;
max-width: 500px;
max-height: 300px;
}
.advice {
width: 25%;
min-width: 200px;
min-width: 300px;
max-width: 500px;
}
}

View File

Binary file not shown.

View File

@@ -23,8 +23,7 @@ const Translations = {
return;
const errorMsg = `Error loading translations for language '${lang}': `;
const translationJsonUrl = import.meta.resolve(`./i18n/${lang}.json`);
this.promises[lang] = fetch(translationJsonUrl).then(async (resp) => {
this.promises[lang] = fetch(`./skin/i18n/${lang}.json`).then(async (resp) => {
if ( resp.ok ) {
this.data[lang] = JSON.parse(await resp.text());
} else {
@@ -191,40 +190,8 @@ function initUILanguageSelector(activeLanguage, languageChangeCallback) {
languageSelector.onchange = languageChangeCallback;
}
function parseDom(html) {
const domParser = new DOMParser();
return domParser.parseFromString(html, "text/html").documentElement;
}
function translatePageInWindow(w) {
if ( w.KIWIX_RESPONSE_TEMPLATE && w.KIWIX_RESPONSE_DATA ) {
const template = parseDom(w.KIWIX_RESPONSE_TEMPLATE).textContent;
// w.KIWIX_RESPONSE_DATA may belong to a different context and running
// I18n.render() on it directly won't work correctly
// because the type checks (obj.__proto__ == ???.prototype) in
// I18n.instantiateParameterizedMessages() will fail (String.prototype
// refers to different objects in different contexts).
// Work arround that issue by copying the object into our context.
const params = JSON.parse(JSON.stringify(w.KIWIX_RESPONSE_DATA));
const newHtml = I18n.render(template, params);
w.document.documentElement.innerHTML = parseDom(newHtml).innerHTML;
}
}
function translateSelf() {
if ( window.KIWIX_RESPONSE_TEMPLATE && window.KIWIX_RESPONSE_DATA ) {
setUserLanguage(getUserLanguage(), () => {
translatePageInWindow(window)
});
}
};
window.$t = $t;
window.getUserLanguage = getUserLanguage;
window.setUserLanguage = setUserLanguage;
window.initUILanguageSelector = initUILanguageSelector;
window.translatePageInWindow = translatePageInWindow;
window.I18n = I18n;
window.addEventListener('load', translateSelf);

View File

@@ -2,9 +2,6 @@
"@metadata": {
"authors": [
"Asma",
"Hamoudak",
"Meno25",
"Mohammed Qays",
"Ravan",
"محمد أحمد عبد الفتاح"
]
@@ -15,7 +12,7 @@
"no-book-found": "لا يوجد كتاب يطابق معايير الاختيار",
"url-not-found": "لم يتم العثور على عنوان URL المطلوب \"{{url}}\" على هذا الخادم.",
"suggest-search": "قم بإجراء بحث عن النص الكامل لـ <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "للأسف ! فشل في اختيار مقال عشوائي :(",
"random-article-failure": "مع الأسف! فشل اختيار مقال عشوائي :(",
"invalid-raw-data-type": "{{DATATYPE}} ليس طلبًا صالحًا للمحتوى الأولي.",
"no-value-for-arg": "لم يتم تقديم قيمة للوسيطة {{ARGUMENT}}",
"no-query": "لم يتم تقديم ملخص.",
@@ -24,32 +21,13 @@
"400-page-heading": "طلب غير صالح",
"404-page-title": "المحتوى غير موجود",
"404-page-heading": "لم يتم العثور عليه",
"500-page-title": "خطأ داخلي بالخادم",
"500-page-heading": "عفواً، الصفحة لا تعمل.",
"500-page-text": "لا يمكن إرسال المسار المطلوب بشكل صحيح:",
"fulltext-search-unavailable": "البحث في كامل النص غير متاح",
"no-search-results": "محرك البحث في كامل النص غير متوفر لهذا المحتوي.",
"library-button-text": "توجه إلي صفحة الترحيب",
"home-button-text": "انتقل إلى الصفحة الرئيسة لـ '{{{BOOK_TITLE}}}'",
"500-page-title": "خطأ في الخادم الداخلي",
"500-page-heading": "خطأ في الخادم الداخلي",
"fulltext-search-unavailable": "البحث عن النص الكامل غير متاح",
"no-search-results": "محرك البحث عن النص الكامل غير متاح لهذا المحتوى.",
"library-button-text": "اذهب لصفحة الترحيب",
"home-button-text": "انتقل إلى الصفحة الرئيسية لـ \"{{BOOK_TITLE}}\"",
"random-page-button-text": "اذهب إلى صفحة عشوائية",
"searchbox-tooltip": "ابحث عن '{{{BOOK_TITLE}}}'",
"confusion-of-tongues": "قد يشارك في البحث كتابان أو أكثر بلغات مختلفة، مما قد يؤدي إلى نتائج محيرة.",
"direct-download-alt-text": "التنزيل مباشرة عبر بروتوكول HTTP(S)",
"hash-download-link-text": "المجموع الإختباري لخوارزمية SHA-256",
"hash-download-alt-text": "عرض المجموع الإختباري لخوارزمية SHA-256 للملف",
"magnet-link-text": "رابط جاذب",
"magnet-alt-text": "التنزيل بواسطة الرابط الجاذب",
"torrent-download-link-text": "بت تورنت",
"torrent-download-alt-text": "التنزيل بواسطة بت تورنت",
"unknown-error": "خطأ غير معروف",
"book-category.wikibooks": "ويكي الكتب",
"book-category.wikinews": "ويكي الأخبار",
"book-category.wikipedia": "ويكيبيديا",
"book-category.wikiquote": "ويكي الإقتباس",
"book-category.wikisource": "ويكي مصدر",
"book-category.wikispecies": "ويكي أنواع",
"book-category.wikiversity": "ويكي الجامعة",
"book-category.wikivoyage": "ويكي الرحلات",
"book-category.wiktionary": "ويكي القاموس",
"book-category.other": "أخرى {كتب أخري}"
"searchbox-tooltip": "بحث \"{{BOOK_TITLE}}\"",
"confusion-of-tongues": "قد يشارك في البحث كتابان أو أكثر بلغات مختلفة، مما قد يؤدي إلى نتائج محيرة."
}

View File

@@ -1,41 +1,18 @@
{
"@metadata": {
"authors": [
"Nokib Sarkar",
"আফতাবুজ্জামান"
]
},
"name": "বাংলা",
"404-page-heading": "পাওয়া যায়নি",
"new-404-page-title": "পাতা পাওয়া যায়নি",
"new-404-page-heading": "ওহ! পাতা খুঁজে পাওয়া যায়নি",
"404-img-text": "পাওয়া যায়নি!",
"path-was-not-found": "অনুরোধ করা পথটি পাওয়া যায়নি:",
"404-advice.p1": "আপনি যে বিষয়বস্তু খুঁজছেন তা এখনও উপলব্ধ থাকতে পারে, তবে এটি জিম ফাইলের মধ্যে অন্য কোনও জায়গায় অবস্থিত হতে পারে।",
"404-advice.p4": "আপনি যে তথ্য খুঁজছেন তার সাথে সম্পর্কিত কীওয়ার্ড বা শিরোনাম খুঁজুন।",
"500-page-title": "অভ্যন্তরীণ সার্ভার ত্রুটি",
"500-page-heading": "অভ্যন্তরীণ সার্ভার ত্রুটি",
"external-link-intro": "আপনি অনলাইনে যাওয়ার জন্য কিউইক্সের জিম রিডার ছেড়ে দিতে চলেছেন",
"external-link-advice.p1": "আপনি যে লিঙ্কটিতে প্রবেশ করার চেষ্টা করছেন তা আপনার অফলাইন প্যাকেজের অংশ নয় এবং এর জন্য ইন্টারনেট সংযোগ প্রয়োজন।",
"external-link-advice.p2": "আপনি যদি অনলাইনে যেতে পারেন, তাহলে লিঙ্কটি খোলার চেষ্টা করতে পারেন।",
"external-link-advice.p3": "অন্যথায়, আপনি আপনার ব্রাউজারের পিছন বোতাম ব্যবহার করে আপনার ZIM-এর অফলাইন বিষয়বস্তুতে ফিরে যেতে পারেন।",
"search-result-book-info": "{{BOOK_TITLE}} থেকে",
"word-count": "{{COUNT}}টি শব্দ",
"library-button-text": "স্বাগত পাতায় চলুন",
"home-button-text": "'{{BOOK_TITLE}}'-এর প্রধান পাতায় চলুন",
"searchbox-tooltip": "'{{BOOK_TITLE}}' অনুসন্ধান করুন",
"search": "অনুসন্ধান",
"welcome-to-kiwix-server": "কিউইক্স সার্ভারে স্বাগতম",
"download-links-title": "বই ডাউনলোড করুন",
"preview-book": "প্রাকদর্শন",
"book-category.wikibooks": "উইকিবই",
"book-category.wikinews": "উইকিসংবাদ",
"book-category.wikipedia": "উইকিপিডিয়া",
"book-category.wikiquote": "উইকিউক্তি",
"book-category.wikisource": "উইকিসংকলন",
"book-category.wikispecies": "উইকিপ্রজাতি",
"book-category.wikiversity": "উইকিবিশ্ববিদ্যালয়",
"book-category.wikivoyage": "উইকিভ্রমণ",
"book-category.wiktionary": "উইকিঅভিধান",
"book-category.other": "অন্যান্য"
"preview-book": "প্রাকদর্শন"
}

View File

@@ -1,42 +0,0 @@
{
"@metadata": {
"authors": [
"Adriendelucca",
"Y-M D"
]
},
"name": "brezhoneg",
"suggest-full-text-search": "E lec'h emañ \"{{{SEARCH_TERMS}}}\"...",
"no-such-book": "Neus ket eus al levr-mañ: {{BOOK_NAME}}",
"no-book-found": "Neus levr ebet a glot gant an dezverkoù-se",
"url-not-found": "Neo ket bet kavet an URL \"{{url}}\" goulennet war ar servijer-mañ.",
"random-article-failure": "Chaous! Nhon eus ket gellet dibab ur pennad dre ziouer evidoch :(",
"400-page-title": "Reked amwiriek",
"400-page-heading": "Reked amwiriek",
"404-page-heading": "N'eo ket bet kavet",
"500-page-title": "Fazi diabarzh ar servijer",
"500-page-heading": "Fazi diabarzh ar servijer",
"search-results-page-title": "Klask: {{SEARCH_PATTERN}}",
"search-results-page-header": "Disochoù <b>{{START}}-{{END}}</b> diwar <b>{{COUNT}}</b> evit <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Disoch ebet kavet evit <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "diouzh {{BOOK_TITLE}}",
"word-count": "{{COUNT}} a cherioù",
"library-button-text": "Mont dar bajenn degemer",
"home-button-text": "Mont da bajenn degemer \"{{BOOK_TITLE}}\"",
"random-page-button-text": "Mont dur bajenn dre zegouezh",
"searchbox-tooltip": "Klask '{{BOOK_TITLE}}'",
"powered-by-kiwix-html": "Lusket gant&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Klask",
"book-filtering-all-categories": "An holl rummadoù",
"book-filtering-all-languages": "An holl yezhoù",
"count-of-matching-books": "{{COUNT}} levr",
"download": "Pellgargañ",
"direct-download-link-text": "Eeun",
"filter-by-tag": "Silañ gant an dikedenn \"{{TAG}}\"",
"stop-filtering-by-tag": "Paouez da silañ gant an dikedenn \"{{TAG}}\"",
"welcome-to-kiwix-server": "Degemer mat er servijer Kiwix",
"download-links-heading": "Liammoù pellgargañ evit <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Pellgargañ al levr",
"preview-book": "Rakwelet",
"unknown-error": "Fazi dianav"
}

View File

@@ -1,55 +0,0 @@
{
"@metadata": {
"authors": [
"Kalakpagh",
"Ruky Wunpini"
]
},
"name": "Silimiinsili",
"suggest-full-text-search": "Gbubi la '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Lala buku kani:{{BOOK_NAME}}",
"too-many-books": "Buku nima pam ka bɛ daa suhi ({{NB_BOOKS}}) din ni ka tariga nyɛ {{LIMIT}}",
"no-book-found": "Buku kani lu zahim a ni piigi yaɣa shɛli",
"url-not-found": "URL \"{{url}}\" shɛli bɛ ni daa suhi daa kani n-ti tum tumda ŋɔ.",
"suggest-search": "Niŋmi lahabali pali vihigu zaŋ n-ti <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Oops! Zaɣisiya ni di gahim piigi lahabali :(",
"invalid-raw-data-type": "{{DATATYPE}} nyɛla din suhibu bi niŋ viɛnyɛla zaŋ n-ti lahabali kahili.",
"invalid-request": "URL \"{{{url}}}\" shɛli bɛ ni daa suhi ŋɔ nyɛla din bi suhi viɛnyɛla.",
"no-value-for-arg": "Dariza shɛli bi ti zaŋ n-ti nangban'kpeeni {{ARGUMENT}}",
"no-query": "Yɛlshɛli bi yiina",
"raw-entry-not-found": "Ku tooi nya {{DATATYPE}} kpɛbu {{ENTRY}}",
"400-page-title": "Suhigu din bi niŋ viɛnyɛla",
"400-page-heading": "Suhigu din bi niŋ viɛnyɛla",
"404-page-title": "Lahabali kani",
"404-page-heading": "Kani",
"500-page-title": "Puuni tum tumda chiriŋ",
"500-page-heading": "Puuni tum tumda chiriŋ",
"500-page-text": "Puuni tum tumda chiriŋ niŋya. Ti niŋ yolitem zaŋ jɛndi li :/",
"fulltext-search-unavailable": "Lahabali pali vihigu kani",
"search-results-page-title": "Vihima:{{SEARCH_PATTERN}}",
"search-results-page-header": "Chaɣili nima <b>{{START}}-{{END}}</b> of <b>{{COUNT}}</b> for <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Chaɣili daa kani zaŋ n-ti\n <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "yina {{BOOK_TITLE}}",
"word-count": "{{COUNT}} bachi nima",
"library-button-text": "Cham solɔɣu",
"home-button-text": "Cham yaɣili maŋmaŋ zaŋ n-ti\n'{{BOOK_TITLE}}'",
"random-page-button-text": "Cham gahim piigi yaɣili",
"searchbox-tooltip": "Vihima '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Buku nima ayi bee gari balli koŋkoba nyɛ din yɛn be vihigu ŋɔ ni ka di ni tooi chɛ ka di laasabu wali.",
"welcome-page-overzealous-filter": "Labisibu kani. A ni yu ni a\n<a href=\"{{URL}}\">reset filter</a>?",
"powered-by-kiwix-html": "Din niŋ li nyɛ &nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Vihima",
"book-filtering-all-categories": "Pubu zaa",
"book-filtering-all-languages": "Bala zaa",
"count-of-matching-books": "{{COUNT}} Buku(nima)",
"download": "Deebu",
"direct-download-link-text": "Tibi",
"direct-download-alt-text": "Tibi deebu",
"hash-download-link-text": "Sha256 hash",
"hash-download-alt-text": "Deebu daliŋ",
"welcome-to-kiwix-server": "Maraba Kiwix tum tumda",
"download-links-heading": "Deemi soli zaŋ n-ti <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Deemi buku",
"preview-book": "Daŋyuli",
"unknown-error": "Chiriŋ din bi tooi baŋ"
}

View File

@@ -1,12 +1,8 @@
{
"@metadata": {
"authors": [
"AnnaTheProgrammer777",
"IMayBeABitShy",
"Justman10000",
"Lucas Werkmeister",
"MoritzMT20",
"Rofiatmustapha12",
"ThisCarthing"
]
},
@@ -19,7 +15,6 @@
"suggest-search": "Führe eine Volltextsuche nach <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a> durch",
"random-article-failure": "Hoppla! Konnte keinen zufälligen Artikel auswählen :(",
"invalid-raw-data-type": "{{DATATYPE}} ist keine gültige Anfrage für unverarbeiteten Inhalt",
"invalid-request": "Die angeforderte URL „{{{url}}}“ ist keine gültige Anfrage.",
"no-value-for-arg": "Kein Wert für den Parameter {{ARGUMENT}} gegeben",
"no-query": "Keine Suchanfrage gegeben.",
"raw-entry-not-found": "Eintrag {{ENTRY}} des Typs {{DATATYPE}} konnte nicht gefunden werden.",
@@ -29,18 +24,12 @@
"404-page-heading": "Nicht gefunden",
"500-page-title": "Interner Server-Fehler",
"500-page-heading": "Interner Server-Fehler",
"500-page-text": "Es ist ein interner Serverfehler aufgetreten. Das tut uns leid :/",
"fulltext-search-unavailable": "Die Volltestsuche steht nicht zur Verfügung.",
"no-search-results": "Die Volltextsuche ist für diesen Inhalt nicht verfügbar.",
"search-results-page-title": "Suche: {{SEARCH_PATTERN}}",
"search-results-page-header": "Ergebnisse <b>{{START}}-{{END}}</b> von <b>{{COUNT}}</b> für <b>„{{{SEARCH_PATTERN}}}“</b>",
"empty-search-results-page-header": "Für <b>„{{{SEARCH_PATTERN}}}“</b> wurden keine Ergebnisse gefunden.",
"search-result-book-info": "von {{BOOK_TITLE}}",
"word-count": "{{COUNT}} Wörter",
"library-button-text": "Zur Willkommensseite gehen",
"home-button-text": "Zur Hauptseite von '{{{BOOK_TITLE}}}' gehen",
"home-button-text": "Zur Hauptseite von '{{BOOK_TITLE}}' gehen",
"random-page-button-text": "Zu einer zufällig ausgewählten Seite gehen",
"searchbox-tooltip": "'{{{BOOK_TITLE}}}' durchsuchen",
"searchbox-tooltip": "Nach '{{BOOK_TITLE}}' suchen",
"confusion-of-tongues": "Zwei oder mehr Bücher unterschiedlicher Sprachen werden durchsucht, was zu unübersichtlichen Ergebnissen führen kann.",
"welcome-page-overzealous-filter": "Keine Ergebnisse gefunden. Möchten Sie den <a href=\"{{URL}}\">Filter zurücksetzen</a>?",
"powered-by-kiwix-html": "Angetrieben durch &nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
@@ -50,30 +39,19 @@
"count-of-matching-books": "{{COUNT}} Bücher",
"download": "Herunterladen",
"direct-download-link-text": "Direkt",
"direct-download-alt-text": "Direktes Herunterladen über HTTP(S)",
"hash-download-link-text": "SHA-256 Prüfsumme",
"hash-download-alt-text": "SHA-256-Dateiprüfsumme anzeigen",
"direct-download-alt-text": "direkt herunterladen",
"hash-download-link-text": "Sha256 Hash",
"hash-download-alt-text": "Hash herunterladen",
"magnet-link-text": "Magnet Link",
"magnet-alt-text": "Download über Magnet-Link",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Herunterladen über BitTorrent",
"magnet-alt-text": "Magnet Link herunterladen",
"torrent-download-link-text": "Torrent-Datei",
"torrent-download-alt-text": "Torrent herunterladen",
"library-opds-feed-all-entries": "ODPS Feed der Bibliothek - Alle Einträge",
"filter-by-tag": "Nach Tag \"{{{TAG}}}\" filtern",
"stop-filtering-by-tag": "Filterung nach Tag \"{{{TAG}}}\" aufheben",
"filter-by-tag": "Nach Tag \"{{TAG}}\" filtern",
"stop-filtering-by-tag": "Filterung nach Tag \"{{TAG}}\" aufheben",
"library-opds-feed-parameterised": "ODPS Feed der Bibliothek - Einträge mit {{#LANG}\nSprache {{LANG}} {{/LANG}}{{#CATEGORY}}\nKategorie: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}}{{/TAG}}{{#Q}}\nQuery: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Wilkommen beim Kiwix Server",
"download-links-heading": "Download Links für <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Buch herunterladen",
"preview-book": "Vorschau",
"unknown-error": "Unbekannter Fehler",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinews",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversity",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wiktionary",
"book-category.other": "Andere"
"preview-book": "Vorschau"
}

View File

@@ -1,7 +1,8 @@
{
"@metadata": {
"authors": [
"Alhaji Yakubu"
"Alhaji Yakubu",
"Amire80"
]
},
"welcome-page-overzealous-filter": "Duoro kyebe. <a href=\"{{URL}}\">E na boɔra ka fo</a>?",

View File

@@ -1,18 +1,10 @@
{
"@metadata": {
"authors": [
"Jimkats",
"Kelson",
"Norhorn",
"Ανώνυμος Βικιπαιδιστής"
"Norhorn"
]
},
"name": "Αγγλικά",
"suggest-full-text-search": "περιέχει '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Δεν υπάρχει τέτοιο βιβλίο: {{BOOK_NAME}}",
"caution-warning": "Προσοχή!",
"search-result-book-info": "από {{BOOK_TITLE}}",
"word-count": "{{COUNT}} λέξεις",
"welcome-page-overzealous-filter": "Κανένα αποτέλεσμα. Θέλετε να <a href=\"{{URL}}\">επαναφέρετε το φίλτρο</a>;",
"powered-by-kiwix-html": "Με την υποστήριξη by&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Αναζήτηση",
@@ -23,14 +15,8 @@
"direct-download-link-text": "Απευθείας",
"direct-download-alt-text": "άμεση λήψη",
"hash-download-alt-text": "λήψη αναγνωριστικού",
"magnet-alt-text": "λήψη μαγνήτη",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Λήψη μέσω BitTorrent",
"filter-by-tag": "Φιλτράρισμα κατά ετικέτα \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Διακοπή φιλτραρίσματος κατά ετικέτα \"{{{TAG}}}\"",
"welcome-to-kiwix-server": "Καλώς ορίσατε στον διακομιστή Kiwix",
"download-links-heading": "Λήψη συνδέσμων για <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Κατεβάστε το βιβλίο",
"preview-book": "Προεπισκόπηση",
"unknown-error": "Άγνωστο σφάλμα"
"torrent-download-link-text": "Αρχείο torrent",
"torrent-download-alt-text": "λήψη torrent",
"filter-by-tag": "Φίλτρο ανά ετικέτα \"{{TAG}}\"",
"stop-filtering-by-tag": "Διακοπή φίλτρου ανά ετικέτα \"{{TAG}}\""
}

View File

@@ -20,25 +20,9 @@
, "400-page-heading" : "Invalid request"
, "404-page-title" : "Content not found"
, "404-page-heading" : "Not Found"
, "new-404-page-title" : "Page not found"
, "new-404-page-heading" : "Oops. Page not found."
, "404-img-text": "Not found!"
, "path-was-not-found": "The requested path was not found:"
, "404-advice.p1": "The content you're looking for may still be available, but it might be located at a different place within the ZIM file."
, "404-advice.p2": "Please:"
, "404-advice.p3": "Try using the search function to find the content you want"
, "404-advice.p4": "Look for keywords or titles related to the information you're seeking"
, "404-advice.p5": "This approach should help you locate the desired content, even if the original link isn't working properly."
, "500-page-title" : "Internal Server Error"
, "500-page-heading" : "Oops. Page isn't working."
, "500-page-text": "The requested path cannot be properly delivered:"
, "500-img-text": "Page isn't working"
, "external-link-detected" : "External Link Detected"
, "caution-warning" : "Caution!"
, "external-link-intro" : "You are about to leave Kiwix's ZIM reader to go online to"
, "external-link-advice.p1": "The link you're trying to access is not part of your offline package and requires an internet connection."
, "external-link-advice.p2": "If you can go online, you can attempt to open the link."
, "external-link-advice.p3": "You can otherwise return to your ZIM's offline content by using your browser's back button."
, "500-page-heading" : "Internal Server Error"
, "500-page-text": "An internal server error occured. We are sorry about that :/"
, "fulltext-search-unavailable" : "Fulltext search unavailable"
, "no-search-results": "The fulltext search engine is not available for this content."
, "search-results-page-title": "Search: {{SEARCH_PATTERN}}"
@@ -47,9 +31,9 @@
, "search-result-book-info": "from {{BOOK_TITLE}}"
, "word-count": "{{COUNT}} words"
, "library-button-text": "Go to welcome page"
, "home-button-text": "Go to the main page of '{{{BOOK_TITLE}}}'"
, "home-button-text": "Go to the main page of '{{BOOK_TITLE}}'"
, "random-page-button-text": "Go to a randomly selected page"
, "searchbox-tooltip": "Search '{{{BOOK_TITLE}}}'"
, "searchbox-tooltip": "Search '{{BOOK_TITLE}}'"
, "confusion-of-tongues": "Two or more books in different languages would participate in search, which may lead to confusing results."
, "welcome-page-overzealous-filter": "No result. Would you like to <a href=\"{{URL}}\">reset filter</a>?"
, "powered-by-kiwix-html": "Powered by&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>"
@@ -59,16 +43,16 @@
, "count-of-matching-books": "{{COUNT}} book(s)"
, "download": "Download"
, "direct-download-link-text": "Direct"
, "direct-download-alt-text": "Download directly via HTTP(S)"
, "hash-download-link-text": "SHA-256 checksum"
, "hash-download-alt-text": "Display SHA-256 file checksum"
, "direct-download-alt-text": "direct download"
, "hash-download-link-text": "Sha256 hash"
, "hash-download-alt-text": "download hash"
, "magnet-link-text": "Magnet link"
, "magnet-alt-text": "Download via Magnet link"
, "torrent-download-link-text": "BitTorrent"
, "torrent-download-alt-text": "Download via BitTorrent"
, "magnet-alt-text": "download magnet"
, "torrent-download-link-text": "Torrent file"
, "torrent-download-alt-text": "download torrent"
, "library-opds-feed-all-entries": "Library OPDS Feed - All entries"
, "filter-by-tag": "Filter by tag \"{{{TAG}}}\""
, "stop-filtering-by-tag": "Stop filtering by tag \"{{{TAG}}}\""
, "filter-by-tag": "Filter by tag \"{{TAG}}\""
, "stop-filtering-by-tag": "Stop filtering by tag \"{{TAG}}\""
, "library-opds-feed-parameterised": "Library OPDS Feed - entries matching {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nQuery: {{Q}} {{/Q}}"
, "welcome-to-kiwix-server": "Welcome to Kiwix Server"
, "download-links-heading": "Download links for <b><i>{{BOOK_TITLE}}</i></b>"
@@ -76,23 +60,4 @@
, "preview-book": "Preview"
, "non-translated-text": "{{MSG}}"
, "unknown-error": "Unknown error"
, "book-category.gutenberg": "Gutenberg"
, "book-category.iFixit": "iFixit"
, "book-category.mooc": "MOOC"
, "book-category.phet": "Phet"
, "book-category.stack_exchange": "Stack Exchange"
, "book-category.ted": "Ted"
, "book-category.vikidia": "Vikidia"
, "book-category.wikibooks": "Wikibooks"
, "book-category.wikihow": "wikiHow"
, "book-category.wikinews": "Wikinews"
, "book-category.wikipedia": "Wikipedia"
, "book-category.wikiquote": "Wikiquote"
, "book-category.wikisource": "Wikisource"
, "book-category.wikispecies": "Wikispecies"
, "book-category.wikiversity": "Wikiversity"
, "book-category.wikivoyage": "Wikivoyage"
, "book-category.wiktionary": "Wiktionary"
, "book-category.other": "Other"
, "text-loading-content": "Loading Content"
}

View File

@@ -1,25 +1,21 @@
{
"@metadata": {
"authors": [
"AlexanderFF",
"Fitoschido",
"Laranxa",
"Ovruni",
"Sinopsistrans",
"SpikeShroom",
"Vis4valentine"
]
},
"name": "Español",
"name": "español",
"suggest-full-text-search": "que contenga \"{{{SEARCH_TERMS}}}\"...",
"no-such-book": "No existe el libro: {{BOOK_NAME}}",
"too-many-books": "Demasiadas solicitudes de libros ({{NB_BOOKS}}) donde el límite es {{LIMIT}}",
"no-book-found": "Ningún libro coincide con los criterios de selección",
"no-book-found": "Ningún libro coincide con los criterios de selección.",
"url-not-found": "La URL solicitada \"{{url}}\" no se encontró en este servidor.",
"suggest-search": "Haga una búsqueda de texto completo para <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "¡Ups! Error al elegir un artículo aleatorio :(",
"invalid-raw-data-type": "{{DATATYPE}} no es una solicitud válida de contenido en crudo.",
"invalid-request": "La URL solicitada \"{{{url}}}\" no es una solicitud válida.",
"no-value-for-arg": "No se ha proporcionado ningún valor para el argumento {{ARGUMENT}}",
"no-query": "No se ha proporcionado ninguna consulta.",
"raw-entry-not-found": "No se puede encontrar la entrada {{DATATYPE}} {{ENTRY}}",
@@ -27,36 +23,14 @@
"400-page-heading": "Solicitud inválida",
"404-page-title": "Contenido no encontrado",
"404-page-heading": "No encontrado",
"new-404-page-title": "Página no encontrada",
"new-404-page-heading": "Ups. Página no encontrado.",
"404-img-text": "¡No encontrado!",
"path-was-not-found": "La pagina solicitada no se ha encontrado:",
"404-advice.p1": "El contenido que está buscando podría estar disponible pero podría estar localizado en un lugar diferente dentro del archivo ZIM.",
"404-advice.p2": "Por favor:",
"404-advice.p3": "Intente usar la función de búsqueda para encontrar el contenido que desea",
"404-advice.p4": "Mire por palabras clave o títulos relacionados a la información que está buscando",
"404-advice.p5": "Este método debería ayudarle a localizar el contenido deseado incluso si el enlace original no funciona correctamente.",
"500-page-title": "Error interno del servidor",
"500-page-heading": "Ups. La página no funciona.",
"500-page-text": "La pagina solicitada no se puede cargar correctamente:",
"500-img-text": "La página no está funcionando",
"external-link-detected": "Enlace externo detectado",
"caution-warning": "¡Precaución!",
"external-link-intro": "Usted está apunto de abandonar el lector ZIM de Kiwix para ir a",
"external-link-advice.p1": "El enlace al que está tratando de acceder no es parte de su descarga sin conexión y requiere de conexión a internet.",
"external-link-advice.p2": "Si usted puede conectarse en linea puede intentar abrir el enlace.",
"external-link-advice.p3": "De lo contrario, puede volver al contenido sin conexión de su ZIM utilizando el botón atrás del navegador.",
"500-page-heading": "Error interno del servidor",
"fulltext-search-unavailable": "Búsqueda de texto completo no disponible",
"no-search-results": "El motor de búsqueda de texto completo no está disponible para este contenido.",
"search-results-page-title": "Buscar: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultados <b>{{START}}-{{END}}</b> de <b>{{COUNT}}</b> para <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "No se encontraron resultados para <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "a partir de {{BOOK_TITLE}}",
"word-count": "{{COUNT}} palabras",
"library-button-text": "Ir a la página de bienvenida",
"home-button-text": "Ir a la página principal de '{{{BOOK_TITLE}}}'",
"home-button-text": "Ir a la página principal de '{{BOOK_TITLE}}'",
"random-page-button-text": "Ir a una página seleccionada al azar",
"searchbox-tooltip": "Buscar '{{{BOOK_TITLE}}}'",
"searchbox-tooltip": "Buscar '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Dos o más libros en diferentes idiomas participarían en la búsqueda, lo que puede llevar a resultados confusos.",
"welcome-page-overzealous-filter": "Sin resultados. ¿Quieres <a href=\"{{URL}}\">restablecer el filtro</a> ?",
"powered-by-kiwix-html": "Desarrollado por&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
@@ -66,39 +40,18 @@
"count-of-matching-books": "{{COUNT}} libro(s)",
"download": "Descargar",
"direct-download-link-text": "Directamente",
"direct-download-alt-text": "Descargar directamente vía HTTP(S)",
"hash-download-link-text": "Suma de verificación SHA-256",
"hash-download-alt-text": "Mostrar la suma de verificación de archivos SHA-256",
"direct-download-alt-text": "descarga directa",
"hash-download-link-text": "hash sha256",
"hash-download-alt-text": "descargar hash",
"magnet-link-text": "Enlace magnético",
"magnet-alt-text": "Descargar mediante enlace Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Descargar a través de BitTorrent",
"library-opds-feed-all-entries": "Biblioteca OPDS Feed - Todas las entradas",
"filter-by-tag": "Filtrar por etiqueta \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Dejar de filtrar por etiqueta \"{{{TAG}}}\"",
"magnet-alt-text": "Descargar link magnético",
"torrent-download-link-text": "Archivo de torrent",
"torrent-download-alt-text": "descargar torrent",
"filter-by-tag": "Filtrar por etiqueta \"{{TAG}}\"",
"stop-filtering-by-tag": "Dejar de filtrar por etiqueta \"{{TAG}}\"",
"library-opds-feed-parameterised": "Feed OPDS de la biblioteca: entradas que coinciden con {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}} {{#TAG}}\nEtiqueta: {{TAG}} {{/TAG}}{{#Q}}\nConsulta: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Bienvenido al servidor Kiwix",
"download-links-heading": "Enlaces de descarga para <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Descargar libro",
"preview-book": "Previsualizar",
"unknown-error": "Error desconocido",
"book-category.gutenberg": "Gutenberg",
"book-category.iFixit": "iFixit",
"book-category.mooc": "MOOC",
"book-category.phet": "Phet",
"book-category.stack_exchange": "Stack Exchange",
"book-category.ted": "Ted",
"book-category.vikidia": "Vikidia",
"book-category.wikibooks": "Wikilibros",
"book-category.wikihow": "wikiHow",
"book-category.wikinews": "Wikinoticias",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikiespecies",
"book-category.wikiversity": "Wikiversidad",
"book-category.wikivoyage": "Wikiviajes",
"book-category.wiktionary": "Wikcionario",
"book-category.other": "Otros",
"text-loading-content": "Cargando contenido"
"preview-book": "Previsualizar"
}

View File

@@ -3,56 +3,28 @@
"authors": [
"MITO",
"Nike",
"Pyscowicz",
"Samoasambia"
"Pyscowicz"
]
},
"name": "suomi",
"suggest-full-text-search": "sisältää '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Kirjaa {{BOOK_NAME}} ei ole olemassa",
"url-not-found": "Pyydettyä URL-osoitetta \"{{url}}\" ei löytynyt tältä palvelimelta.",
"400-page-title": "Virheellinen pyyntö",
"400-page-heading": "Virheellinen pyyntö",
"404-page-title": "Sisältöä ei löytynyt",
"404-page-heading": "Ei löytynyt",
"new-404-page-title": "Sivua ei löytynyt",
"new-404-page-heading": "Hupsista. Artikkelia ei löytynyt.",
"404-img-text": "Ei löytynyt!",
"path-was-not-found": "Pyydettyä polkua ei löytynyt:",
"500-page-title": "Sisäinen palvelinvirhe",
"500-page-heading": "Hupsista. Sivu ei toimi.",
"500-img-text": "Sivu ei toimi",
"external-link-detected": "Ulkoinen linkki havaittu",
"caution-warning": "Huomio!",
"external-link-intro": "Olet poistumassa Kiwixin ZIM-lukijasta siirtyäksesi verkkoon",
"search-results-page-title": "Haku: {{SEARCH_PATTERN}}",
"word-count": "{{COUNT}} sanaa",
"500-page-heading": "Sisäinen palvelinvirhe",
"library-button-text": "Siirry tervetulosivulle",
"home-button-text": "Siirry kirjan '{{{BOOK_TITLE}}}' etusivulle",
"home-button-text": "Siirry kirjan '{{BOOK_TITLE}}' etusivulle",
"random-page-button-text": "Siirry satunnaiselle sivulle",
"searchbox-tooltip": "Hae kirjasta '{{{BOOK_TITLE}}}'",
"searchbox-tooltip": "Hae '{{BOOK_TITLE}}'",
"search": "Hae",
"book-filtering-all-categories": "Kaikki luokat",
"book-filtering-all-languages": "Kaikki kielet",
"count-of-matching-books": "{{COUNT}} kirja(a)",
"download": "Lataa",
"magnet-link-text": "Magnet-linkki",
"magnet-alt-text": "lataa magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "lataa torrent-tiedosto",
"torrent-download-link-text": "Torrent-tiedosto",
"filter-by-tag": "Suodata tunnisteen ”{{TAG}}” mukaan",
"download-links-title": "Lataa kirja",
"preview-book": "Esikatsele",
"unknown-error": "Tuntematon virhe",
"book-category.wikibooks": "Wikikirjasto",
"book-category.wikinews": "Wikiuutiset",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikisitaatit",
"book-category.wikisource": "Wikiaineisto",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiopisto",
"book-category.wikivoyage": "Wikimatkat",
"book-category.wiktionary": "Wikisanakirja",
"book-category.other": "Muu",
"text-loading-content": "Ladataan sisältöä"
"preview-book": "Esikatsele"
}

View File

@@ -1,15 +1,9 @@
{
"@metadata": {
"authors": [
"Adriendelucca",
"Benoit74",
"Derugon",
"Gomoko",
"Goombiis",
"Melimeli",
"Stephane",
"Thibaut120094",
"Urhixidur",
"Verdy p",
"Vikoula5",
"Wladek92"
@@ -32,36 +26,15 @@
"400-page-heading": "Requête non valide",
"404-page-title": "Contenu non trouvé",
"404-page-heading": "Non trouvé",
"new-404-page-title": "Page non trouvée",
"new-404-page-heading": "Oups. Page non trouvée.",
"404-img-text": "Non trouvée !",
"path-was-not-found": "Le chemin demandé na pas été trouvé :",
"404-advice.p1": "Le contenu que vous recherchez peut toujours être disponible, mais il peut être positionné à un endroit différent dans le fichier ZIM.",
"404-advice.p2": "Veuillez :",
"404-advice.p3": "Essayer dutiliser la fonction de recherche pour trouver le contenu que vous souhaitez",
"404-advice.p4": "Rechercher des mots-clés ou des titres liés aux informations que vous recherchez",
"404-advice.p5": "Cette approche devrait vous aider à localiser le contenu souhaité, même si le lien dorigine ne fonctionne pas correctement.",
"500-page-title": "Erreur interne du serveur",
"500-page-heading": "Oups, la page ne fonctionne pas.",
"500-page-text": "Le chemin demandé ne peut pas être fourni correctement :",
"500-img-text": "La page ne fonctionne pas",
"external-link-detected": "Lien externe détecté",
"caution-warning": "Attention!",
"external-link-intro": "Vous êtes sur le point de quitter le lecteur ZIM de Kiwix pour vous connecter à",
"external-link-advice.p1": "Le lien que vous essayez de suivre ne fait pas partie de votre forfait hors ligne et nécessite une connexion Internet.",
"external-link-advice.p2": "Si vous pouvez vous connecter en ligne, vous pouvez essayer douvrir le lien.",
"external-link-advice.p3": "Vous pouvez également revenir au contenu hors ligne de votre ZIM en utilisant le bouton Retour de votre navigateur.",
"500-page-heading": "Erreur interne du serveur",
"500-page-text": "Une erreur de serveur interne s'est produite. Nous en sommes désolés :/",
"fulltext-search-unavailable": "Recherche en texte intégral non disponible",
"no-search-results": "Le moteur de recherche en texte intégral nest pas disponible pour ce contenu.",
"search-results-page-title": "Rechercher : {{SEARCH_PATTERN}}",
"search-results-page-header": "Résultats <b>{{START}}-{{END}}</b> sur<b> {{COUNT}}</b> pour <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Aucun résultat na été trouvé pour <b>« {{{SEARCH_PATTERN}}} »</b>",
"search-result-book-info": "à partir de {{BOOK_TITLE}}",
"word-count": "{{COUNT}} mots",
"library-button-text": "Aller à la page de bienvenue",
"home-button-text": "Aller à la page principale de « {{{BOOK_TITLE}}} »",
"home-button-text": "Aller à la page principale de « {{BOOK_TITLE}} »",
"random-page-button-text": "Aller à une page sélectionnée aléatoirement",
"searchbox-tooltip": "Rechercher « {{{BOOK_TITLE}}} »",
"searchbox-tooltip": "Rechercher « {{BOOK_TITLE}} »",
"confusion-of-tongues": "Deux livres ou plus dans des langues différentes participeraient à la recherche, ce qui pourrait conduire à des résultats confus.",
"welcome-page-overzealous-filter": "Aucun résultat. Souhaitez-vous <a href=\"{{URL}}\">réinitialiser le filtre</a>?",
"powered-by-kiwix-html": "Propulsé par <a href=\"https://kiwix.org/\">Kiwix</a>",
@@ -71,31 +44,20 @@
"count-of-matching-books": "{{COUNT}} livre(s)",
"download": "Télécharger",
"direct-download-link-text": "Direct",
"direct-download-alt-text": "Télécharger directement via HTTP(S)",
"hash-download-link-text": "Hachage SHA-256",
"hash-download-alt-text": "Affiche le hachage SHA-256 du fichier",
"direct-download-alt-text": "téléchargement direct",
"hash-download-link-text": "Hachage sha256",
"hash-download-alt-text": "télécharger le hachage",
"magnet-link-text": "Lien Magnet",
"magnet-alt-text": "Télécharger via le lien Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Télécharger via BitTorrent",
"magnet-alt-text": "télécharger le lien Magnet",
"torrent-download-link-text": "Fichier torrent",
"torrent-download-alt-text": "télécharger le torrent",
"library-opds-feed-all-entries": "Flux OPDS de la bibliothèque Toutes les entrées",
"filter-by-tag": "Filtrer par le tag \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Arrêter de filtrer par le tag \"{{{TAG}}}\"",
"filter-by-tag": "Filtrer par la balise « {{TAG}} »",
"stop-filtering-by-tag": "Arrêter le filtrage par la balise « {{TAG}} »",
"library-opds-feed-parameterised": "Flux OPDS de la bibliothèque Entrées correspondant à {{#LANG}}:\n ▪ Langue: {{LANG}} {{/LANG}}{{#CATEGORY}}\n ▪ Catégorie: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\n ▪ Étiquette: {{TAG}} {{/TAG}}{{#Q}}\n ▪ Requête: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Bienvenue sur le Serveur Kiwix",
"download-links-heading": "Liens de téléchargement pour <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Télécharger le livre",
"preview-book": "Aperçu",
"unknown-error": "Erreur inconnue",
"book-category.wikibooks": "Wikilivres",
"book-category.wikinews": "Wikinews",
"book-category.wikipedia": "Wikipédia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversité",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wiktionnaire",
"book-category.other": "Autre",
"text-loading-content": "Chargement du contenu"
"unknown-error": "Erreur inconnue"
}

View File

@@ -1,67 +0,0 @@
{
"@metadata": {
"authors": [
"Abelidokoo",
"El-hussain14",
"Rofiatmustapha12",
"Smshika",
"Yusuf Sa'adu"
]
},
"name": "Turanci",
"suggest-full-text-search": "dauke da ''{{{SEARCH_TERMS}}}''...",
"no-such-book": "Babu irin wannan littafin: {{BOOK_NAME}}",
"too-many-books": "An nemi littattafai da yawa ({{NB_BOOKS}}) inda iyaka shine {{LIMIT}}",
"no-book-found": "Babu wani littafi da ya dace da ma'aunin zaɓi",
"url-not-found": "Ba a sami URL ɗin da ake nema \"{{url}}\" akan wannan sabar ba.",
"suggest-search": "Yi cikakken bincike na rubutu don <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Kash! An kasa ɗaukar labarin bazuwar :(",
"invalid-raw-data-type": "{{DATATYPE}} ba ingantaccen buƙatun ɗanyen abun ciki bane.",
"invalid-request": "URL ɗin da ake nema \"{{{url}}}\" ba buƙatu mai inganci bane.",
"no-value-for-arg": "Babu ƙima da aka bayar don hujja {{ARGUMENT}}",
"no-query": "Ba a bayar da tambaya ba.",
"raw-entry-not-found": "Ba a iya samun shigarwar {{DATATYPE}} {{ENTRY}}",
"400-page-title": "nema mara inganci",
"400-page-heading": "nema mara inganci",
"404-page-title": "Ba a samo abun ciki ba",
"404-page-heading": "Ba a Samu ba",
"500-page-title": "Kuskuren na Cikin Saba",
"500-page-heading": "Kuskuren na Cikin Saba",
"500-page-text": "An sami kuskuren uwar garken ciki. Munyi nadama akan hakan :/",
"fulltext-search-unavailable": "Babu binciken cikakken rubutu",
"no-search-results": "Babu injin binciken cikakken rubutu don wannan abun ciki.",
"search-results-page-title": "Bincika: {{SEARCH_PATTERN}}",
"search-results-page-header": "Sakamako <b>{{START}}-{{END}}</b> na <b>{{COUNT}}</b> na <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Ba a sami sakamakon <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "daga {{BOOK_TITLE}}",
"word-count": "{{COUNT}} kalmomi",
"library-button-text": "Je zuwa shafin maraba",
"home-button-text": "Jeka babban shafin '{{BOOK_TITLE}}'",
"random-page-button-text": "Je zuwa shafin da aka zaɓa ba da gangan ba",
"searchbox-tooltip": "Bincika '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Littattafai biyu ko fiye a cikin harsuna daban-daban za su shiga cikin bincike, wanda zai iya haifar da sakamako mai ruɗani.",
"welcome-page-overzealous-filter": "Babu sakamako. Kuna so a <a href=\"{{URL}}\">sake saita tace</a>?",
"powered-by-kiwix-html": "<a href=\"https://kiwix.org\">Kiwix</a> ne ke ƙarfafa shi",
"search": "Nema",
"book-filtering-all-categories": "Dukkanin nau'o'in",
"book-filtering-all-languages": "Duka harsuna",
"count-of-matching-books": "{{COUNT}} littafi(s)",
"download": "Sauke",
"direct-download-link-text": "Kai tsaye",
"direct-download-alt-text": "saukewa kai tsaye",
"hash-download-link-text": "Sha256 hash",
"hash-download-alt-text": "sauke hash",
"magnet-link-text": "Magnet link",
"magnet-alt-text": "Magnet ɗin saukewa",
"torrent-download-link-text": "Torrent fayil",
"torrent-download-alt-text": "download torrent",
"library-opds-feed-all-entries": "Ciyarwar OPDS Library - Duk shigarwar",
"filter-by-tag": "Tace da alamar \"{{TAG}}\"",
"stop-filtering-by-tag": "Dakatar da tacewa ta hanyar \"{{TAG}}\"",
"library-opds-feed-parameterised": " OPDS ciyar wa OPDS- entries matching {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nQuery: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Barka da zowa manhajar Kiwix",
"download-links-heading": "Bring out ways through which people can join hands together .{{BOOK_TITLE}}",
"download-links-title": "Sauke littafin",
"preview-book": "Dubawa",
"unknown-error": "Kuskuren da ba a sani ba"
}

View File

@@ -22,30 +22,15 @@
"400-page-heading": "בקשה בלתי־תקינה",
"404-page-title": "התוכן לא נמצא",
"404-page-heading": "לא נמצא",
"new-404-page-title": "הדף לא נמצא",
"new-404-page-heading": "אופס. הדף לא נמצא.",
"404-img-text": "לא נמצא!",
"path-was-not-found": "הנתיב המבוקש לא נמצא:",
"404-advice.p2": "בבקשה:",
"404-advice.p3": "לנסות להשתמש ביכולת החיפוש כדי למצוא את התוכן המבוקש",
"500-page-title": "שגיאת שרת פנימית",
"500-page-heading": "אופס. הדף לא עובד.",
"500-page-text": "אי אפשר למסור את הנתיב המבוקש כראוי:",
"500-img-text": "הדף לא עובד",
"external-link-detected": "התגלה קישור חיצוני",
"caution-warning": "זהירות!",
"external-link-advice.p2": "אם יש לך איך להתחבר לאינטרנט, אפשר לנסות לפתוח את הקישור.",
"500-page-heading": "שגיאת שרת פנימית",
"500-page-text": "אירעה שגיאת שרת פנימית. אנחנו מצטערים על זה :/",
"fulltext-search-unavailable": "חיפוש בטקסט מלא אינו זמין",
"no-search-results": "מנוע החיפוש בטקסט מלא אינו זמין עבור התוכן הזה.",
"search-results-page-title": "חיפוש: {{SEARCH_PATTERN}}",
"search-results-page-header": "תוצאות <b>{{START}} עד {{END}}</b> מתוך <b>{{COUNT}}</b> עבור <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "לא נמצאו תוצאות עבור <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "מתוך {{BOOK_TITLE}}",
"word-count": "{{COUNT}} מילים",
"library-button-text": "מעבר לדף הבית \"ברוך בואך\"",
"home-button-text": "מעבר לדף הראשי של {{{BOOK_TITLE}}}",
"home-button-text": "מעבר לדף הראשי של \"{{BOOK_TITLE}}\"",
"random-page-button-text": "מעבר לדף שנבחר אקראית",
"searchbox-tooltip": "חיפוש אחר {{{BOOK_TITLE}}}",
"searchbox-tooltip": "חיפוש \"{{BOOK_TITLE}}\"",
"confusion-of-tongues": "שני ספרים או יותר בשפות שונות ישתתפו בחיפוש, מה שעלול להוביל לתוצאות מבלבלות.",
"welcome-page-overzealous-filter": "אין תוצאות. האם <a href=\"{{URL}}\">לאפס את המסנן</a>?",
"powered-by-kiwix-html": "מופעל על־ידי&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
@@ -55,33 +40,20 @@
"count-of-matching-books": "{{COUNT}} ספרים",
"download": "הורדה",
"direct-download-link-text": "ישירה",
"direct-download-alt-text": "הורדה ישירה דרך HTTP(S)",
"hash-download-link-text": "סיכום ביקורת Sha256",
"hash-download-alt-text": צגת סיכום ביקורת SHA-256",
"direct-download-alt-text": "הורדה ישירה",
"hash-download-link-text": "גיבוב Sha256",
"hash-download-alt-text": ורדת גיבוב",
"magnet-link-text": "קישור Magnet",
"magnet-alt-text": "הורדה באמצעות קישור magnet",
"torrent-download-link-text": "ביטורנט",
"torrent-download-alt-text": "הורדה באמצעות ביטורנט",
"magnet-alt-text": "הורדת magnet",
"torrent-download-link-text": "קובץ טורנט",
"torrent-download-alt-text": "הורדת טורנט",
"library-opds-feed-all-entries": "הזנת ספריית OPDS - כל הרשומות",
"filter-by-tag": "לסנן לפי התג \"{{{TAG}}}\"",
"stop-filtering-by-tag": "להפסיק סינון לפי התג \"{{{TAG}}}\"",
"filter-by-tag": "סינון לפי התג \"{{TAG}}\"",
"stop-filtering-by-tag": "להפסיק סינון לפי התג \"{{TAG}}\"",
"library-opds-feed-parameterised": "הזנת ספריית OPDS - רשומות שתואמות ל{{#LANG}}\nשפה: {{LANG}} {{/LANG}}{{#CATEGORY}}\nקטגוריה: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nתג: {{TAG}} {{/TAG}}{{#Q}}\nשאילתה: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "ברוך בואך לשרת קיוויקס",
"download-links-heading": "הורדת קישורים עבור <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "הורדת ספר",
"preview-book": "תצוגה מקדימה",
"unknown-error": "שגיאה בלתי־ידועה",
"book-category.gutenberg": "גוטנברג",
"book-category.vikidia": "ויקידיה",
"book-category.wikibooks": "ויקיספר",
"book-category.wikinews": "ויקיחדשות",
"book-category.wikipedia": "ויקיפדיה",
"book-category.wikiquote": "ויקיציטוט",
"book-category.wikisource": "ויקיטקסט (Wikisource)",
"book-category.wikispecies": "ויקימינים",
"book-category.wikiversity": "ויקיברסיטה",
"book-category.wikivoyage": "ויקימסע",
"book-category.wiktionary": "ויקימילון",
"book-category.other": "אחר",
"text-loading-content": "התוכן נטען"
"unknown-error": "שגיאה בלתי־ידועה"
}

View File

@@ -52,15 +52,5 @@
"welcome-to-kiwix-server": "कीविक्स सर्वर में आपका स्वागत है",
"download-links-heading": "<b><i>{{BOOK_TITLE}}</i></b> के लिए डाउनलोड लिंक",
"download-links-title": "पुस्तक डाउनलोड करें",
"preview-book": "पूर्वावलोकन",
"book-category.wikibooks": "विकिपुस्तक",
"book-category.wikinews": "विकिसमाचार",
"book-category.wikipedia": "विकिपीडिया",
"book-category.wikiquote": "विकिसूक्ति",
"book-category.wikisource": "विकिस्रोत",
"book-category.wikispecies": "विकिप्रजाति",
"book-category.wikiversity": "विकिविश्वविद्यालय",
"book-category.wikivoyage": "विकियात्रा",
"book-category.wiktionary": "विकिकोश",
"book-category.other": "अन्य"
"preview-book": "पूर्वावलोकन"
}

View File

@@ -1,39 +0,0 @@
{
"@metadata": {
"authors": [
"Eukarióta",
"Urbalazs"
]
},
"name": "Magyar",
"400-page-title": "Érvénytelen kérés",
"400-page-heading": "Érvénytelen kérés",
"404-page-title": "A tartalom nem található",
"404-page-heading": "Nem található",
"new-404-page-title": "Az oldal nem található",
"new-404-page-heading": "Hoppá! Az oldal nem található.",
"404-img-text": "Nem található!",
"path-was-not-found": "A kért útvonal nem található:",
"404-advice.p1": "A keresett tartalom továbbra is elérhető lehet, de előfordulhat, hogy más helyen található a ZIM-fájlon belül.",
"search": "Keresés",
"download": "Letöltés",
"hash-download-link-text": "SHA-256 ellenőrzőösszeg",
"hash-download-alt-text": "SHA-256 fájl-ellenőrzőösszeg megjelenítése",
"torrent-download-link-text": "BitTorrent",
"welcome-to-kiwix-server": "Üdvözli a Kiwix-kiszolgáló!",
"download-links-heading": "Letöltési hivatkozások ehhez: <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Könyv letöltése",
"preview-book": "Előnézet",
"unknown-error": "Ismeretlen hiba",
"book-category.wikibooks": "Wikikönyvek",
"book-category.wikinews": "Wikihírek",
"book-category.wikipedia": "Wikipédia",
"book-category.wikiquote": "Wikidézet",
"book-category.wikisource": "Wikiforrás",
"book-category.wikispecies": "Wikifajok",
"book-category.wikiversity": "Wikiegyetem",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wikiszótár",
"book-category.other": "Egyéb",
"text-loading-content": "Tartalom betöltése"
}

View File

@@ -13,20 +13,9 @@
"400-page-heading": "Անվավեր հարցում",
"404-page-title": "Սխալ հասցե",
"404-page-heading": "Սխալ հասցե",
"500-img-text": "Էջը չի աշխատում",
"library-button-text": "Գրադարանի էջ",
"home-button-text": "Դեպի '{{BOOK_TITLE}}'֊ի գլխավոր էջը",
"random-page-button-text": "Բացել պատահական էջ",
"searchbox-tooltip": "Որոնել '{{BOOK_TITLE}}'֊ում",
"book-filtering-all-categories": "Բոլոր կատեգորիաներ",
"book-category.wikibooks": "Վիքիգրքեր",
"book-category.wikinews": "Վիքիլուրեր",
"book-category.wikipedia": "Վիքիպեդիա",
"book-category.wikiquote": "Վիքիքաղվածք",
"book-category.wikisource": "Վիքիդարան",
"book-category.wikispecies": "Վիքիցեղեր",
"book-category.wikiversity": "Վիքիլսարան",
"book-category.wikivoyage": "Վիքիճամփորդ",
"book-category.wiktionary": "Վիքիբառարան",
"book-category.other": "Այլ"
"book-filtering-all-categories": "Բոլոր կատեգորիաներ"
}

View File

@@ -13,8 +13,7 @@
"url-not-found": "Le URL reuqestate \"{{url}}\" non ha essite trovate sur iste servitor.",
"suggest-search": "Facer un recerca in texto complete de <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ups! Non poteva eliger un articulo aleatori :(",
"invalid-raw-data-type": "{{DATATYPE}} non es un requesta valide pro contento brute.",
"invalid-request": "Le URL requestate “{{{url}}}” non es un requesta valide.",
"invalid-raw-data-type": "{{DATATYPE}} non es un request valide pro contento crude.",
"no-value-for-arg": "Necun valor fornite pro le argumento {{ARGUMENT}}",
"no-query": "Necun consulta fornite.",
"raw-entry-not-found": "Non pote trovar le entrata {{ENTRY}} del typo {{DATATYPE}}",
@@ -22,36 +21,14 @@
"400-page-heading": "Requesta invalide",
"404-page-title": "Contento non trovate",
"404-page-heading": "Non trovate",
"new-404-page-title": "Pagina non trovate",
"new-404-page-heading": "Pagina non trovate.",
"404-img-text": "Non trovate!",
"path-was-not-found": "Le percurso requestate non ha essite trovate:",
"404-advice.p1": "Le contento que tu cerca pote esser ancora disponibile, ma illo pote esser situate alterubi in le file ZIM.",
"404-advice.p2": "Per favor:",
"404-advice.p3": "Tenta usar le function de recerca pro trovar le contento desirate",
"404-advice.p4": "Cerca parolas-clave o titulos associate al information que tu cerca",
"404-advice.p5": "Iste approche deberea adjutar te a localisar le contento desirate, mesmo si le ligamine original non functiona correctemente.",
"500-page-title": "Error interne del servitor",
"500-page-heading": "Le pagina non functiona.",
"500-page-text": "Le percurso requestate non pote esser livrate correctemente:",
"500-img-text": "Le pagina non functiona",
"external-link-detected": "Ligamine externe detegite",
"caution-warning": "Attention!",
"external-link-intro": "Tu es sur le puncto de quitar le lector ZIM de Kiwix pro connecter te al rete e visitar",
"external-link-advice.p1": "Le ligamine al qual tu tenta acceder non face parte de nostre pacchetto de uso foras de linea e require un connexion a internet.",
"external-link-advice.p2": "Si tu pote connecter te al rete, tu pote tentar aperir le ligamine.",
"external-link-advice.p3": "Si non, tu pote retornar al contento de uso foras de linea de tu ZIM usante le button Retro del navigator.",
"500-page-heading": "Error interne del servitor",
"fulltext-search-unavailable": "Le recerca in texto complete es indisponibile",
"no-search-results": "Le motor de recerca in texto complete non es disponibile pro iste contento.",
"search-results-page-title": "Cercar: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultatos <b>{{START}}-{{END}}</b> de <b>{{COUNT}}</b> pro <b>“{{{SEARCH_PATTERN}}}”</b>",
"empty-search-results-page-header": "Necun resultato ha essite trovate pro <b>“{{{SEARCH_PATTERN}}}”</b>",
"search-result-book-info": "de {{BOOK_TITLE}}",
"word-count": "{{COUNT}} parolas",
"library-button-text": "Ir al pagina de benvenita",
"home-button-text": "Ir al pagina principal de {{{BOOK_TITLE}}}",
"home-button-text": "Ir al pagina principal de ''{{BOOK_TITLE}}",
"random-page-button-text": "Ir a un pagina seligite aleatorimente",
"searchbox-tooltip": "Cercar in {{{BOOK_TITLE}}}",
"searchbox-tooltip": "Cercar '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Duo o plus libros in differente linguas participarea in le recerca, lo que pote menar a resultatos confuse.",
"welcome-page-overzealous-filter": "Nulle resultato. Vole tu <a href=\"{{URL}}\">reinitialisar le filtro</a>?",
"powered-by-kiwix-html": "Actionate per&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
@@ -61,31 +38,19 @@
"count-of-matching-books": "{{COUNT}} libro(s)",
"download": "Discargar",
"direct-download-link-text": "Directe",
"direct-download-alt-text": "Discargamento directe per HTTP(S)",
"hash-download-link-text": "Summa de controlo SHA-256",
"hash-download-alt-text": "Monstrar le summa de controlo SHA-256 del file",
"direct-download-alt-text": "discargamento directe",
"hash-download-link-text": "Hash SHA256",
"hash-download-alt-text": "hash del discargamento",
"magnet-link-text": "Ligamine Magnet",
"magnet-alt-text": "Discargar con ligamine Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Discargar per medio de BitTorrent",
"magnet-alt-text": "ligamine \"magnet\" de discargamento",
"torrent-download-link-text": "File Torrent",
"torrent-download-alt-text": "discargar Torrent",
"library-opds-feed-all-entries": "Fluxo OPDS del bibliotheca Tote le entratas",
"filter-by-tag": "Filtrar per etiquetta “{{{TAG}}}”",
"stop-filtering-by-tag": "Non plus filtrar per etiquetta “{{{TAG}}}”",
"filter-by-tag": "Filtrar per etiquetta \"{{TAG}}\"",
"stop-filtering-by-tag": "Non plus filtrar per etiquetta \"{{TAG}}\"",
"library-opds-feed-parameterised": "Fluxo OPDS del bibliotheca Entratas correspondente a {{#LANG}}\nLingua {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategoria: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nEtiquetta: {{TAG}} {{/TAG}}{{#Q}}\nConsulta: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Benvenite al servitor Kiwix",
"download-links-heading": "Discargar ligamines pro <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Discargar libro",
"preview-book": "Previsualisation",
"unknown-error": "Error incognite",
"book-category.wikibooks": "Wikilibros",
"book-category.wikinews": "Wikinovas",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversitate",
"book-category.wikivoyage": "Wikiviage",
"book-category.wiktionary": "Wiktionario",
"book-category.other": "Altere",
"text-loading-content": "Carga contento"
"preview-book": "Previsualisation"
}

View File

@@ -1,74 +0,0 @@
{
"@metadata": {
"authors": [
"Akmaie Ajam"
]
},
"name": "Bahasa Inggris",
"suggest-full-text-search": "mengandung '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Tidak ada buku seperti ini: {{BOOK_NAME}}",
"too-many-books": "Terlalu banyak buku yang diminta ({{NB_BOOKS}}) dimana batasnya adalah {{LIMIT}}",
"no-book-found": "Tidak ada buku yang sesuai kriteria yang dipilih",
"url-not-found": "URL yang diminta \"{{url}}\" tidak ditemukan di server ini.",
"suggest-search": "Lakukan pencarian teks lengkap untuk <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Waduh! Gagal memilih artikel acak :(",
"invalid-raw-data-type": "{{DATATYPE}} bukan permintaan yang sah untuk konten mentah.",
"invalid-request": "URL yang diminta \"{{{url}}}\" bukan permintaan yang sah.",
"no-value-for-arg": "Tidak ada nilai yang diberikan untuk argumen {{ARGUMENT}}",
"no-query": "Tidak ada kueri yang diberikan.",
"raw-entry-not-found": "Tidak dapat menemukan entri {{DATATYPE}} {{ENTRY}}",
"400-page-title": "Permintaan tidak sah",
"400-page-heading": "Permintaan tidak sah",
"404-page-title": "Konten tidak ditemukan",
"404-page-heading": "Tidak Ditemukan",
"500-page-title": "Kesalahan Server Internal",
"500-page-heading": "Kesalahan Server Internal",
"500-page-text": "Terjadi kesalahan server internal. Kami mohon maaf atas hal ini :/",
"fulltext-search-unavailable": "Pencarian teks lengkap tidak tersedia",
"no-search-results": "Mesin pencari teks lengkap tidak tersedia untuk konten ini.",
"search-results-page-title": "Pencarian: {{SEARCH_PATTERN}}",
"search-results-page-header": "Hasil <b>{{START}}-{{END}}</b> dari <b>{{COUNT}}</b> untuk <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Tidak ada hasil yang ditemukan untuk <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "dari {{BOOK_TITLE}}",
"word-count": "{{COUNT}} kata",
"library-button-text": "Pergi ke halaman selamat datang",
"home-button-text": "Buka halaman utama '{{BOOK_TITLE}}'",
"random-page-button-text": "Buka halaman yang dipilih secara acak",
"searchbox-tooltip": "Cari '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Dua buku atau lebih dalam bahasa berbeda dapat muncul dalam hasil pencarian, yang dapat memicu hasil yang membingungkan.",
"welcome-page-overzealous-filter": "Tidak ada hasil. Apakah Anda ingin <a href=\"{{URL}}\">mengatur ulang filter</a>?",
"powered-by-kiwix-html": "Didukung oleh <a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Cari",
"book-filtering-all-categories": "Semua kategori",
"book-filtering-all-languages": "Semua bahasa",
"count-of-matching-books": "{{COUNT}} buku",
"download": "Unduh",
"direct-download-link-text": "Langsung",
"direct-download-alt-text": "Unduh langsung melalui HTTP(S)",
"hash-download-link-text": "Checksum SHA-256",
"hash-download-alt-text": "Tampilkan checksum berkas SHA-256",
"magnet-link-text": "Tautan magnet",
"magnet-alt-text": "Unduh melalui tautan Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Unduh melalui BitTorrent",
"library-opds-feed-all-entries": "Umpan OPDS Perpustakaan - Semua entri",
"filter-by-tag": "Saring berdasarkan tag \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Berhenti penyaringan berdasarkan tag \"{{{TAG}}}\"",
"library-opds-feed-parameterised": "Umpan OPDS Perpustakaan - entri yang cocok dengan {{#LANG}}\nBahasa: {{LANG}} {{/LANG}}{{#CATEGORY}}\nKategori: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nKueri: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Selamat datang di Server Kiwix",
"download-links-heading": "Tautan unduhan untuk <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Unduh buku",
"preview-book": "Pratayang",
"unknown-error": "Kesalahan yang tidak diketahui",
"book-category.wikibooks": "Wikibuku",
"book-category.wikinews": "Wikiberita",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikikutip",
"book-category.wikisource": "Wikisumber",
"book-category.wikispecies": "Wikispesies",
"book-category.wikiversity": "Wikiversitas",
"book-category.wikivoyage": "Wikiwisata",
"book-category.wiktionary": "Wikikamus",
"book-category.other": "Lainnya",
"text-loading-content": "Memuat Konten"
}

View File

@@ -1,65 +0,0 @@
{
"@metadata": {
"authors": [
"Accuratecy051",
"Ngostary2k",
"Oby Ezeilo"
]
},
"name": "Bekee",
"suggest-full-text-search": "nwere {{{SEARCH_TERMS}}}'",
"no-such-book": "Enweghị akwụkwọ dị otú a: {{BOOK_NAME}}",
"too-many-books": "Arịrịọ ọtụtụ akwụkwọ ({{NB_BOOKS}}) ebe oke bụ {{LIMIT}}",
"no-book-found": "Ọ nweghị akwụkwọ dabara na nhọpụta nhọrọ",
"url-not-found": "Ahụghị URL a rịọrọ \"{{url}}\" na nkesa a.",
"suggest-search": "Mee ọchụchọ ederede zuru oke maka <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ee! Ịhọrọ akụkọ enweghị usoro :(",
"invalid-raw-data-type": "{{DATATYPE}} abụghị arịrịọ ziri ezi maka ọdịnaya raw.",
"invalid-request": "Arịrịọ gbasara URL \"{{{url}}}\" e zighi ezi.",
"no-value-for-arg": "Ọ nweghị uru enyere maka arụmụka {{ARGUMENT}}",
"no-query": "Ọnweghị ajụjụ enyere.",
"raw-entry-not-found": "Enweghị ike ịchọta ntinye {{DATATYPE}} {{ENTRY}}",
"400-page-title": "Arịrịọ na-ezighi ezi",
"400-page-heading": "Arịrịọ na-ezighi ezi",
"404-page-title": "Ahụghị ọdịnaya",
"404-page-heading": "Ahụghị",
"500-page-title": "Mperi Sava Ime",
"500-page-heading": "Mperi Sava Ime",
"500-page-text": "Enwere mperi ihe nkesa dị n'ime. Ọ dị anyị nwute na nke ahụ :/",
"fulltext-search-unavailable": "Ọchịchọ ederede zuru ezu adịghị",
"no-search-results": "Igwe nchọta ederede zuru oke adịghị maka ọdịnaya a.",
"search-results-page-title": "Chọọ: {{SEARCH_PATTERN}}",
"search-results-page-header": "Rịzọlt ga <b>{{START}}-{{END}}</b> nke <b>{{COUNT}}</b> maka <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Ọnweghị rịzọlt ahụrụ maka <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "sitere na {{BOOK_TITLE}}",
"word-count": "Okwu {{COUNT}}",
"library-button-text": "Gaa na ibe nnabata",
"home-button-text": "Gaa na isi ibe nke '{{BOOK_TITLE}}'",
"random-page-button-text": "Gaa na ibe ahọpụtara enweghị usoro",
"searchbox-tooltip": "Chọọ '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Akwụkwọ abụọ ma ọ bụ karịa n'asụsụ dị iche iche ga-esonye na nchọ, nke nwere ike ibute nsonaazụ mgbagwoju anya.",
"welcome-page-overzealous-filter": "Enweghị nsonaazụ. Ọ ga-amasị gị <a href=\"{{URL}}\">ịtọgharịa nzacha</a> ?",
"powered-by-kiwix-html": "<a href=\"https://kiwix.org\">Kiwix</a> kwadoro ya",
"search": "Chọọ",
"book-filtering-all-categories": "Nkeji niile",
"book-filtering-all-languages": "Asụsụ niile",
"count-of-matching-books": "akwụkwọ {{COUNT}}",
"download": "Budata",
"direct-download-link-text": "Gosi",
"direct-download-alt-text": "nbudata ozugbo",
"hash-download-link-text": "Sha256 hash",
"hash-download-alt-text": "budata hash",
"magnet-link-text": "Njikọ magnet",
"magnet-alt-text": "ibudata magnet",
"torrent-download-link-text": " faịlụ nke Torrent",
"torrent-download-alt-text": "Budata torrent",
"library-opds-feed-all-entries": "Ọbá akwụkwọ OPDS Feed - Ihe niile",
"filter-by-tag": "Wepụta site na mkpado \"{{TAG}}\"",
"stop-filtering-by-tag": "Kwụsị nzacha site na mkpado \"{{TAG}}\"",
"library-opds-feed-parameterised": "Ọbá akwụkwọ OPDS nri - ndenye dabara na {{#LANG}}\nAsụsụ: {{LANG}} {{/LANG}}{{#CATEGORY}}\n Kategori: {{CATEGORY}} {{/CATEGORY}} {{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\n Ajụjụ: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Nabata na Kiwix Server",
"download-links-heading": "Budata njikọ maka <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Budata akwụkwọ",
"preview-book": "Ziwe nkirimaàtụ̀",
"unknown-error": "amaghị m njehie"
}

View File

@@ -3,10 +3,8 @@
"authors": [
"Albano",
"Beta16",
"Clorofolle",
"Luca.favorido",
"McDutchie",
"Wheelygay"
"McDutchie"
]
},
"name": "italiano",
@@ -23,47 +21,18 @@
"400-page-heading": "Richiesta non valida",
"404-page-title": "Contenuto non trovato",
"404-page-heading": "Non trovato",
"new-404-page-title": "Pagina non trovata",
"new-404-page-heading": "Oops. Pagina non trovata.",
"404-img-text": "Non trovato!",
"500-page-title": "Errore interno del server",
"500-page-heading": "Oops. La pagina non funziona.",
"500-page-text": "Il percorso richiesto non può essere fornito correttamente:",
"caution-warning": "Attenzione!",
"search-results-page-title": "Cerca: {{SEARCH_PATTERN}}",
"search-results-page-header": "Risultati <b>{{START}}-{{END}}</b> di <b>{{COUNT}}</b> per <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Non è stato trovato alcun risultato per <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "da {{BOOK_TITLE}}",
"word-count": "{{COUNT}} parole",
"500-page-heading": "Errore interno del server",
"500-page-text": "Si è verificato un errore interno del server. Ci dispiace :/",
"library-button-text": "Vai alla pagina di benvenuto",
"home-button-text": "Vai alla pagina principale di '{{{BOOK_TITLE}}}'",
"home-button-text": "Vai alla pagina principale di '{{BOOK_TITLE}}'",
"random-page-button-text": "Vai a una pagina selezionata casualmente",
"searchbox-tooltip": "Cerca '{{{BOOK_TITLE}}}'",
"welcome-page-overzealous-filter": "Nessun risultato. Vuoi <a href=\"{{URL}}\">reimpostare il filtro</a>?",
"search": "Cerca",
"searchbox-tooltip": "Cerca '{{BOOK_TITLE}}'",
"book-filtering-all-categories": "Tutte le categorie",
"book-filtering-all-languages": "Tutte le lingue",
"count-of-matching-books": "{{COUNT}} libro/i",
"download": "Scarica",
"direct-download-link-text": "Download diretto",
"direct-download-alt-text": "Scarica direttamente tramite HTTP(S)",
"magnet-alt-text": "Scarica tramite collegamento Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Scarica tramite BitTorrent",
"welcome-to-kiwix-server": "Benvenuti al server Kiwix",
"download-links-heading": "Link per scaricare <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Scarica libro",
"preview-book": "Anteprima",
"unknown-error": "Errore sconosciuto",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinotizie",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversità",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wikizionario",
"book-category.other": "Altro",
"text-loading-content": "Caricamento contenuto"
"unknown-error": "Errore sconosciuto"
}

View File

@@ -1,80 +1,19 @@
{
"@metadata": {
"authors": [
"YeBoy371",
"Ykhwong"
]
},
"name": "한국어",
"suggest-full-text-search": "'{{{SEARCH_TERMS}}}' 포함...",
"no-such-book": "해당 책이 없습니다: {{BOOK_NAME}}",
"too-many-books": "요청된 책이 너무 많습니다. ({{NB_BOOKS}}) 한도는 {{LIMIT}}입니다.",
"no-book-found": "선택 기준에 해당하는 책이 없습니다",
"url-not-found": "\"{{url}}\" 요청 URL은 이 서버에서 찾을 수 없습니다.",
"suggest-search": "<a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>에 대한 전문 검색을 수행해 보세요",
"random-article-failure": "이런! 임의 문서를 선택하지 못했습니다 :(",
"invalid-raw-data-type": "{{DATATYPE}} 값은 원시 콘텐츠에 대한 유효한 요청이 아닙니다.",
"invalid-request": "\"{{{url}}}\" 요청 URL은 유효한 요청이 아닙니다.",
"no-value-for-arg": "{{ARGUMENT}} 인수에 지정된 값이 없습니다",
"no-query": "지정된 쿼리가 없습니다.",
"raw-entry-not-found": "{{DATATYPE}} 항목인 {{ENTRY}} 항목을 찾을 수 없습니다",
"400-page-title": "잘못된 요청",
"400-page-heading": "잘못된 요청",
"404-page-title": "내용이 없습니다",
"404-page-heading": "찾을 수 없음",
"new-404-page-title": "페이지를 찾을 수 없습니다",
"new-404-page-heading": "이런. 페이지를 찾을 수 없습니다.",
"404-img-text": "찾을 수 없습니다!",
"path-was-not-found": "요청한 경로를 찾을 수 없습니다:",
"404-advice.p3": "원하는 콘텐츠를 찾으려면 검색 기능을 사용해 보세요",
"500-page-title": "내부 서버 오류",
"500-page-heading": "죄송합니다. 문서가 동작하지 않습니다.",
"500-page-text": "요청된 경로를 제대로 전달할 수 없습니다:",
"500-img-text": "문서가 동작하지 않습니다",
"external-link-detected": "외부 링크가 발견되었습니다",
"caution-warning": "경고!",
"500-page-heading": "내부 서버 오류",
"fulltext-search-unavailable": "전문 검색을 사용할 수 없습니다",
"no-search-results": "이 콘텐츠에는 전문 검색 엔진을 사용할 수 없습니다.",
"search-results-page-title": "검색: {{SEARCH_PATTERN}}",
"search-results-page-header": "<b>\"{{{SEARCH_PATTERN}}}\"</b>에 대한 <b>{{COUNT}}</b>개 중 <b>{{START}}-{{END}}</b> 결과",
"empty-search-results-page-header": "<b>\"{{{SEARCH_PATTERN}}}\"</b>의 결과가 없습니다",
"search-result-book-info": "{{BOOK_TITLE}}에서",
"word-count": "단어 {{COUNT}}개",
"home-button-text": "'{{{BOOK_TITLE}}}'의 메인 페이지로 이동",
"random-page-button-text": "무작위로 선택된 문서로 이동",
"searchbox-tooltip": "'{{{BOOK_TITLE}}}' 검색",
"welcome-page-overzealous-filter": "결과가 없습니다. <a href=\"{{URL}}\">필터를 재설정</a>하시겠습니까?",
"powered-by-kiwix-html": "<a href=\"https://kiwix.org\">Kiwix</a>에서 제공",
"search": "검색",
"book-filtering-all-categories": "모든 분류",
"book-filtering-all-languages": "모든 언어",
"count-of-matching-books": "책 {{COUNT}}권",
"download": "다운로드",
"direct-download-link-text": "직접",
"direct-download-alt-text": "HTTP(S)를 통해 직접 다운로드",
"hash-download-link-text": "SHA-256 체크섬",
"hash-download-alt-text": "SHA-256 파일 체크섬 표시",
"magnet-link-text": "마그넷 링크",
"magnet-alt-text": "마그넷 링크를 통해 다운로드",
"torrent-download-link-text": "비트토렌트",
"torrent-download-alt-text": "비트토렌트를 통해 다운로드",
"library-opds-feed-all-entries": "라이브러리 OPDS 피드 - 모든 항목",
"filter-by-tag": "\"{{{TAG}}}\" 태그로 필터링",
"stop-filtering-by-tag": "\"{{{TAG}}}\" 태그로 필터링 중지",
"library-opds-feed-parameterised": "라이브러리 OPDS 피드 - {{#LANG}} 일치 항목\n언어: {{LANG}} {{/LANG}}{{#CATEGORY}}\n분류: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\n태그: {{TAG}} {{/TAG}}{{#Q}}\n쿼리: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Kiwix Server에 오신 것을 환영합니다",
"download-links-heading": "<b><i>{{BOOK_TITLE}}</i></b>의 링크 다운로드",
"download-links-title": "책 다운로드",
"preview-book": "미리 보기",
"unknown-error": "알 수 없는 오류",
"book-category.wikibooks": "위키책",
"book-category.wikinews": "위키뉴스",
"book-category.wikipedia": "위키백과",
"book-category.wikiquote": "위키인용집",
"book-category.wikisource": "위키문헌",
"book-category.wikispecies": "위키생물종",
"book-category.wikiversity": "위키배움터",
"book-category.wikivoyage": "위키여행",
"book-category.wiktionary": "위키낱말사전",
"book-category.other": "기타"
"preview-book": "미리 보기"
}

View File

@@ -7,32 +7,17 @@
]
},
"name": "Lëtzebuergesch",
"suggest-full-text-search": "enthält '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Buch net fonnt: {{BOOK_NAME}}",
"too-many-books": "Ze vill Bicher ugefrot ({{NB_BOOKS}}), d'Limitt läit bei {{LIMIT}}",
"url-not-found": "Déi ugefroten URL „{{url}}“ gouf op dësem Server net fonnt.",
"suggest-search": "Maacht eng Volltext-Sich fir <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ups! Et konnt keen zoufällegen Artikel ausgewielt ginn :(",
"404-page-title": "Inhalt net fonnt",
"404-page-heading": "Net fonnt",
"new-404-page-title": "Säit net fonnt",
"new-404-page-heading": "Ups. Säit net fonnt.",
"404-img-text": "Net fonnt!",
"500-page-title": "Interne Feeler um Server",
"500-page-heading": "Ups. D'Säit funktionéiert net.",
"500-page-heading": "Interne Feeler um Server",
"500-page-text": "Et ass en interne Serverfeeler opgetrueden. Mir entschëllegen eis dofir :/",
"500-img-text": "Säit funktionéiert net",
"external-link-detected": "Externe Link entdeckt",
"caution-warning": "Opgepasst!",
"fulltext-search-unavailable": "Volltext-Sich net verfügbar",
"search-results-page-title": "Sichen: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultater <b>{{START}}-{{END}}</b> vu(n) <b>{{COUNT}}</b> fir <b>„{{{SEARCH_PATTERN}}}“</b>",
"empty-search-results-page-header": "Keng Resultater fonnt fir <b>„{{{SEARCH_PATTERN}}}“</b>",
"search-result-book-info": "aus {{BOOK_TITLE}}",
"word-count": "{{COUNT}} Wierder",
"home-button-text": "Op d'Haaptsäit vun '{{{BOOK_TITLE}}}' goen",
"home-button-text": "Gitt op d'Haaptsäit vun '{{BOOK_TITLE}}'",
"random-page-button-text": "Gitt op eng zoufälleg gewielte Säit",
"searchbox-tooltip": "No '{{{BOOK_TITLE}}}' sichen",
"searchbox-tooltip": "No '{{BOOK_TITLE}}' sichen",
"welcome-page-overzealous-filter": "Kee Resultat. Wëllt Dir <a href=\"{{URL}}\">de Filter zrécksetzen</a>?",
"search": "Sichen",
"book-filtering-all-categories": "All Kategorien",
@@ -40,18 +25,6 @@
"count-of-matching-books": "{{COUNT}} Buch/Bicher",
"download": "Eroflueden",
"direct-download-link-text": "Direkt",
"torrent-download-link-text": "BitTorrent",
"download-links-title": "Buch eroflueden",
"unknown-error": "Onbekannte Feeler",
"book-category.stack_exchange": "Stack Exchange",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinews",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversity",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wiktionnaire",
"book-category.other": "Anerer"
"unknown-error": "Onbekannte Feeler"
}

View File

@@ -22,36 +22,15 @@
"400-page-heading": "Неважечко барање",
"404-page-title": "Содржината не е најдена",
"404-page-heading": "Не е најдено",
"new-404-page-title": "Страницата не е пронајдена",
"new-404-page-heading": "Ах! Страницата не е пронајдена.",
"404-img-text": "Не е најдено!",
"path-was-not-found": "Не ја најдов побараната патека:",
"404-advice.p1": "Содржината што ја барате може сепак да е достапна, но може да се наоѓа на друго место во рамките на ZIM-податотеката.",
"404-advice.p2": "Ве молиме:",
"404-advice.p3": "Пробајте да ја употребите функцијата за пребарување за да ја најдете содржината што ви треба",
"404-advice.p4": "Барајте клучни зборови или наслови поврзани со информациите што ви требаат",
"404-advice.p5": "Овој приод треба да ви помогне да ја најдете саканата содржина, дури и ако изворната врска не работи правилно.",
"500-page-title": "Внатрешна грешка во опслужувачот",
"500-page-heading": "Страницата не работи.",
"500-page-text": "Побараната патека не може правилно да се достави:",
"500-img-text": "Страницата не работи.",
"external-link-detected": "Најдена е надворешна врска",
"caution-warning": "Внимание!",
"external-link-intro": "На пат сте да го напуштите ZIM-читачот на Кивикс за да појдете на",
"external-link-advice.p1": "Врската што пробувате да ја отворите не е дел од вашиот вонмрежен пакет и бара семрежна врска.",
"external-link-advice.p2": "Ако можете да се поврзете со семрежнето, пробајте да ја отворите врската.",
"external-link-advice.p3": "Во спротивно можете повторно да пробате да ја отворите вонмрежната содржина на вашиот ZIM стискајќи на копчето за враќање назад на вашиот прелистувач.",
"500-page-heading": "Внатрешна грешка во опслужувачот",
"500-page-text": "Настана внатрешна грешка во опслужувачот. Жал ни е :/",
"fulltext-search-unavailable": "Целотекстното пребарување е недостапно",
"no-search-results": "Погонот за целотекстно пребарување не е достапен за оваа содржина.",
"search-results-page-title": "Пребарување: {{SEARCH_PATTERN}}",
"search-results-page-header": "Исход <b>{{START}}-{{END}}</b> од <b>{{COUNT}}</b> за <b>„{{{SEARCH_PATTERN}}}“</b>",
"empty-search-results-page-header": "Не најдов ништо за <b>„{{{SEARCH_PATTERN}}}“</b>",
"search-result-book-info": "од {{BOOK_TITLE}}",
"word-count": "{{COUNT}} зборови",
"library-button-text": "Оди на воведната страница",
"home-button-text": "Оди на главната страница на „{{{BOOK_TITLE}}}“",
"home-button-text": "Оди на главната страница на „{{BOOK_TITLE}}“",
"random-page-button-text": "Оди на случајно избрана страница",
"searchbox-tooltip": "Пребарај по „{{{BOOK_TITLE}}}“",
"searchbox-tooltip": "Пребарај го „{{BOOK_TITLE}}“",
"confusion-of-tongues": "Во пребарувањето ќе учествуваат две или повеќе книги на различни јазици, што може да довете до збунувачки исход.",
"welcome-page-overzealous-filter": "Нема исход. Дали би сакале да го <a href=\"{{URL}}\">поништите филтерот</a>?",
"powered-by-kiwix-html": "Овозможено од&nbsp;<a href=\"https://kiwix.org\">Кивикс</a>",
@@ -61,39 +40,20 @@
"count-of-matching-books": "{{COUNT}} книги",
"download": "Преземи",
"direct-download-link-text": "Непосредно",
"direct-download-alt-text": "Непосредно преземање преку HTTP(S)",
"hash-download-link-text": "Контролен збир Sha256",
"hash-download-alt-text": "Прикажи контролен збир SHA-256 на податотеката",
"direct-download-alt-text": "непосредно преземање",
"hash-download-link-text": "Sha256-тараба",
"hash-download-alt-text": "преземи тараба",
"magnet-link-text": "Магнетна врска",
"magnet-alt-text": "Преземи преку Magnet-врска",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Преземи преку BitTorrent",
"magnet-alt-text": "преземи магнет",
"torrent-download-link-text": "Торентна податотека",
"torrent-download-alt-text": "преземи торент",
"library-opds-feed-all-entries": "Библиотечен тековник на OPDS — Сите ставки",
"filter-by-tag": "Филтрирај по ознаката „{{{TAG}}}“",
"stop-filtering-by-tag": "Запри филтрирање по ознаката „{{{TAG}}}“",
"filter-by-tag": "Филтрирај по ознаката „{{TAG}}“",
"stop-filtering-by-tag": "Запри филтрирање по ознаката „{{TAG}}“",
"library-opds-feed-parameterised": "Библиотечен тековник на OPDS — ставки што одговараат на {{#LANG}}\nЈазик: {{LANG}} {{/LANG}}{{#CATEGORY}}\nКатегорија: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nОзнака: {{TAG}} {{/TAG}}{{#Q}}\nБарање: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Добре дојдовте на Опслужувачот на Кивикс",
"download-links-heading": "Врски за преземање на <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Преземи книга",
"preview-book": "Преглед",
"unknown-error": "Непозната грешка",
"book-category.gutenberg": "Гутенберг",
"book-category.iFixit": "iFixit",
"book-category.mooc": "MOOC",
"book-category.phet": "Phet",
"book-category.stack_exchange": "Stack Exchange",
"book-category.ted": "Ted",
"book-category.vikidia": "Викидија",
"book-category.wikibooks": "Викикниги",
"book-category.wikihow": "wikiHow",
"book-category.wikinews": "Викивести",
"book-category.wikipedia": "Википедија",
"book-category.wikiquote": "Викицитат",
"book-category.wikisource": "Викиизвор",
"book-category.wikispecies": "Викивидови",
"book-category.wikiversity": "Викиуниверзитет",
"book-category.wikivoyage": "Википатување",
"book-category.wiktionary": "Викиречник",
"book-category.other": "друго",
"text-loading-content": "Ја вчитувам содржината"
"unknown-error": "Непозната грешка"
}

View File

@@ -1,74 +0,0 @@
{
"@metadata": {
"authors": [
"TorgeirS"
]
},
"name": "Engelsk",
"suggest-full-text-search": "inneholder '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Finner ingen slik bok: {{BOOK_NAME}}",
"too-many-books": "Det er forespurt for mange bøker {{NB_BOOKS}} der grensen er {{LIMIT}}",
"no-book-found": "Ingen bøker med treff på utvalgkriteriene",
"url-not-found": "Den forespurte webadressen «{{url}}» ble ikke funnet på denne serveren.",
"suggest-search": "Gjør et fulltekstsøk etter <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Oops! Klarte ikke å hente en tilfeldig artikkel :(",
"invalid-raw-data-type": "{{DATATYPE}} er ikke en gyldig forespørsel etter uformatert innhold.",
"invalid-request": "Den forespurte URL-en \"{{{url}}}\" er ikke en gyldig forespørsel.",
"no-value-for-arg": "Ingen verdi angitt for argumentet {{ARGUMENT}}",
"no-query": "Ingen søkeord oppgitt.",
"raw-entry-not-found": "Finner ikke {{DATATYPE}}-oppføringen {{ENTRY}}",
"400-page-title": "Ugyldig forespørsel",
"400-page-heading": "Ugyldig forespørsel",
"404-page-title": "Finner ikke innhold",
"404-page-heading": "Ikke funnet",
"500-page-title": "Intern serverfeil",
"500-page-heading": "Intern serverfeil",
"500-page-text": "Det oppstod en intern serverfeil. Vi beklager det :/",
"fulltext-search-unavailable": "Fulltekstsøk utilgjengelig",
"no-search-results": "Søkemotoren for fulltekstsøk er ikke tilgjengelig for dette innholdet.",
"search-results-page-title": "Søk: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultater <b>{{START}}-{{END}}</b> av <b>{{COUNT}}</b> for <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Ingen resultater ble funnet for <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "fra {{BOOK_TITLE}}",
"word-count": "{{COUNT}} ord",
"library-button-text": "Gå til velkomstsiden",
"home-button-text": "Gå til hovedsiden for '{{BOOK_TITLE}}'",
"random-page-button-text": "Gå til en tilfeldig valgt side",
"searchbox-tooltip": "Søk etter '{{BOOK_TITLE}}'",
"confusion-of-tongues": "To eller flere bøker på forskjellige språk vil inngå i søket, noe som kan gi forvirrende resultater.",
"welcome-page-overzealous-filter": "Ingen resultater. Vil du <a href=\"{{URL}}\">tilbakestille filteret</a>?",
"powered-by-kiwix-html": "Drevet av <a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Søk",
"book-filtering-all-categories": "Alle kategorier",
"book-filtering-all-languages": "Alle språk",
"count-of-matching-books": "{{COUNT}} bok/bøker",
"download": "Last ned",
"direct-download-link-text": "Direkte",
"direct-download-alt-text": "Last ned direkte via HTTP(S)",
"hash-download-link-text": "SHA-256 kontrollsum",
"hash-download-alt-text": "Vis filens SHA-256 kontrollsum",
"magnet-link-text": "Magnetlenke",
"magnet-alt-text": "Last ned via Magnetlenke",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Last ned via BitTorrent",
"library-opds-feed-all-entries": "Biblioteks OPDS-feed - Alle oppføringer",
"filter-by-tag": "Filtrer etter taggen \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Slutt å filtrere etter taggen \"{{{TAG}}}\"",
"library-opds-feed-parameterised": "Biblioteks OPDS-feed - oppføringer som samsvarer med {{#LANG}}\nSpråk: {{LANG}} {{/LANG}}{{#CATEGORY}}\nKategori: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTagg: {{TAG}} {{/TAG}}{{#Q}}\nSpørring: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Velkommen til Kiwix Server",
"download-links-heading": "Nedlastingslenker for <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Last ned bok",
"preview-book": "Forhåndsvisning",
"unknown-error": "Ukjent feil",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinews",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversity",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wiktionary",
"book-category.other": "Annet",
"text-loading-content": "Laster innhold"
}

View File

@@ -1,10 +1,8 @@
{
"@metadata": {
"authors": [
"ABPMAB",
"Kelson",
"McDutchie",
"Siebrand",
"Vistaus"
]
},
@@ -17,7 +15,6 @@
"suggest-search": "In volledige tekst zoeen naar <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Oeps! Kan geen willekeurig artikel kiezen :(",
"invalid-raw-data-type": "{{DATATYPE}} is geen geldig verzoek voor onbewerkte inhoud.",
"invalid-request": "De gevraagde URL “{{{url}}}” is onjuist.",
"no-value-for-arg": "Er is geen waarde opgegeven bij {{ARGUMENT}}",
"no-query": "Er is geen zoekterm opgegeven.",
"raw-entry-not-found": "Kan het {{DATATYPE}}-item {{ENTRY}} niet vinden",
@@ -25,36 +22,14 @@
"400-page-heading": "Ongeldig verzoek",
"404-page-title": "Inhoud niet gevonden",
"404-page-heading": "Niet gevonden",
"new-404-page-title": "Pagina niet gevonden",
"new-404-page-heading": "Oeps. Pagina niet gevonden.",
"404-img-text": "Niet gevonden!",
"path-was-not-found": "Het gevraagde pad is niet gevonden:",
"404-advice.p1": "De inhoud die u zoekt is mogelijk toch beschikbaar, maar kan zich ergens anders in het ZIM-bestand bevinden.",
"404-advice.p2": "U kunt:",
"404-advice.p3": "De zoekfunctie proberen om de gewenste inhoud te vinden",
"404-advice.p4": "Trefwoorden of titels opzoeken die verband houden met de informatie die u zoekt",
"404-advice.p5": "Met deze aanpak kunt u hopelijk de gewenste inhoud vinden, ook als de oorspronkelijke koppeling niet goed werkt.",
"500-page-title": "Interne serverfout",
"500-page-heading": "Oeps. De pagina werkt niet.",
"500-page-text": "Het aangevraagde pad kan niet goed beschikbaar worden gesteld:",
"500-img-text": "Pagina werkt niet",
"external-link-detected": "Externe koppeling gedetecteerd",
"caution-warning": "Voorzichtig!",
"external-link-intro": "U staat op het punt de ZIM-lezer van Kiwix te verlaten om online te gaan naar",
"external-link-advice.p1": "De koppeling die u probeert te openen, maakt geen deel uit van uw offline-pakket en vereist een internetverbinding.",
"external-link-advice.p2": "Als u online kunt gaan, kunt u proberen de koppeling te openen.",
"external-link-advice.p3": "Anders kunt u met de terugknop van uw browser terugkeren naar de offline-inhoud van uw ZIM.",
"500-page-heading": "Interne serverfout",
"fulltext-search-unavailable": "Zoeken in volledige tekst is niet beschikbaar",
"no-search-results": "De zoekmachine voor volledige tekst is niet beschikbaar voor deze inhoud.",
"search-results-page-title": "Zoeken: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultaten <b>{{START}}-{{END}}</b> van <b>{{COUNT}}</b> voor <b>“{{{SEARCH_PATTERN}}}”</b>",
"empty-search-results-page-header": "Er zijn geen resultaten gevonden voor <b>“{{{SEARCH_PATTERN}}}”</b>",
"search-result-book-info": "uit {{BOOK_TITLE}}",
"word-count": "{{COUNT}} woorden",
"library-button-text": "Naar de welkomstpagina",
"home-button-text": "Naar de hoofdpagina van {{{BOOK_TITLE}}}",
"home-button-text": "Naar de hoofdpagina van {{BOOK_TITLE}}",
"random-page-button-text": "Naar een willekeurig geselecteerde pagina gaan",
"searchbox-tooltip": "{{{BOOK_TITLE}}} doorzoeken",
"searchbox-tooltip": "Naar {{BOOK_TITLE}} zoeken",
"confusion-of-tongues": "Er zouden twee of meer boeken in verschillende talen deelnemen aan de zoekopdracht, wat tot verwarrende resultaten kan leiden.",
"welcome-page-overzealous-filter": "Geen resultaat. Wilt u <a href=\"{{URL}}\">het filter resetten</a>?",
"powered-by-kiwix-html": "Mogelijk gemaakt door <a href=\"https://kiwix.org\">Kiwix</a>",
@@ -64,31 +39,19 @@
"count-of-matching-books": "{{COUNT}} boek(en)",
"download": "Downloaden",
"direct-download-link-text": "Direct",
"direct-download-alt-text": "Direct downloaden via HTTP(S)",
"hash-download-link-text": "SHA-256-controlesom",
"hash-download-alt-text": "De SHA-256-controlesom van het bestand weergeven",
"direct-download-alt-text": "directe download",
"hash-download-link-text": "SHA256-hash",
"hash-download-alt-text": "controlesom (hash) van de download",
"magnet-link-text": "Magnet-link",
"magnet-alt-text": "Downloaden via Magnet-link",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Downloaden via BitTorrent",
"magnet-alt-text": "magnet-link van de download",
"torrent-download-link-text": "Torrent-bestand",
"torrent-download-alt-text": "torrent downloaden",
"library-opds-feed-all-entries": "OPDS-feed bibliotheek: alle vermeldingen",
"filter-by-tag": "Filteren op label “{{{TAG}}}”",
"stop-filtering-by-tag": "Niet meer filteren op label “{{{TAG}}}”",
"filter-by-tag": "Filteren op label “{{TAG}}”",
"stop-filtering-by-tag": "Niet meer filteren op label “{{TAG}}”",
"library-opds-feed-parameterised": "OPDS-feed bibliotheek: vermeldingen die overeenkomen met {{#LANG}}\nTaal: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategorie: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nLabel: {{TAG}} {{/TAG}}{{#Q}}\nZoekopdracht: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Welkom bij de Kiwix-server",
"download-links-heading": "Downloadkoppelingen voor <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Boek downloaden",
"preview-book": "Voorvertoning",
"unknown-error": "Onbekende fout",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinieuws",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversity",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "WikiWoordenboek",
"book-category.other": "Overige",
"text-loading-content": "Inhoud laden"
"preview-book": "Voorvertoning"
}

View File

@@ -1,6 +1,7 @@
{
"@metadata": {
"authors": [
"Amire80",
"Lancine.kounfantoh.fofana"
]
},

View File

@@ -1,8 +1,7 @@
{
"@metadata": {
"authors": [
"Gouri",
"Psubhashish"
"Gouri"
]
},
"name": "ଓଡ଼ିଆ",
@@ -11,7 +10,7 @@
"too-many-books": "ଅତ୍ୟଧିକ ବହି ଅନୁରୋଧ (${{NB_BOOKS}}) ଯେଉଁଠାରେ ସୀମା ${{LIMIT}} |",
"no-book-found": "କୌଣସି ପୁସ୍ତକ ଚଯ଼ନ ମାନଦଣ୍ଡ ସହ ମେଳ ଖାଉନାହିଁ ।",
"url-not-found": "ଅନୁରୋଧ କରାଯାଇଥିବା URL \"{{url}}\" ଏହି ସର୍ଭରରେ ମିଳିଲା ନାହିଁ |",
"suggest-search": "<a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a> ପାଇଁ ପୂରା ପାଠ ଖୋଜନ୍ତୁ |",
"suggest-search": "<a href=\"${{{SEARCH_URL}}}\">${{PATTERN}} for</a> ପାଇଁ ଏକ ସମ୍ପୂର୍ଣ୍ଣ ପାଠ ସନ୍ଧାନ କର |",
"random-article-failure": "ଓହୋ! ଏକ ଅନିୟମିତ ପ୍ରବନ୍ଧ ବାଛିବାରେ ବିଫଳ :(",
"invalid-raw-data-type": "{{DATATYPE}} କଞ୍ଚା ବିଷୟବସ୍ତୁ ପାଇଁ ଏକ ବ valid ଧ ଅନୁରୋଧ ନୁହେଁ |",
"no-value-for-arg": "ଯୁକ୍ତି ପାଇଁ କୌଣସି ମୂଲ୍ଯ଼ ପ୍ରଦାନ କରାଯାଇନାହିଁ ${{ARGUMENT}}",

View File

@@ -25,15 +25,7 @@
"home-button-text": "Przejdź do głównej strony '{{BOOK_TITLE}}'",
"random-page-button-text": "Przejdź do losowo wybranej strony",
"searchbox-tooltip": "Szukaj '{{BOOK_TITLE}}'",
"search": "Szukaj",
"book-filtering-all-categories": "Wszystkie Kategorie",
"book-filtering-all-languages": "Wszystkie języki",
"download": "Pobierz",
"direct-download-link-text": "Bezpośrednio",
"direct-download-alt-text": "bezpośrednie pobieranie",
"torrent-download-link-text": "Plik torrent",
"welcome-to-kiwix-server": "Witamy na serwerze Kiwix",
"download-links-title": "Pobierz książkę",
"preview-book": "Podgląd",
"book-category.other": "Inne"
"preview-book": "Podgląd"
}

View File

@@ -1,76 +0,0 @@
{
"@metadata": {
"authors": [
"Eduardoaddad",
"Klgor1803",
"Obiru",
"Re demz",
"YoReaper"
]
},
"name": "Português",
"suggest-full-text-search": "Contendo '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Não existe o livro: {{BOOK_NAME}}",
"too-many-books": "Muitos livros solicitados {{NB_BOOKS}} mas o limite é {{LIMIT}}",
"no-book-found": "Nenhum livro corresponde os critérios de seleção",
"url-not-found": "O URL \"{{url}}\" não foi encontrado neste servidor.",
"suggest-search": "Fazer uma pesquisa de texto completo para <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ops! Falha ao escolher um artigo aleatório :(",
"invalid-raw-data-type": "{{DATATYPE}} não é uma solicitação válida para conteúdo bruto.",
"invalid-request": "O URL solicitado \"{{{url}}}\" não é uma solicitação válida.",
"no-value-for-arg": "Nenhum valor para o argumento {{ARGUMENT}}",
"no-query": "Nenhuma consulta fornecida.",
"400-page-title": "Requisição inválida",
"400-page-heading": "Requisição inválida",
"404-page-title": "Conteúdo não encontrado",
"404-page-heading": "Não encontrado",
"500-page-title": "Erro interno do servidor",
"500-page-heading": "Erro interno do servidor",
"500-page-text": "Aconteceu um erro interno do servidor. Nós pedimos desculpas sobre isso :/",
"caution-warning": "Cuidado!",
"fulltext-search-unavailable": "Busca por texto completo está indisponível",
"no-search-results": "O motor de busca de texto completo não está disponível para este conteúdo.",
"search-results-page-title": "Buscar: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultados <b>{{START}}-{{END}}</b> de <b>{{COUNT}}</b> para <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Nenhum resultado encontrado para <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "de {{BOOK_TITLE}}",
"word-count": "{{COUNT}} palavras",
"library-button-text": "Ir para página inicial",
"home-button-text": "Ir para página principal de '{{BOOK_TITLE}}'",
"random-page-button-text": "Ir para uma página aleatória",
"searchbox-tooltip": "Buscar '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Dois ou mais livros em diferentes idiomas podem participar da pesquisa, isso pode proporcionar resultados confusos.",
"welcome-page-overzealous-filter": "Nenhum resultado. Gostaria de <a href=\"{{URL}}\">redefinir o filtro</a> ?",
"powered-by-kiwix-html": "Desenvolvido por <a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Pesquisar",
"book-filtering-all-categories": "Todas as categorias",
"book-filtering-all-languages": "Todos os idiomas",
"count-of-matching-books": "{{COUNT}} livro(s)",
"download": "Baixar",
"direct-download-link-text": "Direto",
"direct-download-alt-text": "Baixar diretamente por HTTP(S)",
"hash-download-link-text": "Verificação SHA-256",
"hash-download-alt-text": "Exibir arquivo de verificação SHA-256",
"magnet-link-text": "Link Magnet",
"magnet-alt-text": "Baixar por link Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Baixar via BitTorrent",
"library-opds-feed-all-entries": "Feed OPDS da biblioteca - Todas as entradas",
"filter-by-tag": "Filtrar por etiqueta \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Parar de filtrar por etiqueta \"{{{TAG}}}\"",
"library-opds-feed-parameterised": "Biblioteca OPDS Feed - entradas que correspondem a {{#LANG}}\nIdioma: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategoria: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nConsulta: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Bem vindo ao servidor Kiwix",
"download-links-heading": "Links para baixar <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Download do livro",
"preview-book": "Pré-visualizar",
"unknown-error": "Erro desconhecido",
"book-category.wikibooks": "Wikilivros",
"book-category.wikinews": "Wikinotícias",
"book-category.wikipedia": "Wikipédia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispécies",
"book-category.wikiversity": "Wikiversidade",
"book-category.wiktionary": "Wikcionário",
"book-category.other": "Outro"
}

View File

@@ -1,73 +0,0 @@
{
"@metadata": {
"authors": [
"B3rnas"
]
},
"name": "português",
"suggest-full-text-search": "contendo '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Não existe o livro: {{BOOK_NAME}}",
"too-many-books": "Demasiadas solicitações de livros ({{NB_BOOKS}}) onde o limite é {{LIMIT}}",
"no-book-found": "Nenhum livro corresponde aos critérios de seleção",
"url-not-found": "A URL solicitada \"{{url}}\" não foi encontrada neste servidor.",
"suggest-search": "Faça uma pesquisa de texto completo para <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ups! Erro ao eleger um artigo aleatório :(",
"invalid-raw-data-type": "{{DATATYPE}} não é uma solicitação válida para conteúdo bruto.",
"invalid-request": "A URL solicitada \"{{{url}}}\" não é uma solicitação válida.",
"no-value-for-arg": "Nenhum valor fornecido para o argumento {{ARGUMENT}}",
"no-query": "Nenhuma consulta fornecida.",
"raw-entry-not-found": "Não é possível encontrar a entrada {{DATATYPE}} {{ENTRY}}",
"400-page-title": "Solicitação inválida",
"400-page-heading": "Solicitação inválida",
"404-page-title": "Conteúdo não encontrado",
"404-page-heading": "Não encontrado",
"500-page-title": "Erro interno do servidor",
"500-page-heading": "Erro interno do servidor",
"500-page-text": "Ocorreu um erro interno do servidor. Lamentamos por isso :/",
"fulltext-search-unavailable": "Pesquisa de texto completo indisponível",
"no-search-results": "O motor de busca de texto completo não está disponível para este conteúdo.",
"search-results-page-title": "Pesquisar: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultados <b>{{START}}-{{END}}</b> de <b>{{COUNT}}</b> para <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Nenhum resultado foi encontrado para <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "de {{BOOK_TITLE}}",
"word-count": "{{COUNT}} palavras",
"library-button-text": "Ir para página inicial",
"home-button-text": "Vá para a página principal de '{{BOOK_TITLE}}'",
"random-page-button-text": "Vá para uma página selecionada aleatoriamente",
"searchbox-tooltip": "Procurar '{{BOOK_TITLE}}'",
"confusion-of-tongues": "Dois ou mais livros em idiomas diferentes participariam da pesquisa, o que poderia levar a resultados confusos.",
"welcome-page-overzealous-filter": "Nenhum resultado. Gostaria de <a href=\"{{URL}}\">redefinir o filtro</a> ?",
"powered-by-kiwix-html": "Desenvolvido por <a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Pesquisar",
"book-filtering-all-categories": "Todas as categorias",
"book-filtering-all-languages": "Todos os idiomas",
"count-of-matching-books": "{{COUNT}} livro(s)",
"download": "Transferir",
"direct-download-link-text": "Direto",
"direct-download-alt-text": "Descarregar diretamente através de HTTP (S)",
"hash-download-link-text": "Verificação SHA-256",
"hash-download-alt-text": "Exibir arquivo de verificação SHA-256",
"magnet-link-text": "Link magnético",
"magnet-alt-text": "Descarregar através do link Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Descarregar através de BitTorrent",
"library-opds-feed-all-entries": "Feed OPDS da biblioteca - Todas as entradas",
"filter-by-tag": "Filtrar por tag \"{{TAG}}\"",
"stop-filtering-by-tag": "Pare de filtrar pela tag \"{{TAG}}\"",
"library-opds-feed-parameterised": "Feed OPDS da biblioteca - entradas que correspondem a {{#LANG}}\nIdioma: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategoria: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nConsulta: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Bem-vindo ao Kiwix Server",
"download-links-heading": "Links para download de <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Descarregar livros",
"preview-book": "Pré-visualização",
"unknown-error": "Erro desconhecido",
"book-category.wikibooks": "Wikilivros",
"book-category.wikinews": "Wikinotícias",
"book-category.wikipedia": "Wikipédia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversidade",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wikcionário",
"book-category.other": "Outro"
}

View File

@@ -24,25 +24,9 @@
"400-page-heading": "Heading of the 400 error page",
"404-page-title": "Title of the 404 error page",
"404-page-heading": "Heading of the 404 error page",
"new-404-page-title": "Title of the 404 error page",
"new-404-page-heading": "Heading of the 404 error page",
"404-img-text": "Fallback text for the image on the 404 error page",
"path-was-not-found": "Message telling that the URL path was not found (to be followed by the actual path)",
"404-advice.p1": "1st paragraph of the multiline advice on the 'Page not found' error page (see 404-advice.p1 through 404-advice.p5 for full text)",
"404-advice.p2": "2nd paragraph of the multiline advice on the 'Page not found' error page (see 404-advice.p1 through 404-advice.p5 for full text)",
"404-advice.p3": "3rd paragraph of the multiline advice on the 'Page not found' error page (see 404-advice.p1 through 404-advice.p5 for full text)",
"404-advice.p4": "4th paragraph of the multiline advice on the 'Page not found' error page (see 404-advice.p1 through 404-advice.p5 for full text)",
"404-advice.p5": "5th paragraph of the multiline advice on the 'Page not found' error page (see 404-advice.p1 through 404-advice.p5 for full text)",
"500-page-title": "Title of the 500 error page",
"500-page-heading": "Heading of the 500 error page",
"500-page-text": "Text of the 500 error page",
"500-img-text": "Fallback text for the image on the 500 error page",
"external-link-detected": "Title & heading of the external link blocker page",
"caution-warning": "Warning of action that shouldn't be carried out carelessly",
"external-link-intro": "Message introducing the external link (to be followed by the actual link)",
"external-link-advice.p1": "1st paragraph of the multiline advice on the external link blocker page (see external-link-advice.p1 through external-link-advice.p3 for full text)",
"external-link-advice.p2": "2nd paragraph of the multiline advice on the external link blocker page (see external-link-advice.p1 through external-link-advice.p3 for full text)",
"external-link-advice.p3": "3rd paragraph of the multiline advice on the external link blocker page (see external-link-advice.p1 through external-link-advice.p3 for full text)",
"fulltext-search-unavailable": "Title of the error page returned when search is attempted in a book without fulltext search database",
"no-search-results": "Text of the error page returned when search is attempted in a book without fulltext search database",
"search-results-page-title": "Title of the search results page",
@@ -78,24 +62,5 @@
"download-links-title": "Title for no-js download page",
"preview-book": "Tooltip of book-tile leading to the book",
"non-translated-text": "{{ignored}}\nUsed to display text that is generated at runtime and cannot be translated. Nothing to translate about this one.",
"unknown-error": "Unknown error",
"book-category.gutenberg": "Name for the category of books from the Gutenberg project",
"book-category.iFixit": "Name for the category of iFixit books",
"book-category.mooc": "Name for the category of MOOC books",
"book-category.phet": "Name for the category of Phet books",
"book-category.stack_exchange": "Name for the category of books from the Stack Exchange network books",
"book-category.ted": "Name for the category of Ted books",
"book-category.vikidia": "Name for the category of Vikidia books",
"book-category.wikibooks": "Name for the category of Wikibooks books books",
"book-category.wikihow": "Name for the category of wikiHow books",
"book-category.wikinews": "Name for the category of Wikinews books",
"book-category.wikipedia": "Name for the category of Wikipedia books",
"book-category.wikiquote": "Name for the category of Wikiquote books",
"book-category.wikisource": "Name for the category of Wikisource books",
"book-category.wikispecies": "Name for the category of Wikispecies books",
"book-category.wikiversity": "Name for the category of Wikiversity books",
"book-category.wikivoyage": "Name for the category of Wikivoyage books",
"book-category.wiktionary": "Name for the category of Wiktionary books",
"book-category.other": "Books not belonging to any special category are listed under this one",
"text-loading-content": "Text displayed while content is being loaded"
"unknown-error": "Unknown error"
}

View File

@@ -1,75 +0,0 @@
{
"@metadata": {
"authors": [
"MSClaudiu",
"Trotinel Iftode"
]
},
"name": "Engleză",
"suggest-full-text-search": "care conține '{{{SEARCH_TERMS}}}'...",
"no-such-book": "Nu există o astfel de carte: {{BOOK_NAME}}",
"too-many-books": "Prea multe cărți solicitate ({{NB_BOOKS}}) unde limita este {{LIMIT}}",
"no-book-found": "Nicio carte nu corespunde criteriilor de selecție",
"url-not-found": "Adresa URL solicitată \"{{url}}\" nu a fost găsită pe acest server.",
"suggest-search": "Efectuați o căutare text complet pentru <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Hopa! Nu s-a putut alege un articol aleatoriu :(",
"invalid-raw-data-type": "{{DATATYPE}} nu este o solicitare validă pentru conținut brut.",
"invalid-request": "Adresa URL solicitată \"{{{url}}}\" nu este o solicitare validă.",
"no-value-for-arg": "Nu este furnizată nicio valoare pentru argumentul {{ARGUMENT}}",
"no-query": "Nu a fost furnizată nicio interogare.",
"raw-entry-not-found": "Nu se poate găsi {{DATATYPE}} intrarea {{ENTRY}}",
"400-page-title": "Cerere invalidă",
"400-page-heading": "Cerere invalidă",
"404-page-title": "Conținut nu a fost găsit",
"404-page-heading": "Nu a fost găsit",
"500-page-title": "Eroare internă de server",
"500-page-heading": "Eroare internă de server",
"500-page-text": "A apărut o eroare internă de server. Ne pare rau pentru asta :/",
"external-link-intro": "Ești pe cale să părăsești cititorul ZIM al Kiwix pentru a te conecta la...",
"fulltext-search-unavailable": "Căutarea text integral indisponibilă",
"no-search-results": "Motorul de căutare textintegral nu este disponibil pentru acest conținut.",
"search-results-page-title": "Căutare: {{SEARCH_PATTERN}}",
"search-results-page-header": "Rezultatele <b>{{START}}-{{END}}</b> din <b>{{COUNT}}</b> pentru <b>„{{{SEARCH_PATTERN}}}”</b>",
"empty-search-results-page-header": "Nu au fost găsite rezultate pentru <b>„{{{SEARCH_PATTERN}}}”</b>",
"search-result-book-info": "din {{BOOK_TITLE}}",
"word-count": "{{COUNT}} cuvinte",
"library-button-text": "Mergi la pagina de pornire",
"home-button-text": "Accesați pagina principală a „{{BOOK_TITLE}}”",
"random-page-button-text": "Accesați o pagină selectată aleatoriu",
"searchbox-tooltip": "Căutați „{{BOOK_TITLE}}”",
"confusion-of-tongues": "Două sau mai multe cărți în limbi diferite ar participa la căutare, ceea ce poate duce la rezultate confuze.",
"welcome-page-overzealous-filter": "Nici un rezultat. Doriți să <a href=\"{{URL}}\">resetați filtrul</a>?",
"powered-by-kiwix-html": "Susținut de&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Caută",
"book-filtering-all-categories": "Toate categoriile",
"book-filtering-all-languages": "Toate limbile",
"count-of-matching-books": "{{COUNT}} cărți",
"download": "Descărcare",
"direct-download-link-text": "Direct",
"direct-download-alt-text": "Descărcați direct prin HTTP(S)",
"hash-download-link-text": "Sumă de control SHA-256",
"hash-download-alt-text": "Afișează suma de verificare a fișierului SHA-256",
"magnet-link-text": "Legătură magnetică",
"magnet-alt-text": "Descărcați prin linkul Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Descărcați prin BitTorrent",
"library-opds-feed-all-entries": "Bibliotecă OPDS Feed - Toate intrările",
"filter-by-tag": "Filtrați după eticheta \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Opriți filtrarea după eticheta \"{{{TAG}}}\"",
"library-opds-feed-parameterised": "Bibliotecă OPDS Feed - intrări care se potrivesc cu {{#LANG}}\nLimba: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategorie: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nEtichetă: {{TAG}} {{/TAG}}{{#Q}}\nInterogare: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Bun venit la Kiwix Server",
"download-links-heading": "Descărcați linkuri pentru <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Descărcă cartea",
"preview-book": "Previzualizare",
"unknown-error": "Eroare necunoscută",
"book-category.wikibooks": "Wikimanuale",
"book-category.wikinews": "Wikiștiri",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikicitat",
"book-category.wikisource": "Wikisursă",
"book-category.wikispecies": "Wikispecii",
"book-category.wikiversity": "Wikiversitate",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wikționar",
"book-category.other": "Altul"
}

View File

@@ -3,11 +3,9 @@
"authors": [
"Fenixs-ru",
"Kareyac",
"Lutece398",
"Okras",
"Pacha Tchernof",
"Razno0",
"Rofiatmustapha12",
"Smavrina"
]
},
@@ -20,7 +18,6 @@
"suggest-search": "Выполните полнотекстовый поиск для <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ой! Не удалось выбрать случайную статью :(",
"invalid-raw-data-type": "{{DATATYPE}} не является допустимым запросом необработанного контента.",
"invalid-request": "Запрошенный URL-адрес «{{{url}}}» не является допустимым запросом.",
"no-value-for-arg": "Не указано значение для аргумента {{ARGUMENT}}",
"no-query": "Не предоставлен запрос.",
"raw-entry-not-found": "Не удаётся найти запись {{ENTRY}} типа {{DATATYPE}}",
@@ -30,51 +27,30 @@
"404-page-heading": "Не найдено",
"500-page-title": "Внутренняя ошибка сервера",
"500-page-heading": "Внутренняя ошибка сервера",
"500-page-text": "Произошла внутренняя ошибка сервера. Мы сожалеем об этом :/",
"fulltext-search-unavailable": "Полнотекстовый поиск недоступен",
"no-search-results": "Полнотекстовая поисковая система недоступна для этого содержания.",
"search-results-page-title": "Поиск: {{SEARCH_PATTERN}}",
"search-results-page-header": "Результаты <b>{{START}}-{{END}}</b> из <b>{{COUNT}}</b> для <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "По запросу <b>\"{{{SEARCH_PATTERN}}}\"</b> результатов не найдено.",
"search-result-book-info": "из {{BOOK_TITLE}}",
"word-count": "{{COUNT}} слов",
"library-button-text": "Перейти на страницу-приветствие",
"home-button-text": "Перейти на главную страницу '{{BOOK_TITLE}}'",
"random-page-button-text": "Перейти на случайно выбранную страницу",
"searchbox-tooltip": "Искать '{{BOOK_TITLE}}'",
"confusion-of-tongues": "В поиске будут участвовать две или более книг на разных языках, что может привести к запутанным результатам.",
"welcome-page-overzealous-filter": "Безрезультатно. Хотите <a href=\"{{URL}}\">сбросить фильтр</a> ?",
"powered-by-kiwix-html": "При поддержке&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
"search": "Найти",
"book-filtering-all-categories": "Все категории",
"book-filtering-all-languages": "Все языки",
"count-of-matching-books": "{{COUNT}} книг(и)",
"download": "Скачать",
"direct-download-link-text": "Прямой",
"direct-download-alt-text": "Загрузка напрямую через HTTP(S)",
"hash-download-link-text": "Контрольная сумма SHA-256",
"hash-download-alt-text": "Показать контрольную сумму SHA-256 у файла",
"direct-download-alt-text": "прямая загрузка",
"hash-download-link-text": "Хэш Sha256",
"hash-download-alt-text": "скачать хэш",
"magnet-link-text": "Магнитная ссылка",
"magnet-alt-text": "Скачать по Magnet-ссылке",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Скачать через BitTorrent",
"torrent-download-link-text": "Торрент-файл",
"torrent-download-alt-text": "скачать торрент",
"library-opds-feed-all-entries": "Канал библиотеки OPDS  все записи",
"filter-by-tag": "Фильтровать по тегу \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Прекратить фильтрацию по тегу \"{{{TAG}}}\"",
"library-opds-feed-parameterised": "Канал OPDS библиотеки – записи, соответствующие {{#LANG}}\nLanguage: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategory: {{CATEGORY}} {{/CATEGORY}} {{#TAG}}\nTag: {{TAG}} {{/TAG}}{{#Q}}\nЗапрос: {{Q}} {{/Q}}",
"filter-by-tag": "Фильтровать по тегу \"{{TAG}}\"",
"stop-filtering-by-tag": "Прекратить фильтрацию по тегу \"{{TAG}}\"",
"welcome-to-kiwix-server": "Добро пожаловать на сервер Kiwix",
"download-links-heading": "Ссылки для скачивания <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Скачать книгу",
"preview-book": "Предпросмотр",
"unknown-error": "Неизвестная ошибка",
"book-category.wikibooks": "Викиучебник",
"book-category.wikinews": "Викиновости",
"book-category.wikipedia": "Википедия",
"book-category.wikiquote": "Викицитатник",
"book-category.wikisource": "Викитека",
"book-category.wikispecies": "Викивиды",
"book-category.wikiversity": "Викиверситет",
"book-category.wikivoyage": "Викигид",
"book-category.wiktionary": "Викисловарь",
"book-category.other": "Другое"
"preview-book": "Предпросмотр"
}

View File

@@ -38,16 +38,16 @@
"count-of-matching-books": "{{COUNT}} libru/os",
"download": "Iscàrriga",
"direct-download-link-text": "Diretu",
"direct-download-alt-text": "Iscàrriga in manera direta tràmite HTTP(S)",
"hash-download-link-text": "Summa de controllu SHA-256",
"hash-download-alt-text": "Mustra sa summa de controllu SHA-256",
"direct-download-alt-text": "iscarrigamentu diretu",
"hash-download-link-text": "Hash SHA256",
"hash-download-alt-text": "hash de s'iscarrigamentu",
"magnet-link-text": "Ligàmene Magnet",
"magnet-alt-text": "Iscàrriga impreende su ligàmene Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Iscàrriga impreende BitTorrent",
"magnet-alt-text": "ligàmene \"magnet\" de iscarrigamentu",
"torrent-download-link-text": "Documentu Torrent",
"torrent-download-alt-text": "iscàrriga su torrent",
"library-opds-feed-all-entries": "Flussu OPDS de sa biblioteca Totu sos elementos",
"filter-by-tag": "Filtra pro eticheta \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Non filtres prus pro eticheta \"{{{TAG}}}\"",
"filter-by-tag": "Filtra pro eticheta \"{{TAG}}\"",
"stop-filtering-by-tag": "Non filtres prus pro eticheta \"{{TAG}}\"",
"library-opds-feed-parameterised": "Flussu OPDS de sa biblioteca - elementos chi currispondet cun {{#LANG}}\nLimba: {{LANG}} {{/LANG}}{{#CATEGORY}}\nCategoria: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nEticheta: {{TAG}} {{/TAG}}{{#Q}}\nChirca: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Bene bènnidu a su serbidore de Kiwix",
"download-links-heading": "Ligàmenes de iscarrigamentu pro <b><i>{{BOOK_TITLE}}</i></b>",

View File

@@ -9,8 +9,6 @@
"400-page-heading": "غلط ارداس",
"404-page-title": "مواد کائنی لبھیا",
"404-page-heading": "کائنی لبھا",
"new-404-page-title": "ورقہ کائنی لبھیا",
"404-img-text": "کائنی لبھا!",
"500-page-title": "اندرلا سرور نقص",
"500-page-heading": "اندرلا سرور نقص",
"search": "ڳولو",
@@ -24,16 +22,5 @@
"torrent-download-link-text": "ٹورنٹ فائل",
"torrent-download-alt-text": "ٹورںٹ ݙاؤن لوڈ کرو",
"download-links-title": "کتاب ڈاؤن لوڈ کرو",
"preview-book": "پیشگی ݙکھالا",
"book-category.vikidia": "وکی ڈیا",
"book-category.wikibooks": "وکی کتاباں",
"book-category.wikinews": "وکی خبراں",
"book-category.wikipedia": "وکیپیڈیا",
"book-category.wikiquote": "وکی ٻول",
"book-category.wikisource": "وکی ماخذ",
"book-category.wikispecies": "وکی سپیشیز",
"book-category.wikiversity": "وکی ورسٹی",
"book-category.wikivoyage": "وکی سیرسپاٹا",
"book-category.wiktionary": "وکشنری",
"book-category.other": "ٻیا"
"preview-book": "پیشگی ݙکھالا"
}

View File

@@ -2,15 +2,14 @@
"@metadata": {
"authors": [
"Eleassar",
"Kelson",
"Rofiatmustapha12"
"Kelson"
]
},
"name": "slovenščina",
"suggest-full-text-search": "vsebuje »{{{SEARCH_TERMS}}}« ...",
"no-such-book": "Ni take knjige: {{BOOK_NAME}}",
"too-many-books": "Preveč zahtevanih knjig ({{NB_BOOKS}}), omejitev je {{LIMIT}}",
"no-book-found": "Izbranim merilom ne ustreza nobena knjiga",
"no-book-found": "Izbirnim merilom ne ustreza nobena knjiga",
"url-not-found": "Zahtevanega URL-ja »{{url}}« v tem strežniku ni bilo mogoče najti.",
"suggest-search": "Preiščite celotno besedilo za <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Ups! Ni bilo mogoče izbrati naključnega članka :(",
@@ -23,36 +22,15 @@
"400-page-heading": "Neveljaven zahtevek",
"404-page-title": "Vsebine ni mogoče najti",
"404-page-heading": "Ni najdeno",
"new-404-page-title": "Stran ni bila najdena",
"new-404-page-heading": "Ups. Stran ni bila najdena.",
"404-img-text": "Ni najdeno!",
"path-was-not-found": "Zahtevana pot ni bila najdena:",
"404-advice.p1": "Vsebina, ki jo iščete, je mogoče še vedno na voljo, vendar se morda nahaja na drugem mestu v datoteki ZIM.",
"404-advice.p2": "Prosimo:",
"404-advice.p3": "Poskusite uporabiti funkcijo iskanja, da najdete želeno vsebino",
"404-advice.p4": "Poiščite ključne besede ali naslove, povezane z informacijami, ki jih iščete",
"404-advice.p5": "Ta pristop bi vam moral pomagati najti želeno vsebino, tudi če izvirna povezava ne deluje pravilno.",
"500-page-title": "Notranja napaka strežnika",
"500-page-heading": "Ups. Stran ne deluje.",
"500-page-text": "Zahtevane poti ni mogoče pravilno dostaviti:",
"500-img-text": "Stran ne deluje",
"external-link-detected": "Zaznana zunanja povezava",
"caution-warning": "Pozor!",
"external-link-intro": "Zapustili boste bralnik ZIM v Kiwixu in se v internetu povezali z",
"external-link-advice.p1": "Povezava, do katere poskušate dostopati, ni del vašega paketa za delo brez povezave in zahteva internetno povezavo.",
"external-link-advice.p2": "Če se lahko povežete z internetom, lahko poskusite odpreti povezavo.",
"external-link-advice.p3": "Na vsebino ZIM brez povezave se lahko vrnete tudi z gumbom za nazaj v brskalniku.",
"500-page-heading": "Notranja napaka strežnika",
"500-page-text": "Prišlo je do notranje napake strežnika. Žal nam je za to. :/",
"fulltext-search-unavailable": "Iskanje po celotnem besedilu ni na voljo",
"no-search-results": "Iskalnik po celotnem besedilu za to vsebino ni na voljo.",
"search-results-page-title": "Iskanje: {{SEARCH_PATTERN}}",
"search-results-page-header": "Zadetki <b>{{START}}{{END}}</b> od <b>{{COUNT}}</b> za »<b>{{{SEARCH_PATTERN}}}</b>«",
"empty-search-results-page-header": "Ni zadetkov za »<b>{{{SEARCH_PATTERN}}}</b>«",
"search-result-book-info": "iz {{BOOK_TITLE}}",
"word-count": "{{COUNT}} besed",
"library-button-text": "Pojdi na pozdravno stran",
"home-button-text": "Pojdi na glavno stran »{{{BOOK_TITLE}}}«",
"random-page-button-text": "Pojdi na naključno izbrano stran",
"searchbox-tooltip": "Poiščite »{{{BOOK_TITLE}}}«",
"library-button-text": "Pojdite na pozdravno stran",
"home-button-text": "Pojdite na glavno stran »{{BOOK_TITLE}}«",
"random-page-button-text": "Pojdite na naključno izbrano stran",
"searchbox-tooltip": "Poiščite »{{BOOK_TITLE}}«",
"confusion-of-tongues": "V iskanju bi bili uporabljeni dve ali več knjig v različnih jezikih, kar lahko pripelje do nejasnih zadetkov.",
"welcome-page-overzealous-filter": "Ni zadetkov. Želite <a href=\"{{URL}}\">ponastaviti filter</a>?",
"powered-by-kiwix-html": "Omogoča <a href=\"https://kiwix.org\">Kiwix</a>",
@@ -62,31 +40,20 @@
"count-of-matching-books": "{{COUNT}} knjiga(i/e)",
"download": "Prenesi",
"direct-download-link-text": "Neposredno",
"direct-download-alt-text": "Neposredni prenos prek HTTP(S)",
"hash-download-link-text": "Kontrolna vsota SHA-256",
"hash-download-alt-text": "Prikaz kontrolne vsote SHA-256 datoteke",
"direct-download-alt-text": "neposredni prenos",
"hash-download-link-text": "Zgoščena vrednost SHA256",
"hash-download-alt-text": "prenesi zgoščeno vrednost",
"magnet-link-text": "Magnetna povezava",
"magnet-alt-text": "Prenos prek magnetne povezave",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Prenos prek BitTorrenta",
"magnet-alt-text": "prenesi magnet",
"torrent-download-link-text": "Torrent datoteka",
"torrent-download-alt-text": "prenesi torrent",
"library-opds-feed-all-entries": "Knjižnični vir OPDS Vsi vnosi",
"filter-by-tag": "Filtriraj po oznaki »{{{TAG}}}«",
"stop-filtering-by-tag": "Ustavi filtriranje po oznaki »{{{TAG}}}«",
"filter-by-tag": "Filtriraj po oznaki »{{TAG}}«",
"stop-filtering-by-tag": "Ustavi filtriranje po oznaki »{{TAG}}«",
"library-opds-feed-parameterised": "Knjižnični vir OPDS vnosi, ki se ujemajo z {{#LANG}}\nJezik: {{LANG}} {{/LANG}}{{#CATEGORY}}\nKategorija: {{CATEGORY}} {{/CATEGORY}} {{#TAG}}\nOznaka: {{TAG}} {{/TAG}}{{#Q}}\nPoizvedba: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Pozdravljeni na strežniku Kiwix",
"download-links-heading": "Povezave za prenos za <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Prenesi knjigo",
"preview-book": "Predogled",
"unknown-error": "Neznana napaka",
"book-category.wikibooks": "Wikiknjige",
"book-category.wikinews": "Wikinovice",
"book-category.wikipedia": "Wikipedija",
"book-category.wikiquote": "Wikinavedek",
"book-category.wikisource": "Wikivir",
"book-category.wikispecies": "Wikivrste",
"book-category.wikiversity": "Wikiverza",
"book-category.wikivoyage": "Wikipotovanje",
"book-category.wiktionary": "Wikislovar",
"book-category.other": "Drugo",
"text-loading-content": "Nalaganje vsebine"
"unknown-error": "Neznana napaka"
}

View File

@@ -13,7 +13,6 @@
"suggest-search": "Bëni një kërkim të plotë teksti për <a href=\"{{{SEARCH_URL}}}\">{{PATTERN}}</a>",
"random-article-failure": "Oh! Su arrit të merrej një artikull kuturu :(",
"invalid-raw-data-type": "{{DATATYPE}} sështë varg i vlefshëm kërkimi për lëndë të papërpunuar.",
"invalid-request": "URL-ja e kërkuar “{{{url}}}” sështë kërkesë e vlefshme.",
"no-value-for-arg": "Su dha vlerë për argumentin {{ARGUMENT}}",
"no-query": "Su dha varg kërkimi.",
"raw-entry-not-found": "Sgjendet dot zëri {{DATATYPE}} {{ENTRY}}",
@@ -23,14 +22,8 @@
"404-page-heading": "Su Gjet",
"500-page-title": "Gabim i Brendshëm Shërbyesi",
"500-page-heading": "Gabim i Brendshëm Shërbyesi",
"500-page-text": "Ndodhi një gabim i brendshëm shërbyesi. Na ndjeni për këtë :/",
"fulltext-search-unavailable": "Kërkim teksti të plotë jo i mundshëm",
"no-search-results": "Sështë i passhëm motori i kërkimit të tekstit të plotë për këtë lëndë.",
"search-results-page-title": "Kërkim: {{SEARCH_PATTERN}}",
"search-results-page-header": "Përfundime <b>{{START}}-{{END}}</b> nga <b>{{COUNT}}</b> gjithsej për <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Su gjetën përfundime për <b>“{{{SEARCH_PATTERN}}}”</b>",
"search-result-book-info": "nga {{BOOK_TITLE}}",
"word-count": "{{COUNT}} fjalë",
"library-button-text": "Kalo te faqja e mirëseardhjes",
"home-button-text": "Kalo te faqja krye e '{{BOOK_TITLE}}'",
"random-page-button-text": "Kalo te një faqe e përzgjedhur kuturu",
@@ -44,30 +37,19 @@
"count-of-matching-books": "{{COUNT}} libër(a)",
"download": "Shkarkoje",
"direct-download-link-text": "Drejtpërsëdrejti",
"direct-download-alt-text": "Shkarkoje drejtpërdrejti përmes HTTP(S)",
"hash-download-link-text": "“Checksum” SHA-256",
"hash-download-alt-text": "Shfaq “checksum” SHA-256 kartele",
"direct-download-alt-text": "shkarkim i drejtpërdrejt",
"hash-download-link-text": "Hash sha256",
"hash-download-alt-text": "shkarko hashin",
"magnet-link-text": "Lidhje Magnet",
"magnet-alt-text": "Shkarkoje përmes lidhjeje Magnet",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Shkarkoje përmes BitTorrent-it",
"magnet-alt-text": "shkarko magnetin",
"torrent-download-link-text": "Kartelë Torrent",
"torrent-download-alt-text": "shkarko torrent-in",
"library-opds-feed-all-entries": "Prurje OPDS Biblioteke - Krejt zërat",
"filter-by-tag": "Filtroji sipas etikete “{{{TAG}}}”",
"stop-filtering-by-tag": "Resht së filtruari sipas etikete “{{{TAG}}}”",
"filter-by-tag": "Filtroji sipas etiketës “{{TAG}}”",
"stop-filtering-by-tag": "Resht së filtruari sipas etiketë “{{TAG}}”",
"library-opds-feed-parameterised": "Prurje OPDS Biblioteke - zëra që kanë përputhje me {{#LANG}}\nGjuhë: {{LANG}} {{/LANG}}{{#CATEGORY}}\nKategori: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nEtiketë: {{TAG}} {{/TAG}}{{#Q}}\nVarg Kërkimi: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Mirë se vini në Shërbyesin Kiwix",
"download-links-heading": "Lidhje shkarkimi për <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Shkarkoje librin",
"preview-book": "Paraparje",
"unknown-error": "Gabim i panjohur",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinews",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversity",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wiktionary",
"book-category.other": "Tjetër"
"preview-book": "Paraparje"
}

View File

@@ -1,10 +1,7 @@
{
"@metadata": {
"authors": [
"Apq",
"Jopparn",
"Larsa",
"Rofiatmustapha12",
"Sabelöga",
"WikiPhoenix"
]
@@ -26,36 +23,15 @@
"400-page-heading": "Ogiltig begäran",
"404-page-title": "Innehållet hittades inte",
"404-page-heading": "Hittades inte",
"new-404-page-title": "Sidan kunde inte hittas",
"new-404-page-heading": "Hoppsan. Sidan hittades inte.",
"404-img-text": "Hittades ej!",
"path-was-not-found": "Den begärda sökvägen hittades ej:",
"404-advice.p1": "Innehållet du letar efter kan fortfarande vara tillgängligt, men det kan finnas på en annan plats i ZIM-filen.",
"404-advice.p2": "Vänligen:",
"404-advice.p3": "Försök att använda sökfunktionen för att hitta det innehåll du vill ha",
"404-advice.p4": "Leta efter nyckelord eller titlar relaterade till den information du söker",
"404-advice.p5": "Den här metoden bör hjälpa dig att hitta önskat innehåll, även om den ursprungliga länken inte fungerar korrekt.",
"500-page-title": "Internt serverfel",
"500-page-heading": "Hoppsan. Sidan fungerar inte.",
"500-page-text": "Den begärda sökvägen kan inte levereras korrekt:",
"500-img-text": "Sidan fungerar ej",
"external-link-detected": "Extern länk upptäckt",
"caution-warning": "Varning!",
"external-link-intro": "Du är på väg att lämna Kiwix ZIM-läsare för att gå online till",
"external-link-advice.p1": "Länken du försöker komma åt är inte en del av ditt offlinepaket och kräver en internetanslutning.",
"external-link-advice.p2": "Om du kan gå online kan du försöka öppna länken.",
"external-link-advice.p3": "Du kan annars återgå till ditt ZIM-innehåll offline genom att använda webbläsarens bakåtknapp.",
"500-page-heading": "Internt serverfel",
"500-page-text": "Ett internt serverfel uppstod. Vi ber om ursäkt för det :/",
"fulltext-search-unavailable": "Fulltextsökning är inte tillgänglig",
"no-search-results": "Sökmaskinen för fulltext är inte tillgänglig för detta innehåll.",
"search-results-page-title": "Sök: {{SEARCH_PATTERN}}",
"search-results-page-header": "Resultat <b>{{START}}-{{END}}</b> av <b>{{COUNT}}</b> för <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"empty-search-results-page-header": "Inga resultat hittades för <b>\"{{{SEARCH_PATTERN}}}\"</b>",
"search-result-book-info": "från {{BOOK_TITLE}}",
"word-count": "{{COUNT}} ord",
"library-button-text": "Gå till hemsidan",
"home-button-text": "Gå till huvudsidan för '{{{BOOK_TITLE}}}'",
"home-button-text": "Gå till huvudsidan för \"{{BOOK_TITLE}}\"",
"random-page-button-text": "Gå till en slumpmässigt utvald sida",
"searchbox-tooltip": "Sök '{{{BOOK_TITLE}}}'",
"searchbox-tooltip": "Sök efter \"{{BOOK_TITLE}}\"",
"confusion-of-tongues": "Två eller fler böcker på olika språk skulle delta i sökningen, vilket kan ge förvirrande resultat.",
"welcome-page-overzealous-filter": "Inga resultat. Vill du <a href=\"{{URL}}\">återställa filtret</a>?",
"powered-by-kiwix-html": "Drivs av&nbsp;<a href=\"https://kiwix.org\">Kiwix</a>",
@@ -65,31 +41,20 @@
"count-of-matching-books": "{{COUNT}} böcker",
"download": "Ladda ned",
"direct-download-link-text": "Direkt",
"direct-download-alt-text": "Ladda ner direkt via HTTP(S)",
"hash-download-link-text": "SHA-256-kontrollsiffra",
"hash-download-alt-text": "Visa SHA-256-filens kontrollsiffra",
"direct-download-alt-text": "direktnedladdning",
"hash-download-link-text": "Sha256-hash",
"hash-download-alt-text": "ladda ned hash",
"magnet-link-text": "Magnetlänk",
"magnet-alt-text": "Ladda ner via Magnet-länk",
"torrent-download-link-text": "BitTorrent",
"torrent-download-alt-text": "Ladda ner via BitTorrent",
"magnet-alt-text": "ladda ned magnet",
"torrent-download-link-text": "Torrent-fil",
"torrent-download-alt-text": "ladda ned torrent",
"library-opds-feed-all-entries": "Library OPDS Feed - Alla poster",
"filter-by-tag": "Filtrera efter taggen \"{{{TAG}}}\"",
"stop-filtering-by-tag": "Sluta filtrera efter taggen \"{{{TAG}}}\"",
"filter-by-tag": "Filtrera efter taggen \"{{TAG}}\"",
"stop-filtering-by-tag": "Sluta filtrera efter taggen \"{{TAG}}\"",
"library-opds-feed-parameterised": "Library OPDS Feed - poster som matchar {{#LANG}}\nSpråk: {{LANG}} {{/LANG}}{{#CATEGORY}}\nKategori: {{CATEGORY}} {{/CATEGORY}}{{#TAG}}\nTagg: {{TAG}} {{/TAG}}{{#Q}}\nFråga: {{Q}} {{/Q}}",
"welcome-to-kiwix-server": "Välkommen till Kiwix Server",
"download-links-heading": "Nedladdningslänkar för <b><i>{{BOOK_TITLE}}</i></b>",
"download-links-title": "Ladda ned bok",
"preview-book": "Förhandsgranska",
"unknown-error": "Okänt fel",
"book-category.wikibooks": "Wikibooks",
"book-category.wikinews": "Wikinews",
"book-category.wikipedia": "Wikipedia",
"book-category.wikiquote": "Wikiquote",
"book-category.wikisource": "Wikisource",
"book-category.wikispecies": "Wikispecies",
"book-category.wikiversity": "Wikiversity",
"book-category.wikivoyage": "Wikivoyage",
"book-category.wiktionary": "Wiktionary",
"book-category.other": "Övriga",
"text-loading-content": "Laddar innehåll"
"unknown-error": "Okänt fel"
}

Some files were not shown because too many files have changed in this diff Show More