Merge pull request 'master' (#2) from Icycoide/searxng:master into master

Reviewed-on: #2
This commit is contained in:
kevadesu 2025-05-31 13:31:47 +02:00
commit 7182190057
186 changed files with 4107 additions and 3923 deletions

View file

@ -5,7 +5,7 @@ name: Cleanup
on:
workflow_dispatch:
schedule:
- cron: "4 4 * * 0"
- cron: "4 4 * * *"
concurrency:
group: ${{ github.workflow }}
@ -16,6 +16,8 @@ permissions:
jobs:
container-cache:
# FIXME: On forks it fails with "Failed to fetch packages: missing field `id` at line 1 column 141"
if: github.repository_owner == 'searxng' || github.event_name == 'workflow_dispatch'
name: Container cache
runs-on: ubuntu-24.04
permissions:
@ -28,7 +30,7 @@ jobs:
with:
account: "${{ github.repository_owner }}"
token: "${{ secrets.GITHUB_TOKEN }}"
image-names: "cache"
image-tags: "!searxng-*"
cut-off: "1w"
image-names: "cache base"
image-tags: "!searxng*"
cut-off: "1d"
keep-n-most-recent: "100"

View file

@ -25,10 +25,83 @@ env:
PYTHON_VERSION: "3.13"
jobs:
build-base:
if: |
(github.repository_owner == 'searxng' && github.event.workflow_run.conclusion == 'success')
|| github.event_name == 'workflow_dispatch'
name: Build base
runs-on: ubuntu-24.04
permissions:
# Organization GHCR
packages: write
steps:
- if: github.repository_owner == 'searxng'
name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: "false"
- if: github.repository_owner == 'searxng'
name: Get date
id: date
run: echo "date=$(date +'%Y%m%d')" >>$GITHUB_OUTPUT
- if: github.repository_owner == 'searxng'
name: Check cache apko
id: cache-apko
uses: actions/cache/restore@v4
with:
# yamllint disable-line rule:line-length
key: "apko-${{ steps.date.outputs.date }}-${{ hashFiles('./container/base.yml', './container/base-builder.yml') }}"
path: "/tmp/.apko/"
lookup-only: true
- if: github.repository_owner == 'searxng' && steps.cache-apko.outputs.cache-hit != 'true'
name: Setup cache apko
uses: actions/cache@v4
with:
# yamllint disable-line rule:line-length
key: "apko-${{ steps.date.outputs.date }}-${{ hashFiles('./container/base.yml', './container/base-builder.yml') }}"
restore-keys: "apko-${{ steps.date.outputs.date }}-"
path: "/tmp/.apko/"
- if: github.repository_owner == 'searxng' && steps.cache-apko.outputs.cache-hit != 'true'
name: Setup apko
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
brew install apko
- if: github.repository_owner == 'searxng' && steps.cache-apko.outputs.cache-hit != 'true'
name: Login to GHCR
uses: docker/login-action@v3
with:
registry: "ghcr.io"
username: "${{ github.repository_owner }}"
password: "${{ secrets.GITHUB_TOKEN }}"
- if: github.repository_owner == 'searxng' && steps.cache-apko.outputs.cache-hit != 'true'
name: Build
run: |
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
apko publish ./container/base.yml ghcr.io/${{ github.repository_owner }}/base:searxng \
--cache-dir=/tmp/.apko/ \
--sbom=false \
--vcs=false \
--log-level=debug
apko publish ./container/base-builder.yml ghcr.io/${{ github.repository_owner }}/base:searxng-builder \
--cache-dir=/tmp/.apko/ \
--sbom=false \
--vcs=false \
--log-level=debug
build:
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
if: github.repository_owner == 'searxng' || github.event_name == 'workflow_dispatch'
name: Build (${{ matrix.arch }})
runs-on: ${{ matrix.os }}
needs: build-base
strategy:
fail-fast: false
matrix:

View file

@ -16,6 +16,7 @@ permissions:
jobs:
container:
if: github.repository_owner == 'searxng'
name: Container
runs-on: ubuntu-24.04-arm
permissions:
@ -30,7 +31,7 @@ jobs:
- name: Run Trivy scanner
uses: aquasecurity/trivy-action@0.30.0
with:
image-ref: "docker.io/searxng/searxng:latest"
image-ref: "ghcr.io/searxng/searxng:latest"
vuln-type: "os,library"
severity: "UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL"
ignore-unfixed: "false"

View file

@ -54,7 +54,7 @@ ci.test: test.yamllint test.black test.types.ci test.pylint test.unit test.robo
test: test.yamllint test.black test.types.dev test.pylint test.unit test.robot test.rst test.shell
test.shell:
$(Q)shellcheck -x -s dash \
container/docker-entrypoint.sh
container/entrypoint.sh
$(Q)shellcheck -x -s bash \
utils/brand.sh \
$(MTOOLS) \

File diff suppressed because it is too large Load diff

View file

@ -9,33 +9,30 @@
"icons.html": "node theme_icons.js"
},
"devDependencies": {
"@eslint/js": "^9.26.0",
"@eslint/js": "^9.27.0",
"copy-webpack-plugin": "^13.0.0",
"css-loader": "^7.1.2",
"edge.js": "^6.2.1",
"eslint": "^9.26.0",
"eslint": "^9.27.0",
"filemanager-webpack-plugin": "^8.0.0",
"globals": "^16.1.0",
"globals": "^16.2.0",
"ionicons": "^8.0.8",
"leaflet": "^1.9.4",
"less": "^4.3.0",
"less-loader": "^12.3.0",
"normalize.css": "^8.0.1",
"sharp": "^0.34.1",
"sharp": "^0.34.2",
"style-loader": "^4.0.0",
"stylelint": "^16.19.1",
"stylelint": "^16.20.0",
"stylelint-config-standard": "^38.0.0",
"stylelint-config-standard-less": "^3.0.1",
"stylelint-prettier": "^5.0.3",
"svgo": "^3.3.2",
"swiped-events": "^1.2.0",
"vite": "^6.3.5",
"vite-plugin-static-copy": "^2.3.1",
"vite-plugin-static-copy": "^3.0.0",
"vite-plugin-stylelint": "^6.0.0",
"webpack": "^5.99.8",
"webpack": "^5.99.9",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"autocomplete-js": "^2.7.1"
}
}

View file

@ -1,14 +1,13 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
/* exported AutoComplete */
import AutoComplete from "../../../node_modules/autocomplete-js/dist/autocomplete.js";
(function (w, d, searxng) {
'use strict';
var qinput_id = "q", qinput;
const isMobile = window.matchMedia("only screen and (max-width: 50em)").matches;
const isResultsPage = document.querySelector("main").id == "main_results";
function submitIfQuery () {
if (qinput.value.length > 0) {
@ -38,8 +37,62 @@ import AutoComplete from "../../../node_modules/autocomplete-js/dist/autocomple
qinput.addEventListener('input', updateClearButton, false);
}
const fetchResults = async (query) => {
let request;
if (searxng.settings.method === 'GET') {
const reqParams = new URLSearchParams();
reqParams.append("q", query);
request = fetch("./autocompleter?" + reqParams.toString());
} else {
const formData = new FormData();
formData.append("q", query);
request = fetch("./autocompleter", {
method: 'POST',
body: formData,
});
}
request.then(async function (response) {
const results = await response.json();
if (!results) return;
const autocomplete = d.querySelector(".autocomplete");
const autocompleteList = d.querySelector(".autocomplete ul");
autocomplete.classList.add("open");
autocompleteList.innerHTML = "";
// show an error message that no result was found
if (!results[1] || results[1].length == 0) {
const noItemFoundMessage = document.createElement("li");
noItemFoundMessage.classList.add('no-item-found');
noItemFoundMessage.innerHTML = searxng.settings.translations.no_item_found;
autocompleteList.appendChild(noItemFoundMessage);
return;
}
for (let result of results[1]) {
const li = document.createElement("li");
li.innerText = result;
searxng.on(li, 'mousedown', () => {
qinput.value = result;
const form = d.querySelector("#search");
form.submit();
autocomplete.classList.remove('open');
});
autocompleteList.appendChild(li);
}
});
};
searxng.ready(function () {
// focus search input on large screens
if (!isMobile && !isResultsPage) document.getElementById("q").focus();
qinput = d.getElementById(qinput_id);
const autocomplete = d.querySelector(".autocomplete");
const autocompleteList = d.querySelector(".autocomplete ul");
if (qinput !== null) {
// clear button
@ -47,109 +100,45 @@ import AutoComplete from "../../../node_modules/autocomplete-js/dist/autocomple
// autocompleter
if (searxng.settings.autocomplete) {
searxng.autocomplete = AutoComplete.call(w, {
Url: "./autocompleter",
EmptyMessage: searxng.settings.translations.no_item_found,
HttpMethod: searxng.settings.method,
HttpHeaders: {
"Content-type": "application/x-www-form-urlencoded",
"X-Requested-With": "XMLHttpRequest"
},
MinChars: searxng.settings.autocomplete_min,
Delay: 300,
_Position: function () {},
_Open: function () {
var params = this;
Array.prototype.forEach.call(this.DOMResults.getElementsByTagName("li"), function (li) {
if (li.getAttribute("class") != "locked") {
li.onmousedown = function () {
params._Select(li);
};
}
});
},
_Select: function (item) {
AutoComplete.defaults._Select.call(this, item);
var form = item.closest('form');
if (form) {
form.submit();
searxng.on(qinput, 'input', () => {
const query = qinput.value;
if (query.length < searxng.settings.autocomplete_min) return;
setTimeout(() => {
if (query == qinput.value) fetchResults(query);
}, 300);
});
searxng.on(qinput, 'keyup', (e) => {
let currentIndex = -1;
const listItems = autocompleteList.children;
for (let i = 0; i < listItems.length; i++) {
if (listItems[i].classList.contains('active')) {
currentIndex = i;
break;
}
},
_MinChars: function () {
if (this.Input.value.indexOf('!') > -1) {
return 0;
} else {
return AutoComplete.defaults._MinChars.call(this);
}
},
KeyboardMappings: Object.assign({}, AutoComplete.defaults.KeyboardMappings, {
"KeyUpAndDown_up": Object.assign({}, AutoComplete.defaults.KeyboardMappings.KeyUpAndDown_up, {
Callback: function (event) {
AutoComplete.defaults.KeyboardMappings.KeyUpAndDown_up.Callback.call(this, event);
var liActive = this.DOMResults.querySelector("li.active");
if (liActive) {
AutoComplete.defaults._Select.call(this, liActive);
}
},
}),
"Tab": Object.assign({}, AutoComplete.defaults.KeyboardMappings.Enter, {
Conditions: [{
Is: 9,
Not: false
}],
Callback: function (event) {
if (this.DOMResults.getAttribute("class").indexOf("open") != -1) {
var liActive = this.DOMResults.querySelector("li.active");
if (liActive !== null) {
AutoComplete.defaults._Select.call(this, liActive);
event.preventDefault();
}
}
},
})
}),
}, "#" + qinput_id);
}
/*
Monkey patch autocomplete.js to fix a bug
With the POST method, the values are not URL encoded: query like "1 + 1" are sent as "1 1" since space are URL encoded as plus.
See HTML specifications:
* HTML5: https://url.spec.whatwg.org/#concept-urlencoded-serializer
* HTML4: https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
autocomplete.js does not URL encode the name and values:
https://github.com/autocompletejs/autocomplete.js/blob/87069524f3b95e68f1b54d8976868e0eac1b2c83/src/autocomplete.ts#L665
The monkey patch overrides the compiled version of the ajax function.
See https://github.com/autocompletejs/autocomplete.js/blob/87069524f3b95e68f1b54d8976868e0eac1b2c83/dist/autocomplete.js#L143-L158
The patch changes only the line 156 from
params.Request.send(params._QueryArg() + "=" + params._Pre());
to
params.Request.send(encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(params._Pre()));
Related to:
* https://github.com/autocompletejs/autocomplete.js/issues/78
* https://github.com/searxng/searxng/issues/1695
*/
AutoComplete.prototype.ajax = function (params, request, timeout) {
if (timeout === void 0) { timeout = true; }
if (params.$AjaxTimer) {
window.clearTimeout(params.$AjaxTimer);
}
if (timeout === true) {
params.$AjaxTimer = window.setTimeout(AutoComplete.prototype.ajax.bind(null, params, request, false), params.Delay);
} else {
if (params.Request) {
params.Request.abort();
}
params.Request = request;
params.Request.send(encodeURIComponent(params._QueryArg()) + "=" + encodeURIComponent(params._Pre()));
}
};
if (!isMobile && document.querySelector('.index_endpoint')) {
qinput.focus();
let newCurrentIndex = -1;
if (e.key === "ArrowUp") {
if (currentIndex >= 0) listItems[currentIndex].classList.remove('active');
// we need to add listItems.length to the index calculation here because the JavaScript modulos
// operator doesn't work with negative numbers
newCurrentIndex = (currentIndex - 1 + listItems.length) % listItems.length;
} else if (e.key === "ArrowDown") {
if (currentIndex >= 0) listItems[currentIndex].classList.remove('active');
newCurrentIndex = (currentIndex + 1) % listItems.length;
} else if (e.key === "Tab" || e.key === "Enter") {
autocomplete.classList.remove('open');
}
if (newCurrentIndex != -1) {
const selectedItem = listItems[newCurrentIndex];
selectedItem.classList.add('active');
if (!selectedItem.classList.contains('no-item-found')) qinput.value = selectedItem.innerText;
}
});
}
}
@ -184,7 +173,7 @@ import AutoComplete from "../../../node_modules/autocomplete-js/dist/autocomple
categoryButton.classList.remove("selected");
}
button.classList.add("selected");
})
});
}
// override form submit action to update the actually selected categories

View file

@ -3,6 +3,7 @@
.autocomplete {
position: absolute;
width: @search-width;
max-width: calc(100% - 2 * @search-padding-horizontal);
max-height: 0;
overflow-y: hidden;
.ltr-text-align-left();
@ -65,8 +66,6 @@
@media screen and (max-width: @phone) {
.autocomplete {
width: 100%;
> ul > li {
padding: 1rem;
}

View file

@ -287,8 +287,9 @@
@results-image-row-height: 12rem;
@results-image-row-height-phone: 10rem;
@search-width: 44rem;
// heigh of #search, see detail.less
// height of #search, see detail.less
@search-height: 13rem;
@search-padding-horizontal: 0.5rem;
/// Device Size
/// @desktop > @tablet

View file

@ -131,7 +131,7 @@ button.category_button {
}
#search_view {
padding: 0.5rem 0.3rem 0 0.5rem;
padding: 0.5rem @search-padding-horizontal 0 @search-padding-horizontal;
grid-area: search;
body.results_endpoint & {
@ -141,7 +141,8 @@ button.category_button {
.search_box {
border-radius: 0.8rem;
width: @search-width;
width: 100%;
max-width: @search-width;
display: inline-flex;
flex-direction: row;
white-space: nowrap;
@ -291,8 +292,7 @@ html.no-js #clear_search.hide_if_nojs {
}
.search_box {
width: 98%;
display: flex;
width: 100%;
}
#q {

View file

@ -1,14 +1,4 @@
FROM docker.io/library/python:3.13-slim AS builder
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
brotli \
# uwsgi
libpcre3-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /usr/local/searxng/
FROM ghcr.io/searxng/base:searxng-builder AS builder
COPY ./requirements.txt ./requirements.txt
@ -19,38 +9,15 @@ RUN --mount=type=cache,id=pip,target=/root/.cache/pip python -m venv ./venv \
COPY ./searx/ ./searx/
ARG TIMESTAMP_SETTINGS=0
ARG TIMESTAMP_UWSGI=0
ARG TIMESTAMP_SETTINGS="0"
RUN python -m compileall -q searx \
&& touch -c --date=@$TIMESTAMP_SETTINGS ./searx/settings.yml \
&& touch -c --date=@$TIMESTAMP_UWSGI ./container/uwsgi.ini \
&& find /usr/local/searxng/searx/static \
\( -name '*.html' -o -name '*.css' -o -name '*.js' -o -name '*.svg' -o -name '*.ttf' -o -name '*.eot' \) \
&& find ./searx/static \
\( -name "*.html" -o -name "*.css" -o -name "*.js" -o -name "*.svg" -o -name "*.ttf" -o -name "*.eot" \) \
-type f -exec gzip -9 -k {} + -exec brotli --best {} +
ARG SEARXNG_UID=977
ARG SEARXNG_GID=977
RUN grep -m1 root /etc/group > /tmp/.searxng.group \
&& grep -m1 root /etc/passwd > /tmp/.searxng.passwd \
&& echo "searxng:x:$SEARXNG_GID:" >> /tmp/.searxng.group \
&& echo "searxng:x:$SEARXNG_UID:$SEARXNG_GID:searxng:/usr/local/searxng:/bin/bash" >> /tmp/.searxng.passwd
FROM docker.io/library/python:3.13-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
# healthcheck
wget \
# uwsgi
libpcre3 \
libxml2 \
mailcap \
&& rm -rf /var/lib/apt/lists/*
COPY --chown=root:root --from=builder /tmp/.searxng.passwd /etc/passwd
COPY --chown=root:root --from=builder /tmp/.searxng.group /etc/group
FROM ghcr.io/searxng/base:searxng AS dist
ARG LABEL_DATE="0001-01-01T00:00:00Z"
ARG GIT_URL="unspecified"
@ -58,37 +25,35 @@ ARG SEARXNG_GIT_VERSION="unspecified"
ARG LABEL_VCS_REF="unspecified"
ARG LABEL_VCS_URL="unspecified"
WORKDIR /usr/local/searxng/
COPY --chown=searxng:searxng --from=builder /usr/local/searxng/venv/ ./venv/
COPY --chown=searxng:searxng --from=builder /usr/local/searxng/searx/ ./searx/
COPY --chown=searxng:searxng ./container/ ./container/
COPY --chown=searxng:searxng ./container/config/ ./.template/
COPY --chown=searxng:searxng ./container/entrypoint.sh ./entrypoint.sh
ARG TIMESTAMP_UWSGI="0"
RUN touch -c --date=@$TIMESTAMP_UWSGI ./.template/uwsgi.ini
LABEL org.opencontainers.image.authors="searxng <$GIT_URL>" \
org.opencontainers.image.created=$LABEL_DATE \
org.opencontainers.image.created="$LABEL_DATE" \
org.opencontainers.image.description="A privacy-respecting, hackable metasearch engine" \
org.opencontainers.image.documentation="https://github.com/searxng/searxng-docker" \
org.opencontainers.image.licenses="AGPL-3.0-or-later" \
org.opencontainers.image.revision=$LABEL_VCS_REF \
org.opencontainers.image.source=$LABEL_VCS_URL \
org.opencontainers.image.revision="$LABEL_VCS_REF" \
org.opencontainers.image.source="$LABEL_VCS_URL" \
org.opencontainers.image.title="searxng" \
org.opencontainers.image.url=$LABEL_VCS_URL \
org.opencontainers.image.version=$SEARXNG_GIT_VERSION
org.opencontainers.image.url="$LABEL_VCS_URL" \
org.opencontainers.image.version="$SEARXNG_GIT_VERSION"
ENV CONFIG_PATH=/etc/searxng \
DATA_PATH=/var/cache/searxng
ENV SEARXNG_VERSION=$SEARXNG_GIT_VERSION \
INSTANCE_NAME=searxng \
ENV SEARXNG_VERSION="$SEARXNG_GIT_VERSION" \
INSTANCE_NAME="SearXNG" \
AUTOCOMPLETE="" \
BASE_URL="" \
BIND_ADDRESS=[::]:8080 \
MORTY_KEY="" \
MORTY_URL="" \
SEARXNG_SETTINGS_PATH=$CONFIG_PATH/settings.yml \
UWSGI_SETTINGS_PATH=$CONFIG_PATH/uwsgi.ini \
UWSGI_WORKERS=%k \
UWSGI_THREADS=4
BIND_ADDRESS="[::]:8080" \
SEARXNG_SETTINGS_PATH="$CONFIG_PATH/settings.yml" \
UWSGI_SETTINGS_PATH="$CONFIG_PATH/uwsgi.ini" \
UWSGI_WORKERS="%k" \
UWSGI_THREADS="4"
VOLUME $CONFIG_PATH
VOLUME $DATA_PATH
@ -97,4 +62,4 @@ EXPOSE 8080
HEALTHCHECK CMD wget --quiet --tries=1 --spider http://localhost:8080/healthz || exit 1
ENTRYPOINT ["/usr/local/searxng/container/docker-entrypoint.sh"]
ENTRYPOINT ["/usr/local/searxng/entrypoint.sh"]

View file

@ -0,0 +1,25 @@
contents:
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
repositories:
- https://packages.wolfi.dev/os
packages:
- wolfi-base
- build-base
- python-3.13-dev
- py3-pip
- brotli
entrypoint:
command: /bin/sh -l
work-dir: /usr/local/searxng/
environment:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
HISTFILE: /dev/null
archs:
- x86_64
- aarch64

61
container/base.yml Normal file
View file

@ -0,0 +1,61 @@
contents:
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
repositories:
- https://packages.wolfi.dev/os
packages:
- wolfi-baselayout
- ca-certificates-bundle
- busybox
- python-3.13
# healthcheck
- wget
# uwsgi
- mailcap
entrypoint:
command: /bin/sh -l
work-dir: /usr/local/searxng/
accounts:
groups:
- groupname: searxng
gid: 977
users:
- username: searxng
uid: 977
shell: /bin/ash
environment:
PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
HISTFILE: /dev/null
CONFIG_PATH: /etc/searxng
DATA_PATH: /var/cache/searxng
paths:
# Workdir
- path: /usr/local/searxng/
type: directory
uid: 977
gid: 977
permissions: 0o755
# Config volume
- path: /etc/searxng/
type: directory
uid: 977
gid: 977
permissions: 0o755
# Data volume
- path: /var/cache/searxng/
type: directory
uid: 977
gid: 977
permissions: 0o755
archs:
- x86_64
- aarch64

View file

@ -1,126 +0,0 @@
#!/bin/sh
help() {
cat <<EOF
Command line:
-h Display this help
-d Dry run to update the configuration files.
-f Always update on the configuration files (existing files are renamed with
the .old suffix). Without this option, the new configuration files are
copied with the .new suffix
Environment variables:
INSTANCE_NAME settings.yml : general.instance_name
AUTOCOMPLETE settings.yml : search.autocomplete
BASE_URL settings.yml : server.base_url
Volume:
/etc/searxng the docker entry point copies settings.yml and uwsgi.ini in
this directory (see the -f command line option)"
EOF
}
# Parse command line
FORCE_CONF_UPDATE=0
DRY_RUN=0
while getopts "fdh" option
do
case $option in
f) FORCE_CONF_UPDATE=1 ;;
d) DRY_RUN=1 ;;
h)
help
exit 0
;;
*)
echo "unknow option ${option}"
exit 42
;;
esac
done
echo "SearXNG version $SEARXNG_VERSION"
# helpers to update the configuration files
patch_uwsgi_settings() {
CONF="$1"
# update uwsg.ini
sed -i \
-e "s|workers = .*|workers = ${UWSGI_WORKERS:-%k}|g" \
-e "s|threads = .*|threads = ${UWSGI_THREADS:-4}|g" \
"${CONF}"
}
patch_searxng_settings() {
CONF="$1"
# Make sure that there is trailing slash at the end of BASE_URL
# see https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion
export BASE_URL="${BASE_URL%/}/"
# update settings.yml
sed -i \
-e "s|base_url: false|base_url: ${BASE_URL}|g" \
-e "s/instance_name: \"SearXNG\"/instance_name: \"${INSTANCE_NAME}\"/g" \
-e "s/autocomplete: \"\"/autocomplete: \"${AUTOCOMPLETE}\"/g" \
-e "s/ultrasecretkey/$(head -c 24 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9')/g" \
"${CONF}"
}
update_conf() {
FORCE_CONF_UPDATE=$1
CONF="$2"
NEW_CONF="${2}.new"
OLD_CONF="${2}.old"
REF_CONF="$3"
PATCH_REF_CONF="$4"
if [ -f "${CONF}" ]; then
if [ "${REF_CONF}" -nt "${CONF}" ]; then
# There is a new version
if [ "$FORCE_CONF_UPDATE" -ne 0 ]; then
# Replace the current configuration
printf '⚠️ Automatically update %s to the new version\n' "${CONF}"
if [ ! -f "${OLD_CONF}" ]; then
printf 'The previous configuration is saved to %s\n' "${OLD_CONF}"
mv "${CONF}" "${OLD_CONF}"
fi
cp "${REF_CONF}" "${CONF}"
$PATCH_REF_CONF "${CONF}"
else
# Keep the current configuration
printf '⚠️ Check new version %s to make sure SearXNG is working properly\n' "${NEW_CONF}"
cp "${REF_CONF}" "${NEW_CONF}"
$PATCH_REF_CONF "${NEW_CONF}"
fi
else
printf 'Use existing %s\n' "${CONF}"
fi
else
printf 'Create %s\n' "${CONF}"
cp "${REF_CONF}" "${CONF}"
$PATCH_REF_CONF "${CONF}"
fi
}
# make sure there are uwsgi settings
update_conf "${FORCE_CONF_UPDATE}" "${UWSGI_SETTINGS_PATH}" "/usr/local/searxng/container/uwsgi.ini" "patch_uwsgi_settings"
# make sure there are searxng settings
update_conf "${FORCE_CONF_UPDATE}" "${SEARXNG_SETTINGS_PATH}" "/usr/local/searxng/searx/settings.yml" "patch_searxng_settings"
# dry run (to update configuration files, then inspect them)
if [ $DRY_RUN -eq 1 ]; then
printf 'Dry run\n'
exit
fi
printf 'Listen on %s\n' "${BIND_ADDRESS}"
# Start uwsgi
# TODO: "--http-socket" will be removed in the future (see uwsgi.ini.new config file): https://github.com/searxng/searxng/pull/4578
exec /usr/local/searxng/venv/bin/uwsgi --http-socket "${BIND_ADDRESS}" "${UWSGI_SETTINGS_PATH}"

166
container/entrypoint.sh Executable file
View file

@ -0,0 +1,166 @@
#!/bin/sh
# shellcheck shell=dash
set -u
check_file() {
local target="$1"
if [ ! -f "$target" ]; then
cat <<EOF
!!!
!!! ERROR
!!! "$target" is not a valid file, exiting...
!!!
EOF
exit 127
fi
}
check_directory() {
local target="$1"
if [ ! -d "$target" ]; then
cat <<EOF
!!!
!!! ERROR
!!! "$target" is not a valid directory, exiting...
!!!
EOF
exit 127
fi
}
setup_ownership() {
local target="$1"
local type="$2"
case "$type" in
file | directory) ;;
*)
cat <<EOF
!!!
!!! ERROR
!!! "$type" is not a valid type, exiting...
!!!
EOF
exit 1
;;
esac
if [ "$(stat -c %U:%G "$target")" != "searxng:searxng" ]; then
if [ "$(id -u)" -eq 0 ]; then
chown -R searxng:searxng "$target"
else
cat <<EOF
!!!
!!! WARNING
!!! "$target" $type is not owned by "searxng"
!!! This may cause issues when running SearXNG
!!!
!!! Run the container as root to fix this issue automatically
!!! Alternatively, you can chown the $type manually:
!!! $ chown -R searxng:searxng "$target"
!!!
EOF
fi
fi
}
# Apply envs to uwsgi.ini
setup_uwsgi() {
local timestamp
timestamp=$(stat -c %Y "$UWSGI_SETTINGS_PATH")
sed -i \
-e "s|workers = .*|workers = ${UWSGI_WORKERS:-%k}|g" \
-e "s|threads = .*|threads = ${UWSGI_THREADS:-4}|g" \
"$UWSGI_SETTINGS_PATH"
# Restore timestamp
touch -c -d "@$timestamp" "$UWSGI_SETTINGS_PATH"
}
# Apply envs to settings.yml
setup_searxng() {
local timestamp
timestamp=$(stat -c %Y "$SEARXNG_SETTINGS_PATH")
# Ensure trailing slash in BASE_URL
# https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion
export BASE_URL="${BASE_URL%/}/"
sed -i \
-e "s|base_url: false|base_url: ${BASE_URL:-false}|g" \
-e "s/instance_name: \"SearXNG\"/instance_name: \"${INSTANCE_NAME:-SearXNG}\"/g" \
-e "s/autocomplete: \"\"/autocomplete: \"${AUTOCOMPLETE:-}\"/g" \
-e "s/ultrasecretkey/$(head -c 24 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9')/g" \
"$SEARXNG_SETTINGS_PATH"
# Restore timestamp
touch -c -d "@$timestamp" "$SEARXNG_SETTINGS_PATH"
}
# Handle volume mounts
volume_handler() {
local target="$1"
# Check if it's a valid directory
check_directory "$target"
setup_ownership "$target" "directory"
}
# Handle configuration file updates
config_handler() {
local target="$1"
local template="$2"
local new_template_target="$target.new"
# Create/Update the configuration file
if [ -f "$target" ]; then
setup_ownership "$target" "file"
if [ "$template" -nt "$target" ]; then
cp -pfT "$template" "$new_template_target"
cat <<EOF
...
... INFORMATION
... Update available for "$target"
... It is recommended to update the configuration file to ensure proper functionality
...
... New version placed at "$new_template_target"
... Please review and merge changes
...
EOF
fi
else
cat <<EOF
...
... INFORMATION
... "$target" does not exist, creating from template...
...
EOF
cp -pfT "$template" "$target"
fi
# Check if it's a valid file
check_file "$target"
}
echo "SearXNG $SEARXNG_VERSION"
# Check for volume mounts
volume_handler "$CONFIG_PATH"
volume_handler "$DATA_PATH"
# Check for updates in files
config_handler "$UWSGI_SETTINGS_PATH" "/usr/local/searxng/.template/uwsgi.ini"
config_handler "$SEARXNG_SETTINGS_PATH" "/usr/local/searxng/searx/settings.yml"
# Update files
setup_uwsgi
setup_searxng
exec /usr/local/searxng/venv/bin/uwsgi --http-socket "$BIND_ADDRESS" "$UWSGI_SETTINGS_PATH"

View file

@ -1,5 +1,3 @@
# For armv7 architecture
FROM docker.io/library/python:3.13-slim AS builder
RUN apt-get update \
@ -26,11 +24,9 @@ RUN --mount=type=cache,id=pip,target=/root/.cache/pip python -m venv ./venv \
COPY ./searx/ ./searx/
ARG TIMESTAMP_SETTINGS=0
ARG TIMESTAMP_UWSGI=0
RUN python -m compileall -q searx \
&& touch -c --date=@$TIMESTAMP_SETTINGS ./searx/settings.yml \
&& touch -c --date=@$TIMESTAMP_UWSGI ./container/uwsgi.ini \
&& find /usr/local/searxng/searx/static \
\( -name '*.html' -o -name '*.css' -o -name '*.js' -o -name '*.svg' -o -name '*.ttf' -o -name '*.eot' \) \
-type f -exec gzip -9 -k {} + -exec brotli --best {} +
@ -70,7 +66,12 @@ WORKDIR /usr/local/searxng/
COPY --chown=searxng:searxng --from=builder /usr/local/searxng/venv/ ./venv/
COPY --chown=searxng:searxng --from=builder /usr/local/searxng/searx/ ./searx/
COPY --chown=searxng:searxng ./container/ ./container/
COPY --chown=searxng:searxng ./container/config/ ./.template/
COPY --chown=searxng:searxng ./container/entrypoint.sh ./entrypoint.sh
ARG TIMESTAMP_UWSGI="0"
RUN touch -c --date=@$TIMESTAMP_UWSGI ./.template/uwsgi.ini
LABEL org.opencontainers.image.authors="searxng <$GIT_URL>" \
org.opencontainers.image.created=$LABEL_DATE \
@ -103,4 +104,4 @@ EXPOSE 8080
HEALTHCHECK CMD wget --quiet --tries=1 --spider http://localhost:8080/healthz || exit 1
ENTRYPOINT ["/usr/local/searxng/container/docker-entrypoint.sh"]
ENTRYPOINT ["/usr/local/searxng/entrypoint.sh"]

View file

@ -181,10 +181,10 @@ Command line
<https://docs.docker.com/engine/reference/run/#foreground>`__.
In the :origin:`Dockerfile` the ENTRYPOINT_ is defined as
:origin:`container/docker-entrypoint.sh`
:origin:`container/entrypoint.sh`
.. code:: sh
docker run --rm -it searxng/searxng -h
.. program-output:: ../container/docker-entrypoint.sh -h
.. program-output:: ../container/entrypoint.sh -h

View file

@ -41,6 +41,7 @@
- ``duckduckgo``
- ``google``
- ``mwmbl``
- ``naver``
- ``quark``
- ``qwant``
- ``seznam``

View file

@ -4,7 +4,7 @@ cov-core==1.15.0
black==24.3.0
pylint==3.3.7
splinter==0.21.0
selenium==4.32.0
selenium==4.33.0
Pallets-Sphinx-Themes==2.3.0
Sphinx==7.4.7
sphinx-issues==5.0.1

View file

@ -7,15 +7,15 @@ lxml==5.4.0
pygments==2.19.1
python-dateutil==2.9.0.post0
pyyaml==6.0.2
httpx[http2]==0.24.1
httpx[http2]==0.28.1
httpx-socks[asyncio]==0.10.0
Brotli==1.1.0
uvloop==0.21.0
httpx-socks[asyncio]==0.7.7
setproctitle==1.3.6
redis==5.2.1
markdown-it-py==3.0.0
fasttext-predict==0.9.2.4
tomli==2.2.1; python_version < '3.11'
msgspec==0.19.0
typer-slim==0.15.3
typer-slim==0.16.0
isodate==0.7.2

View file

@ -21,7 +21,7 @@ log: logging.Logger = logging.getLogger("searx.answerers")
@dataclass
class AnswererInfo:
"""Object that holds informations about an answerer, these infos are shown
"""Object that holds information about an answerer, these infos are shown
to the user in the Preferences menu.
To be able to translate the information into other languages, the text must
@ -53,7 +53,7 @@ class Answerer(abc.ABC):
@abc.abstractmethod
def info(self) -> AnswererInfo:
"""Informations about the *answerer*, see :py:obj:`AnswererInfo`."""
"""Information about the *answerer*, see :py:obj:`AnswererInfo`."""
class ModuleAnswerer(Answerer):

View file

@ -149,6 +149,21 @@ def mwmbl(query, _lang):
return [result for result in results if not result.startswith("go: ") and not result.startswith("search: ")]
def naver(query, _lang):
# Naver search autocompleter
url = f"https://ac.search.naver.com/nx/ac?{urlencode({'q': query, 'r_format': 'json', 'st': 0})}"
response = get(url)
results = []
if response.ok:
data = response.json()
if data.get('items'):
for item in data['items'][0]:
results.append(item[0])
return results
def qihu360search(query, _lang):
# 360Search search autocompleter
url = f"https://sug.so.360.cn/suggest?{urlencode({'format': 'json', 'word': query})}"
@ -300,6 +315,7 @@ backends = {
'duckduckgo': duckduckgo,
'google': google_complete,
'mwmbl': mwmbl,
'naver': naver,
'quark': quark,
'qwant': qwant,
'seznam': seznam,

View file

@ -319,9 +319,9 @@ def dict_deepupdate(base_dict: dict, upd_dict: dict, names=None):
"""
# pylint: disable=too-many-branches
if not isinstance(base_dict, dict):
raise TypeError("argument 'base_dict' is not a ditionary type")
raise TypeError("argument 'base_dict' is not a dictionary type")
if not isinstance(upd_dict, dict):
raise TypeError("argument 'upd_dict' is not a ditionary type")
raise TypeError("argument 'upd_dict' is not a dictionary type")
if names is None:
names = []

View file

@ -42,7 +42,7 @@ class ExpireCacheCfg(msgspec.Struct): # pylint: disable=too-few-public-methods
DB will be created in `/tmp/sxng_cache_{self.name}.db`"""
MAX_VALUE_LEN: int = 1024 * 10
"""Max lenght of a *serialized* value."""
"""Max length of a *serialized* value."""
MAXHOLD_TIME: int = 60 * 60 * 24 * 7 # 7 days
"""Hold time (default in sec.), after which a value is removed from the cache."""
@ -80,7 +80,7 @@ class ExpireCacheCfg(msgspec.Struct): # pylint: disable=too-few-public-methods
@dataclasses.dataclass
class ExpireCacheStats:
"""Dataclass wich provides information on the status of the cache."""
"""Dataclass which provides information on the status of the cache."""
cached_items: dict[str, list[tuple[str, typing.Any, int]]]
"""Values in the cache mapped by context name.

View file

@ -4,29 +4,65 @@
make data.all
"""
from __future__ import annotations
__all__ = [
'ENGINE_TRAITS',
'CURRENCIES',
'USER_AGENTS',
'EXTERNAL_URLS',
'WIKIDATA_UNITS',
'EXTERNAL_BANGS',
'OSM_KEYS_TAGS',
'ENGINE_DESCRIPTIONS',
'LOCALES',
'ahmia_blacklist_loader',
]
__all__ = ["ahmia_blacklist_loader"]
import json
from pathlib import Path
import typing
data_dir = Path(__file__).parent
from .core import log, data_dir
from .currencies import CurrenciesDB
CURRENCIES: CurrenciesDB
USER_AGENTS: dict[str, typing.Any]
EXTERNAL_URLS: dict[str, typing.Any]
WIKIDATA_UNITS: dict[str, typing.Any]
EXTERNAL_BANGS: dict[str, typing.Any]
OSM_KEYS_TAGS: dict[str, typing.Any]
ENGINE_DESCRIPTIONS: dict[str, typing.Any]
ENGINE_TRAITS: dict[str, typing.Any]
LOCALES: dict[str, typing.Any]
lazy_globals = {
"CURRENCIES": CurrenciesDB(),
"USER_AGENTS": None,
"EXTERNAL_URLS": None,
"WIKIDATA_UNITS": None,
"EXTERNAL_BANGS": None,
"OSM_KEYS_TAGS": None,
"ENGINE_DESCRIPTIONS": None,
"ENGINE_TRAITS": None,
"LOCALES": None,
}
data_json_files = {
"USER_AGENTS": "useragents.json",
"EXTERNAL_URLS": "external_urls.json",
"WIKIDATA_UNITS": "wikidata_units.json",
"EXTERNAL_BANGS": "external_bangs.json",
"OSM_KEYS_TAGS": "osm_keys_tags.json",
"ENGINE_DESCRIPTIONS": "engine_descriptions.json",
"ENGINE_TRAITS": "engine_traits.json",
"LOCALES": "locales.json",
}
def _load(filename):
with open(data_dir / filename, encoding='utf-8') as f:
return json.load(f)
def __getattr__(name):
# lazy init of the global objects
if name not in lazy_globals:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
data = lazy_globals[name]
if data is not None:
return data
log.debug("init searx.data.%s", name)
with open(data_dir / data_json_files[name], encoding='utf-8') as f:
lazy_globals[name] = json.load(f)
return lazy_globals[name]
def ahmia_blacklist_loader():
@ -40,14 +76,3 @@ def ahmia_blacklist_loader():
"""
with open(data_dir / 'ahmia_blacklist.txt', encoding='utf-8') as f:
return f.read().split()
CURRENCIES = _load('currencies.json')
USER_AGENTS = _load('useragents.json')
EXTERNAL_URLS = _load('external_urls.json')
WIKIDATA_UNITS = _load('wikidata_units.json')
EXTERNAL_BANGS = _load('external_bangs.json')
OSM_KEYS_TAGS = _load('osm_keys_tags.json')
ENGINE_DESCRIPTIONS = _load('engine_descriptions.json')
ENGINE_TRAITS = _load('engine_traits.json')
LOCALES = _load('locales.json')

File diff suppressed because it is too large Load diff

29
searx/data/core.py Normal file
View file

@ -0,0 +1,29 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=missing-module-docstring
from __future__ import annotations
import pathlib
from searx import logger
from searx.cache import ExpireCacheCfg, ExpireCacheSQLite
log = logger.getChild("data")
data_dir = pathlib.Path(__file__).parent
_DATA_CACHE: ExpireCacheSQLite = None # type: ignore
def get_cache():
global _DATA_CACHE # pylint: disable=global-statement
if _DATA_CACHE is None:
_DATA_CACHE = ExpireCacheSQLite.build_cache(
ExpireCacheCfg(
name="DATA_CACHE",
# MAX_VALUE_LEN=1024 * 200, # max. 200kB length for a *serialized* value.
# MAXHOLD_TIME=60 * 60 * 24 * 7 * 4, # 4 weeks
)
)
return _DATA_CACHE

View file

@ -1428,7 +1428,7 @@
},
"CZK": {
"ar": "كرونة تشيكية",
"bg": "крони",
"bg": "Чешка крона",
"bn": "চেক কোরুনা",
"ca": "corona txeca",
"cs": "koruna česká",
@ -2687,7 +2687,7 @@
"uk": "Йорданський динар"
},
"JPY": {
"af": "Jen",
"af": "jen",
"ar": "ين ياباني",
"bg": "японска йена",
"bn": "জাপানি ইয়েন",
@ -4329,7 +4329,7 @@
"PLN": {
"af": "Złoty",
"ar": "زواتي بولندي",
"bg": "Полска злотаПолска злота",
"bg": "Полска злота",
"ca": "złoty",
"cs": "zlotý",
"da": "zloty",
@ -4375,7 +4375,7 @@
"PLZ": {
"af": "Złoty",
"ar": "زواتي بولندي",
"bg": "Полска злотаПолска злота",
"bg": "Полска злота",
"ca": "złoty",
"cs": "zlotý",
"da": "zloty",
@ -5881,6 +5881,10 @@
"tt": "самоа таласы",
"uk": "Самоанська тала"
},
"XAD": {
"en": "Arab accounting dinar",
"fr": "dinar arabe"
},
"XAF": {
"ar": "فرنك وسط إفريقي",
"bg": "Централноафрикански CFA франк",
@ -6375,6 +6379,7 @@
"$usd": "USD",
"1000 lira": "LBP",
"100ドル紙幣": "NIO",
"1億ベネズエラ・ボリバル": "VES",
"2019 zimbabwean dollar": "ZWL",
"5th zimbabwean dollar": "ZWL",
"A$": "AUD",
@ -6511,6 +6516,7 @@
"Z$": "ZWL",
"ZK": "ZMW",
"a$": "AUD",
"aad": "XAD",
"abd doları": "USD",
"adb unit of account": "XUA",
"ae92 0530 0000 1514 1185 002": "AED",
@ -6610,6 +6616,7 @@
"aoa": "AOA",
"apvienotās karalistes sterliņu mārciņa": "GBP",
"ar": "MGA",
"arab accounting dinar": "XAD",
"arabiemiraattien dirhami": "AED",
"arany mint befektetés": "XAU",
"arg$": "ARS",
@ -7438,6 +7445,7 @@
"dinar algierski": "DZD",
"dinar aljazair": "DZD",
"dinar alxeriano": "DZD",
"dinar arabe": "XAD",
"dinar argelino": "DZD",
"dinar bahrain": "BHD",
"dinar bahraini": "BHD",
@ -7458,6 +7466,7 @@
"dinar bhairéin": "BHD",
"dinar chuáit": "KWD",
"dinar coaitiano": "KWD",
"dinar comptable arabe": "XAD",
"dinar couaitiano": "KWD",
"dinar covaitiano": "KWD",
"dinar coveiteano": "KWD",
@ -9162,7 +9171,6 @@
"jordán dinár": "JOD",
"jordánsky dinár": "JOD",
"jordánský dinár": "JOD",
"jpy": "JPY",
"juan": "CNY",
"juanis": "CNY",
"juaņa": "CNY",
@ -9813,7 +9821,6 @@
"livre de sainte hélène": "SHP",
"livre des îles falkland": "FKP",
"livre des îles malouines": "FKP",
"livre egyptienne": "EGP",
"livre égyptienne": "EGP",
"livre libanaise": "LBP",
"livre soudanaise": "SDG",
@ -13367,7 +13374,6 @@
],
"крона чеська": "CZK",
"крона швеции": "SEK",
"крони": "CZK",
"куба писысы": "CUP",
"кубански пезос": "CUP",
"кубански песо": "CUP",
@ -13645,10 +13651,6 @@
"PLZ",
"PLN"
],
"полска злотаполска злота": [
"PLZ",
"PLN"
],
"польский злотый": [
"PLZ",
"PLN"

55
searx/data/currencies.py Normal file
View file

@ -0,0 +1,55 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Simple implementation to store currencies data in a SQL database."""
from __future__ import annotations
__all__ = ["CurrenciesDB"]
import json
import pathlib
from .core import get_cache, log
class CurrenciesDB:
# pylint: disable=missing-class-docstring
ctx_names = "data_currencies_names"
ctx_iso4217 = "data_currencies_iso4217"
json_file = pathlib.Path(__file__).parent / "currencies.json"
def __init__(self):
self.cache = get_cache()
def init(self):
if self.cache.properties("currencies loaded") != "OK":
self.load()
self.cache.properties.set("currencies loaded", "OK")
# F I X M E:
# do we need a maintenance .. rember: database is stored
# in /tmp and will be rebuild during the reboot anyway
def load(self):
log.debug("init searx.data.CURRENCIES")
with open(self.json_file, encoding="utf-8") as f:
data_dict = json.load(f)
for key, value in data_dict["names"].items():
self.cache.set(key=key, value=value, ctx=self.ctx_names, expire=None)
for key, value in data_dict["iso4217"].items():
self.cache.set(key=key, value=value, ctx=self.ctx_iso4217, expire=None)
def name_to_iso4217(self, name):
self.init()
ret_val = self.cache.get(key=name, default=name, ctx=self.ctx_names)
if isinstance(ret_val, list):
# if more alternatives, use the last in the list
ret_val = ret_val[-1]
return ret_val
def iso4217_to_name(self, iso4217, language):
self.init()
iso4217_languages: dict = self.cache.get(key=iso4217, default={}, ctx=self.ctx_iso4217)
return iso4217_languages.get(language, iso4217)

File diff suppressed because one or more lines are too long

View file

@ -7381,7 +7381,6 @@
"ko": "korean",
"ks": "kashmiri",
"ku": "kurdish",
"kw": "cornish",
"la": "latin",
"lb": "luxembourgish",
"ln": "lingala",

View file

@ -5,7 +5,7 @@
],
"ua": "Mozilla/5.0 ({os}; rv:{version}) Gecko/20100101 Firefox/{version}",
"versions": [
"138.0",
"137.0"
"139.0",
"138.0"
]
}

View file

@ -4199,11 +4199,6 @@
"symbol": "St",
"to_si_factor": 13450.0
},
"Q235729": {
"si_name": "Q11574",
"symbol": "y (365 days)",
"to_si_factor": 31536000.0
},
"Q23823681": {
"si_name": "Q25236",
"symbol": "TW",

View file

@ -79,7 +79,7 @@ class EngineCache:
<searx.cache.ExpireCacheSQLite>`).
In the :origin:`searx/engines/demo_offline.py` engine you can find an
exemplary implementation of such a cache other exaples are implemeted
exemplary implementation of such a cache other examples are implemented
in:
- :origin:`searx/engines/radio_browser.py`

View file

@ -156,7 +156,7 @@ def parse_image_item(item):
def parse_video_item(item):
# in video items, the title is more or less a "content description", we try
# to reduce the lenght of the title ..
# to reduce the length of the title ..
title = item["title"]
content = ""

View file

@ -3,6 +3,7 @@
"""
import json
from searx.result_types import EngineResults
# about
about = {
@ -28,13 +29,15 @@ def request(_query, params):
return params
def response(resp):
def response(resp) -> EngineResults:
res = EngineResults()
# remove first and last lines to get only json
json_resp = resp.text[resp.text.find('\n') + 1 : resp.text.rfind('\n') - 2]
try:
conversion_rate = float(json.loads(json_resp)["to"][0]["mid"])
except IndexError:
return []
return res
answer = '{0} {1} = {2} {3}, 1 {1} ({5}) = {4} {3} ({6})'.format(
resp.search_params['amount'],
resp.search_params['from'],
@ -46,5 +49,5 @@ def response(resp):
)
url = f"https://duckduckgo.com/?q={resp.search_params['from']}+to+{resp.search_params['to']}"
return [{"answer": answer, "url": url}]
res.add(res.types.Answer(answer=answer, url=url))
return res

View file

@ -58,19 +58,21 @@ paging = True
time_range_support = True
safesearch = True # user can't select but the results are filtered
url = "https://html.duckduckgo.com/html"
url = "https://html.duckduckgo.com/html/"
time_range_dict = {'day': 'd', 'week': 'w', 'month': 'm', 'year': 'y'}
form_data = {'v': 'l', 'api': 'd.js', 'o': 'json'}
CACHE: EngineCache
_CACHE: EngineCache = None # type: ignore
"""Persistent (SQLite) key/value cache that deletes its values after ``expire``
seconds."""
def init(_): # pylint: disable=unused-argument
global CACHE # pylint: disable=global-statement
CACHE = EngineCache("duckduckgo") # type:ignore
def get_cache():
global _CACHE # pylint: disable=global-statement
if _CACHE is None:
_CACHE = EngineCache("duckduckgo") # type:ignore
return _CACHE
def get_vqd(query: str, region: str, force_request: bool = False) -> str:
@ -105,8 +107,9 @@ def get_vqd(query: str, region: str, force_request: bool = False) -> str:
seems the block list is a sliding window: to get my IP rid from the bot list
I had to cool down my IP for 1h (send no requests from that IP to DDG).
"""
key = CACHE.secret_hash(f"{query}//{region}")
value = CACHE.get(key=key)
cache = get_cache()
key = cache.secret_hash(f"{query}//{region}")
value = cache.get(key=key)
if value is not None and not force_request:
logger.debug("vqd: re-use cached value: %s", value)
return value
@ -124,15 +127,16 @@ def get_vqd(query: str, region: str, force_request: bool = False) -> str:
logger.error("vqd: got HTTP %s from duckduckgo.com", resp.status_code)
if value:
CACHE.set(key=key, value=value)
cache.set(key=key, value=value)
else:
logger.error("vqd value from duckduckgo.com ", resp.status_code)
return value
def set_vqd(query: str, region: str, value: str):
key = CACHE.secret_hash(f"{query}//{region}")
CACHE.set(key=key, value=value, expire=3600)
cache = get_cache()
key = cache.secret_hash(f"{query}//{region}")
cache.set(key=key, value=value, expire=3600)
def get_ddg_lang(eng_traits: EngineTraits, sxng_locale, default='en_US'):
@ -244,7 +248,6 @@ def quote_ddg_bangs(query):
def request(query, params):
query = quote_ddg_bangs(query)
if len(query) >= 500:
@ -252,93 +255,79 @@ def request(query, params):
params["url"] = None
return
# Advanced search syntax ends in CAPTCHA
# https://duckduckgo.com/duckduckgo-help-pages/results/syntax/
query = " ".join(
[
x.removeprefix("site:").removeprefix("intitle:").removeprefix("inurl:").removeprefix("filetype:")
for x in query.split()
]
)
eng_region: str = traits.get_region(params['searxng_locale'], traits.all_locale) # type: ignore
if eng_region == "wt-wt":
# https://html.duckduckgo.com/html sets an empty value for "all".
eng_region = ""
params['data']['kl'] = eng_region
params['cookies']['kl'] = eng_region
# Note: The API is reverse-engineered from DuckDuckGo's HTML webpage
# (https://html.duckduckgo.com/html/) and may be subject to additional bot detection mechanisms
# and breaking changes in the future.
#
# The params['data'] dictionary can have the following key parameters, in this order:
# - q (str): Search query string
# - b (str): Beginning parameter - empty string for first page requests
# - s (int): Search offset for pagination
# - nextParams (str): Continuation parameters from previous page response, typically empty
# - v (str): Typically 'l' for subsequent pages
# - o (str): Output format, typically 'json'
# - dc (int): Display count - value equal to offset (s) + 1
# - api (str): API endpoint identifier, typically 'd.js'
# - vqd (str): Validation query digest
# - kl (str): Keyboard language/region code (e.g., 'en-us')
# - df (str): Time filter, maps to values like 'd' (day), 'w' (week), 'm' (month), 'y' (year)
# eng_lang = get_ddg_lang(traits, params['searxng_locale'])
params['url'] = url
params['method'] = 'POST'
params['data']['q'] = query
# The API is not documented, so we do some reverse engineering and emulate
# what https://html.duckduckgo.com/html does when you press "next Page" link
# again and again ..
params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'
params['headers']['Sec-Fetch-Dest'] = "document"
params['headers']['Sec-Fetch-Mode'] = "navigate" # at least this one is used by ddg's bot detection
params['headers']['Sec-Fetch-Site'] = "same-origin"
params['headers']['Sec-Fetch-User'] = "?1"
# Form of the initial search page does have empty values in the form
if params['pageno'] == 1:
params['data']['b'] = ""
params['data']['df'] = ''
if params['time_range'] in time_range_dict:
params['data']['df'] = time_range_dict[params['time_range']]
params['cookies']['df'] = time_range_dict[params['time_range']]
if params['pageno'] == 2:
# second page does have an offset of 20
offset = (params['pageno'] - 1) * 20
elif params['pageno'] >= 2:
offset = 10 + (params['pageno'] - 2) * 15 # Page 2 = 10, Page 3+ = 10 + n*15
params['data']['s'] = offset
params['data']['dc'] = offset + 1
elif params['pageno'] > 2:
# third and following pages do have an offset of 20 + n*50
offset = 20 + (params['pageno'] - 2) * 50
params['data']['s'] = offset
params['data']['dc'] = offset + 1
if params['pageno'] > 1:
# initial page does not have these additional data in the input form
params['data']['o'] = form_data.get('o', 'json')
params['data']['api'] = form_data.get('api', 'd.js')
params['data']['nextParams'] = form_data.get('nextParams', '')
params['data']['v'] = form_data.get('v', 'l')
params['headers']['Referer'] = url
params['data']['o'] = form_data.get('o', 'json')
params['data']['dc'] = offset + 1
params['data']['api'] = form_data.get('api', 'd.js')
# vqd is required to request other pages after the first one
vqd = get_vqd(query, eng_region, force_request=False)
# Certain conditions must be met in order to call up one of the
# following pages ...
if vqd:
params['data']['vqd'] = vqd # follow up pages / requests needs a vqd argument
params['data']['vqd'] = vqd
else:
# Don't try to call follow up pages without a vqd value. DDG
# recognizes this as a request from a bot. This lowers the
# Don't try to call follow up pages without a vqd value.
# DDG recognizes this as a request from a bot. This lowers the
# reputation of the SearXNG IP and DDG starts to activate CAPTCHAs.
params["url"] = None
return
if params['searxng_locale'].startswith("zh"):
# Some locales (at least China) do not have a "next page" button and ddg
# Some locales (at least China) do not have a "next page" button and DDG
# will return a HTTP/2 403 Forbidden for a request of such a page.
params["url"] = None
return
# Put empty kl in form data if language/region set to all
if eng_region == "wt-wt":
params['data']['kl'] = ""
else:
params['data']['kl'] = eng_region
params['data']['df'] = ''
if params['time_range'] in time_range_dict:
params['data']['df'] = time_range_dict[params['time_range']]
params['cookies']['df'] = time_range_dict[params['time_range']]
params['cookies']['kl'] = eng_region
params['url'] = url
params['method'] = 'POST'
params['headers']['Content-Type'] = 'application/x-www-form-urlencoded'
params['headers']['Referer'] = url
params['headers']['Sec-Fetch-Dest'] = "document"
params['headers']['Sec-Fetch-Mode'] = "navigate" # at least this one is used by ddg's bot detection
params['headers']['Sec-Fetch-Site'] = "same-origin"
params['headers']['Sec-Fetch-User'] = "?1"
logger.debug("param headers: %s", params['headers'])
logger.debug("param data: %s", params['data'])
logger.debug("param cookies: %s", params['cookies'])
@ -383,8 +372,9 @@ def response(resp) -> EngineResults:
continue
item["title"] = extract_text(title)
item["url"] = eval_xpath(div_result, './/h2/a/@href')[0]
item["content"] = extract_text(eval_xpath(div_result, './/a[contains(@class, "result__snippet")]')[0])
item["content"] = extract_text(
eval_xpath_getindex(div_result, './/a[contains(@class, "result__snippet")]', 0, [])
)
results.append(item)
zero_click_info_xpath = '//div[@id="zero_click_abstract"]'

View file

@ -1,6 +1,13 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Invidious (Videos)
If you want to use invidious with SearXNG you should setup one locally.
No public instance offer a public API now
- https://github.com/searxng/searxng/issues/2722#issuecomment-2884993248
"""
from __future__ import annotations
import time
import random
@ -13,7 +20,7 @@ from searx.utils import humanize_number
about = {
"website": 'https://api.invidious.io/',
"wikidata_id": 'Q79343316',
"official_api_documentation": 'https://github.com/iv-org/documentation/blob/master/API.md',
"official_api_documentation": 'https://docs.invidious.io/api/',
"use_official_api": True,
"require_api_key": False,
"results": 'JSON',
@ -25,7 +32,12 @@ paging = True
time_range_support = True
# base_url can be overwritten by a list of URLs in the settings.yml
base_url = 'https://vid.puffyan.us'
base_url: list | str = []
def init(_):
if not base_url:
raise ValueError("missing invidious base_url")
def request(query, params):

View file

@ -1,7 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Material Icons (images)
"""
"""Material Icons (icons)"""
import re
from json import loads
@ -14,6 +12,8 @@ about = {
"require_api_key": False,
"results": 'JSON',
}
categories = ['images', 'icons']
search_url = "https://fonts.google.com/metadata/icons?key=material_symbols&incomplete=true"
result_url = "https://fonts.google.com/icons?icon.query={query}&selected=Material+Symbols+Outlined:{icon_name}:FILL@0{fill};wght@400;GRAD@0;opsz@24" # pylint: disable=line-too-long
img_src_url = "https://fonts.gstatic.com/s/i/short-term/release/materialsymbolsoutlined/{icon_name}/{svg_type}/24px.svg"
@ -46,7 +46,7 @@ def response(resp):
continue
tags = [tag.title() for tag in result["tags"]]
categories = [category.title() for category in result["categories"]]
icon_categories = [category.title() for category in result["categories"]]
results.append(
{
@ -54,7 +54,7 @@ def response(resp):
'url': result_url.format(icon_name=result["name"], query=result["name"], fill=0 if outlined else 1),
'img_src': img_src_url.format(icon_name=result["name"], svg_type=svg_type),
'title': result["name"].replace("_", "").title(),
'content': ", ".join(tags) + " / " + ", ".join(categories),
'content': ", ".join(tags) + " / " + ", ".join(icon_categories),
}
)

210
searx/engines/naver.py Normal file
View file

@ -0,0 +1,210 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# pylint: disable=line-too-long
"""Naver for SearXNG"""
from urllib.parse import urlencode
from lxml import html
from searx.exceptions import SearxEngineAPIException, SearxEngineXPathException
from searx.result_types import EngineResults, MainResult
from searx.utils import (
eval_xpath_getindex,
eval_xpath_list,
eval_xpath,
extract_text,
extr,
html_to_text,
parse_duration_string,
js_variable_to_python,
)
# engine metadata
about = {
"website": "https://search.naver.com",
"wikidata_id": "Q485639",
"use_official_api": False,
"require_api_key": False,
"results": "HTML",
"language": "ko",
}
categories = []
paging = True
time_range_support = True
time_range_dict = {"day": "1d", "week": "1w", "month": "1m", "year": "1y"}
base_url = "https://search.naver.com"
naver_category = "general"
"""Naver supports general, images, news, videos search.
- ``general``: search for general
- ``images``: search for images
- ``news``: search for news
- ``videos``: search for videos
"""
# Naver cannot set the number of results on one page, set default value for paging
naver_category_dict = {
"general": {
"start": 15,
"where": "web",
},
"images": {
"start": 50,
"where": "image",
},
"news": {
"start": 10,
"where": "news",
},
"videos": {
"start": 48,
"where": "video",
},
}
def init(_):
if naver_category not in ('general', 'images', 'news', 'videos'):
raise SearxEngineAPIException(f"Unsupported category: {naver_category}")
def request(query, params):
query_params = {
"query": query,
}
if naver_category in naver_category_dict:
query_params["start"] = (params["pageno"] - 1) * naver_category_dict[naver_category]["start"] + 1
query_params["where"] = naver_category_dict[naver_category]["where"]
if params["time_range"] in time_range_dict:
query_params["nso"] = f"p:{time_range_dict[params['time_range']]}"
params["url"] = f"{base_url}/search.naver?{urlencode(query_params)}"
return params
def response(resp) -> EngineResults:
parsers = {'general': parse_general, 'images': parse_images, 'news': parse_news, 'videos': parse_videos}
return parsers[naver_category](resp.text)
def parse_general(data):
results = EngineResults()
dom = html.fromstring(data)
for item in eval_xpath_list(dom, "//ul[contains(@class, 'lst_total')]/li[contains(@class, 'bx')]"):
thumbnail = None
try:
thumbnail = eval_xpath_getindex(item, ".//div[contains(@class, 'thumb_single')]//img/@data-lazysrc", 0)
except (ValueError, TypeError, SearxEngineXPathException):
pass
results.add(
MainResult(
title=extract_text(eval_xpath(item, ".//a[contains(@class, 'link_tit')]")),
url=eval_xpath_getindex(item, ".//a[contains(@class, 'link_tit')]/@href", 0),
content=extract_text(
eval_xpath(item, ".//div[contains(@class, 'total_dsc_wrap')]//a[contains(@class, 'api_txt_lines')]")
),
thumbnail=thumbnail,
)
)
return results
def parse_images(data):
results = []
match = extr(data, '<script>var imageSearchTabData=', '</script>')
if match:
json = js_variable_to_python(match.strip())
items = json.get('content', {}).get('items', [])
for item in items:
results.append(
{
"template": "images.html",
"url": item.get('link'),
"thumbnail_src": item.get('thumb'),
"img_src": item.get('originalUrl'),
"title": html_to_text(item.get('title')),
"source": item.get('source'),
"resolution": f"{item.get('orgWidth')} x {item.get('orgHeight')}",
}
)
return results
def parse_news(data):
results = EngineResults()
dom = html.fromstring(data)
for item in eval_xpath_list(
dom, "//div[contains(@class, 'sds-comps-base-layout') and contains(@class, 'sds-comps-full-layout')]"
):
title = extract_text(eval_xpath(item, ".//span[contains(@class, 'sds-comps-text-type-headline1')]/text()"))
url = eval_xpath_getindex(item, ".//a[@href and @nocr='1']/@href", 0)
content = extract_text(eval_xpath(item, ".//span[contains(@class, 'sds-comps-text-type-body1')]"))
thumbnail = None
try:
thumbnail = eval_xpath_getindex(
item,
".//div[contains(@class, 'sds-comps-image') and contains(@class, 'sds-rego-thumb-overlay')]//img[@src]/@src",
0,
)
except (ValueError, TypeError, SearxEngineXPathException):
pass
if title and content and url:
results.add(
MainResult(
title=title,
url=url,
content=content,
thumbnail=thumbnail,
)
)
return results
def parse_videos(data):
results = []
dom = html.fromstring(data)
for item in eval_xpath_list(dom, "//li[contains(@class, 'video_item')]"):
thumbnail = None
try:
thumbnail = eval_xpath_getindex(item, ".//img[contains(@class, 'thumb')]/@src", 0)
except (ValueError, TypeError, SearxEngineXPathException):
pass
length = None
try:
length = parse_duration_string(extract_text(eval_xpath(item, ".//span[contains(@class, 'time')]")))
except (ValueError, TypeError):
pass
results.append(
{
"template": "videos.html",
"title": extract_text(eval_xpath(item, ".//a[contains(@class, 'info_title')]")),
"url": eval_xpath_getindex(item, ".//a[contains(@class, 'info_title')]/@href", 0),
"thumbnail": thumbnail,
'length': length,
}
)
return results

View file

@ -1,6 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Pinterest (images)
"""
"""Pinterest (images)"""
from json import dumps
@ -28,6 +27,11 @@ def request(query, params):
'context': {},
}
params['url'] = f"{base_url}/resource/BaseSearchResource/get/?data={dumps(args)}"
params['headers'] = {
'X-Pinterest-AppState': 'active',
'X-Pinterest-Source-Url': '/ideas/',
'X-Pinterest-PWS-Handler': 'www/ideas.js',
}
return params

View file

@ -137,19 +137,20 @@ def _get_request_id(query, params):
if l.territory:
headers['Accept-Language'] = f"{l.language}-{l.territory},{l.language};" "q=0.9,*;" "q=0.5"
resp_text = get(url, headers=headers).text # type: ignore
resp = get(url, headers=headers)
for line in resp_text.split("\n"):
for line in resp.text.split("\n"):
if "window.searchId = " in line:
return line.split("= ")[1][:-1].replace('"', "")
return line.split("= ")[1][:-1].replace('"', ""), resp.cookies
return None
raise RuntimeError("Couldn't find any request id for presearch")
def request(query, params):
request_id = _get_request_id(query, params)
request_id, cookies = _get_request_id(query, params)
params["headers"]["Accept"] = "application/json"
params["url"] = f"{base_url}/results?id={request_id}"
params["cookies"] = cookies
return params

View file

@ -11,7 +11,7 @@ about = {
"require_api_key": False,
"results": 'JSON',
}
categories = ['images']
categories = ['images', 'icons']
icons_list_url = 'https://cdn.selfh.st/directory/icons.json'

View file

@ -1,5 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Svgrepo (images)
"""Svgrepo (icons)
"""
from lxml import html
@ -14,7 +14,7 @@ about = {
}
paging = True
categories = ['images']
categories = ['images', 'icons']
base_url = "https://www.svgrepo.com"
results_xpath = "//div[@class='style_nodeListing__7Nmro']/div"

View file

@ -77,7 +77,7 @@ def response(resp):
elif item_type == 'video':
results.append(_video(item))
else:
logger.error("unknow result type: %s", item_type)
logger.error("unknown result type: %s", item_type)
return results

50
searx/engines/uxwing.py Normal file
View file

@ -0,0 +1,50 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
"""UXwing (images)"""
from urllib.parse import quote_plus
from lxml import html
from searx.utils import eval_xpath, eval_xpath_list, extract_text
about = {
"website": 'https://uxwing.com',
"wikidata_id": None,
"official_api_documentation": None,
"use_official_api": False,
"require_api_key": False,
"results": 'HTML',
}
categories = ['images', 'icons']
base_url = "https://uxwing.com"
def request(query, params):
params['url'] = f"{base_url}/?s={quote_plus(query)}"
return params
def response(resp):
results = []
doc = html.fromstring(resp.text)
for result in eval_xpath_list(doc, "//article[starts-with(@id, 'post')]"):
classes = extract_text(eval_xpath(result, "./@class")).split(" ")
tags = []
for css_class in classes:
for prefix in ("category", "tag"):
if css_class.startswith(prefix):
tag = css_class.removeprefix(prefix)
tags.append(tag.replace("-", " ").title())
results.append(
{
'template': 'images.html',
'url': extract_text(eval_xpath(result, "./a/@href")),
'img_src': extract_text(eval_xpath(result, ".//img/@src")),
'title': extract_text(eval_xpath(result, ".//img/@alt")),
'content': ', '.join(tags),
}
)
return results

View file

@ -44,6 +44,29 @@ time_range_dict = {
'month': ('1m', 'm'),
}
region2domain = {
"CO": "co.search.yahoo.com", # Colombia
"TH": "th.search.yahoo.com", # Thailand
"VE": "ve.search.yahoo.com", # Venezuela
"CL": "cl.search.yahoo.com", # Chile
"HK": "hk.search.yahoo.com", # Hong Kong
"PE": "pe.search.yahoo.com", # Peru
"CA": "ca.search.yahoo.com", # Canada
"DE": "de.search.yahoo.com", # Germany
"FR": "fr.search.yahoo.com", # France
"TW": "tw.search.yahoo.com", # Taiwan
"GB": "uk.search.yahoo.com", # United Kingdom
"UK": "uk.search.yahoo.com",
"BR": "br.search.yahoo.com", # Brazil
"IN": "in.search.yahoo.com", # India
"ES": "espanol.search.yahoo.com", # Espanol
"PH": "ph.search.yahoo.com", # Philippines
"AR": "ar.search.yahoo.com", # Argentina
"MX": "mx.search.yahoo.com", # Mexico
"SG": "sg.search.yahoo.com", # Singapore
}
"""Map regions to domain"""
lang2domain = {
'zh_chs': 'hk.search.yahoo.com',
'zh_cht': 'tw.search.yahoo.com',
@ -65,40 +88,40 @@ lang2domain = {
yahoo_languages = {
"all": "any",
"ar": "ar",
"bg": "bg",
"cs": "cs",
"da": "da",
"de": "de",
"el": "el",
"en": "en",
"es": "es",
"et": "et",
"fi": "fi",
"fr": "fr",
"he": "he",
"hr": "hr",
"hu": "hu",
"it": "it",
"ja": "ja",
"ko": "ko",
"lt": "lt",
"lv": "lv",
"nl": "nl",
"no": "no",
"pl": "pl",
"pt": "pt",
"ro": "ro",
"ru": "ru",
"sk": "sk",
"sl": "sl",
"sv": "sv",
"th": "th",
"tr": "tr",
"zh": "zh_chs",
"ar": "ar", # Arabic
"bg": "bg", # Bulgarian
"cs": "cs", # Czech
"da": "da", # Danish
"de": "de", # German
"el": "el", # Greek
"en": "en", # English
"es": "es", # Spanish
"et": "et", # Estonian
"fi": "fi", # Finnish
"fr": "fr", # French
"he": "he", # Hebrew
"hr": "hr", # Croatian
"hu": "hu", # Hungarian
"it": "it", # Italian
"ja": "ja", # Japanese
"ko": "ko", # Korean
"lt": "lt", # Lithuanian
"lv": "lv", # Latvian
"nl": "nl", # Dutch
"no": "no", # Norwegian
"pl": "pl", # Polish
"pt": "pt", # Portuguese
"ro": "ro", # Romanian
"ru": "ru", # Russian
"sk": "sk", # Slovak
"sl": "sl", # Slovenian
"sv": "sv", # Swedish
"th": "th", # Thai
"tr": "tr", # Turkish
"zh": "zh_chs", # Chinese (Simplified)
"zh_Hans": "zh_chs",
'zh-CN': "zh_chs",
"zh_Hant": "zh_cht",
"zh_Hant": "zh_cht", # Chinese (Traditional)
"zh-HK": "zh_cht",
'zh-TW': "zh_cht",
}
@ -107,7 +130,7 @@ yahoo_languages = {
def request(query, params):
"""build request"""
lang = params["language"].split("-")[0]
lang, region = (params["language"].split("-") + [None])[:2]
lang = yahoo_languages.get(lang, "any")
offset = (params['pageno'] - 1) * 7 + 1
@ -127,9 +150,11 @@ def request(query, params):
}
)
domain = lang2domain.get(lang, '%s.search.yahoo.com' % lang)
domain = region2domain.get(region)
if not domain:
domain = lang2domain.get(lang, '%s.search.yahoo.com' % lang)
params['url'] = 'https://%s/search?%s' % (domain, args)
return params
params['domain'] = domain
def parse_url(url_string):
@ -157,14 +182,22 @@ def response(resp):
results = []
dom = html.fromstring(resp.text)
url_xpath = './/div[contains(@class,"compTitle")]/h3/a/@href'
title_xpath = './/h3//a/@aria-label'
domain = resp.search_params['domain']
if domain == "search.yahoo.com":
url_xpath = './/div[contains(@class,"compTitle")]/a/@href'
title_xpath = './/div[contains(@class,"compTitle")]/a/h3/span'
# parse results
for result in eval_xpath_list(dom, '//div[contains(@class,"algo-sr")]'):
url = eval_xpath_getindex(result, './/h3/a/@href', 0, default=None)
url = eval_xpath_getindex(result, url_xpath, 0, default=None)
if url is None:
continue
url = parse_url(url)
title = eval_xpath_getindex(result, './/h3//a/@aria-label', 0, default='')
title = eval_xpath_getindex(result, title_xpath, 0, default='')
title: str = extract_text(title)
content = eval_xpath_getindex(result, './/div[contains(@class, "compText")]', 0, default='')
content: str = extract_text(content, allow_none=True)

View file

@ -180,6 +180,8 @@ def fetch_traits(engine_traits: EngineTraits) -> None:
# pylint: disable=import-outside-toplevel, too-many-branches
import babel
import httpx
from searx.network import get # see https://github.com/searxng/searxng/issues/762
from searx.locales import language_tag
@ -191,7 +193,7 @@ def fetch_traits(engine_traits: EngineTraits) -> None:
try:
resp = get(base_url, verify=False)
except SearxException as exc:
except (SearxException, httpx.ConnectError) as exc:
print(f"ERROR: zlibrary domain '{base_url}' is seized?")
print(f" --> {exc}")
_use_old_values()

View file

@ -42,7 +42,7 @@ class SXNG_Request(flask.Request):
"""list of searx.plugins.Plugin.id (the id of the plugins)"""
preferences: "searx.preferences.Preferences"
"""The prefernces of the request."""
"""The preferences of the request."""
errors: list[str]
"""A list of errors (translated text) added by :py:obj:`searx.webapp` in

View file

@ -140,7 +140,7 @@ class FaviconCacheConfig(msgspec.Struct): # pylint: disable=too-few-public-meth
@dataclasses.dataclass
class FaviconCacheStats:
"""Dataclass wich provides information on the status of the cache."""
"""Dataclass which provides information on the status of the cache."""
favicons: int | None = None
bytes: int | None = None
@ -387,7 +387,7 @@ CREATE TABLE IF NOT EXISTS blob_map (
self.properties.set("LAST_MAINTENANCE", "") # hint: this (also) sets the m_time of the property!
# Do maintenance tasks. This can be take a little more time, to avoid
# DB locks, etablish a new DB connecton.
# DB locks, establish a new DB connection.
with self.connect() as conn:

View file

@ -24,7 +24,7 @@ LOOP = None
SSLCONTEXTS: Dict[Any, SSLContext] = {}
def shuffle_ciphers(ssl_context):
def shuffle_ciphers(ssl_context: SSLContext):
"""Shuffle httpx's default ciphers of a SSL context randomly.
From `What Is TLS Fingerprint and How to Bypass It`_
@ -41,16 +41,16 @@ def shuffle_ciphers(ssl_context):
https://www.zenrows.com/blog/what-is-tls-fingerprint#how-to-bypass-tls-fingerprinting
"""
c_list = httpx._config.DEFAULT_CIPHERS.split(':') # pylint: disable=protected-access
c_list = [cipher["name"] for cipher in ssl_context.get_ciphers()]
sc_list, c_list = c_list[:3], c_list[3:]
random.shuffle(c_list)
ssl_context.set_ciphers(":".join(sc_list + c_list))
def get_sslcontexts(proxy_url=None, cert=None, verify=True, trust_env=True, http2=False):
key = (proxy_url, cert, verify, trust_env, http2)
def get_sslcontexts(proxy_url=None, cert=None, verify=True, trust_env=True):
key = (proxy_url, cert, verify, trust_env)
if key not in SSLCONTEXTS:
SSLCONTEXTS[key] = httpx.create_ssl_context(cert, verify, trust_env, http2)
SSLCONTEXTS[key] = httpx.create_ssl_context(verify, cert, trust_env)
shuffle_ciphers(SSLCONTEXTS[key])
return SSLCONTEXTS[key]
@ -120,7 +120,7 @@ def get_transport_for_socks_proxy(verify, http2, local_address, proxy_url, limit
rdns = True
proxy_type, proxy_host, proxy_port, proxy_username, proxy_password = parse_proxy_url(proxy_url)
verify = get_sslcontexts(proxy_url, None, verify, True, http2) if verify is True else verify
verify = get_sslcontexts(proxy_url, None, verify, True) if verify is True else verify
return AsyncProxyTransportFixed(
proxy_type=proxy_type,
proxy_host=proxy_host,
@ -138,7 +138,7 @@ def get_transport_for_socks_proxy(verify, http2, local_address, proxy_url, limit
def get_transport(verify, http2, local_address, proxy_url, limit, retries):
verify = get_sslcontexts(None, None, verify, True, http2) if verify is True else verify
verify = get_sslcontexts(None, None, verify, True) if verify is True else verify
return httpx.AsyncHTTPTransport(
# pylint: disable=protected-access
verify=verify,

View file

@ -180,7 +180,7 @@ class Network:
Network._TOR_CHECK_RESULT[proxies] = result
return result
async def get_client(self, verify=None, max_redirects=None):
async def get_client(self, verify=None, max_redirects=None) -> httpx.AsyncClient:
verify = self.verify if verify is None else verify
max_redirects = self.max_redirects if max_redirects is None else max_redirects
local_address = next(self._local_addresses_cycle)
@ -269,6 +269,8 @@ class Network:
kwargs_clients = Network.extract_kwargs_clients(kwargs)
while retries >= 0: # pragma: no cover
client = await self.get_client(**kwargs_clients)
cookies = kwargs.pop("cookies", None)
client.cookies = httpx.Cookies(cookies)
try:
if stream:
response = client.stream(method, url, **kwargs)

View file

@ -8,7 +8,7 @@ class OpenMetricsFamily: # pylint: disable=too-few-public-methods
The type_hint parameter must be one of 'counter', 'gauge', 'histogram', 'summary'.
The help_hint parameter is a short string explaining the metric.
The data_info parameter is a dictionary of descriptionary parameters for the data point (e.g. request method/path).
The data parameter is a flat list of the actual data in shape of a primive type.
The data parameter is a flat list of the actual data in shape of a primitive type.
See https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md for more information.
"""

View file

@ -26,7 +26,7 @@ log: logging.Logger = logging.getLogger("searx.plugins")
@dataclass
class PluginInfo:
"""Object that holds informations about a *plugin*, these infos are shown to
"""Object that holds information about a *plugin*, these infos are shown to
the user in the Preferences menu.
To be able to translate the information into other languages, the text must
@ -85,7 +85,7 @@ class Plugin(abc.ABC):
constructor (if not already set in the subclass)."""
info: PluginInfo
"""Informations about the *plugin*, see :py:obj:`PluginInfo`."""
"""Information about the *plugin*, see :py:obj:`PluginInfo`."""
fqn: str = ""
@ -129,8 +129,8 @@ class Plugin(abc.ABC):
def init(self, app: "flask.Flask") -> bool: # pylint: disable=unused-argument
"""Initialization of the plugin, the return value decides whether this
plugin is active or not. Initialization only takes place once, at the
time the WEB application is set up. The base methode always returns
``True``, the methode can be overwritten in the inheritances,
time the WEB application is set up. The base method always returns
``True``, the method can be overwritten in the inheritances,
- ``True`` plugin is active
- ``False`` plugin is inactive

View file

@ -12,24 +12,13 @@ from .online import OnlineProcessor
parser_re = re.compile('.*?(\\d+(?:\\.\\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)', re.I)
def normalize_name(name):
def normalize_name(name: str):
name = name.strip()
name = name.lower().replace('-', ' ').rstrip('s')
name = re.sub(' +', ' ', name)
return unicodedata.normalize('NFKD', name).lower()
def name_to_iso4217(name):
name = normalize_name(name)
currency = CURRENCIES['names'].get(name, [name])
if isinstance(currency, str):
return currency
return currency[-1]
def iso4217_to_name(iso4217, language):
return CURRENCIES['iso4217'].get(iso4217, {}).get(language, iso4217)
class OnlineCurrencyProcessor(OnlineProcessor):
"""Processor class used by ``online_currency`` engines."""
@ -52,14 +41,15 @@ class OnlineCurrencyProcessor(OnlineProcessor):
amount = float(amount_str)
except ValueError:
return None
from_currency = name_to_iso4217(from_currency.strip())
to_currency = name_to_iso4217(to_currency.strip())
from_currency = CURRENCIES.name_to_iso4217(normalize_name(from_currency))
to_currency = CURRENCIES.name_to_iso4217(normalize_name(to_currency))
params['amount'] = amount
params['from'] = from_currency
params['to'] = to_currency
params['from_name'] = iso4217_to_name(from_currency, 'en')
params['to_name'] = iso4217_to_name(to_currency, 'en')
params['from_name'] = CURRENCIES.iso4217_to_name(from_currency, "en")
params['to_name'] = CURRENCIES.iso4217_to_name(to_currency, "en")
return params
def get_default_tests(self):

View file

@ -34,7 +34,7 @@ search:
# Filter results. 0: None, 1: Moderate, 2: Strict
safe_search: 0
# Existing autocomplete backends: "360search", "baidu", "brave", "dbpedia", "duckduckgo", "google", "yandex",
# "mwmbl", "seznam", "sogou", "stract", "swisscows", "quark", "qwant", "wikipedia" -
# "mwmbl", "naver", "seznam", "sogou", "stract", "swisscows", "quark", "qwant", "wikipedia" -
# leave blank to turn it off by default.
autocomplete: ""
# minimun characters to type before autocompleter starts
@ -252,8 +252,8 @@ plugins:
#
# hostnames:
# replace:
# '(.*\.)?youtube\.com$': 'invidious.example.com'
# '(.*\.)?youtu\.be$': 'invidious.example.com'
# '(.*\.)?youtube\.com$': 'yt.example.com'
# '(.*\.)?youtu\.be$': 'yt.example.com'
# '(.*\.)?reddit\.com$': 'teddit.example.com'
# '(.*\.)?redd\.it$': 'teddit.example.com'
# '(www\.)?twitter\.com$': 'nitter.example.com'
@ -270,8 +270,8 @@ plugins:
# replace: 'rewrite-hosts.yml'
#
# Content of 'rewrite-hosts.yml' (place the file in the same directory as 'settings.yml'):
# '(.*\.)?youtube\.com$': 'invidious.example.com'
# '(.*\.)?youtu\.be$': 'invidious.example.com'
# '(.*\.)?youtube\.com$': 'yt.example.com'
# '(.*\.)?youtu\.be$': 'yt.example.com'
#
checker:
@ -1053,7 +1053,6 @@ engines:
- name: material icons
engine: material_icons
categories: images
shortcut: mi
disabled: true
@ -1117,6 +1116,7 @@ engines:
- name: il post
engine: il_post
shortcut: pst
disabled: true
- name: huggingface
engine: huggingface
@ -1152,16 +1152,15 @@ engines:
timeout: 6.0
disabled: true
- name: invidious
engine: invidious
# Instanes will be selected randomly, see https://api.invidious.io/ for
# instances that are stable (good uptime) and close to you.
base_url:
- https://invidious.adminforge.de
- https://inv.nadeko.net
shortcut: iv
timeout: 3.0
disabled: true
# - name: invidious
# engine: invidious
# # if you want to use invidious with SearXNG you should setup one locally
# # https://github.com/searxng/searxng/issues/2722#issuecomment-2884993248
# base_url:
# - https://invidious.example1.com
# - https://invidious.example2.com
# shortcut: iv
# timeout: 3.0
- name: ipernity
engine: ipernity
@ -1237,11 +1236,13 @@ engines:
shortcut: zlib
categories: files
timeout: 7.0
disabled: true
- name: library of congress
engine: loc
shortcut: loc
categories: images
disabled: true
- name: libretranslate
engine: libretranslate
@ -1708,6 +1709,7 @@ engines:
engine: qwant
shortcut: qw
categories: [general, web]
disabled: true
additional_tests:
rosebud: *test_rosebud
@ -2359,25 +2361,31 @@ engines:
disabled: true
- name: naver
shortcut: nvr
categories: [general, web]
engine: xpath
paging: true
search_url: https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno}
url_xpath: //a[@class="link_tit"]/@href
title_xpath: //a[@class="link_tit"]
content_xpath: //div[@class="total_dsc_wrap"]/a
first_page_num: 1
page_size: 10
engine: naver
shortcut: nvr
disabled: true
- name: naver images
naver_category: images
categories: [images]
engine: naver
shortcut: nvri
disabled: true
- name: naver news
naver_category: news
categories: [news]
engine: naver
shortcut: nvrn
disabled: true
- name: naver videos
naver_category: videos
categories: [videos]
engine: naver
shortcut: nvrv
disabled: true
about:
website: https://www.naver.com/
wikidata_id: Q485639
official_api_documentation: https://developers.naver.com/docs/nmt/examples/
use_official_api: false
require_api_key: false
results: HTML
language: ko
- name: rubygems
shortcut: rbg
@ -2524,6 +2532,11 @@ engines:
engine: tootfinder
shortcut: toot
- name: uxwing
engine: uxwing
shortcut: ux
disabled: true
- name: voidlinux
engine: voidlinux
shortcut: void

View file

@ -114,7 +114,7 @@ class SQLiteAppl(abc.ABC):
"""
SQLITE_JOURNAL_MODE = "WAL"
"""``SQLiteAppl`` applications are optimzed for WAL_ mode, its not recommend
"""``SQLiteAppl`` applications are optimized for WAL_ mode, its not recommend
to change the journal mode (see :py:obj:`SQLiteAppl.tear_down`).
.. _WAL: https://sqlite.org/wal.html
@ -145,7 +145,7 @@ class SQLiteAppl(abc.ABC):
- https://github.com/python/cpython/issues/118172
- https://github.com/python/cpython/issues/123873
The workaround for SQLite3 multithreading cache inconsistency ist to set
The workaround for SQLite3 multithreading cache inconsistency is to set
option ``cached_statements`` to ``0`` by default.
"""

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -64,7 +64,7 @@
</main>
<footer>
<p>
{{ _('Powered by') }} <a href="{{ url_for('info', pagename='about') }}">searxng</a> - {{ searx_version }} — {{ _('a privacy-respecting, open metasearch engine') }}<br>
{{ _('Powered by') }} <a href="{{ url_for('info', pagename='about') }}">SearXNG</a> - {{ searx_version }} — {{ _('a privacy-respecting, open metasearch engine') }}<br>
<a href="{{ searx_git_url }}">{{ _('Source code') }}</a>
| <a href="{{ get_setting('brand.issue_url') }}">{{ _('Issue tracker') }}</a>
{% if enable_metrics %}| <a href="{{ url_for('stats') }}">{{ _('Engine stats') }}</a>{% endif %}

View file

@ -9,6 +9,7 @@
<input id="q" name="q" type="text" placeholder="{{ _('Search for...') }}" tabindex="1" autocomplete="off" autocapitalize="none" spellcheck="false" autocorrect="off" dir="auto" value="{{ q or '' }}">
<button id="clear_search" type="reset" aria-label="{{ _('clear') }}" class="hide_if_nojs"><span>{{ icon_big('close') }}</span><span class="show_if_nojs">{{ _('clear') }}</span></button>
<button id="send_search" type="submit" {%- if search_on_category_select -%}name="category_{{ selected_categories[0]|replace(' ', '_') }}"{%- endif -%} aria-label="{{ _('search') }}"><span class="hide_if_nojs">{{ icon_big('search') }}</span><span class="show_if_nojs">{{ _('search') }}</span></button>
<div class="autocomplete hide_if_nojs"><ul></ul></div>
</div>
</div>
{% set display_tooltip = true %}

View file

@ -5,6 +5,7 @@
<input id="q" name="q" type="text" placeholder="{{ _('Search for...') }}" autocomplete="off" autocapitalize="none" spellcheck="false" autocorrect="off" dir="auto" value="{{ q or '' }}">
<button id="clear_search" type="reset" aria-label="{{ _('clear') }}"><span class="hide_if_nojs">{{ icon_big('close') }}</span><span class="show_if_nojs">{{ _('clear') }}</span></button>
<button id="send_search" type="submit" aria-label="{{ _('search') }}"><span class="hide_if_nojs">{{ icon_big('search') }}</span><span class="show_if_nojs">{{ _('search') }}</span></button>
<div class="autocomplete hide_if_nojs"><ul></ul></div>
</div>
</div>
</div>

View file

@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-01-28 06:11+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
@ -73,7 +73,7 @@ msgid "videos"
msgstr "video's"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "draadloos"
@ -350,93 +350,93 @@ msgstr "toe"
msgid "answered"
msgstr "geantwoord"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Geen item gevind"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Bron"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Fout met die laai van die volgende bladsy"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Ongeldige opstellings, redigeer asb jou voorkeure"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Ongeldige opstellings"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "soekfout"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "tydsverloop"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "ontledingsfout"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP protokol fout"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "netwerk fout"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL vout: Kon nie sertifikaat verifieer nie"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "onverwagse breek"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP fout"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP koppelingsfout"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "proksie fout"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "te veel versoeke"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "toegang geweier"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "bediener API fout"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Opgehef"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "{minutes} minute terug"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "{hours} ure, {minutes} minute terug"
@ -467,15 +467,15 @@ msgstr "Hierdie inskrywing was vervang deur"
msgid "Channel"
msgstr "Kanaal"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "bitsnelheid"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "stemme"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "klikke"
@ -484,7 +484,7 @@ msgstr "klikke"
msgid "Language"
msgstr "Taal"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -711,10 +711,6 @@ msgstr "Outeur"
msgid "cached"
msgstr "gekas"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "gevolmagtig"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Begin om 'n nuwe probleem op GitHub in te dien"
@ -1826,3 +1822,6 @@ msgstr "versteek video"
#~ "gebruik word om voorkeure oor toestelle"
#~ " heen te sinkroniseer."
#~ msgid "proxied"
#~ msgstr "gevolmagtig"

View file

@ -28,20 +28,19 @@
# DZDevelopers <dzdevelopers@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-04-15 10:37+0000\n"
"Last-Translator: DZDevelopers <dzdevelopers@noreply.codeberg.org>\n"
"Language-Team: Arabic <https://translate.codeberg.org/projects/searxng/"
"searxng/ar/>\n"
"Language: ar\n"
"Language-Team: Arabic "
"<https://translate.codeberg.org/projects/searxng/searxng/ar/>\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : "
"n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -85,7 +84,7 @@ msgid "videos"
msgstr "ڤيديوهات"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "راديو"
@ -362,93 +361,93 @@ msgstr "مغلق"
msgid "answered"
msgstr "أُجيبت"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "تعذر العثور على عناصر"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "المصدر"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "حدث خلل أثناء تحميل الصفحة التالية"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "إنّ الإعدادات خاطئة، يرجى تعديل خياراتك"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "إعدادات غير صالحة"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "خطأ في البحث"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "نفذ الوقت"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "خطأ تحليل"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "خطأ في بروتوكول HTTP"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "خطأ في الشبكة"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "خطأ SSL: فشل التحقق من صحة الشهادة"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "تعطل غير متوقع"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "خطأ HTTP"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "خطأ في اتصال HTTP"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "خطأ في وكيل البروكسي"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "أسئلة التحقق"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "الكثير من الطلبات"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "الدخول مرفوض"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "خطأ في API الخادم"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "معلق"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "{minutes} minute(s) ago"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "قبل {hours} ساعات، {minutes} دقائق"
@ -479,15 +478,15 @@ msgstr "هذا الإدخال تم استبداله بـ"
msgid "Channel"
msgstr "القناة"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "معدل البت"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "تصويتات"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "نقرات"
@ -496,7 +495,7 @@ msgstr "نقرات"
msgid "Language"
msgstr "اللغة"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -724,10 +723,6 @@ msgstr "الكاتب"
msgid "cached"
msgstr "النسخة المخبأة"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "المخدم البروكسي"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "ابدأ بتقديم قضية جديدة على GitHub"
@ -1191,8 +1186,8 @@ msgid ""
"A URL containing your preferences. This URL can be used to restore your "
"settings on a different device."
msgstr ""
"رابط يحتوي على تفضيلاتك. يمكن استخدام هذا الرابط لاستعادة إعداداتك على جهاز "
"مختلف."
"رابط يحتوي على تفضيلاتك. يمكن استخدام هذا الرابط لاستعادة إعداداتك على "
"جهاز مختلف."
#: searx/templates/simple/preferences/cookies.html:46
msgid "Copy preferences hash"
@ -2084,3 +2079,7 @@ msgstr "إخفاء الفيديو"
#~ "يمكن استخدام تحديد الإعدادات المخصصة في"
#~ " تفضيلات URL لمزامنة التفضيلات عبر "
#~ "الأجهزة."
#~ msgid "proxied"
#~ msgstr "المخدم البروكسي"

View file

@ -16,20 +16,22 @@
# Anonymous <anonymous@users.noreply.translate.codeberg.org>, 2025.
# thenack0 <thenack0@users.noreply.translate.codeberg.org>, 2025.
# return42 <return42@noreply.codeberg.org>, 2025.
# devrimer <devrimer@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-03-14 07:09+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-05-16 04:04+0000\n"
"Last-Translator: devrimer <devrimer@noreply.codeberg.org>\n"
"Language-Team: Bulgarian <https://translate.codeberg.org/projects/searxng/"
"searxng/bg/>\n"
"Language: bg\n"
"Language-Team: Bulgarian "
"<https://translate.codeberg.org/projects/searxng/searxng/bg/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.11.4\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -73,7 +75,7 @@ msgid "videos"
msgstr "видео"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "радио"
@ -350,93 +352,93 @@ msgstr "Затворено"
msgid "answered"
msgstr "Отговорено"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Не е намерен артикул"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Източник"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Грешка при зареждането на следващата страница"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Неправилни настройки, моля редактирайте предпочитанията си"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Невалидни настройки"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "Грешка при търсенето"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "изчакване"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "грешка при анализа"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "Грешка в протокола HTTP"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "мрежова грешка"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL грешка: проверката на сертификата е неуспешна"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "неочакван срив"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP грешка"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP грешка във връзката"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "прокси грешка"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "твърде много повиквания"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "отказан достъп"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "грешка в API на сървъра"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "преустановен"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "преди {minutes} минута(минути)"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "преди {hours} час(ове), {minutes} минута(минути)"
@ -448,7 +450,7 @@ msgstr "Генерирайте различни произволни стойн
#: searx/answerers/statistics.py:36
#, python-brace-format
msgid "Compute {func} of the arguments"
msgstr ""
msgstr "Изчислете {func} на аргументите"
#: searx/engines/openstreetmap.py:158
msgid "Show route in map .."
@ -467,15 +469,15 @@ msgstr "Този запис е заменен от"
msgid "Channel"
msgstr "Канал"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "Скорост"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "Гласове"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "клика"
@ -484,7 +486,7 @@ msgstr "клика"
msgid "Language"
msgstr "Език"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -712,10 +714,6 @@ msgstr "Автор"
msgid "cached"
msgstr "кеширана"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "прекарана"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Предявете нов проблем в GitHub"
@ -2083,3 +2081,5 @@ msgstr "скрий видеото"
#~ "позволи синхронизация между различни "
#~ "устройства."
#~ msgid "proxied"
#~ msgstr "прекарана"

View file

@ -26,7 +26,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-03-06 09:54+0000\n"
"Last-Translator: MonsoonFire <monsoonfire@noreply.codeberg.org>\n"
"Language: bn\n"
@ -79,7 +79,7 @@ msgid "videos"
msgstr "ভিডিও"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "বেতার"
@ -356,93 +356,93 @@ msgstr "বন্ধ"
msgid "answered"
msgstr "উত্তরকৃত"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "কোন আইটেম পাওয়া যায়নি"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "উৎস"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "পরবর্তী পৃষ্ঠাটি লোড করায় ত্রুটি দেখা যাচ্ছে"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "অকেজো সেটিংস, অনুগ্রহ করে আপনার পছন্দগুলি সম্পাদনা করুন"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "অকেজো সেটিংস"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "সার্চ ত্রুটি"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "সময় শেষ"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "পার্স ত্রুটি"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP প্রোটোকল ত্রুটি"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "নেটওয়ার্ক ত্রুটি"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL ত্রুটি: সার্টিফিকেট বৈধতা ব্যর্থ হয়েছে৷"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "অপ্রত্যাশিত ক্র্যাশ"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP ত্রুটি"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP সংযোগ ত্রুটি"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "প্রক্সি ত্রুটি"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "ক্যাপচা"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "অনেক বেশি অনুরোধ"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "প্রবেশ অগ্রাহ্য করা হল"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "সার্ভার API ত্রুটি"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "স্থগিত"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "{minutes} মিনিট আগে"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "{hours} ঘণ্টা, {minutes} মিনিট আগে"
@ -473,15 +473,15 @@ msgstr "এই এনট্রিটি দ্বারা বাতিল ক
msgid "Channel"
msgstr "চ্যানেল"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "বিটরেট"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "ভোট"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "ক্লিক সংখ্যা"
@ -490,7 +490,7 @@ msgstr "ক্লিক সংখ্যা"
msgid "Language"
msgstr "ভাষা"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -716,10 +716,6 @@ msgstr "লেখক"
msgid "cached"
msgstr "ক্যাশকৃত"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "প্রক্সিকৃত"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "GitHub এ একটি নতুন সমস্যা জমা দেওয়া শুরু করুন"
@ -1843,3 +1839,6 @@ msgstr "ভিডিও লুকিয়ে ফেলুন"
#~ "প্রেফারেন্সগুলি ডিভাইস জুড়ে সিঙ্ক করে "
#~ "ব্যবহার করতে পারেন।"
#~ msgid "proxied"
#~ msgstr "প্রক্সিকৃত"

View file

@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-01-06 15:52+0000\n"
"Last-Translator: return42 <return42@users.noreply.translate.codeberg.org>"
"\n"
@ -66,7 +66,7 @@ msgid "videos"
msgstr "བརྙན་ཟློས།"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr ""
@ -343,93 +343,93 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "རྣམ་གྲངས་གང་ཡང་རྙེད་རྒྱུ་མ་བྱུང་།"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr ""
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "ནུས་མེད་ཀྱི་སྒྲིག་འགོད།ཁྱེད་ཀྱིས་གདམ་ཀ་ལ་བཅོས་སྒྲིག་གཏོང་རོགས།"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "ནུས་མེད་ཀྱི་སྒྲིག་འགོད།"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "འཚོལ་བཤེར་ལ་ནོར་འཁྲུལ་བྱུང་།"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr ""
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr ""
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr ""
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr ""
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr ""
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr ""
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr ""
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr ""
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr ""
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr ""
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr ""
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr ""
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr ""
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "སྐར་མ་ {minutes} སྔོན་ལ།"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "ཆུ་ཚོད་ {hours} དང་སྐར་མ {minutes} སྔོན་ལ།"
@ -460,15 +460,15 @@ msgstr "འཚོལ་བྱང་འདི་གཞན་གྱིས་ཚབ
msgid "Channel"
msgstr ""
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr ""
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr ""
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr ""
@ -477,7 +477,7 @@ msgstr ""
msgid "Language"
msgstr ""
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -689,10 +689,6 @@ msgstr ""
msgid "cached"
msgstr "འདྲ་བཤུས་རྒྱབ་ཚར།"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "མངག་བཅོལ་བྱེད་ཟིན།"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr ""
@ -1994,3 +1990,6 @@ msgstr "རྙན་ཟློས་སྦས།"
#~ "sync preferences across devices."
#~ msgstr ""
#~ msgid "proxied"
#~ msgstr "མངག་བཅོལ་བྱེད་ཟིན།"

View file

@ -26,7 +26,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-01-06 15:52+0000\n"
"Last-Translator: sserra <sserra@users.noreply.translate.codeberg.org>\n"
"Language: ca\n"
@ -79,7 +79,7 @@ msgid "videos"
msgstr "vídeos"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "radio"
@ -356,93 +356,93 @@ msgstr "tancat"
msgid "answered"
msgstr "contestat"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "No s'ha trobat cap element"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Origen"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "S'ha produït un error en carregar la següent pàgina"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "La configuració no és vàlida, editeu-la"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "La configuració no és vàlida"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "error de cerca"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "expirat"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "error de processament"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "error del protocol HTTP"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "error de xarxa"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "error de SSL: la validació del certificat ha fallat"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "error inesperat"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "error de HTTP"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "error de connexió HTTP"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "error del servidor intermediari"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "masses peticions"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "accés denegat"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "error en l'API del servidor"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Suspès"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "fa {minutes} minut(s)"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "fa {hours} hores i {minutes} minut(s)"
@ -473,15 +473,15 @@ msgstr "Aquesta entrada ha estat substituïda per"
msgid "Channel"
msgstr "Canal"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "tasa de bits"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "vots"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "clics"
@ -490,7 +490,7 @@ msgstr "clics"
msgid "Language"
msgstr "Llengua"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -716,10 +716,6 @@ msgstr "Autor"
msgid "cached"
msgstr "en memòria cau"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "en servidor intermediari"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Obriu una incidència a GitHub"
@ -2100,3 +2096,6 @@ msgstr "oculta el vídeo"
#~ " l'URL de preferències pot usar-se"
#~ " per sincronitzar entre dispositius."
#~ msgid "proxied"
#~ msgstr "en servidor intermediari"

View file

@ -22,20 +22,19 @@
# Fjuro <fjuro@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-03-30 16:28+0000\n"
"Last-Translator: Fjuro <fjuro@noreply.codeberg.org>\n"
"Language-Team: Czech <https://translate.codeberg.org/projects/searxng/"
"searxng/cs/>\n"
"Language: cs\n"
"Language-Team: Czech "
"<https://translate.codeberg.org/projects/searxng/searxng/cs/>\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && "
"n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n "
"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -79,7 +78,7 @@ msgid "videos"
msgstr "videa"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "rádio"
@ -356,93 +355,93 @@ msgstr "zavřené"
msgid "answered"
msgstr "zodpovězené"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Nic nenalezeno"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "zdroj"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Chyba při načítání další stránky"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Neplatné nastavení, upravte své předvolby"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Neplatné nastavení"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "chyba vyhledávání"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "čas vypršel"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "chyba parsování"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "chyba HTTP protokolu"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "síťová chyba"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "chyba SSL: ověření certifikátu selhalo"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "nečekaná chyba"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "chyba HTTP"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "Chyba spojení HTTP"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "chyba proxy"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "příliš mnoho požadavků"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "přístup odepřen"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "chyba API serveru"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Pozastaveno"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "před {minutes} minutami"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "před {hours} hodinami, {minutes} minutami"
@ -473,15 +472,15 @@ msgstr "Tato položka byla nahrazena položkou"
msgid "Channel"
msgstr "Kanál"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "datový tok"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "hlasy"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "kliknutí"
@ -490,7 +489,7 @@ msgstr "kliknutí"
msgid "Language"
msgstr "Jazyk"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -608,9 +607,9 @@ msgid ""
"This plugin checks if the address of the request is a Tor exit-node, and "
"informs the user if it is; like check.torproject.org, but from SearXNG."
msgstr ""
"Tento doplněk kontroluje, zda je adresa požadavku výstupním uzlem sítě Tor, "
"a informuje uživatele, pokud tomu tak je; jako check.torproject.org, ale od "
"SearXNG."
"Tento doplněk kontroluje, zda je adresa požadavku výstupním uzlem sítě "
"Tor, a informuje uživatele, pokud tomu tak je; jako check.torproject.org,"
" ale od SearXNG."
#: searx/plugins/tor_check.py:65
msgid "Could not download the list of Tor exit-nodes from"
@ -718,10 +717,6 @@ msgstr "Autor"
msgid "cached"
msgstr "archivovaná verze"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "přes proxy"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Začněte přidávat novou chybu na Githubu"
@ -2089,3 +2084,7 @@ msgstr "skrýt video"
#~ "Zadání vlastních nastavení v URL "
#~ "předvoleb lze použít k synchronizaci "
#~ "předvoleb mezi zařízeními."
#~ msgid "proxied"
#~ msgstr "přes proxy"

View file

@ -19,7 +19,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-03-07 00:07+0000\n"
"Last-Translator: DanielBoone <danielboone@noreply.codeberg.org>\n"
"Language: cy\n"
@ -73,7 +73,7 @@ msgid "videos"
msgstr "fideos"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "radio"
@ -350,93 +350,93 @@ msgstr "ar gau"
msgid "answered"
msgstr "wedi'i ateb"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Ni chanfuwyd eitem"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Ffynhonnell"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Gwall wrth lwytho'r dudalen nesaf"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Gosodiadau annilys, golygwch eich dewisiadau"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Gosodiadau annilys"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "gwall chwilio"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "terfyn amser"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "gwall dosrannu"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "gwall protocol HTTP"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "gwall rhwydwaith"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "Gwall SSL: dilysu tystysgrif wedi methu"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "damwain annisgwyl"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "gwall HTTP"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "gwall cysylltiad HTTP"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "gwall dirprwy"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "gormod o geisiadau"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "mynediad wedi ei wrthod"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "gwall API gweinydd"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Atal"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "{minutes} munud yn ôl"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "{hours} awr, {minutes} munud yn ôl"
@ -467,15 +467,15 @@ msgstr "Mae'r cofnod hwn wedi ei ddisodli gan"
msgid "Channel"
msgstr "Sianel"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "cyfradd didau"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "pleidleisiau"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "cliciau"
@ -484,7 +484,7 @@ msgstr "cliciau"
msgid "Language"
msgstr "Iaith"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -711,10 +711,6 @@ msgstr "Awdur"
msgid "cached"
msgstr "wedi'i storio"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "wedi'i ddirprwyo"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Dechrau cyflwyno problem newydd ar GitHub"
@ -2052,3 +2048,6 @@ msgstr "cuddio'r fideo"
#~ "gysoni eich dewisiadau ar draws "
#~ "dyfeisiau."
#~ msgid "proxied"
#~ msgstr "wedi'i ddirprwyo"

View file

@ -16,19 +16,18 @@
# AndersNordh <andersnordh@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-03-31 14:03+0000\n"
"Last-Translator: AndersNordh <andersnordh@noreply.codeberg.org>\n"
"Language-Team: Danish <https://translate.codeberg.org/projects/searxng/"
"searxng/da/>\n"
"Language: da\n"
"Language-Team: Danish "
"<https://translate.codeberg.org/projects/searxng/searxng/da/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -72,7 +71,7 @@ msgid "videos"
msgstr "videoer"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "Radio"
@ -349,93 +348,93 @@ msgstr "lukket"
msgid "answered"
msgstr "svaret"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Intet fundet"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Kilde"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Fejl ved indlæsning af den næste side"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Ugyldige indstillinger, redigér venligst dine valg"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Ugyldig indstilling"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "søgefejl"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "udløbstid"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "fortolkningsfejl"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP-protokolfejl"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "netværksfejl"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL-fejl: certifikatvalidering mislykkedes"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "uventet nedbrud"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP-fejl"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP-tilkoblingsfejl"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "proxyfejl"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "for mange forespørgsler"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "adgang nægtet"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "server-API-fejl"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Suspenderet"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "for {minutes} minut(ter) siden"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "for {hours} time(r) og {minutes} minut(ter) siden"
@ -466,15 +465,15 @@ msgstr "Denne værdi er blevet overskrevet af"
msgid "Channel"
msgstr "Kanal"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "Bitrate"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "Stemmer"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "Klik"
@ -483,7 +482,7 @@ msgstr "Klik"
msgid "Language"
msgstr "Sprog"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -712,10 +711,6 @@ msgstr "Forfatter"
msgid "cached"
msgstr "cachet"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "viderestillet"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Begynd at indsende et nyt problem på GitHub"
@ -2089,3 +2084,7 @@ msgstr "skjul video"
#~ "Specificere brugertilpassede indstillinger i "
#~ "præference-URL'en kan bruges til at "
#~ "synkronisere præference over flere enheder."
#~ msgid "proxied"
#~ msgstr "viderestillet"

View file

@ -27,19 +27,18 @@
# return42 <return42@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-03-30 07:37+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language-Team: German <https://translate.codeberg.org/projects/searxng/"
"searxng/de/>\n"
"Language: de\n"
"Language-Team: German "
"<https://translate.codeberg.org/projects/searxng/searxng/de/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -83,7 +82,7 @@ msgid "videos"
msgstr "Videos"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "Radio"
@ -360,93 +359,93 @@ msgstr "geschlossen"
msgid "answered"
msgstr "beantwortet"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Keine Einträge gefunden"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Quelle"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Fehler beim Laden der nächsten Seite"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Ungültige Einstellungen, bitte Einstellungen ändern"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Ungültige Einstellungen"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "Suchfehler"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "Zeitüberschreitung"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "Fehler beim Parsen"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP-Protokollfehler"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "Netzwerkfehler"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL Fehler: Zertifikatsprüfung ist fehlgeschlagen"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "unerwarteter Absturz"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP-Fehler"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP-Verbindungsfehler"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "Proxy-Fehler"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "zu viele Anfragen"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "Zugriff verweigert"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "Server-API-Fehler"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Ausgesetzt"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "vor {minutes} Minute(n)"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "vor {hours} Stunde(n), {minutes} Minute(n)"
@ -477,15 +476,15 @@ msgstr "Dieser Eintrag wurde überschrieben von"
msgid "Channel"
msgstr "Kanal"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "Bitrate"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "Stimmen"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "Clicks"
@ -494,7 +493,7 @@ msgstr "Clicks"
msgid "Language"
msgstr "Sprache"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -540,8 +539,7 @@ msgstr "Ahmia blacklist"
#: searx/plugins/ahmia_filter.py:33
msgid "Filter out onion results that appear in Ahmia's blacklist."
msgstr ""
"Filtern der Onion Links, die in der schwarzen Liste von Ahmia erscheinen."
msgstr "Filtern der Onion Links, die in der schwarzen Liste von Ahmia erscheinen."
#: searx/plugins/calculator.py:38
msgid "Basic Calculator"
@ -726,10 +724,6 @@ msgstr "Autor"
msgid "cached"
msgstr "Im Cache"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "proxy"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Fehlerbericht auf GitHub erstellen"
@ -1200,8 +1194,8 @@ msgid ""
"A URL containing your preferences. This URL can be used to restore your "
"settings on a different device."
msgstr ""
"URL die Ihre Einstellungen enthält. Diese URL kann verwendet werden, um Ihre "
"Einstellungen auf einem anderen Gerät wiederherzustellen"
"URL die Ihre Einstellungen enthält. Diese URL kann verwendet werden, um "
"Ihre Einstellungen auf einem anderen Gerät wiederherzustellen"
#: searx/templates/simple/preferences/cookies.html:46
msgid "Copy preferences hash"
@ -2129,3 +2123,7 @@ msgstr "Video verstecken"
#~ "anderen Browser werden die aktuellen "
#~ "Einstellungen in dem anderen Browser "
#~ "gespeichert (Cookie)."
#~ msgid "proxied"
#~ msgstr "proxy"

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-01-06 15:52+0000\n"
"Last-Translator: Anonymous "
"<anonymous@users.noreply.translate.codeberg.org>\n"
@ -62,7 +62,7 @@ msgid "videos"
msgstr "ވީޑިޔޯތައް"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr ""
@ -339,93 +339,93 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr ""
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr ""
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr ""
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr ""
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr ""
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr ""
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr ""
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr ""
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr ""
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr ""
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr ""
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr ""
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr ""
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr ""
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr ""
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr ""
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr ""
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr ""
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr ""
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr ""
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr ""
@ -456,15 +456,15 @@ msgstr ""
msgid "Channel"
msgstr ""
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr ""
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr ""
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr ""
@ -473,7 +473,7 @@ msgstr ""
msgid "Language"
msgstr ""
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -685,10 +685,6 @@ msgstr ""
msgid "cached"
msgstr ""
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr ""
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr ""
@ -1723,3 +1719,6 @@ msgstr ""
#~ "sync preferences across devices."
#~ msgstr ""
#~ msgid "proxied"
#~ msgstr ""

View file

@ -22,19 +22,18 @@
# sakistzimas <sakistzimas@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-04-03 21:59+0000\n"
"Last-Translator: sakistzimas <sakistzimas@noreply.codeberg.org>\n"
"Language-Team: Greek <https://translate.codeberg.org/projects/searxng/"
"searxng/el/>\n"
"Language: el_GR\n"
"Language-Team: Greek "
"<https://translate.codeberg.org/projects/searxng/searxng/el/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -78,7 +77,7 @@ msgid "videos"
msgstr "Βίντεο"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "ράδιο"
@ -355,93 +354,93 @@ msgstr "κλειστό"
msgid "answered"
msgstr "απάντησε"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Δεν βρέθηκαν αντικείμενα"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Πηγή"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Σφάλμα φόρτωσης της επόμενης σελίδας"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Μη έγκυρες ρυθμίσεις, παρακαλούμε ελέγξτε τις προτιμήσεις σας"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Μη έγκυρες ρυθμίσεις"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "σφάλμα αναζήτησης"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "Λήξη χρόνου"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "σφάλμα ανάλυσης"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "Σφάλμα πρωτοκόλλου HTTP"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "Σφάλμα δικτύου"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "Σφάλμα SSL: η επικύρωση του πιστοποιητικού απέτυχε"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "Απροσδόκητο σφάλμα"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "Σφάλμα HTTP"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "Σφάλμα σύνδεσης HTTP"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "Σφάλμα διακομιστή μεσολάβησης"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "υπέρβαση ορίου αιτημάτων"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "Άρνηση πρόσβασης"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "Σφάλμα API διακομιστή"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Σε αναστολή"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "{minutes} λεπτά πριν"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "{hours} ώρα(-ες), {minutes} λεπτό(-ά) πριν"
@ -472,15 +471,15 @@ msgstr "Αυτή η καταχώριση έχει αντικατασταθεί
msgid "Channel"
msgstr "Κανάλι"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "ρυθμός μετάδοσης"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "ψήφους"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "κλικ"
@ -489,7 +488,7 @@ msgstr "κλικ"
msgid "Language"
msgstr "Γλώσσα"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -720,10 +719,6 @@ msgstr "Συγγραφέας"
msgid "cached"
msgstr "προσωρινά αποθηκευμένο"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "Διαμεσολαβημένα"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Ξεκινήστε την υποβολή ενός νέου ζητήματος στο GitHub"
@ -1197,7 +1192,8 @@ msgid ""
"settings on a different device."
msgstr ""
"Ένα URL που περιέχει τις προτιμήσεις σας. Αυτό το URL μπορεί να "
"χρησιμοποιηθεί για να επαναφέρει τις ρυθμίσεις σας σε διαφορετική συσκευή."
"χρησιμοποιηθεί για να επαναφέρει τις ρυθμίσεις σας σε διαφορετική "
"συσκευή."
#: searx/templates/simple/preferences/cookies.html:46
msgid "Copy preferences hash"
@ -2108,3 +2104,7 @@ msgstr "απόκρυψη βίντεο"
#~ "σύνδεσμο προτιμήσεων μπορεί να χρησιμοποιηθεί"
#~ " για το συγχρονισμό των προτιμήσεων "
#~ "σας σε όλες τις συσκευές."
#~ msgid "proxied"
#~ msgstr "Διαμεσολαβημένα"

View file

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2014-01-30 15:22+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
@ -59,7 +59,7 @@ msgid "videos"
msgstr ""
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr ""
@ -336,93 +336,93 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr ""
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr ""
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr ""
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr ""
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr ""
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr ""
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr ""
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr ""
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr ""
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr ""
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr ""
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr ""
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr ""
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr ""
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr ""
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr ""
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr ""
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr ""
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr ""
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr ""
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr ""
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr ""
@ -453,15 +453,15 @@ msgstr ""
msgid "Channel"
msgstr ""
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr ""
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr ""
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr ""
@ -470,7 +470,7 @@ msgstr ""
msgid "Language"
msgstr ""
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -682,10 +682,6 @@ msgstr ""
msgid "cached"
msgstr ""
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr ""
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr ""
@ -1988,3 +1984,6 @@ msgstr ""
#~ "sync preferences across devices."
#~ msgstr ""
#~ msgid "proxied"
#~ msgstr ""

View file

@ -21,19 +21,18 @@
# return42 <return42@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-05-09 07:09+0000\n"
"Last-Translator: return42 <return42@noreply.codeberg.org>\n"
"Language-Team: Esperanto <https://translate.codeberg.org/projects/searxng/"
"searxng/eo/>\n"
"Language: eo\n"
"Language-Team: Esperanto "
"<https://translate.codeberg.org/projects/searxng/searxng/eo/>\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -77,7 +76,7 @@ msgid "videos"
msgstr "videoj"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "radio"
@ -354,93 +353,93 @@ msgstr ""
msgid "answered"
msgstr ""
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Nenio trovita"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Fonto"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Eraro dum la ŝarĝado de la sekvan paĝon"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Nevalidaj agordoj, bonvolu redaktu viajn agordojn"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Nevalidaj agordoj"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "serĉa eraro"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "eltempiĝo"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "analiza eraro"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP-protokolo-eraro"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "reta eraro"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL-eraro: atestila validigo malsukcesis"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "neatendita kraŝo"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP-eraro"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP-konekto-eraro"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "prokurilo-eraro"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "tro da petoj"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "aliro rifuzita"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "servilo-API-eraro"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Suspendigita"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "antaŭ {minutes} minuto(j)"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "antaŭ {hours} horo(j), {minutes} minuto(j)"
@ -471,15 +470,15 @@ msgstr "Tiu ĉi enigo estis anstataŭigita per"
msgid "Channel"
msgstr "Kanalo"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "bito-rapido"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "voĉoj"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "klakoj"
@ -488,7 +487,7 @@ msgstr "klakoj"
msgid "Language"
msgstr "Lingvo"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -712,10 +711,6 @@ msgstr "Verkisto"
msgid "cached"
msgstr "kaŝmemorigita"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "prokurata"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Komencu sendi novan numeron en GitHub"
@ -2068,3 +2063,7 @@ msgstr "kaŝi videojn"
#~ "Specifante kutimajn agordojn en la URL"
#~ " de preferoj povas esti uzata por "
#~ "sinkronigi preferojn tra aparatoj."
#~ msgid "proxied"
#~ msgstr "prokurata"

View file

@ -44,13 +44,14 @@
# curtwheeler <curtwheeler@users.noreply.translate.codeberg.org>, 2025.
# return42 <return42@noreply.codeberg.org>, 2025.
# Atul_Eterno <atul_eterno@noreply.codeberg.org>, 2025.
# realkendrick_fr <realkendrick_fr@noreply.codeberg.org>, 2025.
msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-04-24 14:06+0000\n"
"Last-Translator: Atul_Eterno <atul_eterno@noreply.codeberg.org>\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-05-27 03:58+0000\n"
"Last-Translator: realkendrick_fr <realkendrick_fr@noreply.codeberg.org>\n"
"Language-Team: Spanish <https://translate.codeberg.org/projects/searxng/"
"searxng/es/>\n"
"Language: es\n"
@ -58,7 +59,7 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"X-Generator: Weblate 5.11.4\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -102,7 +103,7 @@ msgid "videos"
msgstr "vídeos"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "radio"
@ -379,93 +380,93 @@ msgstr "cerrar"
msgid "answered"
msgstr "contestado"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Ningún artículo encontrado"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Fuente"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Error al cargar la siguiente página"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Ajustes inválidos, por favor, cambia tus preferencias"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Ajustes inválidos"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "error en la búsqueda"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "expirado"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "error de análisis"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "Error de protocolo HTTP"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "error de red"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "Error SSL: la validación del certificado ha fallado"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "cierre inesperado"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "Error de HTTP"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "Error de conexión HTTP"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "error de proxy"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "demasiadas peticiones"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "acceso denegado"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "error en la API del servidor"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Suspendido"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "hace {minutes} minuto(s)"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "hace {hours} hora(s) y {minutes} minuto(s)"
@ -496,15 +497,15 @@ msgstr "Esta entrada ha sido sustituida por"
msgid "Channel"
msgstr "Canal"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "bitrate"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "votos"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "clics"
@ -513,7 +514,7 @@ msgstr "clics"
msgid "Language"
msgstr "Idioma"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -558,7 +559,7 @@ msgstr "Lista negra de Ahmia"
#: searx/plugins/ahmia_filter.py:33
msgid "Filter out onion results that appear in Ahmia's blacklist."
msgstr ""
msgstr "Filtrar resultados de onion que aparezcan en la lista negra de Ahmia."
#: searx/plugins/calculator.py:38
msgid "Basic Calculator"
@ -741,10 +742,6 @@ msgstr "Autor"
msgid "cached"
msgstr "en caché"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "por un proxy"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Enviar un nuevo problema a GitHub"
@ -2134,3 +2131,6 @@ msgstr "ocultar video"
#~ "URL de preferencias puede usarse para"
#~ " sincronizar las preferencias entre "
#~ "dispositivos."
#~ msgid "proxied"
#~ msgstr "por un proxy"

View file

@ -20,8 +20,8 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"PO-Revision-Date: 2025-03-30 01:58+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-05-23 07:10+0000\n"
"Last-Translator: Priit Jõerüüt <jrtcdbrg@noreply.codeberg.org>\n"
"Language-Team: Estonian <https://translate.codeberg.org/projects/searxng/"
"searxng/et/>\n"
@ -30,7 +30,7 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.2\n"
"X-Generator: Weblate 5.11.4\n"
"Generated-By: Babel 2.17.0\n"
#. CONSTANT_NAMES['NO_SUBGROUPING']
@ -74,7 +74,7 @@ msgid "videos"
msgstr "videod"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "raadio"
@ -351,93 +351,93 @@ msgstr "suletud"
msgid "answered"
msgstr "vastatud"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Üksust ei leitud"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Allikas"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Viga järgmise lehekülje laadimisel"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Sobimatud seaded, palun muuda oma eelistusi"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Sobimatud seaded"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "otsingu viga"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "päring aegus"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "parsimise viga"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP-protokolli viga"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "võrguviga"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL viga: sertifikaadi valideerimine ei õnnestunud"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "ootamatu krahh"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP-viga"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP-ühenduse viga"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "proksiserveri viga"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "ROBOTILÕKS"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "liiga palju päringuid"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "ligipääs keelatud"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "serveri API viga"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Peatatud"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "{minutes} minut(it) tagasi"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "{hours} tund(i), {minutes} minut(it) tagasi"
@ -468,15 +468,15 @@ msgstr "See üksus on asendatud"
msgid "Channel"
msgstr "Kanal"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "bitikiirus"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "hääled"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "klikid"
@ -485,7 +485,7 @@ msgstr "klikid"
msgid "Language"
msgstr "Keel"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -686,7 +686,7 @@ msgstr "Avalikud serverid"
#: searx/templates/simple/base.html:75
msgid "Privacy policy"
msgstr "Privaatsuspoliitika"
msgstr "Andmekaitsepõhimõtted"
#: searx/templates/simple/base.html:78
msgid "Contact instance maintainer"
@ -715,10 +715,6 @@ msgstr "Autor"
msgid "cached"
msgstr "vahemälus"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "proksiserveris"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Alusta veateate või ettepaneku koostamist GitHubis"
@ -2076,3 +2072,6 @@ msgstr "peida video"
#~ "Kohandatud seadete määramine eelistuste URL-i"
#~ " saad kasutada eelistuste sünkroniseerimiseks "
#~ "eri seadmete vahel."
#~ msgid "proxied"
#~ msgstr "proksiserveris"

View file

@ -18,7 +18,7 @@ msgid ""
msgstr ""
"Project-Id-Version: searx\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2025-03-29 09:21+0000\n"
"POT-Creation-Date: 2025-05-13 19:13+0000\n"
"PO-Revision-Date: 2025-02-12 15:39+0000\n"
"Last-Translator: alexgabi <alexgabi@users.noreply.translate.codeberg.org>"
"\n"
@ -72,7 +72,7 @@ msgid "videos"
msgstr "bideoak"
#. CATEGORY_NAMES['RADIO']
#: searx/engines/radio_browser.py:103 searx/searxng.msg
#: searx/engines/radio_browser.py:151 searx/searxng.msg
msgid "radio"
msgstr "irratia"
@ -349,93 +349,93 @@ msgstr "itxita"
msgid "answered"
msgstr "erantzunda"
#: searx/webapp.py:312
#: searx/webapp.py:291
msgid "No item found"
msgstr "Ez da elementurik aurkitu"
#: searx/engines/qwant.py:291
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:314
#: searx/templates/simple/result_templates/images.html:23 searx/webapp.py:293
msgid "Source"
msgstr "Iturria"
#: searx/webapp.py:316
#: searx/webapp.py:295
msgid "Error loading the next page"
msgstr "Errorea hurrengo orrialdea kargatzean"
#: searx/webapp.py:469 searx/webapp.py:875
#: searx/webapp.py:446 searx/webapp.py:844
msgid "Invalid settings, please edit your preferences"
msgstr "Ezarpen baliogabeak, editatu zure hobespenak"
#: searx/webapp.py:485
#: searx/webapp.py:462
msgid "Invalid settings"
msgstr "Ezarpen baliogabeak"
#: searx/webapp.py:562 searx/webapp.py:652
#: searx/webapp.py:539 searx/webapp.py:629
msgid "search error"
msgstr "bilaketa akatsa"
#: searx/webutils.py:36
#: searx/webutils.py:35
msgid "timeout"
msgstr "itxarote-denbora"
#: searx/webutils.py:37
#: searx/webutils.py:36
msgid "parsing error"
msgstr "analizatze errorea"
#: searx/webutils.py:38
#: searx/webutils.py:37
msgid "HTTP protocol error"
msgstr "HTTP protokoloaren errorea"
#: searx/webutils.py:39
#: searx/webutils.py:38
msgid "network error"
msgstr "sareko errorea"
#: searx/webutils.py:40
#: searx/webutils.py:39
msgid "SSL error: certificate validation has failed"
msgstr "SSL errorea: ziurtagiria baliozkotzeak huts egin du"
#: searx/webutils.py:42
#: searx/webutils.py:41
msgid "unexpected crash"
msgstr "ustekabeko kraskatzea"
#: searx/webutils.py:49
#: searx/webutils.py:48
msgid "HTTP error"
msgstr "HTTP errorea"
#: searx/webutils.py:50
#: searx/webutils.py:49
msgid "HTTP connection error"
msgstr "HTTP konexioaren errorea"
#: searx/webutils.py:56
#: searx/webutils.py:55
msgid "proxy error"
msgstr "proxy-aren errorea"
#: searx/webutils.py:57
#: searx/webutils.py:56
msgid "CAPTCHA"
msgstr "CAPTCHA"
#: searx/webutils.py:58
#: searx/webutils.py:57
msgid "too many requests"
msgstr "eskaera gehiegi"
#: searx/webutils.py:59
#: searx/webutils.py:58
msgid "access denied"
msgstr "sarbidea ukatua"
#: searx/webutils.py:60
#: searx/webutils.py:59
msgid "server API error"
msgstr "API zerbitzariaren errorea"
#: searx/webutils.py:79
#: searx/webutils.py:78
msgid "Suspended"
msgstr "Etenda"
#: searx/webutils.py:314
#: searx/webutils.py:313
#, python-brace-format
msgid "{minutes} minute(s) ago"
msgstr "duela {minutes} minutu"
#: searx/webutils.py:315
#: searx/webutils.py:314
#, python-brace-format
msgid "{hours} hour(s), {minutes} minute(s) ago"
msgstr "duela {hours} ordu eta {minutes} minutu"
@ -466,15 +466,15 @@ msgstr "Sarrera hau hurrengoarekin ordezkatu da"
msgid "Channel"
msgstr "Kanala"
#: searx/engines/radio_browser.py:105
#: searx/engines/radio_browser.py:153
msgid "bitrate"
msgstr "bit emaria"
#: searx/engines/radio_browser.py:106
#: searx/engines/radio_browser.py:154
msgid "votes"
msgstr "botoak"
#: searx/engines/radio_browser.py:107
#: searx/engines/radio_browser.py:155
msgid "clicks"
msgstr "klikak"
@ -483,7 +483,7 @@ msgstr "klikak"
msgid "Language"
msgstr "Hizkuntza"
#: searx/engines/semantic_scholar.py:79
#: searx/engines/semantic_scholar.py:101
#, python-brace-format
msgid ""
"{numCitations} citations from the year {firstCitationVelocityYear} to "
@ -711,10 +711,6 @@ msgstr "Egilea"
msgid "cached"
msgstr "cachean gordeta"
#: searx/templates/simple/macros.html:50
msgid "proxied"
msgstr "proxyan gordeta"
#: searx/templates/simple/new_issue.html:64
msgid "Start submitting a new issue on GitHub"
msgstr "Hasi gai -issue- berri bat bidaltzen GitHub-en"
@ -2072,3 +2068,6 @@ msgstr "ezkutatu bideoa"
#~ "zehaztea erabil daiteke gailuen hobespenak "
#~ "sinkronizatzeko."
#~ msgid "proxied"
#~ msgstr "proxyan gordeta"

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