mirror of https://github.com/miniflux/v2.git
Merge branch 'main' of github.com:miniflux/v2 into patch-1
This commit is contained in:
commit
c32ec02376
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "Miniflux",
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspace",
|
||||
"remoteUser": "vscode",
|
||||
"forwardPorts": [
|
||||
8080
|
||||
],
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"go.toolsManagement.checkForUpdates": "local",
|
||||
"go.useLanguageServer": true,
|
||||
"go.gopath": "/go"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker",
|
||||
"golang.go"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
version: '3.8'
|
||||
services:
|
||||
app:
|
||||
image: mcr.microsoft.com/devcontainers/go
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
command: sleep infinity
|
||||
network_mode: service:db
|
||||
environment:
|
||||
- CREATE_ADMIN=1
|
||||
- ADMIN_USERNAME=admin
|
||||
- ADMIN_PASSWORD=test123
|
||||
db:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
hostname: postgres
|
||||
environment:
|
||||
POSTGRES_DB: miniflux2
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
volumes:
|
||||
postgres-data: null
|
|
@ -2,7 +2,7 @@
|
|||
name: Bug report
|
||||
about: Create a bug report
|
||||
title: ''
|
||||
labels: bug
|
||||
labels: bug, question / discussion
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: Installation Issue
|
||||
about: Do you need help to install Miniflux?
|
||||
title: ''
|
||||
labels: installation issue
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
name: Question / Discussion
|
||||
about: Open discussions
|
||||
title: ''
|
||||
labels: question / discussion
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
paths-ignore:
|
||||
- ./http/client/testdata
|
|
@ -2,6 +2,15 @@ version: 2
|
|||
updates:
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
reviewers:
|
||||
- "fguillot"
|
||||
assignees:
|
||||
- "fguillot"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/packaging/docker/alpine"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
reviewers:
|
||||
|
@ -10,6 +19,15 @@ updates:
|
|||
- "fguillot"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/packaging/docker/distroless"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
reviewers:
|
||||
- "fguillot"
|
||||
assignees:
|
||||
- "fguillot"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
|
|
|
@ -4,7 +4,7 @@ permissions: read-all
|
|||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
|
@ -14,14 +14,14 @@ jobs:
|
|||
max-parallel: 4
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
go-version: [1.16, 1.17, 1.18]
|
||||
go-version: ["1.19", "1.20"]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Run unit tests
|
||||
run: make test
|
||||
|
||||
|
@ -40,13 +40,13 @@ jobs:
|
|||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.16
|
||||
go-version: "1.20"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Install Postgres client
|
||||
run: sudo apt-get install -y postgresql-client
|
||||
run: sudo apt update && sudo apt install -y postgresql-client
|
||||
- name: Run integration tests
|
||||
run: make integration-test
|
||||
env:
|
||||
|
|
|
@ -1,24 +1,13 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
permissions: read-all
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '45 22 * * 3'
|
||||
|
||||
|
@ -35,38 +24,19 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go', 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
config-file: ./.github/codeql/config.yml
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
name: Docker
|
||||
permissions: read-all
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 1 * * *'
|
||||
|
@ -8,10 +7,12 @@ on:
|
|||
- '*.*.*'
|
||||
jobs:
|
||||
docker-images:
|
||||
permissions:
|
||||
packages: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
@ -22,10 +23,10 @@ jobs:
|
|||
DOCKER_VERSION=dev
|
||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
DOCKER_VERSION=nightly
|
||||
TAGS="${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
|
||||
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
|
||||
elif [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
DOCKER_VERSION=${GITHUB_REF#refs/tags/}
|
||||
TAGS="${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},${DOCKER_IMAGE}:latest,ghcr.io/${DOCKER_IMAGE}:latest"
|
||||
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION},docker.io/${DOCKER_IMAGE}:latest,ghcr.io/${DOCKER_IMAGE}:latest,quay.io/${DOCKER_IMAGE}:latest"
|
||||
fi
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
|
||||
|
@ -36,34 +37,41 @@ jobs:
|
|||
DOCKER_VERSION=dev-distroless
|
||||
if [ "${{ github.event_name }}" = "schedule" ]; then
|
||||
DOCKER_VERSION=nightly-distroless
|
||||
TAGS="${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
|
||||
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION}"
|
||||
elif [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
DOCKER_VERSION=${GITHUB_REF#refs/tags/}-distroless
|
||||
TAGS="${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},${DOCKER_IMAGE}:latest-distroless,ghcr.io/${DOCKER_IMAGE}:latest-distroless"
|
||||
TAGS="docker.io/${DOCKER_IMAGE}:${DOCKER_VERSION},ghcr.io/${DOCKER_IMAGE}:${DOCKER_VERSION},quay.io/${DOCKER_IMAGE}:${DOCKER_VERSION},docker.io/${DOCKER_IMAGE}:latest-distroless,ghcr.io/${DOCKER_IMAGE}:latest-distroless,quay.io/${DOCKER_IMAGE}:latest-distroless"
|
||||
fi
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.CR_PAT }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Quay Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ secrets.QUAY_USERNAME }}
|
||||
password: ${{ secrets.QUAY_TOKEN }}
|
||||
|
||||
- name: Build and Push Alpine images
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./packaging/docker/alpine/Dockerfile
|
||||
|
@ -72,7 +80,7 @@ jobs:
|
|||
tags: ${{ steps.docker_alpine_tag.outputs.tags }}
|
||||
|
||||
- name: Build and Push Distroless images
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: ./packaging/docker/distroless/Dockerfile
|
||||
|
|
|
@ -4,14 +4,14 @@ permissions: read-all
|
|||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
jshint:
|
||||
name: Javascript Linter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install jshint
|
||||
run: |
|
||||
sudo npm install -g jshint@2.13.3
|
||||
|
@ -22,11 +22,10 @@ jobs:
|
|||
name: Golang Linter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.17
|
||||
- uses: golangci/golangci-lint-action@v2
|
||||
go-version: "1.20"
|
||||
- uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
args: --skip-dirs tests --disable errcheck --enable sqlclosecheck --enable misspell --enable gofmt --enable goimports --enable whitespace
|
||||
skip-go-installation: true
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
name: Debian and RPM Package Builders
|
||||
permissions: read-all
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*.*.*'
|
||||
jobs:
|
||||
debian-package-builder:
|
||||
name: Build Debian Packages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
id: buildx
|
||||
with:
|
||||
install: true
|
||||
- name: Available Docker Platforms
|
||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
||||
- name: Build Debian Packages
|
||||
run: make debian-packages
|
||||
- name: List generated files
|
||||
run: ls -l *.deb
|
||||
- name: Upload packages to repository
|
||||
env:
|
||||
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
|
||||
run: for f in *.deb; do curl -F package=@$f https://$FURY_TOKEN@push.fury.io/miniflux/; done
|
||||
rpm-package-builder:
|
||||
name: Build RPM Package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Build RPM Package
|
||||
run: make rpm
|
||||
- name: List generated files
|
||||
run: ls -l *.rpm
|
||||
- name: Upload package to repository
|
||||
env:
|
||||
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}
|
||||
run: for f in *.rpm; do curl -F package=@$f https://$FURY_TOKEN@push.fury.io/miniflux/; done
|
|
@ -1,55 +0,0 @@
|
|||
name: Scorecards supply-chain security
|
||||
on:
|
||||
# Only the default branch is supported.
|
||||
branch_protection_rule:
|
||||
schedule:
|
||||
- cron: '31 8 * * 6'
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecards analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # v2.4.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@e3e75cf2ffbf9364bbff86cdbdf52b23176fe492 # v1.0.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
# Read-only PAT token. To create it,
|
||||
# follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation.
|
||||
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
|
||||
# Publish the results to enable scorecard badges. For more details, see
|
||||
# https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories, `publish_results` will automatically be set to `false`,
|
||||
# regardless of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# Upload the results as artifacts (optional).
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # v2.3.1
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26
|
||||
with:
|
||||
sarif_file: results.sarif
|
140
ChangeLog
140
ChangeLog
|
@ -1,3 +1,143 @@
|
|||
Version 2.0.43 (March 16, 2023)
|
||||
-------------------------------
|
||||
|
||||
* Avoid XSS when opening a broken image due to unescaped ServerError in proxy handler (CVE-2023-27592)
|
||||
|
||||
Creating an RSS feed item with the inline description containing an `<img>` tag
|
||||
with a `srcset` attribute pointing to an invalid URL like
|
||||
`http:a<script>alert(1)</script>`, we can coerce the proxy handler into an error
|
||||
condition where the invalid URL is returned unescaped and in full.
|
||||
|
||||
This results in JavaScript execution on the Miniflux instance as soon as the
|
||||
user is convinced to open the broken image.
|
||||
|
||||
* Use `r.RemoteAddr` to check `/metrics` endpoint network access (CVE-2023-27591)
|
||||
|
||||
HTTP headers like `X-Forwarded-For` or `X-Real-Ip` can be easily spoofed. As
|
||||
such, it cannot be used to test if the client IP is allowed.
|
||||
|
||||
The recommendation is to use HTTP Basic authentication to protect the
|
||||
metrics endpoint, or run Miniflux behind a trusted reverse-proxy.
|
||||
|
||||
* Add HTTP Basic authentication for `/metrics` endpoint
|
||||
* Add proxy support for several media types
|
||||
* Parse feed categories from RSS, Atom and JSON feeds
|
||||
* Ignore empty link when discovering feeds
|
||||
* Disable CGO explicitly to make sure the binary is statically linked
|
||||
* Add CSS classes to differentiate between category/feed/entry view and icons
|
||||
* Add rewrite and scraper rules for `blog.cloudflare.com`
|
||||
* Add `color-scheme` to themes
|
||||
* Add new keyboard shortcut to toggle open/close entry attachments section
|
||||
* Sanitizer: allow `id` attribute in `<sup>` element
|
||||
* Add Indonesian Language
|
||||
* Update translations
|
||||
* Update Docker Compose examples:
|
||||
- Run the application in one command
|
||||
- Bring back the health check condition to `depends_on`
|
||||
- Remove deprecated `version` element
|
||||
* Update scraping rules for `ilpost.it`
|
||||
* Bump `github.com/PuerkitoBio/goquery` from `1.8.0` to `1.8.1`
|
||||
* Bump `github.com/tdewolff/minify/v2` from `2.12.4` to `2.12.5`
|
||||
* Bump `github.com/yuin/goldmark` from `1.5.3` to `1.5.4`
|
||||
* Bump `golang.org/x/*` dependencies
|
||||
|
||||
Version 2.0.42 (January 29, 2023)
|
||||
---------------------------------
|
||||
|
||||
* Fix header items wrapping
|
||||
* Add option to enable or disable double tap
|
||||
* Improve PWA display mode label in settings page
|
||||
* Bump `golang.org/x/*` dependencies
|
||||
* Update translations
|
||||
* Add scraping rule for `ilpost.it`
|
||||
* Update reading time HTML element after fetching the original web page
|
||||
* Add category feeds refresh feature
|
||||
|
||||
Version 2.0.41 (December 10, 2022)
|
||||
----------------------------------
|
||||
|
||||
* Reverted PR #1290 (follow the only link) because it leads to several panics/segfaults that prevent feed updates
|
||||
* Disable double-tap mobile gesture if swipe gesture is disabled
|
||||
* Skip integrations if there are no entries to push
|
||||
* Enable TLS-ALPN-01 challenge for ACME
|
||||
- This type of challenge works purely at the TLS layer and is compatible
|
||||
with SNI proxies. The existing HTTP-01 challenge support has been left
|
||||
as-is.
|
||||
* Preconfigure Miniflux for GitHub Codespaces
|
||||
* Updated `golang.org/x/net/*` dependencies
|
||||
|
||||
Version 2.0.40 (November 13, 2022)
|
||||
----------------------------------
|
||||
|
||||
* Update dependencies
|
||||
* Pin Postgres image version in Docker Compose examples to avoid unexpected upgrades
|
||||
* Make English and Spanish translation more consistent:
|
||||
- Use "Feed" everywhere instead of "Subscription"
|
||||
- Use "Entry" instead of "Article"
|
||||
* Allow Content-Type and Accept headers in CORS policy
|
||||
* Use dirs file for Debian package
|
||||
* Use custom home page in PWA manifest
|
||||
* Fix scraper rule that could be incorrect when there is a redirect
|
||||
* Improve web scraper to fetch the only link present as workaround to some landing pages
|
||||
* Add Matrix bot integration
|
||||
* Proxify images in API responses
|
||||
* Add new options in user preferences to configure sorting of entries in the category page
|
||||
* Remove dependency on `github.com/mitchellh/go-server-timing`
|
||||
* Add support for the `continuation` parameter and result for Google Reader API ID calls
|
||||
* Use automatic variable for build target file names
|
||||
* Add rewrite rule for `recalbox.com`
|
||||
* Improve Dutch translation
|
||||
|
||||
Version 2.0.39 (October 16, 2022)
|
||||
---------------------------------
|
||||
|
||||
* Add support for date filtering in Google Reader API item ID calls
|
||||
* Handle RSS entries with only a GUID permalink
|
||||
* Go API Client: Accept endpoint URLs ending with `/v1/`
|
||||
* CORS API headers: Allow `Basic` authorization header
|
||||
* Log feed URL when submitting a subscription that returns an error
|
||||
* Update `make run` command to execute migrations automatically
|
||||
* Add option to send only the URL to Wallabag
|
||||
* Do not convert anchors to absolute links
|
||||
* Add config option to use a custom image proxy URL
|
||||
* Allow zoom on mobile devices
|
||||
* Add scraping rules for `theverge.com`, `royalroad.com`, `swordscomic.com`, and `smbc-comics.com`
|
||||
* Add Ukrainian translation
|
||||
* Update `golang.org/x/*` dependencies
|
||||
* Bump `github.com/tdewolff/minify/v2` from `2.12.0` to `2.12.4`
|
||||
* Bump `github.com/yuin/goldmark` from `1.4.13` to `1.5.2`
|
||||
* Bump `github.com/lib/pq` from `1.10.6` to `1.10.7`
|
||||
|
||||
Version 2.0.38 (August 13, 2022)
|
||||
--------------------------------
|
||||
|
||||
* Rename default branch from master to main
|
||||
* Update GitHub Actions
|
||||
* Bump `github.com/prometheus/client_golang` from `1.12.2` to `1.13.0`
|
||||
* Fix some linter issues
|
||||
* Handle Atom links with a text/html type defined
|
||||
* Add `parse_markdown` rewrite function
|
||||
* Build RPM and Debian packages automatically using GitHub Actions
|
||||
* Add `explosm.net` scraper rule
|
||||
* Make default home page configurable
|
||||
* Add title attribute to entry links because text could be truncated
|
||||
* Highlight categories with unread entries
|
||||
* Allow option to order by title and author in API entry endpoint
|
||||
* Update Russian translation
|
||||
* Make reading speed user-configurable
|
||||
* Added translation for Hindi language used in India
|
||||
* Add rewrite rules for article URL before fetching content
|
||||
* Bump `github.com/tdewolff/minify/v2` from `2.11.7` to `2.12.0`
|
||||
* Support other repo owners in GitHub Docker Action
|
||||
* Proxify empty URL should not crash
|
||||
* Avoid stretched image if specified width is larger than Miniflux's layout
|
||||
* Add support for OPML files with several nested outlines
|
||||
* sanitizer: handle image URLs in `srcset` attribute with comma
|
||||
* Allow `width` and `height` attributes for `img` tags
|
||||
* Document that `-config-dump` command line argument shows sensitive info
|
||||
* Add System-V init service in contrib folder
|
||||
* Fix syntax error in `RequestBuilder.getCsrfToken()` method
|
||||
|
||||
Version 2.0.37 (May 27, 2022)
|
||||
-----------------------------
|
||||
|
||||
|
|
36
Makefile
36
Makefile
|
@ -46,58 +46,58 @@ miniflux:
|
|||
@ go build -buildmode=pie -ldflags=$(LD_FLAGS) -o $(APP) main.go
|
||||
|
||||
linux-amd64:
|
||||
@ GOOS=linux GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-amd64 main.go
|
||||
@ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
linux-arm64:
|
||||
@ GOOS=linux GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-arm64 main.go
|
||||
@ CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
linux-armv7:
|
||||
@ GOOS=linux GOARCH=arm GOARM=7 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv7 main.go
|
||||
@ CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
linux-armv6:
|
||||
@ GOOS=linux GOARCH=arm GOARM=6 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv6 main.go
|
||||
@ CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
linux-armv5:
|
||||
@ GOOS=linux GOARCH=arm GOARM=5 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-armv5 main.go
|
||||
@ CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
darwin-amd64:
|
||||
@ GOOS=darwin GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-amd64 main.go
|
||||
@ GOOS=darwin GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
darwin-arm64:
|
||||
@ GOOS=darwin GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-darwin-arm64 main.go
|
||||
@ GOOS=darwin GOARCH=arm64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
freebsd-amd64:
|
||||
@ GOOS=freebsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-amd64 main.go
|
||||
@ CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
openbsd-amd64:
|
||||
@ GOOS=openbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-openbsd-amd64 main.go
|
||||
@ GOOS=openbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
windows-amd64:
|
||||
@ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-amd64 main.go
|
||||
@ GOOS=windows GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
build: linux-amd64 linux-arm64 linux-armv7 linux-armv6 linux-armv5 darwin-amd64 darwin-arm64 freebsd-amd64 openbsd-amd64 windows-amd64
|
||||
|
||||
# NOTE: unsupported targets
|
||||
netbsd-amd64:
|
||||
@ GOOS=netbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-amd64 main.go
|
||||
@ CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
linux-x86:
|
||||
@ GOOS=linux GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-linux-x86 main.go
|
||||
@ CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
freebsd-x86:
|
||||
@ GOOS=freebsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go
|
||||
@ CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
netbsd-x86:
|
||||
@ GOOS=netbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-netbsd-x86 main.go
|
||||
@ CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
openbsd-x86:
|
||||
@ GOOS=openbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-freebsd-x86 main.go
|
||||
@ GOOS=openbsd GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
windows-x86:
|
||||
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-windows-x86 main.go
|
||||
@ GOOS=windows GOARCH=386 go build -ldflags=$(LD_FLAGS) -o $(APP)-$@ main.go
|
||||
|
||||
run:
|
||||
@ LOG_DATE_TIME=1 go run main.go -debug
|
||||
@ LOG_DATE_TIME=1 DEBUG=1 RUN_MIGRATIONS=1 go run main.go
|
||||
|
||||
clean:
|
||||
@ rm -f $(APP)-* $(APP) $(APP)*.rpm $(APP)*.deb
|
||||
|
@ -153,7 +153,7 @@ rpm: clean
|
|||
rpmbuild -bb --define "_miniflux_version $(VERSION)" /root/rpmbuild/SPECS/miniflux.spec
|
||||
|
||||
debian:
|
||||
@ docker build \
|
||||
@ docker build --load \
|
||||
--build-arg BASE_IMAGE_ARCH=$(DEB_IMG_ARCH) \
|
||||
-t $(DEB_IMG_ARCH)/miniflux-deb-builder \
|
||||
-f packaging/debian/Dockerfile \
|
||||
|
|
|
@ -6,4 +6,6 @@ Only the latest stable version is supported.
|
|||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Send an email to `security AT miniflux DOT net` with all the steps to reproduce the problem.
|
||||
Preferably, [report the vulnerability privately using GitHub](https://github.com/miniflux/v2/security/advisories/new) ([documentation](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability)).
|
||||
|
||||
If you do not want to use GitHub, send an email to `security AT miniflux DOT net` with all the steps to reproduce the problem.
|
||||
|
|
|
@ -14,13 +14,14 @@ import (
|
|||
)
|
||||
|
||||
type handler struct {
|
||||
store *storage.Storage
|
||||
pool *worker.Pool
|
||||
store *storage.Storage
|
||||
pool *worker.Pool
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
// Serve declares API routes for the application.
|
||||
func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
|
||||
handler := &handler{store, pool}
|
||||
handler := &handler{store, pool, router}
|
||||
|
||||
sr := router.PathPrefix("/v1").Subrouter()
|
||||
middleware := newMiddleware(store)
|
||||
|
@ -42,6 +43,7 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
|
|||
sr.HandleFunc("/categories/{categoryID}", handler.removeCategory).Methods(http.MethodDelete)
|
||||
sr.HandleFunc("/categories/{categoryID}/mark-all-as-read", handler.markCategoryAsRead).Methods(http.MethodPut)
|
||||
sr.HandleFunc("/categories/{categoryID}/feeds", handler.getCategoryFeeds).Methods(http.MethodGet)
|
||||
sr.HandleFunc("/categories/{categoryID}/refresh", handler.refreshCategory).Methods(http.MethodPut)
|
||||
sr.HandleFunc("/categories/{categoryID}/entries", handler.getCategoryEntries).Methods(http.MethodGet)
|
||||
sr.HandleFunc("/categories/{categoryID}/entries/{entryID}", handler.getCategoryEntry).Methods(http.MethodGet)
|
||||
sr.HandleFunc("/discover", handler.discoverSubscriptions).Methods(http.MethodPost)
|
||||
|
|
|
@ -123,3 +123,20 @@ func (h *handler) removeCategory(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
json.NoContent(w, r)
|
||||
}
|
||||
|
||||
func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
categoryID := request.RouteInt64Param(r, "categoryID")
|
||||
|
||||
jobs, err := h.store.NewCategoryBatch(userID, categoryID, h.store.CountFeeds(userID))
|
||||
if err != nil {
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
h.pool.Push(jobs)
|
||||
}()
|
||||
|
||||
json.NoContent(w, r)
|
||||
}
|
||||
|
|
33
api/entry.go
33
api/entry.go
|
@ -9,17 +9,21 @@ import (
|
|||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/http/request"
|
||||
"miniflux.app/http/response/json"
|
||||
"miniflux.app/model"
|
||||
"miniflux.app/proxy"
|
||||
"miniflux.app/reader/processor"
|
||||
"miniflux.app/storage"
|
||||
"miniflux.app/url"
|
||||
"miniflux.app/validator"
|
||||
)
|
||||
|
||||
func getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b *storage.EntryQueryBuilder) {
|
||||
func (h *handler) getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b *storage.EntryQueryBuilder) {
|
||||
entry, err := b.GetEntry()
|
||||
if err != nil {
|
||||
json.ServerError(w, r, err)
|
||||
|
@ -31,6 +35,20 @@ func getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b *storage.Entr
|
|||
return
|
||||
}
|
||||
|
||||
entry.Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content)
|
||||
proxyOption := config.Opts.ProxyOption()
|
||||
|
||||
for i := range entry.Enclosures {
|
||||
if proxyOption == "all" || proxyOption != "none" && !url.IsHTTPS(entry.Enclosures[i].URL) {
|
||||
for _, mediaType := range config.Opts.ProxyMediaTypes() {
|
||||
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
|
||||
entry.Enclosures[i].URL = proxy.AbsoluteProxifyURL(h.router, r.Host, entry.Enclosures[i].URL)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json.OK(w, r, entry)
|
||||
}
|
||||
|
||||
|
@ -42,7 +60,7 @@ func (h *handler) getFeedEntry(w http.ResponseWriter, r *http.Request) {
|
|||
builder.WithFeedID(feedID)
|
||||
builder.WithEntryID(entryID)
|
||||
|
||||
getEntryFromBuilder(w, r, builder)
|
||||
h.getEntryFromBuilder(w, r, builder)
|
||||
}
|
||||
|
||||
func (h *handler) getCategoryEntry(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -53,7 +71,7 @@ func (h *handler) getCategoryEntry(w http.ResponseWriter, r *http.Request) {
|
|||
builder.WithCategoryID(categoryID)
|
||||
builder.WithEntryID(entryID)
|
||||
|
||||
getEntryFromBuilder(w, r, builder)
|
||||
h.getEntryFromBuilder(w, r, builder)
|
||||
}
|
||||
|
||||
func (h *handler) getEntry(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -61,7 +79,7 @@ func (h *handler) getEntry(w http.ResponseWriter, r *http.Request) {
|
|||
builder := h.store.NewEntryQueryBuilder(request.UserID(r))
|
||||
builder.WithEntryID(entryID)
|
||||
|
||||
getEntryFromBuilder(w, r, builder)
|
||||
h.getEntryFromBuilder(w, r, builder)
|
||||
}
|
||||
|
||||
func (h *handler) getFeedEntries(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -119,6 +137,8 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
|
|||
return
|
||||
}
|
||||
|
||||
tags := request.QueryStringParamList(r, "tags")
|
||||
|
||||
builder := h.store.NewEntryQueryBuilder(userID)
|
||||
builder.WithFeedID(feedID)
|
||||
builder.WithCategoryID(categoryID)
|
||||
|
@ -127,6 +147,7 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
|
|||
builder.WithDirection(direction)
|
||||
builder.WithOffset(offset)
|
||||
builder.WithLimit(limit)
|
||||
builder.WithTags(tags)
|
||||
configureFilters(builder, r)
|
||||
|
||||
entries, err := builder.GetEntries()
|
||||
|
@ -141,6 +162,10 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
|
|||
return
|
||||
}
|
||||
|
||||
for i := range entries {
|
||||
entries[i].Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entries[i].Content)
|
||||
}
|
||||
|
||||
json.OK(w, r, &entriesResponse{Total: count, Entries: entries})
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ func (m *middleware) handleCORS(next http.Handler) http.Handler {
|
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "X-Auth-Token, Authorization, Content-Type, Accept")
|
||||
if r.Method == http.MethodOptions {
|
||||
w.Header().Set("Access-Control-Max-Age", "3600")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
|
|
@ -10,13 +10,13 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func askCredentials() (string, string) {
|
||||
fd := int(os.Stdin.Fd())
|
||||
|
||||
if !terminal.IsTerminal(fd) {
|
||||
if !term.IsTerminal(fd) {
|
||||
fmt.Fprintf(os.Stderr, "This is not a terminal, exiting.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ func askCredentials() (string, string) {
|
|||
|
||||
fmt.Print("Enter Password: ")
|
||||
|
||||
state, _ := terminal.GetState(fd)
|
||||
defer terminal.Restore(fd, state)
|
||||
bytePassword, _ := terminal.ReadPassword(fd)
|
||||
state, _ := term.GetState(fd)
|
||||
defer term.Restore(fd, state)
|
||||
bytePassword, _ := term.ReadPassword(fd)
|
||||
|
||||
fmt.Printf("\n")
|
||||
return strings.TrimSpace(username), strings.TrimSpace(string(bytePassword))
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package cli implements command line arguments for Miniflux application.
|
||||
|
||||
*/
|
||||
package cli // import "miniflux.app/cli"
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"io"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Client holds API procedure calls.
|
||||
|
@ -19,6 +20,11 @@ type Client struct {
|
|||
|
||||
// New returns a new Miniflux client.
|
||||
func New(endpoint string, credentials ...string) *Client {
|
||||
// Web gives "API Endpoint = https://miniflux.app/v1/", it doesn't work (/v1/v1/me)
|
||||
endpoint = strings.TrimSuffix(endpoint, "/")
|
||||
endpoint = strings.TrimSuffix(endpoint, "/v1")
|
||||
// trim to https://miniflux.app
|
||||
|
||||
if len(credentials) == 2 {
|
||||
return &Client{request: &request{endpoint: endpoint, username: credentials[0], password: credentials[1]}}
|
||||
}
|
||||
|
@ -181,7 +187,6 @@ func (c *Client) CreateCategory(title string) (*Category, error) {
|
|||
body, err := c.request.Post("/v1/categories", map[string]interface{}{
|
||||
"title": title,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -201,7 +206,6 @@ func (c *Client) UpdateCategory(categoryID int64, title string) (*Category, erro
|
|||
body, err := c.request.Put(fmt.Sprintf("/v1/categories/%d", categoryID), map[string]interface{}{
|
||||
"title": title,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -244,6 +248,12 @@ func (c *Client) DeleteCategory(categoryID int64) error {
|
|||
return c.request.Delete(fmt.Sprintf("/v1/categories/%d", categoryID))
|
||||
}
|
||||
|
||||
// RefreshCategory refreshes a category.
|
||||
func (c *Client) RefreshCategory(categoryID int64) error {
|
||||
_, err := c.request.Put(fmt.Sprintf("/v1/categories/%d/refresh", categoryID), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Feeds gets all feeds.
|
||||
func (c *Client) Feeds() (Feeds, error) {
|
||||
body, err := c.request.Get("/v1/feeds")
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package client implements a client library for the Miniflux REST API.
|
||||
|
||||
Examples
|
||||
# Examples
|
||||
|
||||
This code snippet fetch the list of users:
|
||||
|
||||
|
@ -30,6 +29,5 @@ This one discover subscriptions on a website:
|
|||
return
|
||||
}
|
||||
fmt.Println(subscriptions)
|
||||
|
||||
*/
|
||||
package client // import "miniflux.app/client"
|
||||
|
|
|
@ -18,26 +18,29 @@ const (
|
|||
|
||||
// User represents a user in the system.
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
Timezone string `json:"timezone"`
|
||||
EntryDirection string `json:"entry_sorting_direction"`
|
||||
EntryOrder string `json:"entry_sorting_order"`
|
||||
Stylesheet string `json:"stylesheet"`
|
||||
GoogleID string `json:"google_id"`
|
||||
OpenIDConnectID string `json:"openid_connect_id"`
|
||||
EntriesPerPage int `json:"entries_per_page"`
|
||||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
DefaultReadingSpeed int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed int `json:"cjk_reading_speed"`
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password,omitempty"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
Timezone string `json:"timezone"`
|
||||
EntryDirection string `json:"entry_sorting_direction"`
|
||||
EntryOrder string `json:"entry_sorting_order"`
|
||||
Stylesheet string `json:"stylesheet"`
|
||||
GoogleID string `json:"google_id"`
|
||||
OpenIDConnectID string `json:"openid_connect_id"`
|
||||
EntriesPerPage int `json:"entries_per_page"`
|
||||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
GestureNav string `json:"gesture_nav"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
DefaultReadingSpeed int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage string `json:"default_home_page"`
|
||||
CategoriesSortingOrder string `json:"categories_sorting_order"`
|
||||
}
|
||||
|
||||
func (u User) String() string {
|
||||
|
@ -55,24 +58,27 @@ type UserCreationRequest struct {
|
|||
|
||||
// UserModificationRequest represents the request to update a user.
|
||||
type UserModificationRequest struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
Theme *string `json:"theme"`
|
||||
Language *string `json:"language"`
|
||||
Timezone *string `json:"timezone"`
|
||||
EntryDirection *string `json:"entry_sorting_direction"`
|
||||
EntryOrder *string `json:"entry_sorting_order"`
|
||||
Stylesheet *string `json:"stylesheet"`
|
||||
GoogleID *string `json:"google_id"`
|
||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
DisplayMode *string `json:"display_mode"`
|
||||
DefaultReadingSpeed *int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed *int `json:"cjk_reading_speed"`
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
Theme *string `json:"theme"`
|
||||
Language *string `json:"language"`
|
||||
Timezone *string `json:"timezone"`
|
||||
EntryDirection *string `json:"entry_sorting_direction"`
|
||||
EntryOrder *string `json:"entry_sorting_order"`
|
||||
Stylesheet *string `json:"stylesheet"`
|
||||
GoogleID *string `json:"google_id"`
|
||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
GestureNav *string `json:"gesture_nav"`
|
||||
DisplayMode *string `json:"display_mode"`
|
||||
DefaultReadingSpeed *int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed *int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage *string `json:"default_home_page"`
|
||||
CategoriesSortingOrder *string `json:"categories_sorting_order"`
|
||||
}
|
||||
|
||||
// Users represents a list of users.
|
||||
|
@ -212,6 +218,7 @@ type Entry struct {
|
|||
ReadingTime int `json:"reading_time"`
|
||||
Enclosures Enclosures `json:"enclosures,omitempty"`
|
||||
Feed *Feed `json:"feed,omitempty"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// Entries represents a list of entries.
|
||||
|
|
|
@ -1163,7 +1163,97 @@ func TestPocketConsumerKeyFromUserPrefs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProxyImages(t *testing.T) {
|
||||
func TestProxyOption(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := "all"
|
||||
result := opts.ProxyOption()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultProxyOptionValue(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := defaultProxyOption
|
||||
result := opts.ProxyOption()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyMediaTypes(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image,audio")
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := []string{"audio", "image"}
|
||||
|
||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
|
||||
resultMap := make(map[string]bool)
|
||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
||||
resultMap[mediaType] = true
|
||||
}
|
||||
|
||||
for _, mediaType := range expected {
|
||||
if !resultMap[mediaType] {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyMediaTypesWithDuplicatedValues(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image,audio, image")
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := []string{"audio", "image"}
|
||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
|
||||
resultMap := make(map[string]bool)
|
||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
||||
resultMap[mediaType] = true
|
||||
}
|
||||
|
||||
for _, mediaType := range expected {
|
||||
if !resultMap[mediaType] {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyImagesOptionBackwardCompatibility(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
|
||||
|
@ -1173,15 +1263,30 @@ func TestProxyImages(t *testing.T) {
|
|||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := "all"
|
||||
result := opts.ProxyImages()
|
||||
expected := []string{"image"}
|
||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected PROXY_IMAGES value, got %q instead of %q`, result, expected)
|
||||
resultMap := make(map[string]bool)
|
||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
||||
resultMap[mediaType] = true
|
||||
}
|
||||
|
||||
for _, mediaType := range expected {
|
||||
if !resultMap[mediaType] {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
expectedProxyOption := "all"
|
||||
result := opts.ProxyOption()
|
||||
if result != expectedProxyOption {
|
||||
t.Fatalf(`Unexpected PROXY_OPTION value, got %q instead of %q`, result, expectedProxyOption)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultProxyImagesValue(t *testing.T) {
|
||||
func TestDefaultProxyMediaTypes(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
parser := NewParser()
|
||||
|
@ -1190,11 +1295,56 @@ func TestDefaultProxyImagesValue(t *testing.T) {
|
|||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := defaultProxyImages
|
||||
result := opts.ProxyImages()
|
||||
expected := []string{"image"}
|
||||
|
||||
if len(expected) != len(opts.ProxyMediaTypes()) {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
|
||||
resultMap := make(map[string]bool)
|
||||
for _, mediaType := range opts.ProxyMediaTypes() {
|
||||
resultMap[mediaType] = true
|
||||
}
|
||||
|
||||
for _, mediaType := range expected {
|
||||
if !resultMap[mediaType] {
|
||||
t.Fatalf(`Unexpected PROXY_MEDIA_TYPES value, got %v instead of %v`, opts.ProxyMediaTypes(), expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyHTTPClientTimeout(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_HTTP_CLIENT_TIMEOUT", "24")
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := 24
|
||||
result := opts.ProxyHTTPClientTimeout()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected PROXY_IMAGES value, got %q instead of %q`, result, expected)
|
||||
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultProxyHTTPClientTimeoutValue(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := defaultProxyHTTPClientTimeout
|
||||
result := opts.ProxyHTTPClientTimeout()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected PROXY_HTTP_CLIENT_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1297,6 +1447,41 @@ func TestDefaultHTTPClientMaxBodySizeValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestHTTPServerTimeout(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("HTTP_SERVER_TIMEOUT", "342")
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := 342
|
||||
result := opts.HTTPServerTimeout()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected HTTP_SERVER_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultHTTPServerTimeoutValue(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
parser := NewParser()
|
||||
opts, err := parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
expected := defaultHTTPServerTimeout
|
||||
result := opts.HTTPServerTimeout()
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf(`Unexpected HTTP_SERVER_TIMEOUT value, got %d instead of %d`, result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConfigFile(t *testing.T) {
|
||||
content := []byte(`
|
||||
# This is a comment
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package config handles configuration management for the application.
|
||||
|
||||
*/
|
||||
package config // import "miniflux.app/config"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package config // import "miniflux.app/config"
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -45,7 +46,10 @@ const (
|
|||
defaultCleanupArchiveUnreadDays = 180
|
||||
defaultCleanupArchiveBatchSize = 10000
|
||||
defaultCleanupRemoveSessionsDays = 30
|
||||
defaultProxyImages = "http-only"
|
||||
defaultProxyHTTPClientTimeout = 120
|
||||
defaultProxyOption = "http-only"
|
||||
defaultProxyMediaTypes = "image"
|
||||
defaultProxyUrl = ""
|
||||
defaultFetchYouTubeWatchTime = false
|
||||
defaultCreateAdmin = false
|
||||
defaultAdminUsername = ""
|
||||
|
@ -60,6 +64,7 @@ const (
|
|||
defaultHTTPClientTimeout = 20
|
||||
defaultHTTPClientMaxBodySize = 15
|
||||
defaultHTTPClientProxy = ""
|
||||
defaultHTTPServerTimeout = 300
|
||||
defaultAuthProxyHeader = ""
|
||||
defaultAuthProxyUserCreation = false
|
||||
defaultMaintenanceMode = false
|
||||
|
@ -67,6 +72,8 @@ const (
|
|||
defaultMetricsCollector = false
|
||||
defaultMetricsRefreshInterval = 60
|
||||
defaultMetricsAllowedNetworks = "127.0.0.1/8"
|
||||
defaultMetricsUsername = ""
|
||||
defaultMetricsPassword = ""
|
||||
defaultWatchdog = true
|
||||
defaultInvidiousInstance = "yewtu.be"
|
||||
)
|
||||
|
@ -115,7 +122,10 @@ type Options struct {
|
|||
createAdmin bool
|
||||
adminUsername string
|
||||
adminPassword string
|
||||
proxyImages string
|
||||
proxyHTTPClientTimeout int
|
||||
proxyOption string
|
||||
proxyMediaTypes []string
|
||||
proxyUrl string
|
||||
fetchYouTubeWatchTime bool
|
||||
oauth2UserCreationAllowed bool
|
||||
oauth2ClientID string
|
||||
|
@ -128,6 +138,7 @@ type Options struct {
|
|||
httpClientMaxBodySize int64
|
||||
httpClientProxy string
|
||||
httpClientUserAgent string
|
||||
httpServerTimeout int
|
||||
authProxyHeader string
|
||||
authProxyUserCreation bool
|
||||
maintenanceMode bool
|
||||
|
@ -135,12 +146,18 @@ type Options struct {
|
|||
metricsCollector bool
|
||||
metricsRefreshInterval int
|
||||
metricsAllowedNetworks []string
|
||||
metricsUsername string
|
||||
metricsPassword string
|
||||
watchdog bool
|
||||
invidiousInstance string
|
||||
proxyPrivateKey []byte
|
||||
}
|
||||
|
||||
// NewOptions returns Options with default values.
|
||||
func NewOptions() *Options {
|
||||
randomKey := make([]byte, 16)
|
||||
rand.Read(randomKey)
|
||||
|
||||
return &Options{
|
||||
HTTPS: defaultHTTPS,
|
||||
logDateTime: defaultLogDateTime,
|
||||
|
@ -174,7 +191,10 @@ func NewOptions() *Options {
|
|||
pollingParsingErrorLimit: defaultPollingParsingErrorLimit,
|
||||
workerPoolSize: defaultWorkerPoolSize,
|
||||
createAdmin: defaultCreateAdmin,
|
||||
proxyImages: defaultProxyImages,
|
||||
proxyHTTPClientTimeout: defaultProxyHTTPClientTimeout,
|
||||
proxyOption: defaultProxyOption,
|
||||
proxyMediaTypes: []string{defaultProxyMediaTypes},
|
||||
proxyUrl: defaultProxyUrl,
|
||||
fetchYouTubeWatchTime: defaultFetchYouTubeWatchTime,
|
||||
oauth2UserCreationAllowed: defaultOAuth2UserCreation,
|
||||
oauth2ClientID: defaultOAuth2ClientID,
|
||||
|
@ -187,6 +207,7 @@ func NewOptions() *Options {
|
|||
httpClientMaxBodySize: defaultHTTPClientMaxBodySize * 1024 * 1024,
|
||||
httpClientProxy: defaultHTTPClientProxy,
|
||||
httpClientUserAgent: defaultHTTPClientUserAgent,
|
||||
httpServerTimeout: defaultHTTPServerTimeout,
|
||||
authProxyHeader: defaultAuthProxyHeader,
|
||||
authProxyUserCreation: defaultAuthProxyUserCreation,
|
||||
maintenanceMode: defaultMaintenanceMode,
|
||||
|
@ -194,8 +215,11 @@ func NewOptions() *Options {
|
|||
metricsCollector: defaultMetricsCollector,
|
||||
metricsRefreshInterval: defaultMetricsRefreshInterval,
|
||||
metricsAllowedNetworks: []string{defaultMetricsAllowedNetworks},
|
||||
metricsUsername: defaultMetricsUsername,
|
||||
metricsPassword: defaultMetricsPassword,
|
||||
watchdog: defaultWatchdog,
|
||||
invidiousInstance: defaultInvidiousInstance,
|
||||
proxyPrivateKey: randomKey,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,9 +429,24 @@ func (o *Options) FetchYouTubeWatchTime() bool {
|
|||
return o.fetchYouTubeWatchTime
|
||||
}
|
||||
|
||||
// ProxyImages returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
|
||||
func (o *Options) ProxyImages() string {
|
||||
return o.proxyImages
|
||||
// ProxyOption returns "none" to never proxy, "http-only" to proxy non-HTTPS, "all" to always proxy.
|
||||
func (o *Options) ProxyOption() string {
|
||||
return o.proxyOption
|
||||
}
|
||||
|
||||
// ProxyMediaTypes returns a slice of media types to proxy.
|
||||
func (o *Options) ProxyMediaTypes() []string {
|
||||
return o.proxyMediaTypes
|
||||
}
|
||||
|
||||
// ProxyUrl returns a string of a URL to use to proxy image requests
|
||||
func (o *Options) ProxyUrl() string {
|
||||
return o.proxyUrl
|
||||
}
|
||||
|
||||
// ProxyHTTPClientTimeout returns the time limit in seconds before the proxy HTTP client cancel the request.
|
||||
func (o *Options) ProxyHTTPClientTimeout() int {
|
||||
return o.proxyHTTPClientTimeout
|
||||
}
|
||||
|
||||
// HasHTTPService returns true if the HTTP service is enabled.
|
||||
|
@ -443,6 +482,11 @@ func (o *Options) HTTPClientProxy() string {
|
|||
return o.httpClientProxy
|
||||
}
|
||||
|
||||
// HTTPServerTimeout returns the time limit in seconds before the HTTP server cancel the request.
|
||||
func (o *Options) HTTPServerTimeout() int {
|
||||
return o.httpServerTimeout
|
||||
}
|
||||
|
||||
// HasHTTPClientProxyConfigured returns true if the HTTP proxy is configured.
|
||||
func (o *Options) HasHTTPClientProxyConfigured() bool {
|
||||
return o.httpClientProxy != ""
|
||||
|
@ -475,6 +519,14 @@ func (o *Options) MetricsAllowedNetworks() []string {
|
|||
return o.metricsAllowedNetworks
|
||||
}
|
||||
|
||||
func (o *Options) MetricsUsername() string {
|
||||
return o.metricsUsername
|
||||
}
|
||||
|
||||
func (o *Options) MetricsPassword() string {
|
||||
return o.metricsPassword
|
||||
}
|
||||
|
||||
// HTTPClientUserAgent returns the global User-Agent header for miniflux.
|
||||
func (o *Options) HTTPClientUserAgent() string {
|
||||
return o.httpClientUserAgent
|
||||
|
@ -490,6 +542,11 @@ func (o *Options) InvidiousInstance() string {
|
|||
return o.invidiousInstance
|
||||
}
|
||||
|
||||
// ProxyPrivateKey returns the private key used by the media proxy
|
||||
func (o *Options) ProxyPrivateKey() []byte {
|
||||
return o.proxyPrivateKey
|
||||
}
|
||||
|
||||
// SortedOptions returns options as a list of key value pairs, sorted by keys.
|
||||
func (o *Options) SortedOptions(redactSecret bool) []*Option {
|
||||
var keyValues = map[string]interface{}{
|
||||
|
@ -522,6 +579,7 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
|
|||
"HTTP_CLIENT_PROXY": o.httpClientProxy,
|
||||
"HTTP_CLIENT_TIMEOUT": o.httpClientTimeout,
|
||||
"HTTP_CLIENT_USER_AGENT": o.httpClientUserAgent,
|
||||
"HTTP_SERVER_TIMEOUT": o.httpServerTimeout,
|
||||
"HTTP_SERVICE": o.httpService,
|
||||
"KEY_FILE": o.certKeyFile,
|
||||
"INVIDIOUS_INSTANCE": o.invidiousInstance,
|
||||
|
@ -532,6 +590,8 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
|
|||
"METRICS_ALLOWED_NETWORKS": strings.Join(o.metricsAllowedNetworks, ","),
|
||||
"METRICS_COLLECTOR": o.metricsCollector,
|
||||
"METRICS_REFRESH_INTERVAL": o.metricsRefreshInterval,
|
||||
"METRICS_USERNAME": o.metricsUsername,
|
||||
"METRICS_PASSWORD": redactSecretValue(o.metricsPassword, redactSecret),
|
||||
"OAUTH2_CLIENT_ID": o.oauth2ClientID,
|
||||
"OAUTH2_CLIENT_SECRET": redactSecretValue(o.oauth2ClientSecret, redactSecret),
|
||||
"OAUTH2_OIDC_DISCOVERY_ENDPOINT": o.oauth2OidcDiscoveryEndpoint,
|
||||
|
@ -542,7 +602,11 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
|
|||
"POLLING_FREQUENCY": o.pollingFrequency,
|
||||
"POLLING_PARSING_ERROR_LIMIT": o.pollingParsingErrorLimit,
|
||||
"POLLING_SCHEDULER": o.pollingScheduler,
|
||||
"PROXY_IMAGES": o.proxyImages,
|
||||
"PROXY_HTTP_CLIENT_TIMEOUT": o.proxyHTTPClientTimeout,
|
||||
"PROXY_PRIVATE_KEY": redactSecretValue(string(o.proxyPrivateKey), redactSecret),
|
||||
"PROXY_MEDIA_TYPES": o.proxyMediaTypes,
|
||||
"PROXY_OPTION": o.proxyOption,
|
||||
"PROXY_URL": o.proxyUrl,
|
||||
"ROOT_URL": o.rootURL,
|
||||
"RUN_MIGRATIONS": o.runMigrations,
|
||||
"SCHEDULER_ENTRY_FREQUENCY_MAX_INTERVAL": o.schedulerEntryFrequencyMaxInterval,
|
||||
|
@ -578,7 +642,7 @@ func (o *Options) String() string {
|
|||
|
||||
func redactSecretValue(value string, redactSecret bool) string {
|
||||
if redactSecret && value != "" {
|
||||
return "******"
|
||||
return "<secret>"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package config // import "miniflux.app/config"
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -137,8 +138,20 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
|||
p.opts.schedulerEntryFrequencyMinInterval = parseInt(value, defaultSchedulerEntryFrequencyMinInterval)
|
||||
case "POLLING_PARSING_ERROR_LIMIT":
|
||||
p.opts.pollingParsingErrorLimit = parseInt(value, defaultPollingParsingErrorLimit)
|
||||
// kept for compatibility purpose
|
||||
case "PROXY_IMAGES":
|
||||
p.opts.proxyImages = parseString(value, defaultProxyImages)
|
||||
p.opts.proxyOption = parseString(value, defaultProxyOption)
|
||||
case "PROXY_HTTP_CLIENT_TIMEOUT":
|
||||
p.opts.proxyHTTPClientTimeout = parseInt(value, defaultProxyHTTPClientTimeout)
|
||||
case "PROXY_OPTION":
|
||||
p.opts.proxyOption = parseString(value, defaultProxyOption)
|
||||
case "PROXY_MEDIA_TYPES":
|
||||
p.opts.proxyMediaTypes = parseStringList(value, []string{defaultProxyMediaTypes})
|
||||
// kept for compatibility purpose
|
||||
case "PROXY_IMAGE_URL":
|
||||
p.opts.proxyUrl = parseString(value, defaultProxyUrl)
|
||||
case "PROXY_URL":
|
||||
p.opts.proxyUrl = parseString(value, defaultProxyUrl)
|
||||
case "CREATE_ADMIN":
|
||||
p.opts.createAdmin = parseBool(value, defaultCreateAdmin)
|
||||
case "ADMIN_USERNAME":
|
||||
|
@ -177,6 +190,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
|||
p.opts.httpClientProxy = parseString(value, defaultHTTPClientProxy)
|
||||
case "HTTP_CLIENT_USER_AGENT":
|
||||
p.opts.httpClientUserAgent = parseString(value, defaultHTTPClientUserAgent)
|
||||
case "HTTP_SERVER_TIMEOUT":
|
||||
p.opts.httpServerTimeout = parseInt(value, defaultHTTPServerTimeout)
|
||||
case "AUTH_PROXY_HEADER":
|
||||
p.opts.authProxyHeader = parseString(value, defaultAuthProxyHeader)
|
||||
case "AUTH_PROXY_USER_CREATION":
|
||||
|
@ -191,12 +206,24 @@ func (p *Parser) parseLines(lines []string) (err error) {
|
|||
p.opts.metricsRefreshInterval = parseInt(value, defaultMetricsRefreshInterval)
|
||||
case "METRICS_ALLOWED_NETWORKS":
|
||||
p.opts.metricsAllowedNetworks = parseStringList(value, []string{defaultMetricsAllowedNetworks})
|
||||
case "METRICS_USERNAME":
|
||||
p.opts.metricsUsername = parseString(value, defaultMetricsUsername)
|
||||
case "METRICS_USERNAME_FILE":
|
||||
p.opts.metricsUsername = readSecretFile(value, defaultMetricsUsername)
|
||||
case "METRICS_PASSWORD":
|
||||
p.opts.metricsPassword = parseString(value, defaultMetricsPassword)
|
||||
case "METRICS_PASSWORD_FILE":
|
||||
p.opts.metricsPassword = readSecretFile(value, defaultMetricsPassword)
|
||||
case "FETCH_YOUTUBE_WATCH_TIME":
|
||||
p.opts.fetchYouTubeWatchTime = parseBool(value, defaultFetchYouTubeWatchTime)
|
||||
case "WATCHDOG":
|
||||
p.opts.watchdog = parseBool(value, defaultWatchdog)
|
||||
case "INVIDIOUS_INSTANCE":
|
||||
p.opts.invidiousInstance = parseString(value, defaultInvidiousInstance)
|
||||
case "PROXY_PRIVATE_KEY":
|
||||
randomKey := make([]byte, 16)
|
||||
rand.Read(randomKey)
|
||||
p.opts.proxyPrivateKey = parseBytes(value, randomKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,14 +296,29 @@ func parseStringList(value string, fallback []string) []string {
|
|||
}
|
||||
|
||||
var strList []string
|
||||
strMap := make(map[string]bool)
|
||||
|
||||
items := strings.Split(value, ",")
|
||||
for _, item := range items {
|
||||
strList = append(strList, strings.TrimSpace(item))
|
||||
itemValue := strings.TrimSpace(item)
|
||||
|
||||
if _, found := strMap[itemValue]; !found {
|
||||
strMap[itemValue] = true
|
||||
strList = append(strList, itemValue)
|
||||
}
|
||||
}
|
||||
|
||||
return strList
|
||||
}
|
||||
|
||||
func parseBytes(value string, fallback []byte) []byte {
|
||||
if value == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return []byte(value)
|
||||
}
|
||||
|
||||
func readSecretFile(filename, fallback string) string {
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,6 +8,5 @@ Here are few Docker Compose examples:
|
|||
- `traefik.yml`: Use Traefik as reverse-proxy with automatic HTTPS
|
||||
|
||||
```bash
|
||||
docker-compose -f basic.yml up -d db
|
||||
docker-compose -f basic.yml up
|
||||
docker compose -f basic.yml up -d
|
||||
```
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
version: '3.4'
|
||||
services:
|
||||
miniflux:
|
||||
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
|
||||
|
@ -7,7 +6,8 @@ services:
|
|||
ports:
|
||||
- "80:8080"
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
|
||||
- RUN_MIGRATIONS=1
|
||||
|
@ -19,7 +19,7 @@ services:
|
|||
# healthcheck:
|
||||
# test: ["CMD", "/usr/bin/miniflux", "-healthcheck", "auto"]
|
||||
db:
|
||||
image: postgres:latest
|
||||
image: postgres:15
|
||||
container_name: postgres
|
||||
environment:
|
||||
- POSTGRES_USER=miniflux
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
version: '3.4'
|
||||
services:
|
||||
caddy:
|
||||
image: caddy:2
|
||||
|
@ -16,7 +15,8 @@ services:
|
|||
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
|
||||
container_name: miniflux
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
|
||||
- RUN_MIGRATIONS=1
|
||||
|
@ -25,7 +25,7 @@ services:
|
|||
- ADMIN_PASSWORD=test123
|
||||
- BASE_URL=https://miniflux.example.org
|
||||
db:
|
||||
image: postgres:latest
|
||||
image: postgres:15
|
||||
container_name: postgres
|
||||
environment:
|
||||
- POSTGRES_USER=miniflux
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
version: '3.4'
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v2.3"
|
||||
|
@ -21,7 +20,8 @@ services:
|
|||
image: ${MINIFLUX_IMAGE:-miniflux/miniflux:latest}
|
||||
container_name: miniflux
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
condition: service_healthy
|
||||
expose:
|
||||
- "8080"
|
||||
environment:
|
||||
|
@ -37,7 +37,7 @@ services:
|
|||
- "traefik.http.routers.miniflux.entrypoints=websecure"
|
||||
- "traefik.http.routers.miniflux.tls.certresolver=myresolver"
|
||||
db:
|
||||
image: postgres:latest
|
||||
image: postgres:15
|
||||
container_name: postgres
|
||||
environment:
|
||||
- POSTGRES_USER=miniflux
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"panels": [
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
|
@ -104,7 +104,7 @@
|
|||
"type": "bargauge"
|
||||
},
|
||||
{
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
|
@ -289,7 +289,7 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
|
@ -332,7 +332,7 @@
|
|||
"pluginVersion": "7.1.5",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_memstats_sys_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_sys_bytes{job=\"miniflux\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{ instance }} - Memory Used",
|
||||
"refId": "A"
|
||||
|
@ -348,7 +348,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
|
@ -440,7 +440,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
|
@ -529,7 +529,7 @@
|
|||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
|
@ -546,7 +546,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
|
@ -590,7 +590,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_memstats_sys_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_sys_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -699,7 +699,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "process_open_fds{app=\"miniflux\"}",
|
||||
"expr": "process_open_fds{job=\"miniflux\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{instance }} - Open File Descriptors",
|
||||
"refId": "A"
|
||||
|
@ -748,7 +748,7 @@
|
|||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"gridPos": {
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
|
@ -816,7 +816,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_memstats_alloc_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_alloc_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
|
@ -826,7 +826,7 @@
|
|||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "rate(go_memstats_alloc_bytes_total{app=\"miniflux\"}[30s])",
|
||||
"expr": "rate(go_memstats_alloc_bytes_total{job=\"miniflux\"}[30s])",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
|
@ -836,7 +836,7 @@
|
|||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_stack_inuse_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_stack_inuse_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
|
@ -846,7 +846,7 @@
|
|||
"step": 4
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_heap_inuse_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_heap_inuse_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"interval": "",
|
||||
|
@ -904,7 +904,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {}
|
||||
|
@ -945,13 +945,13 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_goroutines{app=\"miniflux\"}",
|
||||
"expr": "go_goroutines{job=\"miniflux\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{ instance }} - Goroutines",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "go_threads{app=\"miniflux\"}",
|
||||
"expr": "go_threads{job=\"miniflux\"}",
|
||||
"interval": "",
|
||||
"legendFormat": "{{ instance }} - OS threads",
|
||||
"refId": "B"
|
||||
|
@ -1003,7 +1003,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
|
@ -1046,7 +1046,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_memstats_stack_inuse_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_stack_inuse_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1054,7 +1054,7 @@
|
|||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_stack_sys_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_stack_sys_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1108,7 +1108,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
|
@ -1150,7 +1150,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_memstats_heap_alloc_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_heap_alloc_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1158,7 +1158,7 @@
|
|||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_heap_sys_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_heap_sys_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1166,7 +1166,7 @@
|
|||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_heap_idle_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_heap_idle_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1174,7 +1174,7 @@
|
|||
"refId": "C"
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_heap_inuse_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_heap_inuse_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1182,7 +1182,7 @@
|
|||
"refId": "D"
|
||||
},
|
||||
{
|
||||
"expr": "go_memstats_heap_released_bytes{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_heap_released_bytes{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1282,7 +1282,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_gc_duration_seconds{app=\"miniflux\"}",
|
||||
"expr": "go_gc_duration_seconds{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 2,
|
||||
|
@ -1339,7 +1339,7 @@
|
|||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "Prometheus",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"custom": {},
|
||||
|
@ -1383,7 +1383,7 @@
|
|||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "go_memstats_mallocs_total{app=\"miniflux\"} - go_memstats_frees_total{app=\"miniflux\"}",
|
||||
"expr": "go_memstats_mallocs_total{job=\"miniflux\"} - go_memstats_frees_total{job=\"miniflux\"}",
|
||||
"format": "time_series",
|
||||
"interval": "",
|
||||
"intervalFactor": 1,
|
||||
|
@ -1462,4 +1462,4 @@
|
|||
"title": "Miniflux",
|
||||
"uid": "vSaPgcFMk",
|
||||
"version": 23
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package crypto implements helpers related to cryptography.
|
||||
|
||||
*/
|
||||
package crypto // import "miniflux.app/crypto"
|
||||
|
|
|
@ -604,4 +604,53 @@ var migrations = []func(tx *sql.Tx) error{
|
|||
`)
|
||||
return
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
_, err = tx.Exec(`
|
||||
ALTER TABLE users ADD COLUMN default_home_page text default 'unread';
|
||||
`)
|
||||
return
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
_, err = tx.Exec(`
|
||||
ALTER TABLE integrations ADD COLUMN wallabag_only_url bool default 'f';
|
||||
`)
|
||||
return
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
_, err = tx.Exec(`
|
||||
ALTER TABLE users ADD COLUMN categories_sorting_order text not null default 'unread_count';
|
||||
`)
|
||||
return
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
sql := `
|
||||
ALTER TABLE integrations ADD COLUMN matrix_bot_enabled bool default 'f';
|
||||
ALTER TABLE integrations ADD COLUMN matrix_bot_user text default '';
|
||||
ALTER TABLE integrations ADD COLUMN matrix_bot_password text default '';
|
||||
ALTER TABLE integrations ADD COLUMN matrix_bot_url text default '';
|
||||
ALTER TABLE integrations ADD COLUMN matrix_bot_chat_id text default '';
|
||||
`
|
||||
_, err = tx.Exec(sql)
|
||||
return
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
sql := `ALTER TABLE users ADD COLUMN double_tap boolean default 't'`
|
||||
_, err = tx.Exec(sql)
|
||||
return err
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
_, err = tx.Exec(`
|
||||
ALTER TABLE entries ADD COLUMN tags text[] default '{}';
|
||||
`)
|
||||
return
|
||||
},
|
||||
func(tx *sql.Tx) (err error) {
|
||||
sql := `
|
||||
ALTER TABLE users RENAME double_tap TO gesture_nav;
|
||||
ALTER TABLE users ALTER COLUMN gesture_nav SET DATA TYPE text using case when gesture_nav = true then 'tap' when gesture_nav = false then 'none' end;
|
||||
ALTER TABLE users ALTER COLUMN gesture_nav SET default 'tap';
|
||||
`
|
||||
_, err = tx.Exec(sql)
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
|
2
doc.go
2
doc.go
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Miniflux is a feed reader application.
|
||||
|
||||
*/
|
||||
package main // import "miniflux.app"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package errors handles localized errors.
|
||||
|
||||
*/
|
||||
package errors // import "miniflux.app/errors"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package fever implements Fever API endpoints.
|
||||
|
||||
*/
|
||||
package fever // import "miniflux.app/fever"
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"miniflux.app/integration"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/model"
|
||||
"miniflux.app/proxy"
|
||||
"miniflux.app/storage"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -22,7 +23,7 @@ import (
|
|||
|
||||
// Serve handles Fever API calls.
|
||||
func Serve(router *mux.Router, store *storage.Storage) {
|
||||
handler := &handler{store}
|
||||
handler := &handler{store, router}
|
||||
|
||||
sr := router.PathPrefix("/fever").Subrouter()
|
||||
sr.Use(newMiddleware(store).serve)
|
||||
|
@ -30,7 +31,8 @@ func Serve(router *mux.Router, store *storage.Storage) {
|
|||
}
|
||||
|
||||
type handler struct {
|
||||
store *storage.Storage
|
||||
store *storage.Storage
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
func (h *handler) serve(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -61,13 +63,13 @@ func (h *handler) serve(w http.ResponseWriter, r *http.Request) {
|
|||
/*
|
||||
A request with the groups argument will return two additional members:
|
||||
|
||||
groups contains an array of group objects
|
||||
feeds_groups contains an array of feeds_group objects
|
||||
groups contains an array of group objects
|
||||
feeds_groups contains an array of feeds_group objects
|
||||
|
||||
A group object has the following members:
|
||||
|
||||
id (positive integer)
|
||||
title (utf-8 string)
|
||||
id (positive integer)
|
||||
title (utf-8 string)
|
||||
|
||||
The feeds_group object is documented under “Feeds/Groups Relationships.”
|
||||
|
||||
|
@ -76,7 +78,6 @@ an is_spark equal to 0.
|
|||
|
||||
The “Sparks” super group is not included in this response and is composed of all feeds with an
|
||||
is_spark equal to 1.
|
||||
|
||||
*/
|
||||
func (h *handler) handleGroups(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
|
@ -107,18 +108,18 @@ func (h *handler) handleGroups(w http.ResponseWriter, r *http.Request) {
|
|||
/*
|
||||
A request with the feeds argument will return two additional members:
|
||||
|
||||
feeds contains an array of group objects
|
||||
feeds_groups contains an array of feeds_group objects
|
||||
feeds contains an array of group objects
|
||||
feeds_groups contains an array of feeds_group objects
|
||||
|
||||
A feed object has the following members:
|
||||
|
||||
id (positive integer)
|
||||
favicon_id (positive integer)
|
||||
title (utf-8 string)
|
||||
url (utf-8 string)
|
||||
site_url (utf-8 string)
|
||||
is_spark (boolean integer)
|
||||
last_updated_on_time (Unix timestamp/integer)
|
||||
id (positive integer)
|
||||
favicon_id (positive integer)
|
||||
title (utf-8 string)
|
||||
url (utf-8 string)
|
||||
site_url (utf-8 string)
|
||||
is_spark (boolean integer)
|
||||
last_updated_on_time (Unix timestamp/integer)
|
||||
|
||||
The feeds_group object is documented under “Feeds/Groups Relationships.”
|
||||
|
||||
|
@ -165,12 +166,12 @@ func (h *handler) handleFeeds(w http.ResponseWriter, r *http.Request) {
|
|||
/*
|
||||
A request with the favicons argument will return one additional member:
|
||||
|
||||
favicons contains an array of favicon objects
|
||||
favicons contains an array of favicon objects
|
||||
|
||||
A favicon object has the following members:
|
||||
|
||||
id (positive integer)
|
||||
data (base64 encoded image data; prefixed by image type)
|
||||
id (positive integer)
|
||||
data (base64 encoded image data; prefixed by image type)
|
||||
|
||||
An example data value:
|
||||
|
||||
|
@ -206,20 +207,20 @@ func (h *handler) handleFavicons(w http.ResponseWriter, r *http.Request) {
|
|||
/*
|
||||
A request with the items argument will return two additional members:
|
||||
|
||||
items contains an array of item objects
|
||||
total_items contains the total number of items stored in the database (added in API version 2)
|
||||
items contains an array of item objects
|
||||
total_items contains the total number of items stored in the database (added in API version 2)
|
||||
|
||||
An item object has the following members:
|
||||
|
||||
id (positive integer)
|
||||
feed_id (positive integer)
|
||||
title (utf-8 string)
|
||||
author (utf-8 string)
|
||||
html (utf-8 string)
|
||||
url (utf-8 string)
|
||||
is_saved (boolean integer)
|
||||
is_read (boolean integer)
|
||||
created_on_time (Unix timestamp/integer)
|
||||
id (positive integer)
|
||||
feed_id (positive integer)
|
||||
title (utf-8 string)
|
||||
author (utf-8 string)
|
||||
html (utf-8 string)
|
||||
url (utf-8 string)
|
||||
is_saved (boolean integer)
|
||||
is_read (boolean integer)
|
||||
created_on_time (Unix timestamp/integer)
|
||||
|
||||
Most servers won’t have enough memory allocated to PHP to dump all items at once.
|
||||
Three optional arguments control determine the items included in the response.
|
||||
|
@ -232,7 +233,6 @@ Three optional arguments control determine the items included in the response.
|
|||
|
||||
Use the with_ids argument with a comma-separated list of item ids to request (a maximum of 50) specific items.
|
||||
(added in API version 2)
|
||||
|
||||
*/
|
||||
func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
|
||||
var result itemsResponse
|
||||
|
@ -310,7 +310,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
|
|||
FeedID: entry.FeedID,
|
||||
Title: entry.Title,
|
||||
Author: entry.Author,
|
||||
HTML: entry.Content,
|
||||
HTML: proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content),
|
||||
URL: entry.URL,
|
||||
IsSaved: isSaved,
|
||||
IsRead: isRead,
|
||||
|
@ -327,7 +327,8 @@ The unread_item_ids and saved_item_ids arguments can be used to keep your local
|
|||
with the remote Fever installation.
|
||||
|
||||
A request with the unread_item_ids argument will return one additional member:
|
||||
unread_item_ids (string/comma-separated list of positive integers)
|
||||
|
||||
unread_item_ids (string/comma-separated list of positive integers)
|
||||
*/
|
||||
func (h *handler) handleUnreadItems(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
|
@ -384,9 +385,9 @@ func (h *handler) handleSavedItems(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
/*
|
||||
mark=item
|
||||
as=? where ? is replaced with read, saved or unsaved
|
||||
id=? where ? is replaced with the id of the item to modify
|
||||
mark=item
|
||||
as=? where ? is replaced with read, saved or unsaved
|
||||
id=? where ? is replaced with the id of the item to modify
|
||||
*/
|
||||
func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
|
@ -448,10 +449,10 @@ func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
/*
|
||||
mark=feed
|
||||
as=read
|
||||
id=? where ? is replaced with the id of the feed or group to modify
|
||||
before=? where ? is replaced with the Unix timestamp of the the local client’s most recent items API request
|
||||
mark=feed
|
||||
as=read
|
||||
id=? where ? is replaced with the id of the feed or group to modify
|
||||
before=? where ? is replaced with the Unix timestamp of the the local client’s most recent items API request
|
||||
*/
|
||||
func (h *handler) handleWriteFeeds(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
|
@ -474,10 +475,10 @@ func (h *handler) handleWriteFeeds(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
/*
|
||||
mark=group
|
||||
as=read
|
||||
id=? where ? is replaced with the id of the feed or group to modify
|
||||
before=? where ? is replaced with the Unix timestamp of the the local client’s most recent items API request
|
||||
mark=group
|
||||
as=read
|
||||
id=? where ? is replaced with the id of the feed or group to modify
|
||||
before=? where ? is replaced with the Unix timestamp of the the local client’s most recent items API request
|
||||
*/
|
||||
func (h *handler) handleWriteGroups(w http.ResponseWriter, r *http.Request) {
|
||||
userID := request.UserID(r)
|
||||
|
@ -510,9 +511,8 @@ func (h *handler) handleWriteGroups(w http.ResponseWriter, r *http.Request) {
|
|||
/*
|
||||
A feeds_group object has the following members:
|
||||
|
||||
group_id (positive integer)
|
||||
feed_ids (string/comma-separated list of positive integers)
|
||||
|
||||
group_id (positive integer)
|
||||
feed_ids (string/comma-separated list of positive integers)
|
||||
*/
|
||||
func (h *handler) buildFeedGroups(feeds model.Feeds) []feedsGroups {
|
||||
feedsGroupedByCategory := make(map[int64][]string)
|
||||
|
|
|
@ -23,8 +23,8 @@ func (b *baseResponse) SetCommonValues() {
|
|||
/*
|
||||
The default response is a JSON object containing two members:
|
||||
|
||||
api_version contains the version of the API responding (positive integer)
|
||||
auth whether the request was successfully authenticated (boolean integer)
|
||||
api_version contains the version of the API responding (positive integer)
|
||||
auth whether the request was successfully authenticated (boolean integer)
|
||||
|
||||
The API can also return XML by passing xml as the optional value of the api argument like so:
|
||||
|
||||
|
@ -37,7 +37,6 @@ at least one additional member:
|
|||
|
||||
last_refreshed_on_time contains the time of the most recently refreshed (not updated)
|
||||
feed (Unix timestamp/integer)
|
||||
|
||||
*/
|
||||
func newBaseResponse() baseResponse {
|
||||
r := baseResponse{}
|
||||
|
|
50
go.mod
50
go.mod
|
@ -1,27 +1,43 @@
|
|||
module miniflux.app
|
||||
|
||||
// +heroku goVersion go1.16
|
||||
// +heroku goVersion go1.19
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/PuerkitoBio/goquery v1.8.1
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/felixge/httpsnoop v1.0.1 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 // indirect
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/mitchellh/go-server-timing v1.0.2-0.20201108055052-feb680ab92c2
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d
|
||||
github.com/stretchr/testify v1.6.1 // indirect
|
||||
github.com/tdewolff/minify/v2 v2.12.0
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c
|
||||
gopkg.in/square/go-jose.v2 v2.5.0 // indirect
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/rylans/getlang v0.0.0-20201227074721-9e7f44ff8aa0
|
||||
github.com/tdewolff/minify/v2 v2.12.5
|
||||
github.com/yuin/goldmark v1.5.4
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/oauth2 v0.7.0
|
||||
golang.org/x/term v0.7.0
|
||||
mvdan.cc/xurls/v2 v2.4.0
|
||||
)
|
||||
|
||||
go 1.16
|
||||
require (
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.6.5 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
)
|
||||
|
||||
go 1.19
|
||||
|
|
234
go.sum
234
go.sum
|
@ -1,4 +1,3 @@
|
|||
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
|
@ -34,9 +33,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
|
|||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
|
||||
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
|
@ -44,15 +42,11 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
|||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
|
@ -62,61 +56,37 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/felixge/httpsnoop v1.0.0/go.mod h1:3+D9sFq0ahK/JeJPhCBUV1xlf4/eIYrUQaxulT0VzX8=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/gddo v0.0.0-20180823221919-9d8ff1c67be5/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
||||
github.com/golang/gddo v0.0.0-20200831202555-721e228c7686 h1:5vu7C+63KTbsSNnLhrgB98Sqy8MNVSW8FdhkcWA/3Rk=
|
||||
github.com/golang/gddo v0.0.0-20200831202555-721e228c7686/go.mod h1:sam69Hju0uq+5uvLJUMDlsKlQ21Vrs1Kd/1YFPNYdOU=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
|
@ -141,10 +111,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.1.1-0.20171103154506-982329095285/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
@ -153,10 +121,8 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||
github.com/google/go-dap v0.3.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
|
@ -168,25 +134,13 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
|
|||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v0.0.0-20170914154624-68e816d1c783/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -196,35 +150,21 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
|||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U=
|
||||
github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
|
||||
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
|
||||
github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-server-timing v1.0.2-0.20201108055052-feb680ab92c2 h1:9kYRAm87/M5VL+HWegDGIorBWDiErrZrksLKTJBF2IQ=
|
||||
github.com/mitchellh/go-server-timing v1.0.2-0.20201108055052-feb680ab92c2/go.mod h1:8mKJVJkyMI20ifXXO0zlV3sh3FrtjvltAeBqz38clBE=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
|
@ -232,118 +172,84 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
|||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
||||
github.com/peterh/liner v1.2.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
|
||||
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d h1:4Jn2kzSKcpxxwpJdyWxfNWKCYe4Uki2VibkExYmBLiA=
|
||||
github.com/rylans/getlang v0.0.0-20200505200108-4c3188ff8a2d/go.mod h1:3vfmZI6aJd5Rb9W2TQ0Nmupl+qem21R05+hmCscI0Bk=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/rylans/getlang v0.0.0-20201227074721-9e7f44ff8aa0 h1:qSaU9YAEIxk/ozcmY1hiauktAYTpbwYIrPdQ0L2E8UM=
|
||||
github.com/rylans/getlang v0.0.0-20201227074721-9e7f44ff8aa0/go.mod h1:3vfmZI6aJd5Rb9W2TQ0Nmupl+qem21R05+hmCscI0Bk=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tdewolff/minify/v2 v2.12.0 h1:ZyvMKeciyR3vzJrK/oHyBcSmpttQ/V+ah7qOqTZclaU=
|
||||
github.com/tdewolff/minify/v2 v2.12.0/go.mod h1:8mvf+KglD7XurfvvFZDUYvVURy6bA/r0oTvmakXMnyg=
|
||||
github.com/tdewolff/parse/v2 v2.6.1 h1:RIfy1erADkO90ynJWvty8VIkqqKYRzf2iLp8ObG174I=
|
||||
github.com/tdewolff/parse/v2 v2.6.1/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
|
||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tdewolff/minify/v2 v2.12.5 h1:s2KDBt/D/3ayE3gcqQF8VIgTmYgkx+btuLvVAeePzZM=
|
||||
github.com/tdewolff/minify/v2 v2.12.5/go.mod h1:i8QXtVyL7Ddwc4I5gqzvgBqKlTMgMNTbiXaPO4Iqg+A=
|
||||
github.com/tdewolff/parse/v2 v2.6.5 h1:lYvWBk55GkqKl0JJenGpmrgu/cPHQQ6/Mm1hBGswoGQ=
|
||||
github.com/tdewolff/parse/v2 v2.6.5/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs=
|
||||
github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM=
|
||||
github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20201006213952-227f4aabceb5/go.mod h1:f0znQkUKRrkk36XxWbGjMqQM8wGv/xHBVE2qc3B5oFU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
@ -374,17 +280,16 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -404,18 +309,24 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
|
||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -426,9 +337,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -438,15 +349,12 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -461,27 +369,37 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -501,7 +419,6 @@ golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
|
@ -523,12 +440,11 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
|
@ -550,9 +466,9 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
@ -582,10 +498,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
|||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
@ -607,17 +521,16 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
|||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.5.0 h1:OZ4sdq+Y+SHfYB7vfthi1Ei8b0vkP8ZPQgUfUwdUSqo=
|
||||
gopkg.in/square/go-jose.v2 v2.5.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -636,6 +549,5 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
|||
mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
|
||||
mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package googlereader implements Google Reader API endpoints.
|
||||
|
||||
*/
|
||||
package googlereader // import "miniflux.app/googlereader"
|
||||
|
|
|
@ -21,9 +21,11 @@ import (
|
|||
"miniflux.app/integration"
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/model"
|
||||
"miniflux.app/proxy"
|
||||
mff "miniflux.app/reader/handler"
|
||||
mfs "miniflux.app/reader/subscription"
|
||||
"miniflux.app/storage"
|
||||
"miniflux.app/url"
|
||||
"miniflux.app/validator"
|
||||
)
|
||||
|
||||
|
@ -88,8 +90,10 @@ const (
|
|||
ParamTitle = "t"
|
||||
// ParamQuickAdd - name of the parameter for a URL being quick subscribed to
|
||||
ParamQuickAdd = "quickadd"
|
||||
// ParamDestination - name fo the parameter for the new name of a tag
|
||||
// ParamDestination - name of the parameter for the new name of a tag
|
||||
ParamDestination = "dest"
|
||||
// ParamContinuation - name of the parameter for callers to pass to receive the next page of results
|
||||
ParamContinuation = "c"
|
||||
)
|
||||
|
||||
// StreamType represents the possible stream types
|
||||
|
@ -130,6 +134,7 @@ type RequestModifiers struct {
|
|||
FilterTargets []Stream
|
||||
Streams []Stream
|
||||
Count int
|
||||
Offset int
|
||||
SortDirection string
|
||||
StartTime int64
|
||||
StopTime int64
|
||||
|
@ -185,6 +190,7 @@ func (r RequestModifiers) String() string {
|
|||
result += fmt.Sprintf(" %v\n", s)
|
||||
}
|
||||
result += fmt.Sprintf("Count: %d\n", r.Count)
|
||||
result += fmt.Sprintf("Offset: %d\n", r.Offset)
|
||||
result += fmt.Sprintf("Sort Direction: %s\n", r.SortDirection)
|
||||
result += fmt.Sprintf("Continuation Token: %s\n", r.ContinuationToken)
|
||||
result += fmt.Sprintf("Start Time: %d\n", r.StartTime)
|
||||
|
@ -243,8 +249,9 @@ func getStreamFilterModifiers(r *http.Request) (RequestModifiers, error) {
|
|||
}
|
||||
|
||||
result.Count = request.QueryIntParam(r, ParamStreamMaxItems, 0)
|
||||
result.StartTime = int64(request.QueryIntParam(r, ParamStreamStartTime, 0))
|
||||
result.StopTime = int64(request.QueryIntParam(r, ParamStreamStopTime, 0))
|
||||
result.Offset = request.QueryIntParam(r, ParamContinuation, 0)
|
||||
result.StartTime = request.QueryInt64Param(r, ParamStreamStartTime, int64(0))
|
||||
result.StopTime = request.QueryInt64Param(r, ParamStreamStopTime, int64(0))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
@ -834,6 +841,20 @@ func (h *handler) streamItemContents(w http.ResponseWriter, r *http.Request) {
|
|||
categories = append(categories, userStarred)
|
||||
}
|
||||
|
||||
entry.Content = proxy.AbsoluteProxyRewriter(h.router, r.Host, entry.Content)
|
||||
proxyOption := config.Opts.ProxyOption()
|
||||
|
||||
for i := range entry.Enclosures {
|
||||
if proxyOption == "all" || proxyOption != "none" && !url.IsHTTPS(entry.Enclosures[i].URL) {
|
||||
for _, mediaType := range config.Opts.ProxyMediaTypes() {
|
||||
if strings.HasPrefix(entry.Enclosures[i].MimeType, mediaType+"/") {
|
||||
entry.Enclosures[i].URL = proxy.AbsoluteProxifyURL(h.router, r.Host, entry.Enclosures[i].URL)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItems[i] = contentItem{
|
||||
ID: fmt.Sprintf(EntryIDLong, entry.ID),
|
||||
Title: entry.Title,
|
||||
|
@ -1120,9 +1141,18 @@ func (h *handler) handleReadingListStream(w http.ResponseWriter, r *http.Request
|
|||
logger.Info("[GoogleReader][ReadingListStreamIDs][ClientIP=%s] xt filter type: %#v", clientIP, s)
|
||||
}
|
||||
}
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
builder.WithLimit(rm.Count)
|
||||
builder.WithOffset(rm.Offset)
|
||||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(rm.SortDirection)
|
||||
if rm.StartTime > 0 {
|
||||
builder.AfterDate(time.Unix(rm.StartTime, 0))
|
||||
}
|
||||
if rm.StopTime > 0 {
|
||||
builder.BeforeDate(time.Unix(rm.StopTime, 0))
|
||||
}
|
||||
|
||||
rawEntryIDs, err := builder.GetEntryIDs()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#reading-list] [ClientIP=%s] %v", clientIP, err)
|
||||
|
@ -1134,17 +1164,38 @@ func (h *handler) handleReadingListStream(w http.ResponseWriter, r *http.Request
|
|||
formattedID := strconv.FormatInt(entryID, 10)
|
||||
itemRefs = append(itemRefs, itemRef{ID: formattedID})
|
||||
}
|
||||
json.OK(w, r, streamIDResponse{itemRefs})
|
||||
|
||||
totalEntries, err := builder.CountEntries()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#reading-list] [ClientIP=%s] %v", clientIP, err)
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
continuation := 0
|
||||
if len(itemRefs)+rm.Offset < totalEntries {
|
||||
continuation = len(itemRefs) + rm.Offset
|
||||
}
|
||||
|
||||
json.OK(w, r, streamIDResponse{itemRefs, continuation})
|
||||
}
|
||||
|
||||
func (h *handler) handleStarredStream(w http.ResponseWriter, r *http.Request, rm RequestModifiers) {
|
||||
clientIP := request.ClientIP(r)
|
||||
|
||||
builder := h.store.NewEntryQueryBuilder(rm.UserID)
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
builder.WithStarred(true)
|
||||
builder.WithLimit(rm.Count)
|
||||
builder.WithOffset(rm.Offset)
|
||||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(rm.SortDirection)
|
||||
if rm.StartTime > 0 {
|
||||
builder.AfterDate(time.Unix(rm.StartTime, 0))
|
||||
}
|
||||
if rm.StopTime > 0 {
|
||||
builder.BeforeDate(time.Unix(rm.StopTime, 0))
|
||||
}
|
||||
|
||||
rawEntryIDs, err := builder.GetEntryIDs()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#starred] [ClientIP=%s] %v", clientIP, err)
|
||||
|
@ -1156,14 +1207,29 @@ func (h *handler) handleStarredStream(w http.ResponseWriter, r *http.Request, rm
|
|||
formattedID := strconv.FormatInt(entryID, 10)
|
||||
itemRefs = append(itemRefs, itemRef{ID: formattedID})
|
||||
}
|
||||
json.OK(w, r, streamIDResponse{itemRefs})
|
||||
|
||||
totalEntries, err := builder.CountEntries()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#starred] [ClientIP=%s] %v", clientIP, err)
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
continuation := 0
|
||||
if len(itemRefs)+rm.Offset < totalEntries {
|
||||
continuation = len(itemRefs) + rm.Offset
|
||||
}
|
||||
|
||||
json.OK(w, r, streamIDResponse{itemRefs, continuation})
|
||||
}
|
||||
|
||||
func (h *handler) handleReadStream(w http.ResponseWriter, r *http.Request, rm RequestModifiers) {
|
||||
clientIP := request.ClientIP(r)
|
||||
|
||||
builder := h.store.NewEntryQueryBuilder(rm.UserID)
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
builder.WithStatus(model.EntryStatusRead)
|
||||
builder.WithLimit(rm.Count)
|
||||
builder.WithOffset(rm.Offset)
|
||||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(rm.SortDirection)
|
||||
if rm.StartTime > 0 {
|
||||
|
@ -1184,7 +1250,19 @@ func (h *handler) handleReadStream(w http.ResponseWriter, r *http.Request, rm Re
|
|||
formattedID := strconv.FormatInt(entryID, 10)
|
||||
itemRefs = append(itemRefs, itemRef{ID: formattedID})
|
||||
}
|
||||
json.OK(w, r, streamIDResponse{itemRefs})
|
||||
|
||||
totalEntries, err := builder.CountEntries()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#read] [ClientIP=%s] %v", clientIP, err)
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
continuation := 0
|
||||
if len(itemRefs)+rm.Offset < totalEntries {
|
||||
continuation = len(itemRefs) + rm.Offset
|
||||
}
|
||||
|
||||
json.OK(w, r, streamIDResponse{itemRefs, continuation})
|
||||
}
|
||||
|
||||
func (h *handler) handleFeedStream(w http.ResponseWriter, r *http.Request, rm RequestModifiers) {
|
||||
|
@ -1197,8 +1275,10 @@ func (h *handler) handleFeedStream(w http.ResponseWriter, r *http.Request, rm Re
|
|||
}
|
||||
|
||||
builder := h.store.NewEntryQueryBuilder(rm.UserID)
|
||||
builder.WithoutStatus(model.EntryStatusRemoved)
|
||||
builder.WithFeedID(feedID)
|
||||
builder.WithLimit(rm.Count)
|
||||
builder.WithOffset(rm.Offset)
|
||||
builder.WithOrder(model.DefaultSortingOrder)
|
||||
builder.WithDirection(rm.SortDirection)
|
||||
if rm.StartTime > 0 {
|
||||
|
@ -1210,7 +1290,7 @@ func (h *handler) handleFeedStream(w http.ResponseWriter, r *http.Request, rm Re
|
|||
|
||||
rawEntryIDs, err := builder.GetEntryIDs()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#starred] [ClientIP=%s] %v", clientIP, err)
|
||||
logger.Error("[GoogleReader][/stream/items/ids#feed] [ClientIP=%s] %v", clientIP, err)
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
@ -1219,5 +1299,17 @@ func (h *handler) handleFeedStream(w http.ResponseWriter, r *http.Request, rm Re
|
|||
formattedID := strconv.FormatInt(entryID, 10)
|
||||
itemRefs = append(itemRefs, itemRef{ID: formattedID})
|
||||
}
|
||||
json.OK(w, r, streamIDResponse{itemRefs})
|
||||
|
||||
totalEntries, err := builder.CountEntries()
|
||||
if err != nil {
|
||||
logger.Error("[GoogleReader][/stream/items/ids#feed] [ClientIP=%s] %v", clientIP, err)
|
||||
json.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
continuation := 0
|
||||
if len(itemRefs)+rm.Offset < totalEntries {
|
||||
continuation = len(itemRefs) + rm.Offset
|
||||
}
|
||||
|
||||
json.OK(w, r, streamIDResponse{itemRefs, continuation})
|
||||
}
|
||||
|
|
|
@ -61,7 +61,8 @@ type itemRef struct {
|
|||
}
|
||||
|
||||
type streamIDResponse struct {
|
||||
ItemRefs []itemRef `json:"itemRefs"`
|
||||
ItemRefs []itemRef `json:"itemRefs"`
|
||||
Continuation int `json:"continuation,omitempty,string"`
|
||||
}
|
||||
|
||||
type tagsResponse struct {
|
||||
|
|
|
@ -29,10 +29,9 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
errInvalidCertificate = "Invalid SSL certificate (original error: %q)"
|
||||
errTemporaryNetworkOperation = "This website is temporarily unreachable (original error: %q)"
|
||||
errPermanentNetworkOperation = "This website is permanently unreachable (original error: %q)"
|
||||
errRequestTimeout = "Website unreachable, the request timed out after %d seconds"
|
||||
errInvalidCertificate = "Invalid SSL certificate (original error: %q)"
|
||||
errNetworkOperation = "This website is unreachable (original error: %q)"
|
||||
errRequestTimeout = "Website unreachable, the request timed out after %d seconds"
|
||||
)
|
||||
|
||||
// Client builds and executes HTTP requests.
|
||||
|
@ -205,17 +204,11 @@ func (c *Client) executeRequest(request *http.Request) (*Response, error) {
|
|||
case x509.CertificateInvalidError, x509.HostnameError:
|
||||
err = errors.NewLocalizedError(errInvalidCertificate, uerr.Err)
|
||||
case *net.OpError:
|
||||
if uerr.Err.(*net.OpError).Temporary() {
|
||||
err = errors.NewLocalizedError(errTemporaryNetworkOperation, uerr.Err)
|
||||
} else {
|
||||
err = errors.NewLocalizedError(errPermanentNetworkOperation, uerr.Err)
|
||||
}
|
||||
err = errors.NewLocalizedError(errNetworkOperation, uerr.Err)
|
||||
case net.Error:
|
||||
nerr := uerr.Err.(net.Error)
|
||||
if nerr.Timeout() {
|
||||
err = errors.NewLocalizedError(errRequestTimeout, c.ClientTimeout)
|
||||
} else if nerr.Temporary() {
|
||||
err = errors.NewLocalizedError(errTemporaryNetworkOperation, nerr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package client provides an HTTP client builder.
|
||||
|
||||
*/
|
||||
package client // import "miniflux.app/http/client"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package cookie provides functions to build cookies.
|
||||
|
||||
*/
|
||||
package cookie // import "miniflux.app/http/cookie"
|
||||
|
|
|
@ -10,15 +10,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
func dropIPv6zone(address string) string {
|
||||
i := strings.IndexByte(address, '%')
|
||||
if i != -1 {
|
||||
address = address[:i]
|
||||
}
|
||||
return address
|
||||
}
|
||||
|
||||
// FindClientIP returns client real IP address.
|
||||
// FindClientIP returns the client real IP address based on trusted Reverse-Proxy HTTP headers.
|
||||
func FindClientIP(r *http.Request) string {
|
||||
headers := []string{"X-Forwarded-For", "X-Real-Ip"}
|
||||
for _, header := range headers {
|
||||
|
@ -36,6 +28,11 @@ func FindClientIP(r *http.Request) string {
|
|||
}
|
||||
|
||||
// Fallback to TCP/IP source IP address.
|
||||
return FindRemoteIP(r)
|
||||
}
|
||||
|
||||
// FindRemoteIP returns remote client IP address.
|
||||
func FindRemoteIP(r *http.Request) string {
|
||||
remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
remoteIP = r.RemoteAddr
|
||||
|
@ -49,3 +46,11 @@ func FindClientIP(r *http.Request) string {
|
|||
|
||||
return remoteIP
|
||||
}
|
||||
|
||||
func dropIPv6zone(address string) string {
|
||||
i := strings.IndexByte(address, '%')
|
||||
if i != -1 {
|
||||
address = address[:i]
|
||||
}
|
||||
return address
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package request contains helper functions to work with the HTTP request.
|
||||
|
||||
*/
|
||||
package request // import "miniflux.app/http/request"
|
||||
|
|
|
@ -12,6 +12,8 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"miniflux.app/logger"
|
||||
)
|
||||
|
||||
const compressionThreshold = 1024
|
||||
|
@ -88,7 +90,10 @@ func (b *Builder) Write() {
|
|||
case io.Reader:
|
||||
// Compression not implemented in this case
|
||||
b.writeHeaders()
|
||||
io.Copy(b.w, v)
|
||||
_, err := io.Copy(b.w, v)
|
||||
if err != nil {
|
||||
logger.Error("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package response contains everything related to HTTP responses.
|
||||
|
||||
*/
|
||||
package response // import "miniflux.app/http/response"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package html contains HTML response functions.
|
||||
|
||||
*/
|
||||
package html // import "miniflux.app/http/response/html"
|
||||
|
|
|
@ -26,6 +26,7 @@ func ServerError(w http.ResponseWriter, r *http.Request, err error) {
|
|||
|
||||
builder := response.New(w, r)
|
||||
builder.WithStatus(http.StatusInternalServerError)
|
||||
builder.WithHeader("Content-Security-Policy", `default-src 'self'`)
|
||||
builder.WithHeader("Content-Type", "text/html; charset=utf-8")
|
||||
builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
|
||||
builder.WithBody(err)
|
||||
|
@ -38,6 +39,7 @@ func BadRequest(w http.ResponseWriter, r *http.Request, err error) {
|
|||
|
||||
builder := response.New(w, r)
|
||||
builder.WithStatus(http.StatusBadRequest)
|
||||
builder.WithHeader("Content-Security-Policy", `default-src 'self'`)
|
||||
builder.WithHeader("Content-Type", "text/html; charset=utf-8")
|
||||
builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
|
||||
builder.WithBody(err)
|
||||
|
@ -72,3 +74,16 @@ func NotFound(w http.ResponseWriter, r *http.Request) {
|
|||
func Redirect(w http.ResponseWriter, r *http.Request, uri string) {
|
||||
http.Redirect(w, r, uri, http.StatusFound)
|
||||
}
|
||||
|
||||
// RequestedRangeNotSatisfiable sends a range not satisfiable error to the client.
|
||||
func RequestedRangeNotSatisfiable(w http.ResponseWriter, r *http.Request, contentRange string) {
|
||||
logger.Error("[HTTP:Range Not Satisfiable] %s", r.URL)
|
||||
|
||||
builder := response.New(w, r)
|
||||
builder.WithStatus(http.StatusRequestedRangeNotSatisfiable)
|
||||
builder.WithHeader("Content-Type", "text/html; charset=utf-8")
|
||||
builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
|
||||
builder.WithHeader("Content-Range", contentRange)
|
||||
builder.WithBody("Range Not Satisfiable")
|
||||
builder.Write()
|
||||
}
|
||||
|
|
|
@ -210,3 +210,32 @@ func TestRedirectResponse(t *testing.T) {
|
|||
t.Fatalf(`Unexpected redirect location, got %q instead of %q`, actualResult, expectedResult)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestedRangeNotSatisfiable(t *testing.T) {
|
||||
r, err := http.NewRequest("GET", "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
RequestedRangeNotSatisfiable(w, r, "bytes */12777")
|
||||
})
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
resp := w.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
expectedStatusCode := http.StatusRequestedRangeNotSatisfiable
|
||||
if resp.StatusCode != expectedStatusCode {
|
||||
t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
|
||||
}
|
||||
|
||||
expectedContentRangeHeader := "bytes */12777"
|
||||
actualContentRangeHeader := resp.Header.Get("Content-Range")
|
||||
if actualContentRangeHeader != expectedContentRangeHeader {
|
||||
t.Fatalf(`Unexpected content range header, got %q instead of %q`, actualContentRangeHeader, expectedContentRangeHeader)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package json contains JSON response functions.
|
||||
|
||||
*/
|
||||
package json // import "miniflux.app/http/response/json"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package xml contains XML response functions.
|
||||
|
||||
*/
|
||||
package xml // import "miniflux.app/http/response/xml"
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package route contains helper functions to work with defined routes.
|
||||
|
||||
*/
|
||||
package route // import "miniflux.app/http/route"
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"miniflux.app/integration/espial"
|
||||
"miniflux.app/integration/instapaper"
|
||||
"miniflux.app/integration/linkding"
|
||||
"miniflux.app/integration/matrixbot"
|
||||
"miniflux.app/integration/nunuxkeeper"
|
||||
"miniflux.app/integration/pinboard"
|
||||
"miniflux.app/integration/pocket"
|
||||
|
@ -54,6 +55,7 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
|
|||
integration.WallabagClientSecret,
|
||||
integration.WallabagUsername,
|
||||
integration.WallabagPassword,
|
||||
integration.WallabagOnlyURL,
|
||||
)
|
||||
|
||||
if err := client.AddEntry(entry.URL, entry.Title, entry.Content); err != nil {
|
||||
|
@ -109,6 +111,18 @@ func SendEntry(entry *model.Entry, integration *model.Integration) {
|
|||
}
|
||||
}
|
||||
|
||||
// PushEntries pushes an entry array to third-party providers during feed refreshes.
|
||||
func PushEntries(entries model.Entries, integration *model.Integration) {
|
||||
if integration.MatrixBotEnabled {
|
||||
logger.Debug("[Integration] Sending %d entries for User #%d to Matrix", len(entries), integration.UserID)
|
||||
|
||||
err := matrixbot.PushEntries(entries, integration.MatrixBotURL, integration.MatrixBotUser, integration.MatrixBotPassword, integration.MatrixBotChatID)
|
||||
if err != nil {
|
||||
logger.Error("[Integration] push entries to matrix bot failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PushEntry pushes an entry to third-party providers during feed refreshes.
|
||||
func PushEntry(entry *model.Entry, integration *model.Integration) {
|
||||
if integration.TelegramBotEnabled {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2021 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package matrixbot // import "miniflux.app/integration/matrixbot"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"miniflux.app/logger"
|
||||
"miniflux.app/model"
|
||||
|
||||
"github.com/matrix-org/gomatrix"
|
||||
)
|
||||
|
||||
// PushEntry pushes entries to matrix chat using integration settings provided
|
||||
func PushEntries(entries model.Entries, serverURL, botLogin, botPassword, chatID string) error {
|
||||
bot, err := gomatrix.NewClient(serverURL, "", "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("matrixbot: bot creation failed: %w", err)
|
||||
}
|
||||
|
||||
resp, err := bot.Login(&gomatrix.ReqLogin{
|
||||
Type: "m.login.password",
|
||||
User: botLogin,
|
||||
Password: botPassword,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Debug("matrixbot: login failed: %w", err)
|
||||
return fmt.Errorf("matrixbot: login failed, please check your credentials or turn on debug mode")
|
||||
}
|
||||
|
||||
bot.SetCredentials(resp.UserID, resp.AccessToken)
|
||||
defer func() {
|
||||
bot.Logout()
|
||||
bot.ClearCredentials()
|
||||
}()
|
||||
|
||||
message := ""
|
||||
for _, entry := range entries {
|
||||
message = message + entry.Title + " " + entry.URL + "\n"
|
||||
}
|
||||
|
||||
if _, err = bot.SendText(chatID, message); err != nil {
|
||||
logger.Debug("matrixbot: sending message failed: %w", err)
|
||||
return fmt.Errorf("matrixbot: sending message failed, turn on debug mode for more informations")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -20,11 +20,12 @@ type Client struct {
|
|||
clientSecret string
|
||||
username string
|
||||
password string
|
||||
onlyURL bool
|
||||
}
|
||||
|
||||
// NewClient returns a new Wallabag client.
|
||||
func NewClient(baseURL, clientID, clientSecret, username, password string) *Client {
|
||||
return &Client{baseURL, clientID, clientSecret, username, password}
|
||||
func NewClient(baseURL, clientID, clientSecret, username, password string, onlyURL bool) *Client {
|
||||
return &Client{baseURL, clientID, clientSecret, username, password, onlyURL}
|
||||
}
|
||||
|
||||
// AddEntry sends a link to Wallabag.
|
||||
|
@ -48,9 +49,14 @@ func (c *Client) createEntry(accessToken, link, title, content string) error {
|
|||
return fmt.Errorf("wallbag: unable to get entries endpoint: %v", err)
|
||||
}
|
||||
|
||||
data := map[string]string{"url": link, "title": title}
|
||||
if !c.onlyURL {
|
||||
data["content"] = content
|
||||
}
|
||||
|
||||
clt := client.New(endpoint)
|
||||
clt.WithAuthorization("Bearer " + accessToken)
|
||||
response, err := clt.PostJSON(map[string]string{"url": link, "title": title, "content": content})
|
||||
response, err := clt.PostJSON(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("wallabag: unable to post entry: %v", err)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package locale handles the internationalization of the application.
|
||||
|
||||
*/
|
||||
package locale // import "miniflux.app/locale"
|
||||
|
|
|
@ -23,5 +23,7 @@ func AvailableLanguages() map[string]string {
|
|||
"el_EL": "Ελληνικά",
|
||||
"fi_FI": "Suomi",
|
||||
"hi_IN": "हिन्दी",
|
||||
"uk_UA": "Українська",
|
||||
"id_ID": "Bahasa Indonesia",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,10 @@ var pluralForms = map[string]pluralFormFunc{
|
|||
|
||||
return 2
|
||||
},
|
||||
// nplurals=1; plural=0;
|
||||
"id_ID": func(n int) int {
|
||||
return 0
|
||||
},
|
||||
// nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
|
||||
"pl_PL": func(n int) int {
|
||||
if n == 1 {
|
||||
|
@ -72,32 +76,24 @@ var pluralForms = map[string]pluralFormFunc{
|
|||
}
|
||||
return 0
|
||||
},
|
||||
// nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
|
||||
"ru_RU": func(n int) int {
|
||||
if n%10 == 1 && n%100 != 11 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 2
|
||||
},
|
||||
// nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
|
||||
"sr_RS": func(n int) int {
|
||||
if n%10 == 1 && n%100 != 11 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 2
|
||||
},
|
||||
"ru_RU": pluralFormRuSrUa,
|
||||
"uk_UA": pluralFormRuSrUa,
|
||||
"sr_RS": pluralFormRuSrUa,
|
||||
// nplurals=1; plural=0;
|
||||
"zh_CN": func(n int) int {
|
||||
return 0
|
||||
},
|
||||
}
|
||||
|
||||
// nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);
|
||||
func pluralFormRuSrUa(n int) int {
|
||||
if n%10 == 1 && n%100 != 11 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if n%10 >= 2 && n%10 <= 4 && (n%100 < 10 || n%100 >= 20) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 2
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Artikel nach oben blättern",
|
||||
"page.keyboard_shortcuts.remove_feed": "Dieses Abonnement entfernen",
|
||||
"page.keyboard_shortcuts.go_to_search": "Fokus auf das Suchformular setzen",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Artikel Anhänge öffnen/schließen",
|
||||
"page.keyboard_shortcuts.close_modal": "Liste der Tastenkürzel schließen",
|
||||
"page.users.title": "Benutzer",
|
||||
"page.users.username": "Benutzername",
|
||||
|
@ -262,7 +263,9 @@
|
|||
"error.invalid_language": "Ungültige Sprache.",
|
||||
"error.invalid_timezone": "Ungültige Zeitzone.",
|
||||
"error.invalid_entry_direction": "Ungültige Sortierreihenfolge.",
|
||||
"error.invalid_display_mode": "Ungültiger Web-App-Anzeigemodus.",
|
||||
"error.invalid_display_mode": "Progressive Web App (PWA) Anzeigemodus",
|
||||
"error.invalid_gesture_nav": "Ungültige Gestennavigation.",
|
||||
"error.invalid_default_home_page": "Ungültige Standard-Startseite!",
|
||||
"form.feed.label.title": "Titel",
|
||||
"form.feed.label.site_url": "Webseite-URL",
|
||||
"form.feed.label.feed_url": "Abonnement-URL",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Browser",
|
||||
"form.prefs.select.publish_time": "Eintrag veröffentlichte Zeit",
|
||||
"form.prefs.select.created_time": "Eintrag erstellt Zeit",
|
||||
"form.prefs.select.alphabetical": "Alphabetisch",
|
||||
"form.prefs.select.unread_count": "Ungelesen zählen",
|
||||
"form.prefs.select.none": "Keiner",
|
||||
"form.prefs.select.tap": "Doppeltippen",
|
||||
"form.prefs.select.swipe": "Wischen",
|
||||
"form.prefs.label.keyboard_shortcuts": "Tastaturkürzel aktivieren",
|
||||
"form.prefs.label.entry_swipe": "Wischgeste für Einträge auf dem Handy aktivieren",
|
||||
"form.prefs.label.entry_swipe": "Aktivieren Sie das Streichen von Einträgen auf Touchscreens",
|
||||
"form.prefs.label.gesture_nav": "Geste zum Navigieren zwischen Einträgen",
|
||||
"form.prefs.label.show_reading_time": "Geschätzte Lesezeit für Artikel anzeigen",
|
||||
"form.prefs.label.custom_css": "Benutzerdefiniertes CSS",
|
||||
"form.prefs.label.entry_order": "Eintrag Sortierspalte",
|
||||
"form.prefs.label.default_home_page": "Standard Startseite",
|
||||
"form.prefs.label.categories_sorting_order": "Kategorien sortieren",
|
||||
"form.import.label.file": "OPML Datei",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Fever API aktivieren",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Verbinden Sie Ihr Pocket Konto",
|
||||
"form.integration.wallabag_activate": "Artikel in Wallabag speichern",
|
||||
"form.integration.wallabag_only_url": "Nur URL senden (anstelle des vollständigen Inhalts)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag URL",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client-ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client-Secret",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Artikel in Linkding speichern",
|
||||
"form.integration.linkding_endpoint": "Linkding API-Endpunkt",
|
||||
"form.integration.linkding_api_key": "Linkding API-Schlüssel",
|
||||
"form.integration.matrix_bot_activate": "Neue Artikel in die Matrix übertragen",
|
||||
"form.integration.matrix_bot_user": "Benutzername für Matrix",
|
||||
"form.integration.matrix_bot_password": "Passwort für Matrix-Benutzer",
|
||||
"form.integration.matrix_bot_url": "URL des Matrix-Servers",
|
||||
"form.integration.matrix_bot_chat_id": "ID des Matrix-Raums",
|
||||
"form.api_key.label.description": "API-Schlüsselbezeichnung",
|
||||
"form.submit.loading": "Lade...",
|
||||
"form.submit.saving": "Speichern...",
|
||||
|
@ -393,8 +410,7 @@
|
|||
"This feed is empty": "Dieses Abonnement ist leer",
|
||||
"This web page is empty": "Diese Webseite ist leer",
|
||||
"Invalid SSL certificate (original error: %q)": "Ungültiges SSL-Zertifikat (ursprünglicher Fehler: %q)",
|
||||
"This website is temporarily unreachable (original error: %q)": "Diese Webseite ist vorübergehend nicht erreichbar (ursprünglicher Fehler: %q)",
|
||||
"This website is permanently unreachable (original error: %q)": "Diese Webseite ist dauerhaft nicht erreichbar (ursprünglicher Fehler: %q)",
|
||||
"This website is unreachable (original error: %q)": "Diese Webseite ist nicht erreichbar (ursprünglicher Fehler: %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Webseite nicht erreichbar, die Anfrage endete nach %d Sekunden",
|
||||
"You are not authorized to access this resource (invalid username/password)": "Sie sind nicht berechtigt, auf diese Ressource zuzugreifen (Benutzername/Passwort ungültig)",
|
||||
"Unable to fetch this resource (Status Code = %d)": "Ressource konnte nicht abgerufen werden (code=%d)",
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Μετακινηση στοιχείου στην κορυφή",
|
||||
"page.keyboard_shortcuts.remove_feed": "Κατάργηση αυτής της ροής",
|
||||
"page.keyboard_shortcuts.go_to_search": "Ορίστε εστίαση στη φόρμα αναζήτησης",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Κλείσιμο παραθύρου διαλόγου",
|
||||
"page.users.title": "Χρήστες",
|
||||
"page.users.username": "Χρήστης",
|
||||
|
@ -241,6 +242,8 @@
|
|||
"error.invalid_timezone": "Μη έγκυρη ζώνη ώρας.",
|
||||
"error.invalid_entry_direction": "Μη έγκυρη κατεύθυνση ταξινόμησης άρθρων.",
|
||||
"error.invalid_display_mode": "Μη έγκυρη λειτουργία εμφάνισης εφαρμογών ιστού.",
|
||||
"error.invalid_gesture_nav": "Μη έγκυρη πλοήγηση με χειρονομίες.",
|
||||
"error.invalid_default_home_page": "Μη έγκυρη προεπιλεγμένη αρχική σελίδα!",
|
||||
"error.empty_file": "Αυτό το αρχείο είναι κενό.",
|
||||
"error.bad_credentials": "Μη έγκυρο όνομα χρήστη ή κωδικό πρόσβασης.",
|
||||
"error.fields_mandatory": "Όλα τα πεδία είναι υποχρεωτικά.",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Καταχωρήσεις ανά σελίδα",
|
||||
"form.prefs.label.default_reading_speed": "Ταχύτητα ανάγνωσης άλλων γλωσσών (λέξεις ανά λεπτό)",
|
||||
"form.prefs.label.cjk_reading_speed": "Ταχύτητα ανάγνωσης για κινέζικα, κορεάτικα και ιαπωνικά (χαρακτήρες ανά λεπτό)",
|
||||
"form.prefs.label.display_mode": "Λειτουργία προβολής εφαρμογών ιστού (χρειάζεται επανεγκατάσταση)",
|
||||
"form.prefs.label.display_mode": "Λειτουργία προβολής προοδευτικής εφαρμογής Ιστού (PWA)",
|
||||
"form.prefs.select.older_first": "Παλαιότερες καταχωρήσεις πρώτα",
|
||||
"form.prefs.select.recent_first": "Πρόσφατες καταχωρήσεις πρώτα",
|
||||
"form.prefs.select.fullscreen": "Πλήρης οθόνη",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Περιηγητής",
|
||||
"form.prefs.select.publish_time": "Δημοσιευμένος χρόνος εισόδου",
|
||||
"form.prefs.select.created_time": "Χρόνος δημιουργίας καταχώρησης",
|
||||
"form.prefs.select.alphabetical": "Αλφαβητική σειρά",
|
||||
"form.prefs.select.unread_count": "Αριθμός μη αναγνωσμένων",
|
||||
"form.prefs.select.none": "Κανένας",
|
||||
"form.prefs.select.tap": "Διπλό χτύπημα",
|
||||
"form.prefs.select.swipe": "Σουφρώνω",
|
||||
"form.prefs.label.keyboard_shortcuts": "Ενεργοποίηση συντομεύσεων πληκτρολογίου",
|
||||
"form.prefs.label.entry_swipe": "Ενεργοποιήστε τη χειρονομία σάρωσης στις καταχωρήσεις στο κινητό",
|
||||
"form.prefs.label.entry_swipe": "Ενεργοποιήστε το σάρωση καταχώρισης στις οθόνες αφής",
|
||||
"form.prefs.label.gesture_nav": "Χειρονομία για πλοήγηση μεταξύ των καταχωρήσεων",
|
||||
"form.prefs.label.show_reading_time": "Εμφάνιση εκτιμώμενου χρόνου ανάγνωσης για άρθρα",
|
||||
"form.prefs.label.custom_css": "Προσαρμοσμένο CSS",
|
||||
"form.prefs.label.entry_order": "Στήλη ταξινόμησης εισόδου",
|
||||
"form.prefs.label.default_home_page": "Προεπιλεγμένη αρχική σελίδα",
|
||||
"form.prefs.label.categories_sorting_order": "Ταξινόμηση κατηγοριών",
|
||||
"form.import.label.file": "Αρχείο OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Ενεργοποιήστε το Fever API",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Συνδέστε τον λογαριασμό Pocket σας",
|
||||
"form.integration.wallabag_activate": "Αποθήκευση άρθρων στο Wallabag",
|
||||
"form.integration.wallabag_only_url": "Αποστολή μόνο URL (αντί για πλήρες περιεχόμενο)",
|
||||
"form.integration.wallabag_endpoint": "Τελικό σημείο Wallabag API ",
|
||||
"form.integration.wallabag_client_id": "Ταυτότητα πελάτη Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Μυστικό Πελάτη",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Αποθήκευση άρθρων στο Linkding",
|
||||
"form.integration.linkding_endpoint": "Τελικό σημείο Linkding API",
|
||||
"form.integration.linkding_api_key": "Κλειδί API Linkding",
|
||||
"form.integration.matrix_bot_activate": "Μεταφορά νέων άρθρων στο Matrix",
|
||||
"form.integration.matrix_bot_user": "Όνομα χρήστη για το Matrix",
|
||||
"form.integration.matrix_bot_password": "Κωδικός πρόσβασης για τον χρήστη Matrix",
|
||||
"form.integration.matrix_bot_url": "URL διακομιστή Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "Αναγνωριστικό της αίθουσας Matrix",
|
||||
"form.api_key.label.description": "Ετικέτα κλειδιού API",
|
||||
"form.submit.loading": "Φόρτωση...",
|
||||
"form.submit.saving": "Αποθήκευση...",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"confirm.question": "Are you sure?",
|
||||
"confirm.yes": "yes",
|
||||
"confirm.no": "no",
|
||||
"confirm.loading": "In progress...",
|
||||
"confirm.loading": "In progress…",
|
||||
"action.subscribe": "Subscribe",
|
||||
"action.save": "Save",
|
||||
"action.or": "or",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"menu.refresh_all_feeds": "Refresh all feeds in the background",
|
||||
"menu.edit_feed": "Edit",
|
||||
"menu.edit_category": "Edit",
|
||||
"menu.add_feed": "Add subscription",
|
||||
"menu.add_feed": "Add feed",
|
||||
"menu.add_user": "Add user",
|
||||
"menu.flush_history": "Flush history",
|
||||
"menu.feed_entries": "Entries",
|
||||
|
@ -48,7 +48,7 @@
|
|||
"menu.create_api_key": "Create a new API key",
|
||||
"menu.shared_entries": "Shared entries",
|
||||
"search.label": "Search",
|
||||
"search.placeholder": "Search...",
|
||||
"search.placeholder": "Search…",
|
||||
"pagination.next": "Next",
|
||||
"pagination.previous": "Previous",
|
||||
"entry.status.unread": "Unread",
|
||||
|
@ -60,12 +60,12 @@
|
|||
"entry.bookmark.toggle.off": "Unstar",
|
||||
"entry.bookmark.toast.on": "Starred",
|
||||
"entry.bookmark.toast.off": "Unstarred",
|
||||
"entry.state.saving": "Saving...",
|
||||
"entry.state.loading": "Loading...",
|
||||
"entry.state.saving": "Saving…",
|
||||
"entry.state.loading": "Loading…",
|
||||
"entry.save.label": "Save",
|
||||
"entry.save.title": "Save this article",
|
||||
"entry.save.title": "Save this entry",
|
||||
"entry.save.completed": "Done!",
|
||||
"entry.save.toast.completed": "Article saved",
|
||||
"entry.save.toast.completed": "Entry saved",
|
||||
"entry.scraper.label": "Download",
|
||||
"entry.scraper.title": "Fetch original content",
|
||||
"entry.scraper.completed": "Done!",
|
||||
|
@ -73,7 +73,7 @@
|
|||
"entry.comments.label": "Comments",
|
||||
"entry.comments.title": "View Comments",
|
||||
"entry.share.label": "Share",
|
||||
"entry.share.title": "Share this article",
|
||||
"entry.share.title": "Share this entry",
|
||||
"entry.unshare.label": "Unshare",
|
||||
"entry.shared_entry.title": "Open the public link",
|
||||
"entry.shared_entry.label": "Share",
|
||||
|
@ -81,13 +81,13 @@
|
|||
"%d minute read",
|
||||
"%d minutes read"
|
||||
],
|
||||
"page.shared_entries.title": "Shared Entries",
|
||||
"page.shared_entries.title": "Shared entries",
|
||||
"page.unread.title": "Unread",
|
||||
"page.starred.title": "Starred",
|
||||
"page.categories.title": "Categories",
|
||||
"page.categories.no_feed": "No feed.",
|
||||
"page.categories.entries": "Articles",
|
||||
"page.categories.feeds": "Subscriptions",
|
||||
"page.categories.entries": "Entries",
|
||||
"page.categories.feeds": "Feeds",
|
||||
"page.categories.feed_count": [
|
||||
"There is %d feed.",
|
||||
"There are %d feeds."
|
||||
|
@ -117,12 +117,12 @@
|
|||
"page.about.global_config_options": "Global configuration options",
|
||||
"page.about.postgres_version": "Postgres version:",
|
||||
"page.about.go_version": "Go version:",
|
||||
"page.add_feed.title": "New Subscription",
|
||||
"page.add_feed.title": "New feed",
|
||||
"page.add_feed.no_category": "There is no category. You must have at least one category.",
|
||||
"page.add_feed.label.url": "URL",
|
||||
"page.add_feed.submit": "Find a subscription",
|
||||
"page.add_feed.submit": "Find a feed",
|
||||
"page.add_feed.legend.advanced_options": "Advanced Options",
|
||||
"page.add_feed.choose_feed": "Choose a Subscription",
|
||||
"page.add_feed.choose_feed": "Choose a feed",
|
||||
"page.edit_feed.title": "Edit Feed: %s",
|
||||
"page.edit_feed.last_check": "Last check:",
|
||||
"page.edit_feed.last_modified_header": "LastModified header:",
|
||||
|
@ -158,10 +158,11 @@
|
|||
"page.keyboard_shortcuts.mark_page_as_read": "Mark current page as read",
|
||||
"page.keyboard_shortcuts.download_content": "Download original content",
|
||||
"page.keyboard_shortcuts.toggle_bookmark_status": "Toggle bookmark",
|
||||
"page.keyboard_shortcuts.save_article": "Save article",
|
||||
"page.keyboard_shortcuts.save_article": "Save entry",
|
||||
"page.keyboard_shortcuts.scroll_item_to_top": "Scroll item to top",
|
||||
"page.keyboard_shortcuts.remove_feed": "Remove this feed",
|
||||
"page.keyboard_shortcuts.go_to_search": "Set focus on search form",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Close modal dialog",
|
||||
"page.users.title": "Users",
|
||||
"page.users.username": "Username",
|
||||
|
@ -209,20 +210,20 @@
|
|||
"alert.no_shared_entry": "There is no shared entry.",
|
||||
"alert.no_bookmark": "There is no bookmark at the moment.",
|
||||
"alert.no_category": "There is no category.",
|
||||
"alert.no_category_entry": "There are no articles in this category.",
|
||||
"alert.no_feed_entry": "There are no articles for this feed.",
|
||||
"alert.no_feed": "You don't have any subscriptions.",
|
||||
"alert.no_feed_in_category": "There is no subscription for this category.",
|
||||
"alert.no_category_entry": "There are no entries in this category.",
|
||||
"alert.no_feed_entry": "There are no entries for this feed.",
|
||||
"alert.no_feed": "You don’t have any feeds.",
|
||||
"alert.no_feed_in_category": "There is no feed for this category.",
|
||||
"alert.no_history": "There is no history at the moment.",
|
||||
"alert.feed_error": "There is a problem with this feed",
|
||||
"alert.no_search_result": "There are no results for this search.",
|
||||
"alert.no_unread_entry": "There are no unread articles.",
|
||||
"alert.no_unread_entry": "There are no unread entries.",
|
||||
"alert.no_user": "You are the only user.",
|
||||
"alert.account_unlinked": "Your external account is now dissociated!",
|
||||
"alert.account_linked": "Your external account is now linked!",
|
||||
"alert.pocket_linked": "Your Pocket account is now linked!",
|
||||
"alert.prefs_saved": "Preferences saved!",
|
||||
"error.unlink_account_without_password": "You must define a password otherwise you won't be able to login again.",
|
||||
"error.unlink_account_without_password": "You must define a password otherwise you won’t be able to login again.",
|
||||
"error.duplicate_linked_account": "There is already someone associated with this provider!",
|
||||
"error.duplicate_fever_username": "There is already someone else with the same Fever username!",
|
||||
"error.duplicate_googlereader_username": "There is already someone else with the same Google Reader username!",
|
||||
|
@ -235,12 +236,14 @@
|
|||
"error.unable_to_create_user": "Unable to create this user.",
|
||||
"error.unable_to_update_user": "Unable to update this user.",
|
||||
"error.unable_to_update_feed": "Unable to update this feed.",
|
||||
"error.subscription_not_found": "Unable to find any subscription.",
|
||||
"error.subscription_not_found": "Unable to find any feed.",
|
||||
"error.invalid_theme": "Invalid theme.",
|
||||
"error.invalid_language": "Invalid language.",
|
||||
"error.invalid_timezone": "Invalid timezone.",
|
||||
"error.invalid_entry_direction": "Invalid entry direction.",
|
||||
"error.invalid_display_mode": "Invalid web app display mode.",
|
||||
"error.invalid_gesture_nav": "Invalid gesture navigation.",
|
||||
"error.invalid_default_home_page": "Invalid default homepage!",
|
||||
"error.empty_file": "This file is empty.",
|
||||
"error.bad_credentials": "Invalid username or password.",
|
||||
"error.fields_mandatory": "All fields are mandatory.",
|
||||
|
@ -291,11 +294,11 @@
|
|||
"form.prefs.label.language": "Language",
|
||||
"form.prefs.label.timezone": "Timezone",
|
||||
"form.prefs.label.theme": "Theme",
|
||||
"form.prefs.label.entry_sorting": "Entry Sorting",
|
||||
"form.prefs.label.entry_sorting": "Entry sorting",
|
||||
"form.prefs.label.entries_per_page": "Entries per page",
|
||||
"form.prefs.label.default_reading_speed": "Reading speed for other languages (words per minute)",
|
||||
"form.prefs.label.cjk_reading_speed": "Reading speed for Chinese, Korean and Japanese (characters per minute)",
|
||||
"form.prefs.label.display_mode": "Web app display mode (needs reinstalling)",
|
||||
"form.prefs.label.display_mode": "Progressive Web App (PWA) display mode",
|
||||
"form.prefs.select.older_first": "Older entries first",
|
||||
"form.prefs.select.recent_first": "Recent entries first",
|
||||
"form.prefs.select.fullscreen": "Fullscreen",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Browser",
|
||||
"form.prefs.select.publish_time": "Entry published time",
|
||||
"form.prefs.select.created_time": "Entry created time",
|
||||
"form.prefs.select.alphabetical": "Alphabetical",
|
||||
"form.prefs.select.unread_count": "Unread count",
|
||||
"form.prefs.select.none": "None",
|
||||
"form.prefs.select.tap": "Double tap",
|
||||
"form.prefs.select.swipe": "Swipe",
|
||||
"form.prefs.label.keyboard_shortcuts": "Enable keyboard shortcuts",
|
||||
"form.prefs.label.entry_swipe": "Enable swipe gesture on entries on mobile",
|
||||
"form.prefs.label.show_reading_time": "Show estimated reading time for articles",
|
||||
"form.prefs.label.entry_swipe": "Enable entry swipe on touch screens",
|
||||
"form.prefs.label.gesture_nav": "Gesture to navigate between entries",
|
||||
"form.prefs.label.show_reading_time": "Show estimated reading time for entries",
|
||||
"form.prefs.label.custom_css": "Custom CSS",
|
||||
"form.prefs.label.entry_order": "Entry Sorting Column",
|
||||
"form.prefs.label.entry_order": "Entry sorting column",
|
||||
"form.prefs.label.default_home_page": "Default home page",
|
||||
"form.prefs.label.categories_sorting_order": "Categories sorting",
|
||||
"form.import.label.file": "OPML file",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Activate Fever API",
|
||||
|
@ -319,39 +330,45 @@
|
|||
"form.integration.googlereader_username": "Google Reader Username",
|
||||
"form.integration.googlereader_password": "Google Reader Password",
|
||||
"form.integration.googlereader_endpoint": "Google Reader API endpoint:",
|
||||
"form.integration.pinboard_activate": "Save articles to Pinboard",
|
||||
"form.integration.pinboard_activate": "Save entries to Pinboard",
|
||||
"form.integration.pinboard_token": "Pinboard API Token",
|
||||
"form.integration.pinboard_tags": "Pinboard Tags",
|
||||
"form.integration.pinboard_bookmark": "Mark bookmark as unread",
|
||||
"form.integration.instapaper_activate": "Save articles to Instapaper",
|
||||
"form.integration.instapaper_activate": "Save entries to Instapaper",
|
||||
"form.integration.instapaper_username": "Instapaper Username",
|
||||
"form.integration.instapaper_password": "Instapaper Password",
|
||||
"form.integration.pocket_activate": "Save articles to Pocket",
|
||||
"form.integration.pocket_activate": "Save entries to Pocket",
|
||||
"form.integration.pocket_consumer_key": "Pocket Consumer Key",
|
||||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Connect your Pocket account",
|
||||
"form.integration.wallabag_activate": "Save articles to Wallabag",
|
||||
"form.integration.wallabag_activate": "Save entries to Wallabag",
|
||||
"form.integration.wallabag_only_url": "Send only URL (instead of full content)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag API Endpoint",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
|
||||
"form.integration.wallabag_username": "Wallabag Username",
|
||||
"form.integration.wallabag_password": "Wallabag Password",
|
||||
"form.integration.nunux_keeper_activate": "Save articles to Nunux Keeper",
|
||||
"form.integration.nunux_keeper_activate": "Save entries to Nunux Keeper",
|
||||
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
|
||||
"form.integration.nunux_keeper_api_key": "Nunux Keeper API key",
|
||||
"form.integration.espial_activate": "Save articles to Espial",
|
||||
"form.integration.espial_activate": "Save entries to Espial",
|
||||
"form.integration.espial_endpoint": "Espial API Endpoint",
|
||||
"form.integration.espial_api_key": "Espial API key",
|
||||
"form.integration.espial_tags": "Espial Tags",
|
||||
"form.integration.telegram_bot_activate": "Push new articles to Telegram chat",
|
||||
"form.integration.telegram_bot_activate": "Push new entries to Telegram chat",
|
||||
"form.integration.telegram_bot_token": "Bot token",
|
||||
"form.integration.telegram_chat_id": "Chat ID",
|
||||
"form.integration.linkding_activate": "Save articles to Linkding",
|
||||
"form.integration.linkding_activate": "Save entries to Linkding",
|
||||
"form.integration.linkding_endpoint": "Linkding API Endpoint",
|
||||
"form.integration.linkding_api_key": "Linkding API key",
|
||||
"form.integration.matrix_bot_activate": "Push new entries to Matrix",
|
||||
"form.integration.matrix_bot_user": "Username for Matrix",
|
||||
"form.integration.matrix_bot_password": "Password for Matrix user",
|
||||
"form.integration.matrix_bot_url": "Matrix server URL",
|
||||
"form.integration.matrix_bot_chat_id": "ID of Matrix Room",
|
||||
"form.api_key.label.description": "API Key Label",
|
||||
"form.submit.loading": "Loading...",
|
||||
"form.submit.saving": "Saving...",
|
||||
"form.submit.loading": "Loading…",
|
||||
"form.submit.saving": "Saving…",
|
||||
"time_elapsed.not_yet": "not yet",
|
||||
"time_elapsed.yesterday": "yesterday",
|
||||
"time_elapsed.now": "just now",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"confirm.question": "Estás seguro?",
|
||||
"confirm.question": "¿Estás seguro?",
|
||||
"confirm.yes": "sí",
|
||||
"confirm.no": "no",
|
||||
"confirm.loading": "En progreso...",
|
||||
|
@ -21,7 +21,7 @@
|
|||
"menu.starred": "Marcadores",
|
||||
"menu.history": "Historial",
|
||||
"menu.feeds": "Fuentes",
|
||||
"menu.categories": "Categorias",
|
||||
"menu.categories": "Categorías",
|
||||
"menu.settings": "Configuración",
|
||||
"menu.logout": "Cerrar sesión",
|
||||
"menu.preferences": "Preferencias",
|
||||
|
@ -32,21 +32,21 @@
|
|||
"menu.export": "Exportar",
|
||||
"menu.import": "Importar",
|
||||
"menu.create_category": "Crear una categoría",
|
||||
"menu.mark_page_as_read": "Marcar esta pagína como leída",
|
||||
"menu.mark_page_as_read": "Marcar esta página como leída",
|
||||
"menu.mark_all_as_read": "Marcar todos como leídos",
|
||||
"menu.show_all_entries": "Mostrar todas las entradas",
|
||||
"menu.show_only_unread_entries": "Mostrar solo las entradas no leídas",
|
||||
"menu.show_all_entries": "Mostrar todos los artículos",
|
||||
"menu.show_only_unread_entries": "Mostrar solo los artículos no leídos",
|
||||
"menu.refresh_feed": "Refrescar",
|
||||
"menu.refresh_all_feeds": "Refrescar todas las fuentes en el fondo",
|
||||
"menu.edit_feed": "Editar",
|
||||
"menu.edit_category": "Editar",
|
||||
"menu.add_feed": "Agregar suscripción",
|
||||
"menu.add_feed": "Agregar fuente",
|
||||
"menu.add_user": "Agregar usuario",
|
||||
"menu.flush_history": "Borrar historial",
|
||||
"menu.feed_entries": "Artículos",
|
||||
"menu.api_keys": "Claves API",
|
||||
"menu.create_api_key": "Crear una nueva clave API",
|
||||
"menu.shared_entries": "Entradas compartidas",
|
||||
"menu.shared_entries": "Artículos compartidos",
|
||||
"search.label": "Buscar",
|
||||
"search.placeholder": "Búsqueda...",
|
||||
"pagination.next": "Siguiente",
|
||||
|
@ -55,7 +55,7 @@
|
|||
"entry.status.read": "Leído",
|
||||
"entry.status.toast.unread": "Marcado como no leído",
|
||||
"entry.status.toast.read": "Marcado como leído",
|
||||
"entry.status.title": "Cambiar estado de entrada",
|
||||
"entry.status.title": "Cambiar estado del artículo",
|
||||
"entry.bookmark.toggle.on": "Marcar",
|
||||
"entry.bookmark.toggle.off": "Desmarcar",
|
||||
"entry.bookmark.toast.on": "Sembrado de estrellas",
|
||||
|
@ -63,17 +63,17 @@
|
|||
"entry.state.saving": "Guardando...",
|
||||
"entry.state.loading": "Cargando...",
|
||||
"entry.save.label": "Guardar",
|
||||
"entry.save.title": "Guardar este articulo",
|
||||
"entry.save.title": "Guardar este artículo",
|
||||
"entry.save.completed": "¡Hecho!",
|
||||
"entry.save.toast.completed": "Artículo guardado",
|
||||
"entry.save.toast.completed": "Artículos guardados",
|
||||
"entry.scraper.label": "Descargar",
|
||||
"entry.scraper.title": "Obtener contenido original",
|
||||
"entry.scraper.completed": "¡Hecho!",
|
||||
"entry.external_link.label": "Enlace externo",
|
||||
"entry.comments.label": "Comentarios",
|
||||
"entry.comments.title": "Ver comentarios",
|
||||
"entry.share.label": "Comparta",
|
||||
"entry.share.title": "Comparta este articulo",
|
||||
"entry.share.label": "Compartir",
|
||||
"entry.share.title": "Compartir este artículo",
|
||||
"entry.unshare.label": "No compartir",
|
||||
"entry.shared_entry.title": "Abrir el enlace público",
|
||||
"entry.shared_entry.label": "Compartir",
|
||||
|
@ -81,26 +81,26 @@
|
|||
"%d minuto de lectura",
|
||||
"%d minutos de lectura"
|
||||
],
|
||||
"page.shared_entries.title": "Entradas compartidas",
|
||||
"page.shared_entries.title": "Artículos compartidos",
|
||||
"page.unread.title": "No leídos",
|
||||
"page.starred.title": "Marcadores",
|
||||
"page.categories.title": "Categorias",
|
||||
"page.categories.no_feed": "No fuente.",
|
||||
"page.categories.title": "Categorías",
|
||||
"page.categories.no_feed": "Sin fuente.",
|
||||
"page.categories.entries": "Artículos",
|
||||
"page.categories.feeds": "Suscripciones",
|
||||
"page.categories.feeds": "Fuentes",
|
||||
"page.categories.feed_count": [
|
||||
"Hay %d fuente.",
|
||||
"Hay %d fuentes."
|
||||
],
|
||||
"page.categories.unread_counter": "Número de entradas no leídas",
|
||||
"page.categories.unread_counter": "Número de artículos no leídos",
|
||||
"page.new_category.title": "Nueva categoría",
|
||||
"page.new_user.title": "Nuevo usario",
|
||||
"page.new_user.title": "Nuevo usuario",
|
||||
"page.edit_category.title": "Editar categoría: %s",
|
||||
"page.edit_user.title": "Editar usuario: %s",
|
||||
"page.feeds.title": "Fuentes",
|
||||
"page.feeds.last_check": "Última verificación:",
|
||||
"page.feeds.unread_counter": "Número de entradas no leídas",
|
||||
"page.feeds.read_counter": "Número de entradas leídas",
|
||||
"page.feeds.unread_counter": "Número de artículos no leídos",
|
||||
"page.feeds.read_counter": "Número de artículos leídos",
|
||||
"page.feeds.error_count": [
|
||||
"%d error",
|
||||
"%d errores"
|
||||
|
@ -109,7 +109,7 @@
|
|||
"page.import.title": "Importar",
|
||||
"page.search.title": "Resultados de la búsqueda",
|
||||
"page.about.title": "Acerca de",
|
||||
"page.about.credits": "Creditos",
|
||||
"page.about.credits": "Créditos",
|
||||
"page.about.version": "Versión:",
|
||||
"page.about.build_date": "Fecha de construcción:",
|
||||
"page.about.author": "Autor:",
|
||||
|
@ -117,12 +117,12 @@
|
|||
"page.about.global_config_options": "Opciones de configuración global",
|
||||
"page.about.postgres_version": "Postgres versión:",
|
||||
"page.about.go_version": "Go versión:",
|
||||
"page.add_feed.title": "Nueva suscripción",
|
||||
"page.add_feed.title": "Nueva fuente",
|
||||
"page.add_feed.no_category": "No hay categoría. Debe tener al menos una categoría.",
|
||||
"page.add_feed.label.url": "URL",
|
||||
"page.add_feed.submit": "Encontrar una suscripción",
|
||||
"page.add_feed.submit": "Encontrar una fuente",
|
||||
"page.add_feed.legend.advanced_options": "Opciones avanzadas",
|
||||
"page.add_feed.choose_feed": "Elegir una suscripción",
|
||||
"page.add_feed.choose_feed": "Elegir una fuente",
|
||||
"page.edit_feed.title": "Editar fuente: %s",
|
||||
"page.edit_feed.last_check": "Última verificación:",
|
||||
"page.edit_feed.last_modified_header": "Cabecera de LastModified:",
|
||||
|
@ -139,14 +139,14 @@
|
|||
"page.keyboard_shortcuts.go_to_starred": "Ir a los marcadores",
|
||||
"page.keyboard_shortcuts.go_to_history": "Ir al historial",
|
||||
"page.keyboard_shortcuts.go_to_feeds": "Ir a las fuentes",
|
||||
"page.keyboard_shortcuts.go_to_categories": "Ir a las categorias",
|
||||
"page.keyboard_shortcuts.go_to_categories": "Ir a las categorías",
|
||||
"page.keyboard_shortcuts.go_to_settings": "Ir a la configuración",
|
||||
"page.keyboard_shortcuts.show_keyboard_shortcuts": "Mostrar atajos de teclado",
|
||||
"page.keyboard_shortcuts.go_to_previous_item": "Ir al elemento anterior",
|
||||
"page.keyboard_shortcuts.go_to_next_item": "Ir al elemento siguiente",
|
||||
"page.keyboard_shortcuts.go_to_feed": "Ir a la fuente",
|
||||
"page.keyboard_shortcuts.go_to_previous_page": "Ir al pagína anterior",
|
||||
"page.keyboard_shortcuts.go_to_next_page": "Ir al pagína siguiente",
|
||||
"page.keyboard_shortcuts.go_to_previous_page": "Ir al página anterior",
|
||||
"page.keyboard_shortcuts.go_to_next_page": "Ir al página siguiente",
|
||||
"page.keyboard_shortcuts.open_item": "Abrir el elemento seleccionado",
|
||||
"page.keyboard_shortcuts.open_original": "Abrir el enlace original",
|
||||
"page.keyboard_shortcuts.open_original_same_window": "Abrir enlace original en la pestaña actual",
|
||||
|
@ -155,13 +155,14 @@
|
|||
"page.keyboard_shortcuts.toggle_read_status_next": "Marcar como leído o no leído, enfoque siguiente",
|
||||
"page.keyboard_shortcuts.toggle_read_status_prev": "Marcar como leído o no leído, foco anterior",
|
||||
"page.keyboard_shortcuts.refresh_all_feeds": "Refrescar todas las fuentes en el fondo",
|
||||
"page.keyboard_shortcuts.mark_page_as_read": "Marcar pagína actual como leída",
|
||||
"page.keyboard_shortcuts.mark_page_as_read": "Marcar página actual como leída",
|
||||
"page.keyboard_shortcuts.download_content": "Descargar el contento original",
|
||||
"page.keyboard_shortcuts.toggle_bookmark_status": "Agregar o quitar marcador",
|
||||
"page.keyboard_shortcuts.save_article": "Guardar artículo",
|
||||
"page.keyboard_shortcuts.scroll_item_to_top": "Desplazar elemento hacia arriba",
|
||||
"page.keyboard_shortcuts.remove_feed": "Quitar esta fuente",
|
||||
"page.keyboard_shortcuts.go_to_search": "Centrarse en el cuadro de búsqueda",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Alternar abrir/cerrar adjuntos de la entrada",
|
||||
"page.keyboard_shortcuts.close_modal": "Cerrar el cuadro de diálogo modal",
|
||||
"page.users.title": "Usuarios",
|
||||
"page.users.username": "Nombre de usuario",
|
||||
|
@ -185,7 +186,7 @@
|
|||
"page.integration.miniflux_api_username": "Nombre de usuario",
|
||||
"page.integration.miniflux_api_password": "Contraseña",
|
||||
"page.integration.miniflux_api_password_value": "Contraseña de tu cuenta",
|
||||
"page.integration.bookmarklet": "Bookmarklet",
|
||||
"page.integration.bookmarklet": "Marcapáginas",
|
||||
"page.integration.bookmarklet.name": "Agregar a Miniflux",
|
||||
"page.integration.bookmarklet.instructions": "Arrastrar y soltar este enlace a tus marcadores del navegador.",
|
||||
"page.integration.bookmarklet.help": "Este enlace especial te permite suscribirte a un sitio de web directamente usando un marcador del navegador.",
|
||||
|
@ -206,18 +207,18 @@
|
|||
"page.offline.title": "Modo offline",
|
||||
"page.offline.message": "Estas desconectado",
|
||||
"page.offline.refresh_page": "Intenta actualizar la página",
|
||||
"alert.no_shared_entry": "No hay entrada compartida.",
|
||||
"alert.no_shared_entry": "No hay artículos compartidos.",
|
||||
"alert.no_bookmark": "No hay marcador en este momento.",
|
||||
"alert.no_category": "No hay categoría.",
|
||||
"alert.no_category_entry": "No hay artículos en esta categoria.",
|
||||
"alert.no_category_entry": "No hay artículos en esta categoría.",
|
||||
"alert.no_feed_entry": "No hay artículos para esta fuente.",
|
||||
"alert.no_feed": "No tienes suscripciones.",
|
||||
"alert.no_feed_in_category": "No hay suscripción para esta categoría.",
|
||||
"alert.no_feed": "No tienes fuentes.",
|
||||
"alert.no_feed_in_category": "No hay fuentes para esta categoría.",
|
||||
"alert.no_history": "No hay historial en este momento.",
|
||||
"alert.feed_error": "Hay un problema con esta fuente.",
|
||||
"alert.no_search_result": "No hay resultados para esta búsqueda.",
|
||||
"alert.no_unread_entry": "No hay artículos sin leer.",
|
||||
"alert.no_user": "Eres el unico usuario.",
|
||||
"alert.no_user": "Eres el único usuario.",
|
||||
"alert.account_unlinked": "¡Tu cuenta externa ya está desvinculada!",
|
||||
"alert.account_linked": "¡Tu cuenta externa ya está vinculada!",
|
||||
"alert.pocket_linked": "¡Tu cuenta de Pocket ya está vinculada!",
|
||||
|
@ -235,7 +236,7 @@
|
|||
"error.unable_to_create_user": "Incapaz de crear este usuario.",
|
||||
"error.unable_to_update_user": "Incapaz de actualizar este usuario.",
|
||||
"error.unable_to_update_feed": "Incapaz de actualizar esta fuente.",
|
||||
"error.subscription_not_found": "Incapaz de encontrar ninguna suscripción.",
|
||||
"error.subscription_not_found": "Incapaz de encontrar alguna fuente.",
|
||||
"error.empty_file": "Este archivo está vacío.",
|
||||
"error.bad_credentials": "Usuario o contraseña no válido.",
|
||||
"error.fields_mandatory": "Todos los campos son obligatorios.",
|
||||
|
@ -244,7 +245,7 @@
|
|||
"error.password_min_length": "La contraseña debería tener al menos 6 caracteres.",
|
||||
"error.settings_mandatory_fields": "Los campos de nombre de usuario, tema, idioma y zona horaria son obligatorios.",
|
||||
"error.settings_reading_speed_is_positive": "Las velocidades de lectura deben ser números enteros positivos.",
|
||||
"error.entries_per_page_invalid": "El número de entradas por página no es válido.",
|
||||
"error.entries_per_page_invalid": "El número de artículos por página no es válido.",
|
||||
"error.feed_mandatory_fields": "Los campos de URL y categoría son obligatorios.",
|
||||
"error.feed_already_exists": "Este feed ya existe.",
|
||||
"error.invalid_feed_url": "URL de feed no válida.",
|
||||
|
@ -261,29 +262,31 @@
|
|||
"error.invalid_theme": "Tema no válido.",
|
||||
"error.invalid_language": "Idioma no válido.",
|
||||
"error.invalid_timezone": "Zona horaria no válida.",
|
||||
"error.invalid_entry_direction": "Dirección de entrada no válida.",
|
||||
"error.invalid_entry_direction": "Dirección de artículo no válida.",
|
||||
"error.invalid_display_mode": "Modo de visualización de la aplicación web no válido.",
|
||||
"error.invalid_gesture_nav": "Navegación por gestos no válida.",
|
||||
"error.invalid_default_home_page": "¡Página de inicio por defecto no válida!",
|
||||
"form.feed.label.title": "Título",
|
||||
"form.feed.label.site_url": "URL del sitio",
|
||||
"form.feed.label.feed_url": "URL de la fuente",
|
||||
"form.feed.label.category": "Categoría",
|
||||
"form.feed.label.crawler": "Obtener contento original",
|
||||
"form.feed.label.feed_username": "Nombre de usuario de fuente",
|
||||
"form.feed.label.feed_password": "Contraseña de fuente",
|
||||
"form.feed.label.crawler": "Obtener rastreador original",
|
||||
"form.feed.label.feed_username": "Nombre de usuario de la fuente",
|
||||
"form.feed.label.feed_password": "Contraseña de la fuente",
|
||||
"form.feed.label.user_agent": "Invalidar el agente de usuario predeterminado",
|
||||
"form.feed.label.cookie": "Configurar las cookies",
|
||||
"form.feed.label.scraper_rules": "Reglas de raspador",
|
||||
"form.feed.label.scraper_rules": "Reglas de extracción de información",
|
||||
"form.feed.label.rewrite_rules": "Reglas de reescribir",
|
||||
"form.feed.label.blocklist_rules": "Reglas de Filtrado(Bloquear)",
|
||||
"form.feed.label.keeplist_rules": "Reglas de Filtrado(Permitir)",
|
||||
"form.feed.label.urlrewrite_rules": "Reglas de Filtrado(reescritura)",
|
||||
"form.feed.label.blocklist_rules": "Reglas de Filtrado (Bloquear)",
|
||||
"form.feed.label.keeplist_rules": "Reglas de Filtrado (Permitir)",
|
||||
"form.feed.label.urlrewrite_rules": "Reglas de Filtrado (Reescritura)",
|
||||
"form.feed.label.ignore_http_cache": "Ignorar caché HTTP",
|
||||
"form.feed.label.allow_self_signed_certificates": "Permitir certificados autofirmados o no válidos",
|
||||
"form.feed.label.fetch_via_proxy": "Buscar a través de proxy",
|
||||
"form.feed.label.disabled": "No actualice este feed",
|
||||
"form.feed.label.hide_globally": "Ocultar entradas en la lista global de no leídos",
|
||||
"form.feed.label.hide_globally": "Ocultar artículos en la lista global de no leídos",
|
||||
"form.category.label.title": "Título",
|
||||
"form.category.hide_globally": "Ocultar entradas en la lista global de no leídos",
|
||||
"form.category.hide_globally": "Ocultar artículos en la lista global de no leídos",
|
||||
"form.user.label.username": "Nombre de usuario",
|
||||
"form.user.label.password": "Contraseña",
|
||||
"form.user.label.confirmation": "Confirmación de contraseña",
|
||||
|
@ -291,64 +294,78 @@
|
|||
"form.prefs.label.language": "Idioma",
|
||||
"form.prefs.label.timezone": "Zona horaria",
|
||||
"form.prefs.label.theme": "Tema",
|
||||
"form.prefs.label.entry_sorting": "Clasificación de entradas",
|
||||
"form.prefs.label.entries_per_page": "Entradas por página",
|
||||
"form.prefs.label.entry_sorting": "Clasificación de artículos",
|
||||
"form.prefs.label.entries_per_page": "Artículos por página",
|
||||
"form.prefs.label.default_reading_speed": "Velocidad de lectura de otras lenguas (palabras por minuto)",
|
||||
"form.prefs.label.cjk_reading_speed": "Velocidad de lectura en chino, coreano y japonés (caracteres por minuto)",
|
||||
"form.prefs.label.display_mode": "Modo de visualización de la aplicación web (necesita reinstalación)",
|
||||
"form.prefs.select.older_first": "Entradas más viejas primero",
|
||||
"form.prefs.select.recent_first": "Entradas recientes primero",
|
||||
"form.prefs.label.display_mode": "Modo de visualización de aplicación web progresiva (PWA)",
|
||||
"form.prefs.select.older_first": "Artículos antiguos primero",
|
||||
"form.prefs.select.recent_first": "Artículos recientes primero",
|
||||
"form.prefs.select.fullscreen": "Pantalla completa",
|
||||
"form.prefs.select.standalone": "Ser único",
|
||||
"form.prefs.select.standalone": "Autónomo",
|
||||
"form.prefs.select.minimal_ui": "Mínimo",
|
||||
"form.prefs.select.browser": "Navegador",
|
||||
"form.prefs.select.publish_time": "Hora de publicación de la entrada",
|
||||
"form.prefs.select.created_time": "Hora de creación de la entrada",
|
||||
"form.prefs.select.publish_time": "Hora de publicación del artículo",
|
||||
"form.prefs.select.created_time": "Hora de creación del artículo",
|
||||
"form.prefs.select.alphabetical": "Alfabético",
|
||||
"form.prefs.select.unread_count": "Recuento de no leídos",
|
||||
"form.prefs.select.none": "Ninguno",
|
||||
"form.prefs.select.tap": "Doble toque",
|
||||
"form.prefs.select.swipe": "Golpe fuerte",
|
||||
"form.prefs.label.keyboard_shortcuts": "Habilitar atajos de teclado",
|
||||
"form.prefs.label.entry_swipe": "Habilitar el gesto de deslizar el dedo en las entradas en el móvil",
|
||||
"form.prefs.label.entry_swipe": "Habilitar deslizamiento de entrada en pantallas táctiles",
|
||||
"form.prefs.label.gesture_nav": "Gesto para navegar entre entradas",
|
||||
"form.prefs.label.show_reading_time": "Mostrar el tiempo estimado de lectura de los artículos",
|
||||
"form.prefs.label.custom_css": "CSS personalizado",
|
||||
"form.prefs.label.entry_order": "Columna de clasificación de entradas",
|
||||
"form.prefs.label.entry_order": "Columna de clasificación de artículos",
|
||||
"form.prefs.label.default_home_page": "Página de inicio por defecto",
|
||||
"form.prefs.label.categories_sorting_order": "Clasificación por categorías",
|
||||
"form.import.label.file": "Archivo OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Activar API de Fever",
|
||||
"form.integration.fever_username": "Nombre de usuario de Fever",
|
||||
"form.integration.fever_password": "Contraseña de Fever",
|
||||
"form.integration.fever_endpoint": "Extremo de API de Fever:",
|
||||
"form.integration.fever_endpoint": "Acceso API de Fever:",
|
||||
"form.integration.googlereader_activate": "Activar API de Google Reader",
|
||||
"form.integration.googlereader_username": "Nombre de usuario de Google Reader",
|
||||
"form.integration.googlereader_password": "Contraseña de Google Reader",
|
||||
"form.integration.googlereader_endpoint": "Extremo de API de Google Reader:",
|
||||
"form.integration.pinboard_activate": "Guardar artículos a Pinboard",
|
||||
"form.integration.googlereader_endpoint": "Acceso API de Google Reader:",
|
||||
"form.integration.pinboard_activate": "Enviar artículos a Pinboard",
|
||||
"form.integration.pinboard_token": "Token de API de Pinboard",
|
||||
"form.integration.pinboard_tags": "Etiquetas de Pinboard",
|
||||
"form.integration.pinboard_bookmark": "Marcar marcador como no leído",
|
||||
"form.integration.instapaper_activate": "Guardar artículos a Instapaper",
|
||||
"form.integration.instapaper_activate": "Enviar artículos a Instapaper",
|
||||
"form.integration.instapaper_username": "Nombre de usuario de Instapaper",
|
||||
"form.integration.instapaper_password": "Contraseña de Instapaper",
|
||||
"form.integration.pocket_activate": "Guardar artículos a Pocket",
|
||||
"form.integration.pocket_activate": "Enviar artículos a Pocket",
|
||||
"form.integration.pocket_consumer_key": "Clave del consumidor de Pocket",
|
||||
"form.integration.pocket_access_token": "Token de acceso de Pocket",
|
||||
"form.integration.pocket_connect_link": "Conectar a la cuenta de Pocket",
|
||||
"form.integration.wallabag_activate": "Guardar artículos a Wallabag",
|
||||
"form.integration.wallabag_endpoint": "Extremo de API de Wallabag",
|
||||
"form.integration.wallabag_activate": "Enviar artículos a Wallabag",
|
||||
"form.integration.wallabag_only_url": "Enviar solo URL (en lugar de contenido completo)",
|
||||
"form.integration.wallabag_endpoint": "Acceso API de Wallabag",
|
||||
"form.integration.wallabag_client_id": "ID de cliente de Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Secreto cliente de Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Secreto de cliente de Wallabag",
|
||||
"form.integration.wallabag_username": "Nombre de usuario de Wallabag",
|
||||
"form.integration.wallabag_password": "Contraseña de Wallabag",
|
||||
"form.integration.nunux_keeper_activate": "Guardar artículos a Nunux Keeper",
|
||||
"form.integration.nunux_keeper_endpoint": "Extremo de API de Nunux Keeper",
|
||||
"form.integration.nunux_keeper_activate": "Enviar artículos a Nunux Keeper",
|
||||
"form.integration.nunux_keeper_endpoint": "Acceso API de Nunux Keeper",
|
||||
"form.integration.nunux_keeper_api_key": "Clave de API de Nunux Keeper",
|
||||
"form.integration.espial_activate": "Guardar artículos a Espial",
|
||||
"form.integration.espial_endpoint": "Extremo de API de Espial",
|
||||
"form.integration.espial_activate": "Enviar artículos a Espial",
|
||||
"form.integration.espial_endpoint": "Acceso API de Espial",
|
||||
"form.integration.espial_api_key": "Clave de API de Espial",
|
||||
"form.integration.espial_tags": "Etiquetas de Espial",
|
||||
"form.integration.telegram_bot_activate": "Envíe nuevos artículos al chat de Telegram",
|
||||
"form.integration.telegram_bot_token": "Token de bot",
|
||||
"form.integration.telegram_chat_id": "ID de chat",
|
||||
"form.integration.linkding_activate": "Guardar artículos a Linkding",
|
||||
"form.integration.linkding_endpoint": "Extremo de API de Linkding",
|
||||
"form.integration.linkding_activate": "Enviar artículos a Linkding",
|
||||
"form.integration.linkding_endpoint": "Acceso API de Linkding",
|
||||
"form.integration.linkding_api_key": "Clave de API de Linkding",
|
||||
"form.integration.matrix_bot_activate": "Transferir nuevos artículos a Matrix",
|
||||
"form.integration.matrix_bot_user": "Nombre de usuario para Matrix",
|
||||
"form.integration.matrix_bot_password": "Contraseña para el usuario de Matrix",
|
||||
"form.integration.matrix_bot_url": "URL del servidor de Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "ID de la sala de Matrix",
|
||||
"form.api_key.label.description": "Etiqueta de clave API",
|
||||
"form.submit.loading": "Cargando...",
|
||||
"form.submit.saving": "Guardando...",
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Vieritä ylös",
|
||||
"page.keyboard_shortcuts.remove_feed": "Poista tämä syöte",
|
||||
"page.keyboard_shortcuts.go_to_search": "Aseta painopiste hakukenttään",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Sulje modaalinen valintaikkuna",
|
||||
"page.users.title": "Käyttäjät",
|
||||
"page.users.username": "Käyttäjätunnus",
|
||||
|
@ -241,6 +242,8 @@
|
|||
"error.invalid_timezone": "Virheellinen aikavyöhyke.",
|
||||
"error.invalid_entry_direction": "Invalid entry direction.",
|
||||
"error.invalid_display_mode": "Virheellinen verkkosovelluksen näyttötila.",
|
||||
"error.invalid_gesture_nav": "Virheellinen ele-navigointi.",
|
||||
"error.invalid_default_home_page": "Väärä oletusarvoinen kotisivu!",
|
||||
"error.empty_file": "Tiedosto on tyhjä.",
|
||||
"error.bad_credentials": "Virheellinen käyttäjänimi tai salasana.",
|
||||
"error.fields_mandatory": "Kaikki kentät ovat pakollisia.",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Artikkelia sivulla",
|
||||
"form.prefs.label.default_reading_speed": "Muiden kielten lukunopeus (sanaa minuutissa)",
|
||||
"form.prefs.label.cjk_reading_speed": "Kiinan, Korean ja Japanin lukunopeus (merkkejä minuutissa)",
|
||||
"form.prefs.label.display_mode": "Verkkosovelluksen näyttötila (vaatii uudelleenasennuksen)",
|
||||
"form.prefs.label.display_mode": "Progressive Web App (PWA) -näyttötila",
|
||||
"form.prefs.select.older_first": "Vanhin ensin",
|
||||
"form.prefs.select.recent_first": "Uusin ensin",
|
||||
"form.prefs.select.fullscreen": "Kokoruututila",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Selain",
|
||||
"form.prefs.select.publish_time": "Julkaisuaika",
|
||||
"form.prefs.select.created_time": "Luomisaika",
|
||||
"form.prefs.select.alphabetical": "Aakkosjärjestys",
|
||||
"form.prefs.select.unread_count": "Lukemattomien määrä",
|
||||
"form.prefs.select.none": "Ei mitään",
|
||||
"form.prefs.select.tap": "Kaksoisnapauta",
|
||||
"form.prefs.select.swipe": "Pyyhkäise",
|
||||
"form.prefs.label.keyboard_shortcuts": "Ota pikanäppäimet käyttöön",
|
||||
"form.prefs.label.entry_swipe": "Ota pyyhkäisyele käyttöön mobiililaitteella",
|
||||
"form.prefs.label.entry_swipe": "Ota syöttöpyyhkäisy käyttöön kosketusnäytöissä",
|
||||
"form.prefs.label.gesture_nav": "Ele siirtyäksesi merkintöjen välillä",
|
||||
"form.prefs.label.show_reading_time": "Näytä artikkeleiden arvioitu lukuaika",
|
||||
"form.prefs.label.custom_css": "Mukautettu CSS",
|
||||
"form.prefs.label.entry_order": "Lajittele sarakkeen mukaan",
|
||||
"form.prefs.label.default_home_page": "Oletusarvoinen etusivu",
|
||||
"form.prefs.label.categories_sorting_order": "Kategorioiden lajittelu",
|
||||
"form.import.label.file": "OPML-tiedosto",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Ota Fever API käyttöön",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket-käyttöoikeustunnus",
|
||||
"form.integration.pocket_connect_link": "Yhdistä Pocket-tilisi",
|
||||
"form.integration.wallabag_activate": "Tallenna artikkelit Wallabagiin",
|
||||
"form.integration.wallabag_only_url": "Lähetä vain URL-osoite (koko sisällön sijaan)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag API -päätepiste",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Tallenna artikkelit Linkkiin",
|
||||
"form.integration.linkding_endpoint": "Linkding API-päätepiste",
|
||||
"form.integration.linkding_api_key": "Linkding API-avain",
|
||||
"form.integration.matrix_bot_activate": "Siirrä uudet artikkelit Matrixiin",
|
||||
"form.integration.matrix_bot_user": "Matrixin käyttäjätunnus",
|
||||
"form.integration.matrix_bot_password": "Matrix-käyttäjän salasana",
|
||||
"form.integration.matrix_bot_url": "Matrix-palvelimen URL-osoite",
|
||||
"form.integration.matrix_bot_chat_id": "Matrix-huoneen tunnus",
|
||||
"form.api_key.label.description": "API Key Label",
|
||||
"form.submit.loading": "Ladataan...",
|
||||
"form.submit.saving": "Tallennetaan...",
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"menu.integrations": "Intégrations",
|
||||
"menu.sessions": "Sessions",
|
||||
"menu.users": "Utilisateurs",
|
||||
"menu.about": "A propos",
|
||||
"menu.about": "À propos",
|
||||
"menu.export": "Export",
|
||||
"menu.import": "Import",
|
||||
"menu.create_category": "Créer une catégorie",
|
||||
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Faire défiler l'élément vers le haut",
|
||||
"page.keyboard_shortcuts.remove_feed": "Supprimer ce flux",
|
||||
"page.keyboard_shortcuts.go_to_search": "Mettre le focus sur le champ de recherche",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Fermer la boite de dialogue",
|
||||
"page.users.title": "Utilisateurs",
|
||||
"page.users.username": "Nom d'utilisateur",
|
||||
|
@ -263,6 +264,8 @@
|
|||
"error.invalid_timezone": "Fuseau horaire non valide.",
|
||||
"error.invalid_entry_direction": "Ordre de trie non valide.",
|
||||
"error.invalid_display_mode": "Mode d'affichage de l'application web non valide.",
|
||||
"error.invalid_gesture_nav": "Navigation gestuelle non valide.",
|
||||
"error.invalid_default_home_page": "Page d'accueil par défaut invalide !",
|
||||
"form.feed.label.title": "Titre",
|
||||
"form.feed.label.site_url": "URL du site web",
|
||||
"form.feed.label.feed_url": "URL du flux",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Entrées par page",
|
||||
"form.prefs.label.default_reading_speed": "Vitesse de lecture pour les autres langues (mots par minute)",
|
||||
"form.prefs.label.cjk_reading_speed": "Vitesse de lecture pour le Chinois, le Coréen et le Japonais (caractères par minute)",
|
||||
"form.prefs.label.display_mode": "Mode d'affichage de l'application web (doit être réinstallé)",
|
||||
"form.prefs.label.display_mode": "Mode d'affichage de l'Application Web Progressive (PWA)",
|
||||
"form.prefs.select.older_first": "Ancien éléments en premier",
|
||||
"form.prefs.select.recent_first": "Éléments récents en premier",
|
||||
"form.prefs.select.fullscreen": "Plein écran",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Navigateur",
|
||||
"form.prefs.select.publish_time": "Heure de publication de l'entrée",
|
||||
"form.prefs.select.created_time": "Heure de création de l'entrée",
|
||||
"form.prefs.select.alphabetical": "Alphabétique",
|
||||
"form.prefs.select.unread_count": "Nombre d'articles non lus",
|
||||
"form.prefs.select.none": "Aucun",
|
||||
"form.prefs.select.tap": "Tapez deux fois",
|
||||
"form.prefs.select.swipe": "Glisser",
|
||||
"form.prefs.label.keyboard_shortcuts": "Activer les raccourcis clavier",
|
||||
"form.prefs.label.entry_swipe": "Activer le geste de balayage sur les entrées sur mobile",
|
||||
"form.prefs.label.entry_swipe": "Activer le balayage des entrées sur les écrans tactiles",
|
||||
"form.prefs.label.gesture_nav": "Geste pour naviguer entre les entrées",
|
||||
"form.prefs.label.show_reading_time": "Afficher le temps de lecture estimé des articles",
|
||||
"form.prefs.label.custom_css": "CSS personnalisé",
|
||||
"form.prefs.label.entry_order": "Colonne de tri des entrées",
|
||||
"form.prefs.label.default_home_page": "Page d'accueil par défaut",
|
||||
"form.prefs.label.categories_sorting_order": "Colonne de tri des catégories",
|
||||
"form.import.label.file": "Fichier OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Activer l'API de Fever",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Jeton d'accès de l'API de Pocket",
|
||||
"form.integration.pocket_connect_link": "Connectez votre compte Pocket",
|
||||
"form.integration.wallabag_activate": "Sauvegarder les articles vers Wallabag",
|
||||
"form.integration.wallabag_only_url": "Envoyer uniquement l'URL (au lieu du contenu complet)",
|
||||
"form.integration.wallabag_endpoint": "URL de l'API de Wallabag",
|
||||
"form.integration.wallabag_client_id": "Identifiant unique du client Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Clé secrète du client Wallabag",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Sauvegarder les articles vers Linkding",
|
||||
"form.integration.linkding_endpoint": "URL de l'API de Linkding",
|
||||
"form.integration.linkding_api_key": "Clé d'API de Linkding",
|
||||
"form.integration.matrix_bot_activate": "Envoyer les nouveaux articles vers Matrix",
|
||||
"form.integration.matrix_bot_user": "Nom de l'utilisateur Matrix",
|
||||
"form.integration.matrix_bot_password": "Mot de passe de l'utilisateur Matrix",
|
||||
"form.integration.matrix_bot_url": "URL du serveur Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "Identifiant de la salle Matrix",
|
||||
"form.api_key.label.description": "Libellé de la clé d'API",
|
||||
"form.submit.loading": "Chargement...",
|
||||
"form.submit.saving": "Sauvegarde en cours...",
|
||||
|
@ -393,8 +410,7 @@
|
|||
"This feed is empty": "Cet abonnement est vide",
|
||||
"This web page is empty": "Cette page web est vide",
|
||||
"Invalid SSL certificate (original error: %q)": "Certificat SSL invalide (erreur originale : %q)",
|
||||
"This website is temporarily unreachable (original error: %q)": "Ce site web est temporairement injoignable (erreur originale : %q)",
|
||||
"This website is permanently unreachable (original error: %q)": "Ce site web n'est pas joignable de façon permanente (erreur originale : %q)",
|
||||
"This website is unreachable (original error: %q)": "Ce site web n'est pas joignable (erreur originale : %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes",
|
||||
"You are not authorized to access this resource (invalid username/password)": "Vous n'êtes pas autorisé à accéder à cette ressource (nom d'utilisateur / mot de passe incorrect)",
|
||||
"Unable to fetch this resource (Status Code = %d)": "Impossible de récupérer cette ressource (code=%d)",
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "आइटम को ऊपर तक स्क्रॉल करें",
|
||||
"page.keyboard_shortcuts.remove_feed": "यह फ़ीड हटाएं",
|
||||
"page.keyboard_shortcuts.go_to_search": "सर्च फॉर्म पर फोकस सेट करें",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "मोडल डायलॉग बंद करें",
|
||||
"page.users.title": "उपभोक्ता",
|
||||
"page.users.username": "यूसर्नेम",
|
||||
|
@ -241,6 +242,8 @@
|
|||
"error.invalid_timezone": "अमान्य समयक्षेत्र.",
|
||||
"error.invalid_entry_direction": "अमान्य प्रवेश दिशा।",
|
||||
"error.invalid_display_mode": "अमान्य वेब ऐप्लिकेशन प्रदर्शन मोड.",
|
||||
"error.invalid_gesture_nav": "अमान्य इशारा नेविगेशन।",
|
||||
"error.invalid_default_home_page": "अमान्य डिफ़ॉल्ट मुखपृष्ठ!",
|
||||
"error.empty_file": "यह फ़ाइल खाली है।",
|
||||
"error.bad_credentials": "अमान्य उपयोगकर्ता नाम या पासवर्ड।",
|
||||
"error.fields_mandatory": "सभी फील्ड अनिवार्य।",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "प्रति पृष्ठ प्रविष्टियाँ",
|
||||
"form.prefs.label.default_reading_speed": "अन्य भाषाओं के लिए पढ़ने की गति (प्रति मिनट शब्द)",
|
||||
"form.prefs.label.cjk_reading_speed": "चीनी, कोरियाई और जापानी के लिए पढ़ने की गति (प्रति मिनट वर्ण)",
|
||||
"form.prefs.label.display_mode": "वेब ऐप डिस्प्ले मोड (पुनः स्थापित करने की आवश्यकता है)",
|
||||
"form.prefs.label.display_mode": "प्रोग्रेसिव वेब ऐप (PWA) डिस्प्ले मोड",
|
||||
"form.prefs.select.older_first": "पहले पुरानी प्रविष्टियाँ",
|
||||
"form.prefs.select.recent_first": "हाल की प्रविष्टियाँ पहले",
|
||||
"form.prefs.select.fullscreen": "पूर्ण स्क्रीन",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "ब्राउज़र",
|
||||
"form.prefs.select.publish_time": "प्रवेश प्रकाशित समय",
|
||||
"form.prefs.select.created_time": "प्रवेश बनाया समय",
|
||||
"form.prefs.select.alphabetical": "वर्णक्रम",
|
||||
"form.prefs.select.unread_count": "अपठित गणना",
|
||||
"form.prefs.select.none": "कोई नहीं",
|
||||
"form.prefs.select.tap": "दो बार टैप",
|
||||
"form.prefs.select.swipe": "कड़ी चोट",
|
||||
"form.prefs.label.keyboard_shortcuts": "कीबोर्ड शॉर्टकट सक्षम करें",
|
||||
"form.prefs.label.entry_swipe": "मोबाइल पर प्रविष्टियों पर स्वाइप जेस्चर सक्षम करें",
|
||||
"form.prefs.label.entry_swipe": "टच स्क्रीन पर एंट्री स्वाइप सक्षम करें",
|
||||
"form.prefs.label.gesture_nav": "प्रविष्टियों के बीच नेविगेट करने के लिए इशारा",
|
||||
"form.prefs.label.show_reading_time": "विषय के लिए अनुमानित पढ़ने का समय दिखाएं",
|
||||
"form.prefs.label.custom_css": "कस्टम सीएसएस",
|
||||
"form.prefs.label.entry_order": "प्रवेश छँटाई कॉलम",
|
||||
"form.prefs.label.default_home_page": "डिफ़ॉल्ट होमपेज़",
|
||||
"form.prefs.label.categories_sorting_order": "श्रेणियाँ छँटाई",
|
||||
"form.import.label.file": "ओपीएमएल फ़ाइल",
|
||||
"form.import.label.url": "यूआरएल",
|
||||
"form.integration.fever_activate": "फीवर एपीआई सक्रिय करें",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "पॉकेट एक्सेस टोकन",
|
||||
"form.integration.pocket_connect_link": "अपना पॉकेट खाता कनेक्ट करें",
|
||||
"form.integration.wallabag_activate": "विषय सहेजें वालाबाग में ",
|
||||
"form.integration.wallabag_only_url": "केवल URL भेजें (पूर्ण सामग्री के बजाय)",
|
||||
"form.integration.wallabag_endpoint": "वालबैग एपीआई एंडपॉइंट",
|
||||
"form.integration.wallabag_client_id": "वालाबैग क्लाइंट आईडी",
|
||||
"form.integration.wallabag_client_secret": "वालाबैग क्लाइंट सीक्रेट",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "लिंक्डिन में विषयवस्तु सहेजें",
|
||||
"form.integration.linkding_endpoint": "लिंकिंग एपीआई समापन बिंदु",
|
||||
"form.integration.linkding_api_key": "लिंकिंग एपीआई कुंजी",
|
||||
"form.integration.matrix_bot_activate": "नए लेखों को मैट्रिक्स में स्थानांतरित करें",
|
||||
"form.integration.matrix_bot_user": "मैट्रिक्स के लिए उपयोगकर्ता नाम",
|
||||
"form.integration.matrix_bot_password": "मैट्रिक्स उपयोगकर्ता के लिए पासवर्ड",
|
||||
"form.integration.matrix_bot_url": "मैट्रिक्स सर्वर URL",
|
||||
"form.integration.matrix_bot_chat_id": "मैट्रिक्स रूम की आईडी",
|
||||
"form.api_key.label.description": "एपीआई कुंजी लेबल",
|
||||
"form.submit.loading": "लोड हो रहा है...",
|
||||
"form.submit.saving": "सहेजा जा रहा है...",
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
{
|
||||
"confirm.question": "Apakah Anda yakin?",
|
||||
"confirm.yes": "ya",
|
||||
"confirm.no": "tidak",
|
||||
"confirm.loading": "Sedang progres...",
|
||||
"action.subscribe": "Langgan",
|
||||
"action.save": "Simpan",
|
||||
"action.or": "atau",
|
||||
"action.cancel": "batal",
|
||||
"action.remove": "Hapus",
|
||||
"action.remove_feed": "Hapus umpan ini",
|
||||
"action.update": "Perbarui",
|
||||
"action.edit": "Sunting",
|
||||
"action.download": "Unduh",
|
||||
"action.import": "Impor",
|
||||
"action.login": "Masuk",
|
||||
"action.home_screen": "Tambahkan ke beranda",
|
||||
"tooltip.keyboard_shortcuts": "Pintasan Papan Tik: %s",
|
||||
"tooltip.logged_user": "Masuk sebagai %s",
|
||||
"menu.unread": "Belum Dibaca",
|
||||
"menu.starred": "Markah",
|
||||
"menu.history": "Riwayat",
|
||||
"menu.feeds": "Umpan",
|
||||
"menu.categories": "Kategori",
|
||||
"menu.settings": "Pengaturan",
|
||||
"menu.logout": "Keluar",
|
||||
"menu.preferences": "Preferensi",
|
||||
"menu.integrations": "Integrasi",
|
||||
"menu.sessions": "Sesi",
|
||||
"menu.users": "Pengguna",
|
||||
"menu.about": "Tentang",
|
||||
"menu.export": "Ekspor",
|
||||
"menu.import": "Impor",
|
||||
"menu.create_category": "Buat kategori",
|
||||
"menu.mark_page_as_read": "Tandai halaman ini sebagai telah dibaca",
|
||||
"menu.mark_all_as_read": "Tandai semua sebagai telah dibaca",
|
||||
"menu.show_all_entries": "Tampilkan semua entri",
|
||||
"menu.show_only_unread_entries": "Tampilkan hanya entri yang belum dibaca",
|
||||
"menu.refresh_feed": "Muat ulang",
|
||||
"menu.refresh_all_feeds": "Muat ulang semua umpan di latar belakang",
|
||||
"menu.edit_feed": "Sunting",
|
||||
"menu.edit_category": "Sunting",
|
||||
"menu.add_feed": "Tambah langganan",
|
||||
"menu.add_user": "Tambah pengguna",
|
||||
"menu.flush_history": "Hapus riwayat",
|
||||
"menu.feed_entries": "Entri",
|
||||
"menu.api_keys": "Kunci API",
|
||||
"menu.create_api_key": "Buat kunci API baru",
|
||||
"menu.shared_entries": "Entri yang Dibagikan",
|
||||
"search.label": "Cari",
|
||||
"search.placeholder": "Cari...",
|
||||
"pagination.next": "Berikutnya",
|
||||
"pagination.previous": "Sebelumnya",
|
||||
"entry.status.unread": "Belum dibaca",
|
||||
"entry.status.read": "Telah dibaca",
|
||||
"entry.status.toast.unread": "Ditandai sebagai belum dibaca",
|
||||
"entry.status.toast.read": "Ditandai sebagai telah dibaca",
|
||||
"entry.status.title": "Ubah status entri",
|
||||
"entry.bookmark.toggle.on": "Markahi",
|
||||
"entry.bookmark.toggle.off": "Batal Markahi",
|
||||
"entry.bookmark.toast.on": "Markahi",
|
||||
"entry.bookmark.toast.off": "Batal Markahi",
|
||||
"entry.state.saving": "Menyimpan...",
|
||||
"entry.state.loading": "Memuat...",
|
||||
"entry.save.label": "Simpan",
|
||||
"entry.save.title": "Simpan artikel ini",
|
||||
"entry.save.completed": "Selesai!",
|
||||
"entry.save.toast.completed": "Artikel tersimpan",
|
||||
"entry.scraper.label": "Unduh",
|
||||
"entry.scraper.title": "Ambil konten asli",
|
||||
"entry.scraper.completed": "Selesai!",
|
||||
"entry.external_link.label": "Tautan eksternal",
|
||||
"entry.comments.label": "Komentar",
|
||||
"entry.comments.title": "Lihat Komentar",
|
||||
"entry.share.label": "Bagikan",
|
||||
"entry.share.title": "Bagikan artikel ini",
|
||||
"entry.unshare.label": "Batal bagikan",
|
||||
"entry.shared_entry.title": "Buka tautan publik",
|
||||
"entry.shared_entry.label": "Bagikan",
|
||||
"entry.estimated_reading_time": [
|
||||
"%d menit untuk dibaca"
|
||||
],
|
||||
"page.shared_entries.title": "Entri yang Dibagikan",
|
||||
"page.unread.title": "Belum Dibaca",
|
||||
"page.starred.title": "Markah",
|
||||
"page.categories.title": "Kategori",
|
||||
"page.categories.no_feed": "Tidak ada umpan.",
|
||||
"page.categories.entries": "Artikel",
|
||||
"page.categories.feeds": "Langganan",
|
||||
"page.categories.feed_count": [
|
||||
"Ada %d umpan."
|
||||
],
|
||||
"page.categories.unread_counter": "Jumlah entri yang belum dibaca",
|
||||
"page.new_category.title": "Kategori Baru",
|
||||
"page.new_user.title": "Pengguna Baru",
|
||||
"page.edit_category.title": "Sunting Kategori: %s",
|
||||
"page.edit_user.title": "Sunting Pengguna: %s",
|
||||
"page.feeds.title": "Umpan",
|
||||
"page.feeds.last_check": "Terakhir diperiksa:",
|
||||
"page.feeds.unread_counter": "Jumlah entri yang belum dibaca",
|
||||
"page.feeds.read_counter": "Jumlah entri yang telah dibaca",
|
||||
"page.feeds.error_count": [
|
||||
"%d galat"
|
||||
],
|
||||
"page.history.title": "Riwayat",
|
||||
"page.import.title": "Impor",
|
||||
"page.search.title": "Hasil Pencarian",
|
||||
"page.about.title": "Tentang",
|
||||
"page.about.credits": "Pengembang",
|
||||
"page.about.version": "Versi:",
|
||||
"page.about.build_date": "Tanggal Penyusunan:",
|
||||
"page.about.author": "Pengembang:",
|
||||
"page.about.license": "Lisensi:",
|
||||
"page.about.global_config_options": "Pengaturan Konfigurasi Global",
|
||||
"page.about.postgres_version": "Versi Postgres:",
|
||||
"page.about.go_version": "Versi Go:",
|
||||
"page.add_feed.title": "Langganan Baru",
|
||||
"page.add_feed.no_category": "Tidak ada kategori. Anda harus paling tidak memiliki satu kategori.",
|
||||
"page.add_feed.label.url": "URL",
|
||||
"page.add_feed.submit": "Cari langganan",
|
||||
"page.add_feed.legend.advanced_options": "Pilihan Tingkat Lanjut",
|
||||
"page.add_feed.choose_feed": "Pilih Umpan",
|
||||
"page.edit_feed.title": "Sunting Umpan: %s",
|
||||
"page.edit_feed.last_check": "Terakhir diperiksa:",
|
||||
"page.edit_feed.last_modified_header": "Tajuk LastModified:",
|
||||
"page.edit_feed.etag_header": "Tajuk ETag:",
|
||||
"page.edit_feed.no_header": "Tidak Ada",
|
||||
"page.edit_feed.last_parsing_error": "Galat Penguraian Terakhir",
|
||||
"page.entry.attachments": "Lampiran",
|
||||
"page.keyboard_shortcuts.title": "Pintasan Papan Tik",
|
||||
"page.keyboard_shortcuts.subtitle.sections": "Navigasi Bagian",
|
||||
"page.keyboard_shortcuts.subtitle.items": "Navigasi Entri",
|
||||
"page.keyboard_shortcuts.subtitle.pages": "Navigasi Halaman",
|
||||
"page.keyboard_shortcuts.subtitle.actions": "Tindakan",
|
||||
"page.keyboard_shortcuts.go_to_unread": "Ke bagian yang belum dibaca",
|
||||
"page.keyboard_shortcuts.go_to_starred": "Ke markah",
|
||||
"page.keyboard_shortcuts.go_to_history": "Ke riwayat",
|
||||
"page.keyboard_shortcuts.go_to_feeds": "Ke umpan",
|
||||
"page.keyboard_shortcuts.go_to_categories": "Ke kategori",
|
||||
"page.keyboard_shortcuts.go_to_settings": "Ke pengaturan",
|
||||
"page.keyboard_shortcuts.show_keyboard_shortcuts": "Tampilkan pintasan papan tik",
|
||||
"page.keyboard_shortcuts.go_to_previous_item": "Ke entri sebelumnya",
|
||||
"page.keyboard_shortcuts.go_to_next_item": "Ke entri berikutnya",
|
||||
"page.keyboard_shortcuts.go_to_feed": "Ke umpan",
|
||||
"page.keyboard_shortcuts.go_to_previous_page": "Ke halaman sebelumnya",
|
||||
"page.keyboard_shortcuts.go_to_next_page": "Ke halaman berikutnya",
|
||||
"page.keyboard_shortcuts.open_item": "Buka entri yang dipilih",
|
||||
"page.keyboard_shortcuts.open_original": "Buka tautan asli",
|
||||
"page.keyboard_shortcuts.open_original_same_window": "Buka tautan asli di bilah saat ini",
|
||||
"page.keyboard_shortcuts.open_comments": "Buka tautan komentar",
|
||||
"page.keyboard_shortcuts.open_comments_same_window": "Buka tautan komentar di bilah saat ini",
|
||||
"page.keyboard_shortcuts.toggle_read_status_next": "Ubah status baca, fokus ke selanjutnya",
|
||||
"page.keyboard_shortcuts.toggle_read_status_prev": "Ubah status baca, fokus ke sebelumnya",
|
||||
"page.keyboard_shortcuts.refresh_all_feeds": "Muat ulang semua umpan di latar belakang",
|
||||
"page.keyboard_shortcuts.mark_page_as_read": "Tandai halaman saat ini sebagai telah dibaca",
|
||||
"page.keyboard_shortcuts.download_content": "Unduh konten asli",
|
||||
"page.keyboard_shortcuts.toggle_bookmark_status": "Ubah status markah",
|
||||
"page.keyboard_shortcuts.save_article": "Simpan Artikel",
|
||||
"page.keyboard_shortcuts.scroll_item_to_top": "Gulir ke atas",
|
||||
"page.keyboard_shortcuts.remove_feed": "Hapus umpan ini",
|
||||
"page.keyboard_shortcuts.go_to_search": "Atur fokus ke pencaarian",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Buka/tutup lampiran entri",
|
||||
"page.keyboard_shortcuts.close_modal": "Tutup bilah modal",
|
||||
"page.users.title": "Pengguna",
|
||||
"page.users.username": "Nama Pengguna",
|
||||
"page.users.never_logged": "Tidak Pernah",
|
||||
"page.users.admin.yes": "Ya",
|
||||
"page.users.admin.no": "Tidak",
|
||||
"page.users.actions": "Tindakan",
|
||||
"page.users.last_login": "Terakhir Masuk",
|
||||
"page.users.is_admin": "Administrator",
|
||||
"page.settings.title": "Pengaturan",
|
||||
"page.settings.link_google_account": "Tautkan akun Google saya",
|
||||
"page.settings.unlink_google_account": "Putuskan akun Google saya",
|
||||
"page.settings.link_oidc_account": "Tautkan akun OpenID Connect saya",
|
||||
"page.settings.unlink_oidc_account": "Putuskan akun OpenID Connect saya",
|
||||
"page.login.title": "Masuk",
|
||||
"page.login.google_signin": "Masuk dengan Google",
|
||||
"page.login.oidc_signin": "Masuk dengan OpenID Connect",
|
||||
"page.integrations.title": "Integrasi",
|
||||
"page.integration.miniflux_api": "API Miniflux",
|
||||
"page.integration.miniflux_api_endpoint": "Titik URL API",
|
||||
"page.integration.miniflux_api_username": "Nama Pengguna",
|
||||
"page.integration.miniflux_api_password": "Kata Sandi",
|
||||
"page.integration.miniflux_api_password_value": "Kata sandi akun Anda",
|
||||
"page.integration.bookmarklet": "Bookmarklet",
|
||||
"page.integration.bookmarklet.name": "Tambahkan ke Miniflux",
|
||||
"page.integration.bookmarklet.instructions": "Seret dan tempatkan tautan ini ke markah Anda.",
|
||||
"page.integration.bookmarklet.help": "Tautan spesial ini memperbolehkan Anda untuk berlangganan ke situs langsung dengan menggunakan markah di peramban web Anda.",
|
||||
"page.sessions.title": "Sesi",
|
||||
"page.sessions.table.date": "Tanggal",
|
||||
"page.sessions.table.ip": "Alamat IP",
|
||||
"page.sessions.table.user_agent": "User Agent",
|
||||
"page.sessions.table.actions": "Tindakan",
|
||||
"page.sessions.table.current_session": "Sesi Saat Ini",
|
||||
"page.api_keys.title": "Kunci API",
|
||||
"page.api_keys.table.description": "Deskripsi",
|
||||
"page.api_keys.table.token": "Token",
|
||||
"page.api_keys.table.last_used_at": "Terakhir Digunakan",
|
||||
"page.api_keys.table.created_at": "Tanggal Pembuatan",
|
||||
"page.api_keys.table.actions": "Tindakan",
|
||||
"page.api_keys.never_used": "Tidak Pernah Digunakan",
|
||||
"page.new_api_key.title": "Kunci API Baru",
|
||||
"page.offline.title": "Mode Luring",
|
||||
"page.offline.message": "Anda sedang luring",
|
||||
"page.offline.refresh_page": "Coba untuk memuat ulang halaman ini",
|
||||
"alert.no_shared_entry": "Tidak ada entri yang dibagikan.",
|
||||
"alert.no_bookmark": "Tidak ada markah.",
|
||||
"alert.no_category": "Tidak ada kategori.",
|
||||
"alert.no_category_entry": "Tidak ada artikel di kategori ini.",
|
||||
"alert.no_feed_entry": "Tidak ada artikel di umpan ini.",
|
||||
"alert.no_feed": "Anda tidak memiliki langganan.",
|
||||
"alert.no_feed_in_category": "Tidak ada langganan untuk kategori ini.",
|
||||
"alert.no_history": "Tidak ada riwayat untuk saat ini.",
|
||||
"alert.feed_error": "Ada masalah dengan umpan ini",
|
||||
"alert.no_search_result": "Tidak ada hasil untuk pencarian ini.",
|
||||
"alert.no_unread_entry": "Belum ada artikel yang dibaca.",
|
||||
"alert.no_user": "Anda adalah satu-satunya pengguna.",
|
||||
"alert.account_unlinked": "Akun eksternal Anda sudah terputus!",
|
||||
"alert.account_linked": "Akun eksternal Anda sudah terhubung!",
|
||||
"alert.pocket_linked": "Akun Pocket Anda sudah terhubung!",
|
||||
"alert.prefs_saved": "Preferensi disimpan!",
|
||||
"error.unlink_account_without_password": "Anda harus mengatur kata sandi atau Anda tidak bisa masuk kembali.",
|
||||
"error.duplicate_linked_account": "Sudah ada orang lain yang terhubung dengan penyedia ini!",
|
||||
"error.duplicate_fever_username": "Sudah ada orang lain dengan nama pengguna Fever yang sama!",
|
||||
"error.duplicate_googlereader_username": "Sudah ada orang lain dengan nama pengguna Google Reader yang sama!",
|
||||
"error.pocket_request_token": "Tidak bisa mendapatkan token permintaan dari Pocket!",
|
||||
"error.pocket_access_token": "Tidak bisa mendapatkan token akses dari Pocket!",
|
||||
"error.category_already_exists": "Kategori ini telah ada.",
|
||||
"error.unable_to_create_category": "Tidak bisa membuat kategori ini.",
|
||||
"error.unable_to_update_category": "Tidak bisa memperbarui kategori ini.",
|
||||
"error.user_already_exists": "Pengguna ini sudah ada.",
|
||||
"error.unable_to_create_user": "Tidak bisa membuat pengguna tersebut.",
|
||||
"error.unable_to_update_user": "Tidak bisa memperbarui pengguna tersebut.",
|
||||
"error.unable_to_update_feed": "Tidak bisa memperbarui umpan ini.",
|
||||
"error.subscription_not_found": "Tidak bisa mencari langganan apa pun.",
|
||||
"error.invalid_theme": "Tema tidak valid.",
|
||||
"error.invalid_language": "Bahasa tidak valid.",
|
||||
"error.invalid_timezone": "Zona waktu tidak valid.",
|
||||
"error.invalid_entry_direction": "Urutan entri tidak valid.",
|
||||
"error.invalid_display_mode": "Mode tampilan aplikasi web tidak valid.",
|
||||
"error.invalid_gesture_nav": "Navigasi gestur tidak valid.",
|
||||
"error.invalid_default_home_page": "Beranda baku tidak valid!",
|
||||
"error.empty_file": "Berkas ini kosong.",
|
||||
"error.bad_credentials": "Nama pengguna atau kata sandi tidak valid.",
|
||||
"error.fields_mandatory": "Semua bidang diharuskan.",
|
||||
"error.title_required": "Judul diharuskan.",
|
||||
"error.different_passwords": "Kata sandi tidak sama.",
|
||||
"error.password_min_length": "Kata sandi harus memiliki setidaknya 6 karakter.",
|
||||
"error.settings_mandatory_fields": "Harus ada nama pengguna, tema, bahasa, dan zona waktu.",
|
||||
"error.settings_reading_speed_is_positive": "Kecepatan membaca harus integer positif.",
|
||||
"error.entries_per_page_invalid": "Jumlah entri per halaman tidak valid.",
|
||||
"error.feed_mandatory_fields": "Harus ada URL dan kategorinya.",
|
||||
"error.feed_already_exists": "Umpan ini sudah ada.",
|
||||
"error.invalid_feed_url": "URL umpan tidak valid.",
|
||||
"error.invalid_site_url": "URL situs tidak valid.",
|
||||
"error.feed_url_not_empty": "URL umpan tidak boleh kosong.",
|
||||
"error.site_url_not_empty": "URL situs tidak boleh kosong.",
|
||||
"error.feed_title_not_empty": "Judul umpan tidak boleh kosong.",
|
||||
"error.feed_category_not_found": "Kategori ini tidak ada atau tidak dipunyai oleh pengguna ini.",
|
||||
"error.feed_invalid_blocklist_rule": "Aturan blokir tidak valid.",
|
||||
"error.feed_invalid_keeplist_rule": "Aturan simpan tidak valid.",
|
||||
"error.user_mandatory_fields": "Harus ada nama pengguna.",
|
||||
"error.api_key_already_exists": "Kunci API ini sudah ada.",
|
||||
"error.unable_to_create_api_key": "Tidak bisa membuat kunci API ini.",
|
||||
"form.feed.label.title": "Judul",
|
||||
"form.feed.label.site_url": "URL Situs",
|
||||
"form.feed.label.feed_url": "URL Umpan",
|
||||
"form.feed.label.category": "Kategori",
|
||||
"form.feed.label.crawler": "Ambil konten asli",
|
||||
"form.feed.label.feed_username": "Nama Pengguna Umpan",
|
||||
"form.feed.label.feed_password": "Kata Sandi Umpan",
|
||||
"form.feed.label.user_agent": "Timpa User Agent Baku",
|
||||
"form.feed.label.cookie": "Atur Kuki",
|
||||
"form.feed.label.scraper_rules": "Aturan Pengambil Data",
|
||||
"form.feed.label.rewrite_rules": "Aturan Tulis Ulang",
|
||||
"form.feed.label.blocklist_rules": "Aturan Blokir",
|
||||
"form.feed.label.keeplist_rules": "Aturan Simpan",
|
||||
"form.feed.label.urlrewrite_rules": "Aturan Tulis Ulang URL",
|
||||
"form.feed.label.ignore_http_cache": "Abaikan Tembolok HTTP",
|
||||
"form.feed.label.allow_self_signed_certificates": "Perbolehkan sertifikat web tidak valid atau sertifikasi sendiri",
|
||||
"form.feed.label.fetch_via_proxy": "Ambil via Proksi",
|
||||
"form.feed.label.disabled": "Jangan perbarui umpan ini",
|
||||
"form.feed.label.hide_globally": "Sembunyikan entri di daftar belum dibaca global",
|
||||
"form.category.label.title": "Judul",
|
||||
"form.category.hide_globally": "Sembunyikan entri di daftar belum dibaca global",
|
||||
"form.user.label.username": "Nama Pengguna",
|
||||
"form.user.label.password": "Kata Sandi",
|
||||
"form.user.label.confirmation": "Konfirmasi Kata Sandi",
|
||||
"form.user.label.admin": "Administrator",
|
||||
"form.prefs.label.language": "Bahasa",
|
||||
"form.prefs.label.timezone": "Zona Waktu",
|
||||
"form.prefs.label.theme": "Tema",
|
||||
"form.prefs.label.entry_sorting": "Pengurutan Entri",
|
||||
"form.prefs.label.entries_per_page": "Entri per Halaman",
|
||||
"form.prefs.label.default_reading_speed": "Kecepatan membaca untuk bahasa lain (kata per menit)",
|
||||
"form.prefs.label.cjk_reading_speed": "Kecepatan membaca untuk bahasa Tiongkok, Korea, dan Jepang (karakter per menit)",
|
||||
"form.prefs.label.display_mode": "Mode Tampilan Aplikasi Web (perlu pemasangan ulang)",
|
||||
"form.prefs.select.older_first": "Entri tertua dulu",
|
||||
"form.prefs.select.recent_first": "Entri terbaru dulu",
|
||||
"form.prefs.select.fullscreen": "Layar Penuh",
|
||||
"form.prefs.select.standalone": "Tersendiri",
|
||||
"form.prefs.select.minimal_ui": "Minimal",
|
||||
"form.prefs.select.browser": "Peramban",
|
||||
"form.prefs.select.publish_time": "Waktu entri dipublikasikan",
|
||||
"form.prefs.select.created_time": "Waktu entri dibuat",
|
||||
"form.prefs.select.alphabetical": "Secara alfabet",
|
||||
"form.prefs.select.unread_count": "Jumlah yang belum dibaca",
|
||||
"form.prefs.select.none": "Tidak ada",
|
||||
"form.prefs.select.tap": "Ketuk dua kali",
|
||||
"form.prefs.select.swipe": "Geser",
|
||||
"form.prefs.label.keyboard_shortcuts": "Aktifkan pintasan papan tik",
|
||||
"form.prefs.label.entry_swipe": "Aktifkan tindakan geser pada entri di ponsel",
|
||||
"form.prefs.label.gesture_nav": "Isyarat untuk menavigasi antar entri",
|
||||
"form.prefs.label.show_reading_time": "Tampilkan perkiraan waktu baca untuk artikel",
|
||||
"form.prefs.label.custom_css": "Modifikasi CSS",
|
||||
"form.prefs.label.entry_order": "Pengurutan Kolom Entri",
|
||||
"form.prefs.label.default_home_page": "Beranda Baku",
|
||||
"form.prefs.label.categories_sorting_order": "Pengurutan Kategori",
|
||||
"form.import.label.file": "Berkas OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Aktifkan API Fever",
|
||||
"form.integration.fever_username": "Nama Pengguna Fever",
|
||||
"form.integration.fever_password": "Kata Sandi Fever",
|
||||
"form.integration.fever_endpoint": "Titik URL API Fever:",
|
||||
"form.integration.googlereader_activate": "Aktifkan API Google Reader",
|
||||
"form.integration.googlereader_username": "Nama Pengguna Google Reader",
|
||||
"form.integration.googlereader_password": "Kata Sandi Google Reader",
|
||||
"form.integration.googlereader_endpoint": "Titik URL API Google Reader:",
|
||||
"form.integration.pinboard_activate": "Simpan artikel ke Pinboard",
|
||||
"form.integration.pinboard_token": "Token API Pinboard",
|
||||
"form.integration.pinboard_tags": "Tanda di Pinboard",
|
||||
"form.integration.pinboard_bookmark": "Tandai markah sebagai belum dibaca",
|
||||
"form.integration.instapaper_activate": "Simpan artikel ke Instapaper",
|
||||
"form.integration.instapaper_username": "Nama Pengguna Instapaper",
|
||||
"form.integration.instapaper_password": "Kata Sandi Instapaper",
|
||||
"form.integration.pocket_activate": "Simpan artikel ke Pocket",
|
||||
"form.integration.pocket_consumer_key": "Kunci Pelanggan Pocket",
|
||||
"form.integration.pocket_access_token": "Token Akses Pocket",
|
||||
"form.integration.pocket_connect_link": "Hubungkan akun Pocket Anda",
|
||||
"form.integration.wallabag_activate": "Simpan artikel ke Wallabag",
|
||||
"form.integration.wallabag_only_url": "Kirim hanya URL (alih-alih konten penuh)",
|
||||
"form.integration.wallabag_endpoint": "Titik URL API Wallabag",
|
||||
"form.integration.wallabag_client_id": "ID Klien Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Rahasia Klien Wallabag",
|
||||
"form.integration.wallabag_username": "Nama Pengguna Wallabag",
|
||||
"form.integration.wallabag_password": "Kata Sandi Wallabag",
|
||||
"form.integration.nunux_keeper_activate": "Simpan artikel ke Nunux Keeper",
|
||||
"form.integration.nunux_keeper_endpoint": "Titik URL API Nunux Keeper",
|
||||
"form.integration.nunux_keeper_api_key": "Kunci API Nunux Keeper",
|
||||
"form.integration.espial_activate": "Simpan artikel ke Espial",
|
||||
"form.integration.espial_endpoint": "Titik URL API Espial",
|
||||
"form.integration.espial_api_key": "Kunci API Espial",
|
||||
"form.integration.espial_tags": "Tanda di Espial",
|
||||
"form.integration.telegram_bot_activate": "Kirim artikel baru ke percakapan Telegram",
|
||||
"form.integration.telegram_bot_token": "Token Bot",
|
||||
"form.integration.telegram_chat_id": "ID Obrolan",
|
||||
"form.integration.linkding_activate": "Simpan artikel ke Linkding",
|
||||
"form.integration.linkding_endpoint": "Titik URL API Linkding",
|
||||
"form.integration.linkding_api_key": "Kunci API Linkding",
|
||||
"form.integration.matrix_bot_activate": "Kirim entri baru ke Matrix",
|
||||
"form.integration.matrix_bot_user": "Nama Pengguna Matrix",
|
||||
"form.integration.matrix_bot_password": "Kata Sandi Matrix",
|
||||
"form.integration.matrix_bot_url": "URL Peladen Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "ID Ruang Matrix",
|
||||
"form.api_key.label.description": "Label Kunci API",
|
||||
"form.submit.loading": "Memuat...",
|
||||
"form.submit.saving": "Menyimpan...",
|
||||
"time_elapsed.not_yet": "belum",
|
||||
"time_elapsed.yesterday": "kemarin",
|
||||
"time_elapsed.now": "baru saja",
|
||||
"time_elapsed.minutes": [
|
||||
"%d menit yang lalu"
|
||||
],
|
||||
"time_elapsed.hours": [
|
||||
"%d jam yang lalu"
|
||||
],
|
||||
"time_elapsed.days": [
|
||||
"%d hari yang lalu"
|
||||
],
|
||||
"time_elapsed.weeks": [
|
||||
"%d pekan yang lalu"
|
||||
],
|
||||
"time_elapsed.months": [
|
||||
"%d bulan yang lalu"
|
||||
],
|
||||
"time_elapsed.years": [
|
||||
"%d tahun yang lalu"
|
||||
],
|
||||
"This feed already exists (%s)": "Umpan ini sudah ada (%s)",
|
||||
"Unable to fetch feed (Status Code = %d)": "Tidak bisa mengambil umpan (Kode Status = %d)",
|
||||
"Unable to open this link: %v": "Tidak bisa membuka tautan ini: %v",
|
||||
"Unable to analyze this page: %v": "Tidak bisa menganalisis halaman ini: %v",
|
||||
"Unable to execute request: %v": "Tidak bisa mengeksekusi permintaan: %v",
|
||||
"Unable to parse OPML file: %q": "Tidak bisa mengurai berkas OPML: %q",
|
||||
"Unable to parse RSS feed: %q": "Tidak bisa mengurai umpan RSS: %q",
|
||||
"Unable to parse Atom feed: %q": "Tidak bisa mengurai umpan Atom: %q",
|
||||
"Unable to parse JSON feed: %q": "Tidak bisa mengurai umpan JSON: %q",
|
||||
"Unable to parse RDF feed: %q": "Tidak bisa mengurai umpan RDF: %q",
|
||||
"Unable to normalize encoding: %q": "Tidak dapat menormalisasi enkode: %q",
|
||||
"This feed is empty": "Umpan ini kosong",
|
||||
"This web page is empty": "Halaman web ini kosong",
|
||||
"Invalid SSL certificate (original error: %q)": "Sertifikat SSL tidak valid (galat: %q)",
|
||||
"This website is unreachable (original error: %q)": "Situs ini tidak dapat tersambung (galat: %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Situs tidak dapat tersambung, permintaan galat setelah %d detik",
|
||||
"You are not authorized to access this resource (invalid username/password)": "Anda tidak memiliki izin yang cukup untuk mengakses umpan ini (nama pengguna/kata sandi tidak valid)",
|
||||
"Unable to fetch this resource (Status Code = %d)": "Tidak bisa mengambil umpan ini (Kode Status = %d)",
|
||||
"Resource not found (404), this feed doesn't exist anymore, check the feed URL": "Umpan tidak ditemukan (404), umpan ini tidak ada lagi, periksa URL umpan"
|
||||
}
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Scorri l'articolo in alto",
|
||||
"page.keyboard_shortcuts.remove_feed": "Rimuovi questo feed",
|
||||
"page.keyboard_shortcuts.go_to_search": "Apri la casella di ricerca",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Chiudi la finestra di dialogo",
|
||||
"page.users.title": "Utenti",
|
||||
"page.users.username": "Nome utente",
|
||||
|
@ -263,6 +264,8 @@
|
|||
"error.invalid_timezone": "Fuso orario non valido.",
|
||||
"error.invalid_entry_direction": "Ordinamento non valido.",
|
||||
"error.invalid_display_mode": "Modalità di visualizzazione web app non valida.",
|
||||
"error.invalid_gesture_nav": "Navigazione gestuale non valida.",
|
||||
"error.invalid_default_home_page": "Pagina iniziale predefinita non valida!",
|
||||
"form.feed.label.title": "Titolo",
|
||||
"form.feed.label.site_url": "URL del sito",
|
||||
"form.feed.label.feed_url": "URL del feed",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Articoli per pagina",
|
||||
"form.prefs.label.default_reading_speed": "Velocità di lettura di altre lingue (parole al minuto)",
|
||||
"form.prefs.label.cjk_reading_speed": "Velocità di lettura per cinese, coreano e giapponese (caratteri al minuto)",
|
||||
"form.prefs.label.display_mode": "Modalità di visualizzazione web app (necessita la reinstallazione)",
|
||||
"form.prefs.label.display_mode": "Modalità di visualizzazione dell'app Web progressiva (PWA).",
|
||||
"form.prefs.select.older_first": "Prima i più vecchi",
|
||||
"form.prefs.select.recent_first": "Prima i più recenti",
|
||||
"form.prefs.select.fullscreen": "Schermo intero",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Browser",
|
||||
"form.prefs.select.publish_time": "Ora di pubblicazione dell'entrata",
|
||||
"form.prefs.select.created_time": "Tempo di creazione dell'entrata",
|
||||
"form.prefs.select.alphabetical": "In ordine alfabetico",
|
||||
"form.prefs.select.unread_count": "Conteggio dei non letti",
|
||||
"form.prefs.select.none": "Nessuno",
|
||||
"form.prefs.select.tap": "Tocca due volte",
|
||||
"form.prefs.select.swipe": "Scorri",
|
||||
"form.prefs.label.keyboard_shortcuts": "Abilita le scorciatoie da tastiera",
|
||||
"form.prefs.label.entry_swipe": "Abilita il gesto di scorrimento sulle voci sul cellulare",
|
||||
"form.prefs.label.entry_swipe": "Abilita lo scorrimento della voce sui touch screen",
|
||||
"form.prefs.label.gesture_nav": "Gesto per navigare tra le voci",
|
||||
"form.prefs.label.show_reading_time": "Mostra il tempo di lettura stimato per gli articoli",
|
||||
"form.prefs.label.custom_css": "CSS personalizzati",
|
||||
"form.prefs.label.entry_order": "Colonna di ordinamento delle voci",
|
||||
"form.prefs.label.default_home_page": "Pagina iniziale predefinita",
|
||||
"form.prefs.label.categories_sorting_order": "Ordinamento delle categorie",
|
||||
"form.import.label.file": "File OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Abilita l'API di Fever",
|
||||
|
@ -332,6 +343,7 @@
|
|||
"form.integration.pocket_connect_link": "Collega il tuo account Pocket",
|
||||
"form.integration.wallabag_activate": "Salva gli articoli su Wallabag",
|
||||
"form.integration.wallabag_endpoint": "Endpoint dell'API di Wallabag",
|
||||
"form.integration.wallabag_only_url": "Invia solo URL (invece del contenuto completo)",
|
||||
"form.integration.wallabag_client_id": "Client ID dell'account Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Client secret dell'account Wallabag",
|
||||
"form.integration.wallabag_username": "Nome utente dell'account Wallabag",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Salva gli articoli su Linkding",
|
||||
"form.integration.linkding_endpoint": "Endpoint dell'API di Linkding",
|
||||
"form.integration.linkding_api_key": "API key dell'account Linkding",
|
||||
"form.integration.matrix_bot_activate": "Trasferimento di nuovi articoli a Matrix",
|
||||
"form.integration.matrix_bot_user": "Nome utente per Matrix",
|
||||
"form.integration.matrix_bot_password": "Password per l'utente Matrix",
|
||||
"form.integration.matrix_bot_url": "URL del server Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "ID della stanza Matrix",
|
||||
"form.api_key.label.description": "Etichetta chiave API",
|
||||
"form.submit.loading": "Caricamento in corso...",
|
||||
"form.submit.saving": "Salvataggio in corso...",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"action.import": "インポート",
|
||||
"action.login": "ログイン",
|
||||
"action.home_screen": "ホームスクリーンに追加",
|
||||
"tooltip.keyboard_shortcuts": "キーボード・ショートカット: %s",
|
||||
"tooltip.keyboard_shortcuts": "キーボードショートカット: %s",
|
||||
"tooltip.logged_user": "%s としてログイン中",
|
||||
"menu.unread": "未読",
|
||||
"menu.starred": "星付き",
|
||||
|
@ -25,41 +25,41 @@
|
|||
"menu.settings": "設定",
|
||||
"menu.logout": "ログアウト",
|
||||
"menu.preferences": "設定情報",
|
||||
"menu.integrations": "関連付け",
|
||||
"menu.integrations": "連携",
|
||||
"menu.sessions": "セッション",
|
||||
"menu.users": "ユーザー一覧",
|
||||
"menu.about": "ソフトウエア情報",
|
||||
"menu.about": "ソフトウェア情報",
|
||||
"menu.export": "エクスポート",
|
||||
"menu.import": "インポート",
|
||||
"menu.create_category": "カテゴリを作成",
|
||||
"menu.mark_page_as_read": "このページを既読にする",
|
||||
"menu.mark_all_as_read": "全て既読にする",
|
||||
"menu.show_all_entries": "全ての記事を表示",
|
||||
"menu.mark_all_as_read": "すべて既読にする",
|
||||
"menu.show_all_entries": "すべての記事を表示",
|
||||
"menu.show_only_unread_entries": "未読の記事だけを表示",
|
||||
"menu.refresh_feed": "更新",
|
||||
"menu.refresh_all_feeds": "全てのフィードをバックグラウンドで更新",
|
||||
"menu.refresh_all_feeds": "すべてのフィードをバックグラウンドで更新",
|
||||
"menu.edit_feed": "編集",
|
||||
"menu.edit_category": "編集",
|
||||
"menu.add_feed": "フィードを購読する",
|
||||
"menu.add_feed": "フィードを購読",
|
||||
"menu.add_user": "ユーザーを追加",
|
||||
"menu.flush_history": "履歴を更新",
|
||||
"menu.flush_history": "履歴をクリア",
|
||||
"menu.feed_entries": "記事一覧",
|
||||
"menu.api_keys": "APIキー",
|
||||
"menu.create_api_key": "新しいAPIキーを作成する",
|
||||
"menu.api_keys": "API キー",
|
||||
"menu.create_api_key": "新しい API キーを作成する",
|
||||
"menu.shared_entries": "共有エントリ",
|
||||
"search.label": "検索",
|
||||
"search.placeholder": "…を検索",
|
||||
"pagination.next": "次",
|
||||
"pagination.previous": "前",
|
||||
"entry.status.unread": "未読",
|
||||
"entry.status.read": "既読",
|
||||
"entry.status.toast.unread": "未読にする",
|
||||
"entry.status.toast.read": "既読にする",
|
||||
"entry.status.unread": "未読にする",
|
||||
"entry.status.read": "既読にする",
|
||||
"entry.status.toast.unread": "未読にしました",
|
||||
"entry.status.toast.read": "既読にしました",
|
||||
"entry.status.title": "記事の状態を変更",
|
||||
"entry.bookmark.toggle.on": "星を付ける",
|
||||
"entry.bookmark.toggle.off": "星を外す",
|
||||
"entry.bookmark.toast.on": "星付き",
|
||||
"entry.bookmark.toast.off": "星無し",
|
||||
"entry.bookmark.toast.on": "星を付けました",
|
||||
"entry.bookmark.toast.off": "星を外しました",
|
||||
"entry.state.saving": "保存中…",
|
||||
"entry.state.loading": "読み込み中…",
|
||||
"entry.save.label": "保存",
|
||||
|
@ -74,28 +74,28 @@
|
|||
"entry.comments.title": "コメントを見る",
|
||||
"entry.share.label": "共有",
|
||||
"entry.share.title": "この記事を共有する",
|
||||
"entry.unshare.label": "共有解除",
|
||||
"entry.unshare.label": "共有を解除",
|
||||
"entry.shared_entry.title": "公開リンクを開く",
|
||||
"entry.shared_entry.label": "共有する",
|
||||
"entry.estimated_reading_time": [
|
||||
"%d分で読む",
|
||||
"%d分で読む"
|
||||
"%d 分で読めます",
|
||||
"%d 分で読めます"
|
||||
],
|
||||
"page.shared_entries.title": "共有エントリ",
|
||||
"page.unread.title": "未読",
|
||||
"page.starred.title": "星付き",
|
||||
"page.categories.title": "カテゴリ",
|
||||
"page.categories.no_feed": "フィード無し",
|
||||
"page.categories.entries": "記事",
|
||||
"page.categories.feeds": "フィード購読を見る",
|
||||
"page.categories.no_feed": "フィードはありません。",
|
||||
"page.categories.entries": "記事一覧",
|
||||
"page.categories.feeds": "フィード一覧",
|
||||
"page.categories.feed_count": [
|
||||
"%d 個の記事があります。",
|
||||
"%d 個の記事があります。"
|
||||
"%d 件のフィードがあります。",
|
||||
"%d 件のフィードがあります。"
|
||||
],
|
||||
"page.categories.unread_counter": "未読記事の数",
|
||||
"page.new_category.title": "新規カテゴリ",
|
||||
"page.new_user.title": "新規ユーザー",
|
||||
"page.edit_category.title": "カテゴリーを編集: %s",
|
||||
"page.edit_category.title": "カテゴリを編集: %s",
|
||||
"page.edit_user.title": "ユーザーを編集: %s",
|
||||
"page.feeds.title": "フィード一覧",
|
||||
"page.feeds.last_check": "最終チェック:",
|
||||
|
@ -108,7 +108,7 @@
|
|||
"page.history.title": "履歴",
|
||||
"page.import.title": "インポート",
|
||||
"page.search.title": "検索結果",
|
||||
"page.about.title": "ソフトウエア情報",
|
||||
"page.about.title": "ソフトウェア情報",
|
||||
"page.about.credits": "著作権表示",
|
||||
"page.about.version": "バージョン:",
|
||||
"page.about.build_date": "ビルド日時:",
|
||||
|
@ -117,51 +117,52 @@
|
|||
"page.about.global_config_options": "グローバル構成オプション",
|
||||
"page.about.postgres_version": "Postgres バージョン:",
|
||||
"page.about.go_version": "Go バージョン:",
|
||||
"page.add_feed.title": "新規購読",
|
||||
"page.add_feed.no_category": "カテゴリが存在しません。 少なくとも1つのカテゴリが必要です。",
|
||||
"page.add_feed.title": "新規フィード",
|
||||
"page.add_feed.no_category": "カテゴリが存在しません。カテゴリが少なくとも1つ必要です。",
|
||||
"page.add_feed.label.url": "URL",
|
||||
"page.add_feed.submit": "購読フィードを探して追加",
|
||||
"page.add_feed.legend.advanced_options": "追加の設定",
|
||||
"page.add_feed.choose_feed": "購読を選択",
|
||||
"page.edit_feed.title": "フィード(%s)を編集",
|
||||
"page.add_feed.submit": "フィードを探索して追加",
|
||||
"page.add_feed.legend.advanced_options": "高度な設定",
|
||||
"page.add_feed.choose_feed": "フィードを選択",
|
||||
"page.edit_feed.title": "フィードを編集: %s",
|
||||
"page.edit_feed.last_check": "最終チェック:",
|
||||
"page.edit_feed.last_modified_header": "最後に更新されたヘッダー:",
|
||||
"page.edit_feed.last_modified_header": "Last-Modified ヘッダー:",
|
||||
"page.edit_feed.etag_header": "ETag ヘッダー:",
|
||||
"page.edit_feed.no_header": " なし",
|
||||
"page.edit_feed.last_parsing_error": "最新の解析エラー",
|
||||
"page.entry.attachments": "添付物",
|
||||
"page.keyboard_shortcuts.title": "キーボード・ショートカット",
|
||||
"page.keyboard_shortcuts.subtitle.sections": "セクション 移動",
|
||||
"page.keyboard_shortcuts.subtitle.items": "アイテム 移動",
|
||||
"page.keyboard_shortcuts.subtitle.pages": "ページ 移動",
|
||||
"page.edit_feed.no_header": "なし",
|
||||
"page.edit_feed.last_parsing_error": "直近の解析エラー",
|
||||
"page.entry.attachments": "添付ファイル",
|
||||
"page.keyboard_shortcuts.title": "キーボードショートカット",
|
||||
"page.keyboard_shortcuts.subtitle.sections": "セクションを移動する",
|
||||
"page.keyboard_shortcuts.subtitle.items": "アイテム間を移動する",
|
||||
"page.keyboard_shortcuts.subtitle.pages": "ページ間を移動する",
|
||||
"page.keyboard_shortcuts.subtitle.actions": "アクション",
|
||||
"page.keyboard_shortcuts.go_to_unread": "未読へ移動",
|
||||
"page.keyboard_shortcuts.go_to_starred": "ブックマークへ移動",
|
||||
"page.keyboard_shortcuts.go_to_history": "履歴へ移動",
|
||||
"page.keyboard_shortcuts.go_to_feeds": "購読へ移動",
|
||||
"page.keyboard_shortcuts.go_to_categories": "カテゴリへ移動",
|
||||
"page.keyboard_shortcuts.go_to_settings": "設定に移動",
|
||||
"page.keyboard_shortcuts.show_keyboard_shortcuts": "キーボード・ショートカット表示",
|
||||
"page.keyboard_shortcuts.go_to_unread": "未読",
|
||||
"page.keyboard_shortcuts.go_to_starred": "星付き",
|
||||
"page.keyboard_shortcuts.go_to_history": "履歴",
|
||||
"page.keyboard_shortcuts.go_to_feeds": "フィード一覧",
|
||||
"page.keyboard_shortcuts.go_to_categories": "カテゴリ",
|
||||
"page.keyboard_shortcuts.go_to_settings": "設定",
|
||||
"page.keyboard_shortcuts.show_keyboard_shortcuts": "キーボードショートカットを表示",
|
||||
"page.keyboard_shortcuts.go_to_previous_item": "前のアイテム",
|
||||
"page.keyboard_shortcuts.go_to_next_item": "次のアイテム",
|
||||
"page.keyboard_shortcuts.go_to_feed": "フィードへ移動",
|
||||
"page.keyboard_shortcuts.go_to_previous_page": "前のページに移動",
|
||||
"page.keyboard_shortcuts.go_to_next_page": "次のページに移動",
|
||||
"page.keyboard_shortcuts.go_to_feed": "フィード",
|
||||
"page.keyboard_shortcuts.go_to_previous_page": "前のページ",
|
||||
"page.keyboard_shortcuts.go_to_next_page": "次のページ",
|
||||
"page.keyboard_shortcuts.open_item": "選択されたアイテムを開く",
|
||||
"page.keyboard_shortcuts.open_original": "オリジナルのリンクを開く",
|
||||
"page.keyboard_shortcuts.open_original_same_window": "現在のタブでオリジナルのリンクを開く",
|
||||
"page.keyboard_shortcuts.open_comments": "コメントリンクを開く",
|
||||
"page.keyboard_shortcuts.open_comments_same_window": "現在のタブでコメントリンクを開く",
|
||||
"page.keyboard_shortcuts.toggle_read_status_next": "既読/未読 切り替え, 次に焦点を合わせる",
|
||||
"page.keyboard_shortcuts.toggle_read_status_prev": "既読/未読 切り替え, 前にフォーカス",
|
||||
"page.keyboard_shortcuts.refresh_all_feeds": "全てのフィードをバックグラウンドで更新",
|
||||
"page.keyboard_shortcuts.mark_page_as_read": "現在のページを既読にする",
|
||||
"page.keyboard_shortcuts.toggle_read_status_next": "既読/未読を切り替えて次のアイテムに移動",
|
||||
"page.keyboard_shortcuts.toggle_read_status_prev": "既読/未読を切り替えて前のアイテムに移動",
|
||||
"page.keyboard_shortcuts.refresh_all_feeds": "すべてのフィードをバックグラウンドで更新",
|
||||
"page.keyboard_shortcuts.mark_page_as_read": "現在のページの記事をすべて既読にする",
|
||||
"page.keyboard_shortcuts.download_content": "オリジナルの内容をダウンロード",
|
||||
"page.keyboard_shortcuts.toggle_bookmark_status": "星を付ける/外す",
|
||||
"page.keyboard_shortcuts.save_article": "記事を保存",
|
||||
"page.keyboard_shortcuts.scroll_item_to_top": "アイテムを上にスクロール",
|
||||
"page.keyboard_shortcuts.scroll_item_to_top": "アイテムが上端になるようにスクロール",
|
||||
"page.keyboard_shortcuts.remove_feed": "このフィードを削除",
|
||||
"page.keyboard_shortcuts.go_to_search": "検索フォームにフォーカスを移す",
|
||||
"page.keyboard_shortcuts.go_to_search": "検索フォームに移動",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "添付ファイルを開く/閉じる",
|
||||
"page.keyboard_shortcuts.close_modal": "モーダルダイアログを閉じる",
|
||||
"page.users.title": "ユーザー一覧",
|
||||
"page.users.username": "ユーザー名",
|
||||
|
@ -179,7 +180,7 @@
|
|||
"page.login.title": "ログイン",
|
||||
"page.login.google_signin": "Google アカウントでログイン",
|
||||
"page.login.oidc_signin": "OpenID Connect アカウントでログイン",
|
||||
"page.integrations.title": "関連付け",
|
||||
"page.integrations.title": "連携",
|
||||
"page.integration.miniflux_api": "Miniflux API",
|
||||
"page.integration.miniflux_api_endpoint": "API Endpoint",
|
||||
"page.integration.miniflux_api_username": "ユーザー名",
|
||||
|
@ -195,14 +196,14 @@
|
|||
"page.sessions.table.user_agent": "User Agent",
|
||||
"page.sessions.table.actions": "アクション",
|
||||
"page.sessions.table.current_session": "現在のセッション",
|
||||
"page.api_keys.title": "APIキー",
|
||||
"page.api_keys.title": "API キー",
|
||||
"page.api_keys.table.description": "説明",
|
||||
"page.api_keys.table.token": "トークン",
|
||||
"page.api_keys.table.last_used_at": "最終使用",
|
||||
"page.api_keys.table.created_at": "作成日",
|
||||
"page.api_keys.table.actions": "アクション",
|
||||
"page.api_keys.never_used": "使われたことがない",
|
||||
"page.new_api_key.title": "新しいAPIキー",
|
||||
"page.api_keys.never_used": "未使用",
|
||||
"page.new_api_key.title": "新しい API キー",
|
||||
"page.offline.title": "オフラインモード",
|
||||
"page.offline.message": "オフラインです",
|
||||
"page.offline.refresh_page": "ページを更新してみてください",
|
||||
|
@ -212,8 +213,8 @@
|
|||
"alert.no_category_entry": "このカテゴリには記事がありません。",
|
||||
"alert.no_feed_entry": "このフィードには記事がありません。",
|
||||
"alert.no_feed": "何も購読していません。",
|
||||
"alert.no_feed_in_category": "このカテゴリにはフィードの購読がありません。",
|
||||
"alert.no_history": "現時点では履歴がありません。",
|
||||
"alert.no_feed_in_category": "このカテゴリには購読中のフィードがありません。",
|
||||
"alert.no_history": "現在履歴はありません。",
|
||||
"alert.feed_error": "このフィードには問題があります。",
|
||||
"alert.no_search_result": "検索で何も見つかりませんでした。",
|
||||
"alert.no_unread_entry": "未読の記事はありません。",
|
||||
|
@ -228,41 +229,43 @@
|
|||
"error.duplicate_googlereader_username": "既に同じ名前の Google Reader ユーザー名が使われています!",
|
||||
"error.pocket_request_token": "Pocket の request token が取得できません!",
|
||||
"error.pocket_access_token": "Pocket の access token が取得できません!",
|
||||
"error.category_already_exists": "このカテゴリは既に存在しています。",
|
||||
"error.unable_to_create_category": "カテゴリを作成できません。",
|
||||
"error.unable_to_update_category": "カテゴリを更新できません。",
|
||||
"error.category_already_exists": "このカテゴリは既に存在します。",
|
||||
"error.unable_to_create_category": "このカテゴリは作成できません。",
|
||||
"error.unable_to_update_category": "このカテゴリは更新できません。",
|
||||
"error.user_already_exists": "このユーザーは既に存在します。",
|
||||
"error.unable_to_create_user": "このユーザーを作ることはできません。",
|
||||
"error.unable_to_update_user": "このユーザーを更新することはできません。",
|
||||
"error.unable_to_update_feed": "このフィードを更新することはできません。",
|
||||
"error.subscription_not_found": "購読フィードが見つかりません。",
|
||||
"error.unable_to_create_user": "このユーザーは作成できません。",
|
||||
"error.unable_to_update_user": "このユーザーは更新できません。",
|
||||
"error.unable_to_update_feed": "このフィードは更新できません。",
|
||||
"error.subscription_not_found": "フィードが見つかりません。",
|
||||
"error.invalid_theme": "テーマが無効です。",
|
||||
"error.invalid_language": "言語が無効です。",
|
||||
"error.invalid_timezone": "タイムゾーンが無効です。",
|
||||
"error.invalid_entry_direction": "記事の表示順が無効です。",
|
||||
"error.invalid_display_mode": "Web アプリの表示モードが無効です。",
|
||||
"error.invalid_gesture_nav": "ジェスチャー ナビゲーションが無効です。",
|
||||
"error.invalid_default_home_page": "デフォルトのトップページが無効です",
|
||||
"error.empty_file": "このファイルは空です。",
|
||||
"error.bad_credentials": "ユーザー名かパスワードが間違っています。",
|
||||
"error.fields_mandatory": "全ての項目が必要です。",
|
||||
"error.fields_mandatory": "すべての項目が必要です。",
|
||||
"error.title_required": "タイトルが必要です。",
|
||||
"error.different_passwords": "パスワードが一致しません。",
|
||||
"error.password_min_length": "パスワードは6文字以上である必要があります。",
|
||||
"error.settings_mandatory_fields": "ユーザー名、テーマ、言語、タイムゾーンの全てが必要です。",
|
||||
"error.settings_reading_speed_is_positive": "読み取り速度は正の整数でなければならない。",
|
||||
"error.entries_per_page_invalid": "ページあたりのエントリ数が無効です。",
|
||||
"error.settings_mandatory_fields": "ユーザー名、テーマ、言語、タイムゾーンのすべてが必要です。",
|
||||
"error.settings_reading_speed_is_positive": "読書速度は正の整数である必要があります。",
|
||||
"error.entries_per_page_invalid": "ページあたりの記事数が無効です。",
|
||||
"error.feed_mandatory_fields": "URL と カテゴリが必要です。",
|
||||
"error.feed_already_exists": "このフィードはすでに存在します。",
|
||||
"error.invalid_feed_url": "無効なフィードURL。",
|
||||
"error.invalid_site_url": "無効なサイトURL。",
|
||||
"error.feed_url_not_empty": "フィードURLを空にすることはできません。",
|
||||
"error.site_url_not_empty": "サイトのURLを空にすることはできません。",
|
||||
"error.feed_already_exists": "このフィードは既に存在します。",
|
||||
"error.invalid_feed_url": "フィード URL が無効です。",
|
||||
"error.invalid_site_url": "サイト URL が無効です。",
|
||||
"error.feed_url_not_empty": "フィード URL を空にすることはできません。",
|
||||
"error.site_url_not_empty": "サイトの URL を空にすることはできません。",
|
||||
"error.feed_title_not_empty": "フィードのタイトルを空にすることはできません。",
|
||||
"error.feed_category_not_found": "このカテゴリは存在しないか、このユーザーに属していません。",
|
||||
"error.feed_invalid_blocklist_rule": "ブロックリストルールが無効です。",
|
||||
"error.feed_invalid_keeplist_rule": "リストの保持ルールが無効です。",
|
||||
"error.user_mandatory_fields": "ユーザー名が必要です。",
|
||||
"error.api_key_already_exists": "このAPIキーは既に存在します。",
|
||||
"error.unable_to_create_api_key": "このAPIキーを作成できません。",
|
||||
"error.invalid_theme": "テーマが無効です。",
|
||||
"error.invalid_language": "言語が無効です。",
|
||||
"error.invalid_timezone": "タイムゾーンが無効です。",
|
||||
"error.invalid_entry_direction": "ソート順が無効です。",
|
||||
"error.invalid_display_mode": "Webアプリの表示モードが無効です。",
|
||||
"error.api_key_already_exists": "この API キーは既に存在します。",
|
||||
"error.unable_to_create_api_key": "この API キーを作成できません。",
|
||||
"form.feed.label.title": "タイトル",
|
||||
"form.feed.label.site_url": "サイト URL",
|
||||
"form.feed.label.feed_url": "フィード URL",
|
||||
|
@ -270,20 +273,20 @@
|
|||
"form.feed.label.crawler": "オリジナルの内容を取得",
|
||||
"form.feed.label.feed_username": "フィードのユーザー名",
|
||||
"form.feed.label.feed_password": "フィードのパスワード",
|
||||
"form.feed.label.user_agent": "ディフォルトの User Agent を上書きする",
|
||||
"form.feed.label.cookie": "クッキーの設定",
|
||||
"form.feed.label.scraper_rules": "スクラップルール",
|
||||
"form.feed.label.user_agent": "デフォルトの User Agent を上書きする",
|
||||
"form.feed.label.cookie": "Cookie の設定",
|
||||
"form.feed.label.scraper_rules": "Scraper ルール",
|
||||
"form.feed.label.rewrite_rules": "Rewrite ルール",
|
||||
"form.feed.label.blocklist_rules": "ブロックルール",
|
||||
"form.feed.label.keeplist_rules": "許可規則",
|
||||
"form.feed.label.urlrewrite_rules": "URL書き換えルール",
|
||||
"form.feed.label.blocklist_rules": "Block ルール",
|
||||
"form.feed.label.keeplist_rules": "Keep ルール",
|
||||
"form.feed.label.urlrewrite_rules": "Rewrite URL ルール",
|
||||
"form.feed.label.ignore_http_cache": "HTTPキャッシュを無視",
|
||||
"form.feed.label.allow_self_signed_certificates": "自己署名証明書または無効な証明書を許可する",
|
||||
"form.feed.label.fetch_via_proxy": "プロキシ経由でフェッチ",
|
||||
"form.feed.label.fetch_via_proxy": "プロキシ経由で取得",
|
||||
"form.feed.label.disabled": "このフィードを更新しない",
|
||||
"form.feed.label.hide_globally": "グローバル未読リストのエントリーを隠す",
|
||||
"form.feed.label.hide_globally": "未読一覧に記事を表示しない",
|
||||
"form.category.label.title": "タイトル",
|
||||
"form.category.hide_globally": "グローバル未読リストのエントリーを隠す",
|
||||
"form.category.hide_globally": "未読一覧に記事を表示しない",
|
||||
"form.user.label.username": "ユーザー名",
|
||||
"form.user.label.password": "パスワード",
|
||||
"form.user.label.confirmation": "パスワード確認",
|
||||
|
@ -291,51 +294,60 @@
|
|||
"form.prefs.label.language": "言語",
|
||||
"form.prefs.label.timezone": "タイムゾーン",
|
||||
"form.prefs.label.theme": "テーマ",
|
||||
"form.prefs.label.entry_sorting": "記事の並べ替え",
|
||||
"form.prefs.label.entries_per_page": "ページあたりのエントリ",
|
||||
"form.prefs.label.default_reading_speed": "他言語の読解速度(単語/分)",
|
||||
"form.prefs.label.cjk_reading_speed": "中国語、韓国語、日本語の読書速度(1分間あたりの文字数)",
|
||||
"form.prefs.label.display_mode": "Webアプリの表示モード (再インストールが必要)",
|
||||
"form.prefs.label.entry_sorting": "記事の表示順",
|
||||
"form.prefs.label.entries_per_page": "ページあたりの記事数",
|
||||
"form.prefs.label.default_reading_speed": "他言語の読書速度(単語/分)",
|
||||
"form.prefs.label.cjk_reading_speed": "中国語、韓国語、日本語の読書速度(文字数/分)",
|
||||
"form.prefs.label.display_mode": "プログレッシブ Web アプリ (PWA) 表示モード",
|
||||
"form.prefs.select.older_first": "古い記事を最初に",
|
||||
"form.prefs.select.recent_first": "新しい記事を最初に",
|
||||
"form.prefs.select.fullscreen": "全画面表示",
|
||||
"form.prefs.select.standalone": "スタンドアロン",
|
||||
"form.prefs.select.minimal_ui": "最小限",
|
||||
"form.prefs.select.browser": "ブラウザ",
|
||||
"form.prefs.select.publish_time": "エントリー公開時間",
|
||||
"form.prefs.select.created_time": "エントリー作成時間",
|
||||
"form.prefs.label.keyboard_shortcuts": "キーボード・ショートカットを有効にする",
|
||||
"form.prefs.label.entry_swipe": "モバイルのエントリでスワイプジェスチャーを有効にする",
|
||||
"form.prefs.select.fullscreen": "Fullscreen",
|
||||
"form.prefs.select.standalone": "Standalone",
|
||||
"form.prefs.select.minimal_ui": "Minimal",
|
||||
"form.prefs.select.browser": "Browser",
|
||||
"form.prefs.select.publish_time": "記事の公開時刻",
|
||||
"form.prefs.select.created_time": "記事の取得時刻",
|
||||
"form.prefs.select.alphabetical": "アルファベット順",
|
||||
"form.prefs.select.unread_count": "未読数",
|
||||
"form.prefs.select.none": "なし",
|
||||
"form.prefs.select.tap": "ダブルタップ",
|
||||
"form.prefs.select.swipe": "スワイプ",
|
||||
"form.prefs.label.keyboard_shortcuts": "キーボードショートカットを有効にする",
|
||||
"form.prefs.label.entry_swipe": "タッチスクリーンでスワイプ入力を有効にする",
|
||||
"form.prefs.label.gesture_nav": "エントリ間を移動するジェスチャー",
|
||||
"form.prefs.label.show_reading_time": "記事の推定読書時間を表示する",
|
||||
"form.prefs.label.custom_css": "カスタムCSS",
|
||||
"form.prefs.label.entry_order": "エントリーソートカラム",
|
||||
"form.prefs.label.custom_css": "カスタム CSS",
|
||||
"form.prefs.label.entry_order": "記事の表示順の基準",
|
||||
"form.prefs.label.default_home_page": "デフォルトのトップページ",
|
||||
"form.prefs.label.categories_sorting_order": "カテゴリの表示順",
|
||||
"form.import.label.file": "OPML ファイル",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Fever API を有効にする",
|
||||
"form.integration.fever_username": "Fever の ユーザー名",
|
||||
"form.integration.fever_password": "Fever の パスワード",
|
||||
"form.integration.fever_username": "Fever のユーザー名",
|
||||
"form.integration.fever_password": "Fever のパスワード",
|
||||
"form.integration.fever_endpoint": "Fever API endpoint:",
|
||||
"form.integration.googlereader_activate": "Google Reader API を有効にする",
|
||||
"form.integration.googlereader_username": "Google Reader の ユーザー名",
|
||||
"form.integration.googlereader_password": "Google Reader の パスワード",
|
||||
"form.integration.googlereader_username": "Google Reader のユーザー名",
|
||||
"form.integration.googlereader_password": "Google Reader のパスワード",
|
||||
"form.integration.googlereader_endpoint": "Google Reader API endpoint:",
|
||||
"form.integration.pinboard_activate": "Pinboard に記事を保存する",
|
||||
"form.integration.pinboard_token": "Pinboard の API Token",
|
||||
"form.integration.pinboard_tags": "Pinboard の Tag",
|
||||
"form.integration.pinboard_bookmark": "ブックマークを未読にする",
|
||||
"form.integration.instapaper_activate": "Instapaper に記事を保存する",
|
||||
"form.integration.instapaper_username": "Instapaper の ユーザー名",
|
||||
"form.integration.instapaper_password": "Instapaper の パスワード",
|
||||
"form.integration.instapaper_username": "Instapaper のユーザー名",
|
||||
"form.integration.instapaper_password": "Instapaper のパスワード",
|
||||
"form.integration.pocket_activate": "Pocket に記事を保存する",
|
||||
"form.integration.pocket_consumer_key": "Pocket の Consumer Key",
|
||||
"form.integration.pocket_access_token": "Pocket の Access Token",
|
||||
"form.integration.pocket_connect_link": "Pocket account に接続",
|
||||
"form.integration.wallabag_activate": "Wallabag に記事を保存する",
|
||||
"form.integration.wallabag_only_url": "URL のみを送信 (完全なコンテンツではなく)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag の API Endpoint",
|
||||
"form.integration.wallabag_client_id": "Wallabag の Client ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag の Client Secret",
|
||||
"form.integration.wallabag_username": "Wallabag の ユーザー名",
|
||||
"form.integration.wallabag_password": "Wallabag の パスワード",
|
||||
"form.integration.wallabag_username": "Wallabag のユーザー名",
|
||||
"form.integration.wallabag_password": "Wallabag のパスワード",
|
||||
"form.integration.nunux_keeper_activate": "Nunux Keeper に記事を保存する",
|
||||
"form.integration.nunux_keeper_endpoint": "Nunux Keeper の API Endpoint",
|
||||
"form.integration.nunux_keeper_api_key": "Nunux Keeper の API key",
|
||||
|
@ -343,13 +355,18 @@
|
|||
"form.integration.espial_endpoint": "Espial の API Endpoint",
|
||||
"form.integration.espial_api_key": "Espial の API key",
|
||||
"form.integration.espial_tags": "Espial の Tag",
|
||||
"form.integration.telegram_bot_activate": "新しい記事をTelegramチャットにプッシュする",
|
||||
"form.integration.telegram_bot_activate": "新しい記事を Telegram チャットにプッシュする",
|
||||
"form.integration.telegram_bot_token": "ボットトークン",
|
||||
"form.integration.telegram_chat_id": "チャットID",
|
||||
"form.integration.telegram_chat_id": "チャット ID",
|
||||
"form.integration.linkding_activate": "Linkding に記事を保存する",
|
||||
"form.integration.linkding_endpoint": "Linkding の API Endpoint",
|
||||
"form.integration.linkding_api_key": "Linkding の API key",
|
||||
"form.api_key.label.description": "APIキーラベル",
|
||||
"form.integration.matrix_bot_activate": "新しい記事をMatrixに転送する",
|
||||
"form.integration.matrix_bot_user": "Matrixのユーザー名",
|
||||
"form.integration.matrix_bot_password": "Matrixユーザ用パスワード",
|
||||
"form.integration.matrix_bot_url": "MatrixサーバーのURL",
|
||||
"form.integration.matrix_bot_chat_id": "MatrixルームのID",
|
||||
"form.api_key.label.description": "API キーラベル",
|
||||
"form.submit.loading": "読み込み中…",
|
||||
"form.submit.saving": "保存中…",
|
||||
"time_elapsed.not_yet": "未来",
|
||||
|
@ -368,12 +385,12 @@
|
|||
"%d 日前"
|
||||
],
|
||||
"time_elapsed.weeks": [
|
||||
"%d 週前",
|
||||
"%d 週前"
|
||||
"%d 週間前",
|
||||
"%d 週間前"
|
||||
],
|
||||
"time_elapsed.months": [
|
||||
"%d 月前",
|
||||
"%d 月前"
|
||||
"%d か月前",
|
||||
"%d か月前"
|
||||
],
|
||||
"time_elapsed.years": [
|
||||
"%d 年前",
|
||||
|
|
|
@ -78,8 +78,8 @@
|
|||
"entry.shared_entry.title": "Open de openbare link",
|
||||
"entry.shared_entry.label": "Delen",
|
||||
"entry.estimated_reading_time": [
|
||||
"%d minuut gelezen",
|
||||
"%d minuten gelezen"
|
||||
"%d minuut leestijd",
|
||||
"%d minuten leestijd"
|
||||
],
|
||||
"page.shared_entries.title": "Gedeelde vermeldingen",
|
||||
"page.unread.title": "Ongelezen",
|
||||
|
@ -163,6 +163,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Scroll artikel naar boven",
|
||||
"page.keyboard_shortcuts.remove_feed": "Verwijder deze feed",
|
||||
"page.keyboard_shortcuts.go_to_search": "Focus instellen op zoekformulier",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Sluit dialoogscherm",
|
||||
"page.users.title": "Gebruikers",
|
||||
"page.users.username": "Gebruikersnaam",
|
||||
|
@ -263,6 +264,8 @@
|
|||
"error.invalid_timezone": "Ongeldige tijdzone.",
|
||||
"error.invalid_entry_direction": "Ongeldige sorteervolgorde.",
|
||||
"error.invalid_display_mode": "Ongeldige weergavemodus voor webapp.",
|
||||
"error.invalid_gesture_nav": "Ongeldige gebarennavigatie.",
|
||||
"error.invalid_default_home_page": "Ongeldige standaard homepage!",
|
||||
"form.feed.label.title": "Naam",
|
||||
"form.feed.label.site_url": "Website URL",
|
||||
"form.feed.label.feed_url": "Feed URL",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Inzendingen per pagina",
|
||||
"form.prefs.label.default_reading_speed": "Leessnelheid voor andere talen (woorden per minuut)",
|
||||
"form.prefs.label.cjk_reading_speed": "Leessnelheid voor Chinees, Koreaans en Japans (tekens per minuut)",
|
||||
"form.prefs.label.display_mode": "Weergavemodus voor webapp (moet opnieuw worden geïnstalleerd)",
|
||||
"form.prefs.label.display_mode": "Weergavemodus Progressive Web App (PWA).",
|
||||
"form.prefs.select.older_first": "Oudere items eerst",
|
||||
"form.prefs.select.recent_first": "Recente items eerst",
|
||||
"form.prefs.select.fullscreen": "Volledig scherm",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Browser",
|
||||
"form.prefs.select.publish_time": "Tijd van binnenkomst",
|
||||
"form.prefs.select.created_time": "Tijdstip van binnenkomst",
|
||||
"form.prefs.select.alphabetical": "Alfabetisch",
|
||||
"form.prefs.select.unread_count": "Ongelezen tellen",
|
||||
"form.prefs.select.none": "Geen",
|
||||
"form.prefs.select.tap": "Dubbeltik",
|
||||
"form.prefs.select.swipe": "Vegen",
|
||||
"form.prefs.label.keyboard_shortcuts": "Schakel sneltoetsen in",
|
||||
"form.prefs.label.entry_swipe": "Schakel veegbewegingen in voor items op mobiel",
|
||||
"form.prefs.label.entry_swipe": "Invoervegen inschakelen op aanraakschermen",
|
||||
"form.prefs.label.gesture_nav": "Gebaar om tussen ingangen te navigeren",
|
||||
"form.prefs.label.show_reading_time": "Toon geschatte leestijd voor artikelen",
|
||||
"form.prefs.label.custom_css": "Aangepaste CSS",
|
||||
"form.prefs.label.entry_order": "Ingang Sorteerkolom",
|
||||
"form.prefs.label.default_home_page": "Standaard startpagina",
|
||||
"form.prefs.label.categories_sorting_order": "Categorieën sorteren",
|
||||
"form.import.label.file": "OPML-bestand",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Activeer Fever API",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Verbind je Pocket-account",
|
||||
"form.integration.wallabag_activate": "Opslaan naar Wallabag",
|
||||
"form.integration.wallabag_only_url": "Alleen URL verzenden (in plaats van volledige inhoud)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag URL",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client-ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client-Secret",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Opslaan naar Linkding",
|
||||
"form.integration.linkding_endpoint": "Linkding URL",
|
||||
"form.integration.linkding_api_key": "Linkding API-sleutel",
|
||||
"form.integration.matrix_bot_activate": "Nieuwe artikelen overbrengen naar Matrix",
|
||||
"form.integration.matrix_bot_user": "Gebruikersnaam voor Matrix",
|
||||
"form.integration.matrix_bot_password": "Wachtwoord voor Matrix-gebruiker",
|
||||
"form.integration.matrix_bot_url": "URL van de Matrix-server",
|
||||
"form.integration.matrix_bot_chat_id": "ID van Matrix-kamer",
|
||||
"form.api_key.label.description": "API-sleutellabel",
|
||||
"form.submit.loading": "Laden...",
|
||||
"form.submit.saving": "Opslaag...",
|
||||
|
@ -394,7 +411,6 @@
|
|||
"Category not found for this user": "Categorie niet gevonden voor deze gebruiker",
|
||||
"This web page is empty": "Deze webpagina is leeg",
|
||||
"Invalid SSL certificate (original error: %q)": "Ongeldig SSL-certificaat (originele error: %q)",
|
||||
"This website is temporarily unreachable (original error: %q)": "Deze website is tijdelijk onbereikbaar (originele error: %q)",
|
||||
"This website is permanently unreachable (original error: %q)": "Deze website is permanent onbereikbaar (originele error: %q)",
|
||||
"This website is unreachable (original error: %q)": "Deze website is onbereikbaar (originele error: %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Website onbereikbaar, de request gaf een timeout na %d seconden"
|
||||
}
|
||||
|
|
|
@ -164,6 +164,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Przewiń artykuł do góry",
|
||||
"page.keyboard_shortcuts.remove_feed": "Usuń ten kanał",
|
||||
"page.keyboard_shortcuts.go_to_search": "Ustaw fokus na formularzu wyszukiwania",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Zamknij listę skrótów klawiszowych",
|
||||
"page.users.title": "Użytkownicy",
|
||||
"page.users.username": "Nazwa użytkownika",
|
||||
|
@ -265,6 +266,8 @@
|
|||
"error.invalid_timezone": "Nieprawidłowa strefa czasowa.",
|
||||
"error.invalid_entry_direction": "Nieprawidłowa kolejność sortowania.",
|
||||
"error.invalid_display_mode": "Nieprawidłowy tryb wyświetlania aplikacji internetowej.",
|
||||
"error.invalid_gesture_nav": "Nieprawidłowa nawigacja gestami.",
|
||||
"error.invalid_default_home_page": "Nieprawidłowa domyślna strona główna!",
|
||||
"form.feed.label.title": "Tytuł",
|
||||
"form.feed.label.site_url": "URL strony",
|
||||
"form.feed.label.feed_url": "URL kanału",
|
||||
|
@ -282,7 +285,7 @@
|
|||
"form.feed.label.ignore_http_cache": "Zignoruj pamięć podręczną HTTP",
|
||||
"form.feed.label.allow_self_signed_certificates": "Zezwalaj na certyfikaty z podpisem własnym lub nieprawidłowe certyfikaty",
|
||||
"form.feed.label.fetch_via_proxy": "Pobierz przez proxy",
|
||||
"form.feed.label.disabled": "Не обновлять этот канал",
|
||||
"form.feed.label.disabled": "Nie odświeżaj tego kanału",
|
||||
"form.feed.label.hide_globally": "Ukryj wpisy na globalnej liście nieprzeczytanych",
|
||||
"form.category.label.title": "Tytuł",
|
||||
"form.category.hide_globally": "Ukryj wpisy na globalnej liście nieprzeczytanych",
|
||||
|
@ -295,12 +298,13 @@
|
|||
"form.prefs.label.theme": "Wygląd",
|
||||
"form.prefs.label.entry_sorting": "Sortowanie artykułów",
|
||||
"form.prefs.label.entries_per_page": "Wpisy na stronie",
|
||||
"form.prefs.label.default_reading_speed": "Prędkość czytania dla innych języków (słowa na minutę)",
|
||||
"form.prefs.label.default_reading_speed": "Tryb wyświetlania Progressive Web App (PWA).",
|
||||
"form.prefs.label.cjk_reading_speed": "Prędkość czytania dla języka chińskiego, koreańskiego i japońskiego (znaki na minutę)",
|
||||
"form.prefs.label.display_mode": "Tryb wyświetlania aplikacji internetowej (wymaga ponownej instalacji)",
|
||||
"form.prefs.select.older_first": "Najstarsze wpisy jako pierwsze",
|
||||
"form.prefs.label.keyboard_shortcuts": "Włącz skróty klawiaturowe",
|
||||
"form.prefs.label.entry_swipe": "Włącz gest przesuwania na wpisach na telefonie komórkowym",
|
||||
"form.prefs.label.entry_swipe": "Włącz machnięcie wpisu na ekranach dotykowych",
|
||||
"form.prefs.label.gesture_nav": "Gest, aby poruszać się między wpisami",
|
||||
"form.prefs.label.show_reading_time": "Pokaż szacowany czas czytania artykułów",
|
||||
"form.prefs.select.recent_first": "Najnowsze wpisy jako pierwsze",
|
||||
"form.prefs.select.fullscreen": "Pełny ekran",
|
||||
|
@ -309,8 +313,15 @@
|
|||
"form.prefs.select.browser": "Przeglądarka",
|
||||
"form.prefs.select.publish_time": "Czas publikacji wpisu",
|
||||
"form.prefs.select.created_time": "Czas utworzenia wpisu",
|
||||
"form.prefs.select.alphabetical": "Alfabetycznie",
|
||||
"form.prefs.select.unread_count": "Liczba nieprzeczytanych",
|
||||
"form.prefs.select.none": "Nic",
|
||||
"form.prefs.select.tap": "Podwójne wciśnięcie",
|
||||
"form.prefs.select.swipe": "Trzepnąć",
|
||||
"form.prefs.label.custom_css": "Niestandardowy CSS",
|
||||
"form.prefs.label.entry_order": "Kolumna sortowania wpisów",
|
||||
"form.prefs.label.default_home_page": "Domyślna strona główna",
|
||||
"form.prefs.label.categories_sorting_order": "Sortowanie kategorii",
|
||||
"form.import.label.file": "Plik OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Aktywuj Fever API",
|
||||
|
@ -333,6 +344,7 @@
|
|||
"form.integration.pocket_access_token": "Token dostępu kieszeń",
|
||||
"form.integration.pocket_connect_link": "Połącz swoje konto Pocket",
|
||||
"form.integration.wallabag_activate": "Zapisz artykuły do Wallabag",
|
||||
"form.integration.wallabag_only_url": "Wyślij tylko adres URL (zamiast pełnej treści)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag URL",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client-ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
|
||||
|
@ -351,6 +363,11 @@
|
|||
"form.integration.linkding_activate": "Zapisz artykuły do Linkding",
|
||||
"form.integration.linkding_endpoint": "Linkding URL",
|
||||
"form.integration.linkding_api_key": "Linkding API key",
|
||||
"form.integration.matrix_bot_activate": "Przenieś nowe artykuły do Matrix",
|
||||
"form.integration.matrix_bot_user": "Nazwa użytkownika dla Matrix",
|
||||
"form.integration.matrix_bot_password": "Hasło dla użytkownika Matrix",
|
||||
"form.integration.matrix_bot_url": "URL serwera Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "Identyfikator pokoju Matrix",
|
||||
"form.api_key.label.description": "Etykieta klucza API",
|
||||
"form.submit.loading": "Ładowanie...",
|
||||
"form.submit.saving": "Zapisywanie...",
|
||||
|
@ -402,7 +419,6 @@
|
|||
"This feed is empty": "Ten kanał jest pusty",
|
||||
"This web page is empty": "Ta strona jest pusta",
|
||||
"Invalid SSL certificate (original error: %q)": "Certyfikat SSL jest nieprawidłowy (błąd: %q)",
|
||||
"This website is temporarily unreachable (original error: %q)": "Ta strona jest tymczasowo niedostępna (błąd: %q)",
|
||||
"This website is permanently unreachable (original error: %q)": "Ta strona jest niedostępna (błąd: %q)",
|
||||
"This website is unreachable (original error: %q)": "Ta strona jest niedostępna (błąd: %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Strona internetowa nieosiągalna, żądanie wygasło po %d sekundach"
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Role o item para cima",
|
||||
"page.keyboard_shortcuts.remove_feed": "Remover essa fonte",
|
||||
"page.keyboard_shortcuts.go_to_search": "Ir para o campo de busca",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Fechar janela",
|
||||
"page.users.title": "Usuários",
|
||||
"page.users.username": "Nome de usuário",
|
||||
|
@ -263,6 +264,8 @@
|
|||
"error.invalid_timezone": "Fuso horário inválido.",
|
||||
"error.invalid_entry_direction": "Direção de entrada inválida.",
|
||||
"error.invalid_display_mode": "Modo de exibição de aplicativo inválido da web.",
|
||||
"error.invalid_gesture_nav": "Navegação por gestos inválida.",
|
||||
"error.invalid_default_home_page": "Página inicial por defeito inválida!",
|
||||
"form.feed.label.title": "Título",
|
||||
"form.feed.label.site_url": "URL do site",
|
||||
"form.feed.label.feed_url": "URL da fonte",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Itens por página",
|
||||
"form.prefs.label.default_reading_speed": "Velocidade de leitura para outros idiomas (palavras por minuto)",
|
||||
"form.prefs.label.cjk_reading_speed": "Velocidade de leitura para chinês, coreano e japonês (caracteres por minuto)",
|
||||
"form.prefs.label.display_mode": "Modo de exibição do aplicativo Web (precisa ser reinstalado)",
|
||||
"form.prefs.label.display_mode": "Modo de exibição Progressive Web App (PWA)",
|
||||
"form.prefs.select.older_first": "Itens mais velhos primeiro",
|
||||
"form.prefs.select.recent_first": "Itens mais recentes",
|
||||
"form.prefs.select.fullscreen": "Tela completa",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Navegador",
|
||||
"form.prefs.select.publish_time": "Entrada hora de publicação",
|
||||
"form.prefs.select.created_time": "Entrada tempo criado",
|
||||
"form.prefs.select.alphabetical": "Por ordem alfabética",
|
||||
"form.prefs.select.unread_count": "Contagem não lida",
|
||||
"form.prefs.select.none": "Nenhum",
|
||||
"form.prefs.select.tap": "Toque duplo",
|
||||
"form.prefs.select.swipe": "Deslize",
|
||||
"form.prefs.label.keyboard_shortcuts": "Habilitar atalhos do teclado",
|
||||
"form.prefs.label.entry_swipe": "Ativar gesto de deslizar nas entradas no celular",
|
||||
"form.prefs.label.entry_swipe": "Ativar entrada de furto em telas sensíveis ao toque",
|
||||
"form.prefs.label.gesture_nav": "Gesto para navegar entre as entradas",
|
||||
"form.prefs.label.show_reading_time": "Mostrar tempo estimado de leitura de artigos",
|
||||
"form.prefs.label.custom_css": "CSS customizado",
|
||||
"form.prefs.label.entry_order": "Coluna de Ordenação de Entrada",
|
||||
"form.prefs.label.default_home_page": "Página inicial predefinida",
|
||||
"form.prefs.label.categories_sorting_order": "Classificação das categorias",
|
||||
"form.import.label.file": "Arquivo OPML",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Ativar API do Fever",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Token de acesso do Pocket",
|
||||
"form.integration.pocket_connect_link": "Conectar a conta do Pocket",
|
||||
"form.integration.wallabag_activate": "Salvar itens no Wallabag",
|
||||
"form.integration.wallabag_only_url": "Enviar apenas URL (em vez de conteúdo completo)",
|
||||
"form.integration.wallabag_endpoint": "Endpoint da API do Wallabag",
|
||||
"form.integration.wallabag_client_id": "ID de cliente (Client ID) do Wallabag",
|
||||
"form.integration.wallabag_client_secret": "Segredo do cliente (Client Secret) do Wallabag",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Salvar itens no Linkding",
|
||||
"form.integration.linkding_endpoint": "Endpoint de API do Linkding",
|
||||
"form.integration.linkding_api_key": "Chave de API do Linkding",
|
||||
"form.integration.matrix_bot_activate": "Transferir novos artigos para o Matrix",
|
||||
"form.integration.matrix_bot_user": "Nome de utilizador para Matrix",
|
||||
"form.integration.matrix_bot_password": "Palavra-passe para utilizador da Matrix",
|
||||
"form.integration.matrix_bot_url": "URL do servidor Matrix",
|
||||
"form.integration.matrix_bot_chat_id": "Identificação da sala Matrix",
|
||||
"form.api_key.label.description": "Etiqueta da chave de API",
|
||||
"form.submit.loading": "Carregando...",
|
||||
"form.submit.saving": "Salvando...",
|
||||
|
|
|
@ -164,6 +164,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Прокрутите элемент вверх",
|
||||
"page.keyboard_shortcuts.remove_feed": "Удалить эту подписку",
|
||||
"page.keyboard_shortcuts.go_to_search": "Установить фокус в поисковой форме",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Закрыть модальный диалог",
|
||||
"page.users.title": "Пользователи",
|
||||
"page.users.username": "Имя пользователя",
|
||||
|
@ -265,6 +266,8 @@
|
|||
"error.invalid_timezone": "Неверный часовой пояс.",
|
||||
"error.invalid_entry_direction": "Неверное направление входа.",
|
||||
"error.invalid_display_mode": "Недопустимый режим отображения веб-приложения.",
|
||||
"error.invalid_gesture_nav": "Неверная жестовая навигация.",
|
||||
"error.invalid_default_home_page": "Неверная домашняя страница по умолчанию!",
|
||||
"form.feed.label.title": "Название",
|
||||
"form.feed.label.site_url": "URL сайта",
|
||||
"form.feed.label.feed_url": "URL подписки",
|
||||
|
@ -297,7 +300,7 @@
|
|||
"form.prefs.label.entries_per_page": "Записи на странице",
|
||||
"form.prefs.label.default_reading_speed": "Скорость чтения на других языках (слов в минуту)",
|
||||
"form.prefs.label.cjk_reading_speed": "Скорость чтения на китайском, корейском и японском языках (знаков в минуту)",
|
||||
"form.prefs.label.display_mode": "Режим отображения веб-приложения (требуется переустановка)",
|
||||
"form.prefs.label.display_mode": "Режим отображения Progressive Web App (PWA)",
|
||||
"form.prefs.select.older_first": "Сначала старые записи",
|
||||
"form.prefs.select.recent_first": "Сначала последние записи",
|
||||
"form.prefs.select.fullscreen": "Полноэкранный",
|
||||
|
@ -306,11 +309,19 @@
|
|||
"form.prefs.select.browser": "Браузер",
|
||||
"form.prefs.select.publish_time": "Время публикации заявки",
|
||||
"form.prefs.select.created_time": "Время создания записи",
|
||||
"form.prefs.select.alphabetical": "По алфавиту",
|
||||
"form.prefs.select.unread_count": "Количество непрочитанных",
|
||||
"form.prefs.select.none": "Никто",
|
||||
"form.prefs.select.tap": "Двойное нажатие",
|
||||
"form.prefs.select.swipe": "Проведите",
|
||||
"form.prefs.label.keyboard_shortcuts": "Включить сочетания клавиш",
|
||||
"form.prefs.label.entry_swipe": "Включить жест смахивания для записей на мобильном устройстве",
|
||||
"form.prefs.label.entry_swipe": "Включить пролистывание ввода на сенсорных экранах",
|
||||
"form.prefs.label.gesture_nav": "Жест для перехода между записями",
|
||||
"form.prefs.label.show_reading_time": "Показать примерное время чтения статей",
|
||||
"form.prefs.label.custom_css": "Пользовательские CSS",
|
||||
"form.prefs.label.entry_order": "Колонка сортировки ввода",
|
||||
"form.prefs.label.default_home_page": "Домашняя страница по умолчанию",
|
||||
"form.prefs.label.categories_sorting_order": "Сортировка категорий",
|
||||
"form.import.label.file": "OPML файл",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Активировать Fever API",
|
||||
|
@ -332,6 +343,7 @@
|
|||
"form.integration.pocket_consumer_key": "Pocket Consumer Key",
|
||||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Подключить аккаунт Pocket",
|
||||
"form.integration.wallabag_only_url": "Отправлять только URL (вместо всего содержимого)",
|
||||
"form.integration.wallabag_activate": "Сохранять статьи в Wallabag",
|
||||
"form.integration.wallabag_endpoint": "Конечная точка Wallabag API",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client ID",
|
||||
|
@ -351,6 +363,11 @@
|
|||
"form.integration.linkding_activate": "Сохранять статьи в Linkding",
|
||||
"form.integration.linkding_endpoint": "Конечная точка Linkding API",
|
||||
"form.integration.linkding_api_key": "Linkding API key",
|
||||
"form.integration.matrix_bot_activate": "Перенос новых статей в Матрицу",
|
||||
"form.integration.matrix_bot_user": "Имя пользователя для Matrix",
|
||||
"form.integration.matrix_bot_password": "Пароль для пользователя Matrix",
|
||||
"form.integration.matrix_bot_url": "URL сервера Матрицы",
|
||||
"form.integration.matrix_bot_chat_id": "ID комнаты Матрицы",
|
||||
"form.api_key.label.description": "Описание API-ключа",
|
||||
"form.submit.loading": "Загрузка…",
|
||||
"form.submit.saving": "Сохранение…",
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "Öğeyi en üste kaydır",
|
||||
"page.keyboard_shortcuts.remove_feed": "Bu beslemeyi kaldır",
|
||||
"page.keyboard_shortcuts.go_to_search": "Arama formuna odakla",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "İletişim kutusunu kapat",
|
||||
"page.users.title": "Kullanıcılar",
|
||||
"page.users.username": "Kullanıcı adı",
|
||||
|
@ -241,6 +242,8 @@
|
|||
"error.invalid_timezone": "Geçersiz saat dilimi",
|
||||
"error.invalid_entry_direction": "Geçersiz giriş yönü.",
|
||||
"error.invalid_display_mode": "Geçersiz web uygulaması görüntüleme modu.",
|
||||
"error.invalid_gesture_nav": "Hareketle gezinme geçersiz.",
|
||||
"error.invalid_default_home_page": "Geçersiz varsayılan ana sayfa!",
|
||||
"error.empty_file": "Bu dosya boş.",
|
||||
"error.bad_credentials": "Geçersiz kullanıcı veya parola.",
|
||||
"error.fields_mandatory": "Tüm alanlar zorunlu.",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "Sayfa başına ileti",
|
||||
"form.prefs.label.default_reading_speed": "Diğer diller için okuma hızı (dakika başına kelime)",
|
||||
"form.prefs.label.cjk_reading_speed": "Çince, Korece ve Japonca için okuma hızı (dakika başına karakter)",
|
||||
"form.prefs.label.display_mode": "Web uygulaması görüntüleme modu (yeniden kurulum gerektirir)",
|
||||
"form.prefs.label.display_mode": "Aşamalı Web Uygulaması (PWA) görüntüleme modu",
|
||||
"form.prefs.select.older_first": "Önce eski iletiler",
|
||||
"form.prefs.select.recent_first": "Önce yeni iletiler",
|
||||
"form.prefs.select.fullscreen": "Tam Ekran",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "Tarayıcı",
|
||||
"form.prefs.select.publish_time": "Giriş yayınlanma zamanı",
|
||||
"form.prefs.select.created_time": "Girişin oluşturulma zamanı",
|
||||
"form.prefs.select.alphabetical": "Alfabetik",
|
||||
"form.prefs.select.unread_count": "Okunmamış sayısı",
|
||||
"form.prefs.select.none": "Hiçbiri",
|
||||
"form.prefs.select.tap": "çift dokunma",
|
||||
"form.prefs.select.swipe": "Tokatlamak",
|
||||
"form.prefs.label.keyboard_shortcuts": "Klavye kısayollarını etkinleştir",
|
||||
"form.prefs.label.entry_swipe": "Mobil cihazlarda iletiler için kaydırma hareketlerini etkinleştir",
|
||||
"form.prefs.label.entry_swipe": "Увімкніть введення пальцем на сенсорних екранах",
|
||||
"form.prefs.label.gesture_nav": "Girişler arasında gezinmek için hareket",
|
||||
"form.prefs.label.show_reading_time": "Makaleler için tahmini okuma süresini göster",
|
||||
"form.prefs.label.custom_css": "Özel CSS",
|
||||
"form.prefs.label.entry_order": "Giriş Sıralama Sütunu",
|
||||
"form.prefs.label.default_home_page": "Varsayılan ana sayfa",
|
||||
"form.prefs.label.categories_sorting_order": "Kategoriler sıralama",
|
||||
"form.import.label.file": "OPML dosyası",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "Fever API'yi Etkinleştir",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Pocket hesabını bağla",
|
||||
"form.integration.wallabag_activate": "Makaleleri Wallabag'e kaydet",
|
||||
"form.integration.wallabag_only_url": "Yalnızca URL gönder (tam içerik yerine)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag API Uç Noktası",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "Makaleleri Linkding'e kaydet",
|
||||
"form.integration.linkding_endpoint": "Linkding API Uç Noktası",
|
||||
"form.integration.linkding_api_key": "Linkding API Anahtarı",
|
||||
"form.integration.matrix_bot_activate": "Yeni makaleleri Matrix'e aktarın",
|
||||
"form.integration.matrix_bot_user": "Matrix için Kullanıcı Adı",
|
||||
"form.integration.matrix_bot_password": "Matrix kullanıcısı için şifre",
|
||||
"form.integration.matrix_bot_url": "Matris sunucusu URL'si",
|
||||
"form.integration.matrix_bot_chat_id": "Matris odasının kimliği",
|
||||
"form.api_key.label.description": "API Anahtar Etiketi",
|
||||
"form.submit.loading": "Yükleniyor...",
|
||||
"form.submit.saving": "Kaydediliyor...",
|
||||
|
|
|
@ -0,0 +1,388 @@
|
|||
{
|
||||
"confirm.question": "Ви впевнені?",
|
||||
"confirm.yes": "так",
|
||||
"confirm.no": "ні",
|
||||
"confirm.loading": "В процесі...",
|
||||
"action.subscribe": "Підписатись",
|
||||
"action.save": "Зберегти",
|
||||
"action.or": "або",
|
||||
"action.cancel": "скасувати",
|
||||
"action.remove": "Видалити",
|
||||
"action.remove_feed": "Видалити стрічку",
|
||||
"action.update": "Зберегти",
|
||||
"action.edit": "Редагувати",
|
||||
"action.download": "Завантажити",
|
||||
"action.import": "Імпортувати",
|
||||
"action.login": "Увійти",
|
||||
"action.home_screen": "Додати до головного екрану",
|
||||
"tooltip.keyboard_shortcuts": "Комбінація клавіш: %s",
|
||||
"tooltip.logged_user": "Здійснено вхід як %s",
|
||||
"menu.unread": "Непрочитане",
|
||||
"menu.starred": "З зірочкою",
|
||||
"menu.history": "Історія",
|
||||
"menu.feeds": "Стрічки",
|
||||
"menu.categories": "Категорії",
|
||||
"menu.settings": "Налаштування",
|
||||
"menu.logout": "Вийти",
|
||||
"menu.preferences": "Уподобання",
|
||||
"menu.integrations": "Інтеграції",
|
||||
"menu.sessions": "Сеанси",
|
||||
"menu.users": "Користувачі",
|
||||
"menu.about": "Про додаток",
|
||||
"menu.export": "Експорт",
|
||||
"menu.import": "Імпорт",
|
||||
"menu.create_category": "Створити категорію",
|
||||
"menu.mark_page_as_read": "Відмітити цю сторінку як прочитане",
|
||||
"menu.mark_all_as_read": "Відмітити все як прочитане",
|
||||
"menu.show_all_entries": "Показати всі записи",
|
||||
"menu.show_only_unread_entries": "Показати тільки непрочитані записи",
|
||||
"menu.refresh_feed": "Оновити",
|
||||
"menu.refresh_all_feeds": "Оновити всі стрічки у фоновому режимі",
|
||||
"menu.edit_feed": "Редагувати",
|
||||
"menu.edit_category": "Редагувати",
|
||||
"menu.add_feed": "Додати підписку",
|
||||
"menu.add_user": "Додати користувачв",
|
||||
"menu.flush_history": "Очистити історію",
|
||||
"menu.feed_entries": "Записи",
|
||||
"menu.api_keys": "Ключі API",
|
||||
"menu.create_api_key": "Створити новий ключ API",
|
||||
"menu.shared_entries": "Спільні записи",
|
||||
"search.label": "Пошук",
|
||||
"search.placeholder": "Шукати...",
|
||||
"pagination.next": "Вперед",
|
||||
"pagination.previous": "Назад",
|
||||
"entry.status.unread": "Непрочитане",
|
||||
"entry.status.read": "Прочитане",
|
||||
"entry.status.toast.unread": "Відмічено непрочитаним",
|
||||
"entry.status.toast.read": "Відмічено прочитаним",
|
||||
"entry.status.title": "Змінити стан запису",
|
||||
"entry.bookmark.toggle.on": "Поставити зірочку",
|
||||
"entry.bookmark.toggle.off": "Прибрати зірочку",
|
||||
"entry.bookmark.toast.on": "З зірочкою",
|
||||
"entry.bookmark.toast.off": "Без зірочки",
|
||||
"entry.state.saving": "Зберігаю...",
|
||||
"entry.state.loading": "Завантаження...",
|
||||
"entry.save.label": "Зберегти",
|
||||
"entry.save.title": "Зберегти цю статтю",
|
||||
"entry.save.completed": "Готово!",
|
||||
"entry.save.toast.completed": "Стаття збережена",
|
||||
"entry.scraper.label": "Завантажити",
|
||||
"entry.scraper.title": "Отримати оригінальний зміст",
|
||||
"entry.scraper.completed": "Готово!",
|
||||
"entry.external_link.label": "Зовнішнє посилання",
|
||||
"entry.comments.label": "Коментарі",
|
||||
"entry.comments.title": "Дивитися коментарі",
|
||||
"entry.share.label": "Поділитись",
|
||||
"entry.share.title": "Поділитись статтєю",
|
||||
"entry.unshare.label": "Не ділитися",
|
||||
"entry.shared_entry.title": "Відкрити публічне посилання",
|
||||
"entry.shared_entry.label": "Поділитись",
|
||||
"entry.estimated_reading_time": [
|
||||
"читати %d хвилину",
|
||||
"читати %d хвилини",
|
||||
"читати %d хвилин"
|
||||
],
|
||||
"page.shared_entries.title": "Спильні записи",
|
||||
"page.unread.title": "Непрочитане",
|
||||
"page.starred.title": "З зірочкою",
|
||||
"page.categories.title": "Категорії",
|
||||
"page.categories.no_feed": "Немає стрічки.",
|
||||
"page.categories.entries": "Статті",
|
||||
"page.categories.feeds": "Підписки",
|
||||
"page.categories.feed_count": [
|
||||
"Містить %d стрічку.",
|
||||
"Містить %d стрічки.",
|
||||
"Містить %d стрічок."
|
||||
],
|
||||
"page.categories.unread_counter": "Кількість непрочитаних записів",
|
||||
"page.new_category.title": "Нова категорія",
|
||||
"page.new_user.title": "Новий користувач",
|
||||
"page.edit_category.title": "Редагування категорії: %s",
|
||||
"page.edit_user.title": "Редагування користувача: %s",
|
||||
"page.feeds.title": "Стрічки",
|
||||
"page.feeds.last_check": "Остання перевірка:",
|
||||
"page.feeds.unread_counter": "Кількість непрочитаних записів",
|
||||
"page.feeds.read_counter": "Кількість прочитаних записів",
|
||||
"page.feeds.error_count": ["%d помилка", "%d помилки", "%d помилок"],
|
||||
"page.history.title": "Історія",
|
||||
"page.import.title": "Імпорт",
|
||||
"page.search.title": "Результати пошуку",
|
||||
"page.about.title": "Про додадок",
|
||||
"page.about.credits": "Титри",
|
||||
"page.about.version": "Версія:",
|
||||
"page.about.build_date": "Дата побудови:",
|
||||
"page.about.author": "Автор:",
|
||||
"page.about.license": "Ліцензія:",
|
||||
"page.about.global_config_options": "Параметри глобальної конфігурації",
|
||||
"page.about.postgres_version": "Версія Postgres:",
|
||||
"page.about.go_version": "Версія Go:",
|
||||
"page.add_feed.title": "Нова підписка",
|
||||
"page.add_feed.no_category": "Немає категорії. Ви маєте додати принаймні одну категорію.",
|
||||
"page.add_feed.label.url": "URL",
|
||||
"page.add_feed.submit": "Знайти підписку",
|
||||
"page.add_feed.legend.advanced_options": "Розширені опції",
|
||||
"page.add_feed.choose_feed": "Обрати підписку",
|
||||
"page.edit_feed.title": "Редагування стрічки: %s",
|
||||
"page.edit_feed.last_check": "Остання перевірка:",
|
||||
"page.edit_feed.last_modified_header": "Заголовок LastModified:",
|
||||
"page.edit_feed.etag_header": "Заголовок ETag:",
|
||||
"page.edit_feed.no_header": "Немає",
|
||||
"page.edit_feed.last_parsing_error": "Остання помилка аналізу",
|
||||
"page.entry.attachments": "Додатки",
|
||||
"page.keyboard_shortcuts.title": "Комбінації клавиш",
|
||||
"page.keyboard_shortcuts.subtitle.sections": "Навігація по розділах",
|
||||
"page.keyboard_shortcuts.subtitle.items": "Навігація по записах",
|
||||
"page.keyboard_shortcuts.subtitle.pages": "Навігація по сторінках",
|
||||
"page.keyboard_shortcuts.subtitle.actions": "Дії",
|
||||
"page.keyboard_shortcuts.go_to_unread": "Перейти до непрочитаних",
|
||||
"page.keyboard_shortcuts.go_to_starred": "Перейти до закладок",
|
||||
"page.keyboard_shortcuts.go_to_history": "Перейти до історії",
|
||||
"page.keyboard_shortcuts.go_to_feeds": "Перейти до стрічок",
|
||||
"page.keyboard_shortcuts.go_to_categories": "Перейти до категорій",
|
||||
"page.keyboard_shortcuts.go_to_settings": "Перейти до налаштувань",
|
||||
"page.keyboard_shortcuts.show_keyboard_shortcuts": "Показати комбінації клавиш",
|
||||
"page.keyboard_shortcuts.go_to_previous_item": "Перейти до попереднього запису",
|
||||
"page.keyboard_shortcuts.go_to_next_item": "Перейти до наступного запису",
|
||||
"page.keyboard_shortcuts.go_to_feed": "Перейти до стрічки",
|
||||
"page.keyboard_shortcuts.go_to_previous_page": "Перейти до попередньої сторінки",
|
||||
"page.keyboard_shortcuts.go_to_next_page": "Перейти до наступної сторінки",
|
||||
"page.keyboard_shortcuts.open_item": "Відкрити виділений запис",
|
||||
"page.keyboard_shortcuts.open_original": "Відкрити оригінальне посилання",
|
||||
"page.keyboard_shortcuts.open_original_same_window": "Відкрити оригінальне посилання в поточній вкладці",
|
||||
"page.keyboard_shortcuts.open_comments": "Відкрити посилання на коментарі",
|
||||
"page.keyboard_shortcuts.open_comments_same_window": "Відкрити посилання на коментарі в поточній вкладці",
|
||||
"page.keyboard_shortcuts.toggle_read_status_next": "Переключити статус читання, перейти до наступного",
|
||||
"page.keyboard_shortcuts.toggle_read_status_prev": "Переключити статус читання, перейти до попереднього",
|
||||
"page.keyboard_shortcuts.refresh_all_feeds": "Оновити всі стрічки в фоновому режимі",
|
||||
"page.keyboard_shortcuts.mark_page_as_read": "Відмітити поточну сторінку як прочитане",
|
||||
"page.keyboard_shortcuts.download_content": "Завантажити оригінальний зміст",
|
||||
"page.keyboard_shortcuts.toggle_bookmark_status": "Переключити статус закладки",
|
||||
"page.keyboard_shortcuts.save_article": "Зберегти статтю",
|
||||
"page.keyboard_shortcuts.scroll_item_to_top": "Прокрутити запис догори",
|
||||
"page.keyboard_shortcuts.remove_feed": "Видалити цю стрічку",
|
||||
"page.keyboard_shortcuts.go_to_search": "Поставити фокус на поле пошуку",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "Закрити модальне діалогове вікно",
|
||||
"page.users.title": "Користувачі",
|
||||
"page.users.username": "Ім’я користувача",
|
||||
"page.users.never_logged": "Ніколи",
|
||||
"page.users.admin.yes": "Так",
|
||||
"page.users.admin.no": "Ні",
|
||||
"page.users.actions": "Дії",
|
||||
"page.users.last_login": "Дата останнього входу",
|
||||
"page.users.is_admin": "Адміністратор",
|
||||
"page.settings.title": "Налаштування ",
|
||||
"page.settings.link_google_account": "Підключити мій обліковий запис Google",
|
||||
"page.settings.unlink_google_account": "Відключити мій обліковий запис Google",
|
||||
"page.settings.link_oidc_account": "Підключити мій обліковий запис OpenID Connect",
|
||||
"page.settings.unlink_oidc_account": "Відключити мій обліковий запис OpenID Connect",
|
||||
"page.login.title": "Вхід",
|
||||
"page.login.google_signin": "Увійти через Google",
|
||||
"page.login.oidc_signin": "Увійти через OpenID Connect",
|
||||
"page.integrations.title": "Інтеграції",
|
||||
"page.integration.miniflux_api": "Miniflux API",
|
||||
"page.integration.miniflux_api_endpoint": "Адреса доступу API",
|
||||
"page.integration.miniflux_api_username": "Ім’я користувача",
|
||||
"page.integration.miniflux_api_password": "Пароль",
|
||||
"page.integration.miniflux_api_password_value": "Пароль до вашого облікового запису",
|
||||
"page.integration.bookmarklet": "Букмарклет",
|
||||
"page.integration.bookmarklet.name": "Додати до Miniflux",
|
||||
"page.integration.bookmarklet.instructions": "Перетягніть це посилання до своїх закладок.",
|
||||
"page.integration.bookmarklet.help": "Це спеціальне посилання дозволяє підписатися на веб-сайт безпосередньо за допомогою закладки у вашому веб-браузері.",
|
||||
"page.sessions.title": "Сеанси",
|
||||
"page.sessions.table.date": "Дата",
|
||||
"page.sessions.table.ip": "IP адреса",
|
||||
"page.sessions.table.user_agent": "User Agent",
|
||||
"page.sessions.table.actions": "Дії",
|
||||
"page.sessions.table.current_session": "Поточний сеанс",
|
||||
"page.api_keys.title": "Ключі API",
|
||||
"page.api_keys.table.description": "Опис",
|
||||
"page.api_keys.table.token": "Токен",
|
||||
"page.api_keys.table.last_used_at": "Дата останнього використання",
|
||||
"page.api_keys.table.created_at": "Дата створення",
|
||||
"page.api_keys.table.actions": "Дії",
|
||||
"page.api_keys.never_used": "Ніколи не використався",
|
||||
"page.new_api_key.title": "Створити ключ API",
|
||||
"page.offline.title": "Автономний режим",
|
||||
"page.offline.message": "Ви офлайн",
|
||||
"page.offline.refresh_page": "Спробуйте оновити сторінку",
|
||||
"alert.no_shared_entry": "Немає спільного запису.",
|
||||
"alert.no_bookmark": "Наразі закладки відсутні.",
|
||||
"alert.no_category": "Немає категорії.",
|
||||
"alert.no_category_entry": "У цій категорії немає записів.",
|
||||
"alert.no_feed_entry": "У цій стрічці немає записів.",
|
||||
"alert.no_feed": "У вас немає підписок.",
|
||||
"alert.no_feed_in_category": "У цій категорії немає підписок.",
|
||||
"alert.no_history": "Наразі історія порожня.",
|
||||
"alert.feed_error": "З цією стрічкою трапилась помилка",
|
||||
"alert.no_search_result": "Немає результатів для цього пошуку.",
|
||||
"alert.no_unread_entry": "Немає непрочитаних статей.",
|
||||
"alert.no_user": "Ви єдиний користувач.",
|
||||
"alert.account_unlinked": "Тепер ваш зовнішній обліковий запис підключено!",
|
||||
"alert.account_linked": "Тепер ваш зовнішній обліковий запис від’єднано!",
|
||||
"alert.pocket_linked": "Тепер ваш обліковий запис Pocket підключено!",
|
||||
"alert.prefs_saved": "Уподобання збережено!",
|
||||
"error.unlink_account_without_password": "Ви маєте встановити пароль, щоб мати можливість увійти наступного разу",
|
||||
"error.duplicate_linked_account": "Вже є обліковий запис, під’єднаний до цього провайдера!",
|
||||
"error.duplicate_fever_username": "Вже є обліковий запис з таким самим користувачем Fever!",
|
||||
"error.duplicate_googlereader_username": "Вже є обліковий запис з таким самим користувачем Google Reader!",
|
||||
"error.pocket_request_token": "Не вдалося отримати токен доступу з Pocket!",
|
||||
"error.pocket_access_token": "Не вдалося отримати токен доступу з Pocket!",
|
||||
"error.category_already_exists": "Така категорія вже існує.",
|
||||
"error.unable_to_create_category": "Не вдається сворити категорію.",
|
||||
"error.unable_to_update_category": "Не вдається відредагувати категорію.",
|
||||
"error.user_already_exists": "Такий користувач вже існує.",
|
||||
"error.unable_to_create_user": "Не вдається створити користувача.",
|
||||
"error.unable_to_update_user": "Не вдається оновити користувача.",
|
||||
"error.unable_to_update_feed": "Не вдається оновити стрічку.",
|
||||
"error.subscription_not_found": "Не знайшлося жодної підписки.",
|
||||
"error.invalid_theme": "Недійсна тема.",
|
||||
"error.invalid_language": "Недійсна мова.",
|
||||
"error.invalid_timezone": "Недійсний часовий пояс.",
|
||||
"error.invalid_entry_direction": "Недійсний напрямок запису.",
|
||||
"error.invalid_display_mode": "Недійсний режим відображення.",
|
||||
"error.invalid_gesture_nav": "Недійсна навігація жестами.",
|
||||
"error.invalid_default_home_page": "Недійсна домашня сторінка за замовчуванням!",
|
||||
"error.empty_file": "Цей файл порожній.",
|
||||
"error.bad_credentials": "Невірне ім’я користувача або пароль.",
|
||||
"error.fields_mandatory": "Всі поля є обов’язковими.",
|
||||
"error.title_required": "Назва є обов’язковою.",
|
||||
"error.different_passwords": "Паролі не співпадають.",
|
||||
"error.password_min_length": "Пароль має складати щонайменше 6 символів.",
|
||||
"error.settings_mandatory_fields": "Поля імені, теми, мови та часового поясу є обов’язковими.",
|
||||
"error.settings_reading_speed_is_positive": "Швидкість читання має бути додатнім цілим числом.",
|
||||
"error.entries_per_page_invalid": "Число записів на сторінку недійсне.",
|
||||
"error.feed_mandatory_fields": "URL та категорія є обов’язковими.",
|
||||
"error.feed_already_exists": "Така стрічка вже існує.",
|
||||
"error.invalid_feed_url": "Недійсна URL-адреса стрічки.",
|
||||
"error.invalid_site_url": "Недійсна URL-адреса сайту.",
|
||||
"error.feed_url_not_empty": "URL-адреса стрічки не може бути порожньою.",
|
||||
"error.site_url_not_empty": "URL-адреса сайту не може бути порожньою.",
|
||||
"error.feed_title_not_empty": "Назва стрічки не може бути порожньою.",
|
||||
"error.feed_category_not_found": "Категорія не існує або належить до іншого користувача.",
|
||||
"error.feed_invalid_blocklist_rule": "Правило списку блокувань недійсне.",
|
||||
"error.feed_invalid_keeplist_rule": "Правило списку дозволень недійсне.",
|
||||
"error.user_mandatory_fields": "Ім’я користувача є обов’язковим.",
|
||||
"error.api_key_already_exists": "Такий ключ API вже існує.",
|
||||
"error.unable_to_create_api_key": "Не вдається створити такий ключ API",
|
||||
"form.feed.label.title": "Назва",
|
||||
"form.feed.label.site_url": "URL-адреса сайту",
|
||||
"form.feed.label.feed_url": "URL-адреса стрічки",
|
||||
"form.feed.label.category": "Категорія",
|
||||
"form.feed.label.crawler": "Завантажувати оригінальний вміст",
|
||||
"form.feed.label.feed_username": "Ім’я користувача для завантаження",
|
||||
"form.feed.label.feed_password": "Пароль для завантаження",
|
||||
"form.feed.label.user_agent": "Назначити User Agent",
|
||||
"form.feed.label.cookie": "Встановити кукі",
|
||||
"form.feed.label.scraper_rules": "Правила Scraper",
|
||||
"form.feed.label.rewrite_rules": "Правила Rewrite",
|
||||
"form.feed.label.blocklist_rules": "Правила блокування",
|
||||
"form.feed.label.keeplist_rules": "Правила дозволення",
|
||||
"form.feed.label.urlrewrite_rules": "Правила перезапису URL-адрес",
|
||||
"form.feed.label.ignore_http_cache": "Ігнорувати кеш HTTP",
|
||||
"form.feed.label.allow_self_signed_certificates": "Дозволити сертифікати з власним підписом або недійсні",
|
||||
"form.feed.label.fetch_via_proxy": "Використати проксі-сервер",
|
||||
"form.feed.label.disabled": "Не оновлювати цю стрічку",
|
||||
"form.feed.label.hide_globally": "Приховати записи в глобальному списку непрочитаного",
|
||||
"form.category.label.title": "Назва",
|
||||
"form.category.hide_globally": "Приховати записи в глобальному списку непрочитаного",
|
||||
"form.user.label.username": "Ім’я користувача",
|
||||
"form.user.label.password": "Пароль",
|
||||
"form.user.label.confirmation": "Підтверждення паролю",
|
||||
"form.user.label.admin": "Адміністратор",
|
||||
"form.prefs.label.language": "Мова",
|
||||
"form.prefs.label.timezone": "Часовий пояс",
|
||||
"form.prefs.label.theme": "Тема",
|
||||
"form.prefs.label.entry_sorting": "Сортування записів",
|
||||
"form.prefs.label.entries_per_page": "Кількість записів на сторінку",
|
||||
"form.prefs.label.default_reading_speed": "Швидкість читання для інших мов (слів на хвилину)",
|
||||
"form.prefs.label.cjk_reading_speed": "Швидкість читання для китайської, корейської та японської мови (символів на хвилину)",
|
||||
"form.prefs.label.display_mode": "Режим відображення Progressive Web App (PWA).",
|
||||
"form.prefs.select.older_first": "Старіші записи спочатку",
|
||||
"form.prefs.select.recent_first": "Останні записи спочатку",
|
||||
"form.prefs.select.fullscreen": "Повний екран",
|
||||
"form.prefs.select.standalone": "Автономний",
|
||||
"form.prefs.select.minimal_ui": "Мінімальний",
|
||||
"form.prefs.select.browser": "Браузер",
|
||||
"form.prefs.select.publish_time": "Дата публікації запису",
|
||||
"form.prefs.select.created_time": "Дата створення запису",
|
||||
"form.prefs.select.alphabetical": "За алфавітом",
|
||||
"form.prefs.select.unread_count": "Кількість непрочитаних",
|
||||
"form.prefs.select.none": "Жодного",
|
||||
"form.prefs.select.tap": "Двічі натисніть",
|
||||
"form.prefs.select.swipe": "Проведіть пальцем",
|
||||
"form.prefs.label.keyboard_shortcuts": "Увімкнути комбінації клавиш",
|
||||
"form.prefs.label.entry_swipe": "Увімкніть введення пальцем на сенсорних екранах",
|
||||
"form.prefs.label.gesture_nav": "Жест для переходу між записами",
|
||||
"form.prefs.label.show_reading_time": "Показувати приблизний час читання для записів",
|
||||
"form.prefs.label.custom_css": "Спеціальний CSS",
|
||||
"form.prefs.label.entry_order": "Стовпець сортування записів",
|
||||
"form.prefs.label.default_home_page": "Домашня сторінка за умовчанням",
|
||||
"form.prefs.label.categories_sorting_order": "Сортування за категоріями",
|
||||
"form.import.label.file": "Файл OPML",
|
||||
"form.import.label.url": "URL-адреса",
|
||||
"form.integration.fever_activate": "Увімкнути API Fever",
|
||||
"form.integration.fever_username": "Ім’я користувача Fever",
|
||||
"form.integration.fever_password": "Пароль Fever",
|
||||
"form.integration.fever_endpoint": "Адреса доступу API Fever:",
|
||||
"form.integration.googlereader_activate": "Увімкнути API Google Reader",
|
||||
"form.integration.googlereader_username": "Ім’я користувача Google Reader",
|
||||
"form.integration.googlereader_password": "Пароль Google Reader",
|
||||
"form.integration.googlereader_endpoint": "Адреса доступу API Google Reader:",
|
||||
"form.integration.pinboard_activate": "Зберігати статті до Pinboard",
|
||||
"form.integration.pinboard_token": "API ключ від Pinboard",
|
||||
"form.integration.pinboard_tags": "Теги для Pinboard",
|
||||
"form.integration.pinboard_bookmark": "Відмічати закладку як непрочитану",
|
||||
"form.integration.instapaper_activate": "Зберігати статті до Instapaper",
|
||||
"form.integration.instapaper_username": "Ім’я користувача Instapaper",
|
||||
"form.integration.instapaper_password": "Пароль Instapaper",
|
||||
"form.integration.pocket_activate": "Зберігати статті до Pocket",
|
||||
"form.integration.pocket_consumer_key": "Pocket Consumer Key",
|
||||
"form.integration.pocket_access_token": "Pocket Access Token",
|
||||
"form.integration.pocket_connect_link": "Підключити ваш обліковий запис Pocket",
|
||||
"form.integration.wallabag_activate": "Зберігати статті до Wallabag",
|
||||
"form.integration.wallabag_only_url": "Надіслати лише URL (замість повного вмісту)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag API Endpoint",
|
||||
"form.integration.wallabag_client_id": "Wallabag Client ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag Client Secret",
|
||||
"form.integration.wallabag_username": "Ім’я користувача Wallabag",
|
||||
"form.integration.wallabag_password": "Пароль Wallabag",
|
||||
"form.integration.nunux_keeper_activate": "Зберігати статті до Nunux Keeper",
|
||||
"form.integration.nunux_keeper_endpoint": "Nunux Keeper API Endpoint",
|
||||
"form.integration.nunux_keeper_api_key": "Ключ API Nunux Keeper",
|
||||
"form.integration.espial_activate": "Зберігати статті до Espial",
|
||||
"form.integration.espial_endpoint": "Espial API Endpoint",
|
||||
"form.integration.espial_api_key": "Ключ API Espial",
|
||||
"form.integration.espial_tags": "Теги для Espial",
|
||||
"form.integration.telegram_bot_activate": "Відправляти нові статті до чату Telegram",
|
||||
"form.integration.telegram_bot_token": "Токен боту",
|
||||
"form.integration.telegram_chat_id": "ID чату",
|
||||
"form.integration.linkding_activate": "Зберігати статті до Linkding",
|
||||
"form.integration.linkding_endpoint": "Linkding API Endpoint",
|
||||
"form.integration.linkding_api_key": "Ключ API Linkding",
|
||||
"form.integration.matrix_bot_activate": "Перенесення нових статей в Матрицю",
|
||||
"form.integration.matrix_bot_user": "Ім'я користувача для Matrix",
|
||||
"form.integration.matrix_bot_password": "Пароль для користувача Matrix",
|
||||
"form.integration.matrix_bot_url": "URL-адреса сервера Матриці",
|
||||
"form.integration.matrix_bot_chat_id": "Ідентифікатор кімнати Матриці",
|
||||
"form.api_key.label.description": "Назва ключа API",
|
||||
"form.submit.loading": "Завантаження...",
|
||||
"form.submit.saving": "Зберігаю...",
|
||||
"time_elapsed.not_yet": "ще ні",
|
||||
"time_elapsed.yesterday": "вчора",
|
||||
"time_elapsed.now": "прямо зараз",
|
||||
"time_elapsed.minutes": [
|
||||
"%d хвилину тому",
|
||||
"%d хвилини тому",
|
||||
"%d хвилин тому"
|
||||
],
|
||||
"time_elapsed.hours": ["%d годину тому", "%d години тому", "%d годин тому"],
|
||||
"time_elapsed.days": ["%d день тому", "%d дні тому", "%d днів тому"],
|
||||
"time_elapsed.weeks": ["%d тиждень тому", "%d тижня тому", "%d тижнів тому"],
|
||||
"time_elapsed.months": [
|
||||
"%d місяць тому",
|
||||
"%d місяця тому",
|
||||
"%d місяців тому"
|
||||
],
|
||||
"time_elapsed.years": ["%d рік тому", "%d роки тому", "%d років тому"]
|
||||
}
|
|
@ -160,6 +160,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "滚动到顶部",
|
||||
"page.keyboard_shortcuts.remove_feed": "删除此源",
|
||||
"page.keyboard_shortcuts.go_to_search": "将焦点放在搜索表单上",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "关闭对话窗口",
|
||||
"page.users.title": "用户",
|
||||
"page.users.username": "用户名",
|
||||
|
@ -261,6 +262,8 @@
|
|||
"error.invalid_timezone": "无效的时区。",
|
||||
"error.invalid_entry_direction": "无效的输入方向。",
|
||||
"error.invalid_display_mode": "无效的网页应用显示模式。",
|
||||
"error.invalid_gesture_nav": "手势导航无效。",
|
||||
"error.invalid_default_home_page": "无效的默认主页!",
|
||||
"form.feed.label.title": "标题",
|
||||
"form.feed.label.site_url": "源网站 URL",
|
||||
"form.feed.label.feed_url": "订阅源 URL",
|
||||
|
@ -291,7 +294,7 @@
|
|||
"form.prefs.label.theme": "主题",
|
||||
"form.prefs.label.entry_sorting": "文章排序",
|
||||
"form.prefs.label.entries_per_page": "每页文章数",
|
||||
"form.prefs.label.display_mode": "渐进式网页应用显示模式(需要重新添加)",
|
||||
"form.prefs.label.display_mode": "渐进式网络应用程序 (PWA) 显示模式",
|
||||
"form.prefs.label.default_reading_speed": "其他语言的阅读速度(每分钟字数)",
|
||||
"form.prefs.label.cjk_reading_speed": "中文、韩文和日文的阅读速度(每分钟字符数)",
|
||||
"form.prefs.select.older_first": "旧->新",
|
||||
|
@ -302,11 +305,19 @@
|
|||
"form.prefs.select.browser": "浏览器",
|
||||
"form.prefs.select.publish_time": "文章发布时间",
|
||||
"form.prefs.select.created_time": "文章创建时间",
|
||||
"form.prefs.select.alphabetical": "按字母顺序",
|
||||
"form.prefs.select.unread_count": "未读计数",
|
||||
"form.prefs.select.none": "没有任何",
|
||||
"form.prefs.select.tap": "双击",
|
||||
"form.prefs.select.swipe": "滑动",
|
||||
"form.prefs.label.keyboard_shortcuts": "启用键盘快捷键",
|
||||
"form.prefs.label.entry_swipe": "在移动设备上启用滑动手势",
|
||||
"form.prefs.label.entry_swipe": "在触摸屏上启用输入滑动",
|
||||
"form.prefs.label.gesture_nav": "在条目之间导航的手势",
|
||||
"form.prefs.label.show_reading_time": "显示文章的预计阅读时间",
|
||||
"form.prefs.label.custom_css": "自定义 CSS",
|
||||
"form.prefs.label.entry_order": "文章排序依据",
|
||||
"form.prefs.label.default_home_page": "默认主页",
|
||||
"form.prefs.label.categories_sorting_order": "分类排序",
|
||||
"form.import.label.file": "OPML 文件",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "启用 Fever API",
|
||||
|
@ -329,6 +340,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket 访问密钥",
|
||||
"form.integration.pocket_connect_link": "连接您的 Pocket 帐户",
|
||||
"form.integration.wallabag_activate": "保存文章到 Wallabag",
|
||||
"form.integration.wallabag_only_url": "仅发送 URL(而不是完整内容)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag URL",
|
||||
"form.integration.wallabag_client_id": "Wallabag 客户端 ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag 客户端 Secret",
|
||||
|
@ -347,6 +359,11 @@
|
|||
"form.integration.linkding_activate": "保存文章到 Linkding",
|
||||
"form.integration.linkding_endpoint": "Linkding API 端点",
|
||||
"form.integration.linkding_api_key": "Linkding API 密钥",
|
||||
"form.integration.matrix_bot_activate": "将新文章转移到 Matrix",
|
||||
"form.integration.matrix_bot_user": "矩阵的用户名",
|
||||
"form.integration.matrix_bot_password": "矩阵用户密码",
|
||||
"form.integration.matrix_bot_url": "矩阵服务器 URL",
|
||||
"form.integration.matrix_bot_chat_id": "Matrix房间ID",
|
||||
"form.api_key.label.description": "API密钥标签",
|
||||
"form.submit.loading": "载入中…",
|
||||
"form.submit.saving": "保存中…",
|
||||
|
@ -387,7 +404,6 @@
|
|||
"This feed is empty": "该源是空的",
|
||||
"This web page is empty": "该网页是空的",
|
||||
"Invalid SSL certificate (original error: %q)": "无效的 SSL 证书 (原始错误: %q)",
|
||||
"This website is temporarily unreachable (original error: %q)": "该网站暂时不可达 (原始错误: %q)",
|
||||
"This website is permanently unreachable (original error: %q)": "该网站永久不可达 (原始错误: %q)",
|
||||
"This website is unreachable (original error: %q)": "该网站永久不可达 (原始错误: %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "网站不可达, 请求已在 %d 秒后超时"
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@
|
|||
"page.keyboard_shortcuts.scroll_item_to_top": "滾動到頂部",
|
||||
"page.keyboard_shortcuts.remove_feed": "刪除此Feed",
|
||||
"page.keyboard_shortcuts.go_to_search": "將焦點放在搜尋表單上",
|
||||
"page.keyboard_shortcuts.toggle_entry_attachments": "Toggle open/close entry attachments",
|
||||
"page.keyboard_shortcuts.close_modal": "關閉對話視窗",
|
||||
"page.users.title": "使用者",
|
||||
"page.users.username": "使用者名稱",
|
||||
|
@ -263,6 +264,8 @@
|
|||
"error.invalid_timezone": "無效的時區。",
|
||||
"error.invalid_entry_direction": "無效的輸入方向。",
|
||||
"error.invalid_display_mode": "無效的網頁應用顯示模式。",
|
||||
"error.invalid_gesture_nav": "手勢導航無效.",
|
||||
"error.invalid_default_home_page": "默認主頁無效!",
|
||||
"form.feed.label.title": "標題",
|
||||
"form.feed.label.site_url": "網站 URL",
|
||||
"form.feed.label.feed_url": "訂閱Feed URL",
|
||||
|
@ -295,7 +298,7 @@
|
|||
"form.prefs.label.entries_per_page": "每頁文章數",
|
||||
"form.prefs.label.default_reading_speed": "Reading speed for other languages (words per minute)",
|
||||
"form.prefs.label.cjk_reading_speed": "Reading speed for Chinese, Korean and Japanese (characters per minute)",
|
||||
"form.prefs.label.display_mode": "漸進式網頁應用顯示模式(需要重新新增)",
|
||||
"form.prefs.label.display_mode": "漸進式網絡應用程序 (PWA) 顯示模式",
|
||||
"form.prefs.select.older_first": "舊->新",
|
||||
"form.prefs.select.recent_first": "新->舊",
|
||||
"form.prefs.select.fullscreen": "全屏",
|
||||
|
@ -304,11 +307,19 @@
|
|||
"form.prefs.select.browser": "瀏覽器",
|
||||
"form.prefs.select.publish_time": "文章釋出時間",
|
||||
"form.prefs.select.created_time": "文章建立時間",
|
||||
"form.prefs.select.alphabetical": "按字母順序",
|
||||
"form.prefs.select.unread_count": "未讀計數",
|
||||
"form.prefs.select.none": "沒有任何",
|
||||
"form.prefs.select.tap": "雙擊",
|
||||
"form.prefs.select.swipe": "滑動",
|
||||
"form.prefs.label.keyboard_shortcuts": "啟用鍵盤快捷鍵",
|
||||
"form.prefs.label.entry_swipe": "在移動裝置上啟用滑動手勢",
|
||||
"form.prefs.label.entry_swipe": "在触摸屏上启用输入滑动",
|
||||
"form.prefs.label.gesture_nav": "在條目之間導航的手勢",
|
||||
"form.prefs.label.show_reading_time": "顯示文章的預計閱讀時間",
|
||||
"form.prefs.label.custom_css": "自定義 CSS",
|
||||
"form.prefs.label.entry_order": "文章排序依據",
|
||||
"form.prefs.label.default_home_page": "默認主頁",
|
||||
"form.prefs.label.categories_sorting_order": "分類排序",
|
||||
"form.import.label.file": "OPML 檔案",
|
||||
"form.import.label.url": "URL",
|
||||
"form.integration.fever_activate": "啟用 Fever API",
|
||||
|
@ -331,6 +342,7 @@
|
|||
"form.integration.pocket_access_token": "Pocket 訪問金鑰",
|
||||
"form.integration.pocket_connect_link": "連線您的 Pocket 帳戶",
|
||||
"form.integration.wallabag_activate": "儲存文章到 Wallabag",
|
||||
"form.integration.wallabag_only_url": "仅发送 URL(而不是完整内容)",
|
||||
"form.integration.wallabag_endpoint": "Wallabag URL",
|
||||
"form.integration.wallabag_client_id": "Wallabag 客戶端 ID",
|
||||
"form.integration.wallabag_client_secret": "Wallabag 客戶端 Secret",
|
||||
|
@ -349,6 +361,11 @@
|
|||
"form.integration.linkding_activate": "儲存文章到 Linkding",
|
||||
"form.integration.linkding_endpoint": "Linkding API 端點",
|
||||
"form.integration.linkding_api_key": "Linkding API 金鑰",
|
||||
"form.integration.matrix_bot_activate": "將新文章轉移到 Matrix",
|
||||
"form.integration.matrix_bot_user": "矩陣的用戶名",
|
||||
"form.integration.matrix_bot_password": "矩陣用戶密碼",
|
||||
"form.integration.matrix_bot_url": "矩陣服務器 URL",
|
||||
"form.integration.matrix_bot_chat_id": "Matrix房間ID",
|
||||
"form.api_key.label.description": "API金鑰標籤",
|
||||
"form.submit.loading": "載入中…",
|
||||
"form.submit.saving": "儲存中…",
|
||||
|
@ -395,7 +412,6 @@
|
|||
"This feed is empty": "該Feed是空的",
|
||||
"This web page is empty": "該網頁是空的",
|
||||
"Invalid SSL certificate (original error: %q)": "無效的 SSL 憑證 (錯誤: %q)",
|
||||
"This website is temporarily unreachable (original error: %q)": "該網站暫時無法訪問 (原始錯誤: %q)",
|
||||
"This website is permanently unreachable (original error: %q)": "該網站永久無法訪問(原始錯誤: %q)",
|
||||
"This website is unreachable (original error: %q)": "該網站永久無法訪問(原始錯誤: %q)",
|
||||
"Website unreachable, the request timed out after %d seconds": "網站無法訪問, 請求已在 %d 秒後超時"
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package logger handles application log messages with different levels.
|
||||
|
||||
*/
|
||||
package logger // import "miniflux.app/logger"
|
||||
|
|
51
miniflux.1
51
miniflux.1
|
@ -1,5 +1,5 @@
|
|||
.\" Manpage for miniflux.
|
||||
.TH "MINIFLUX" "1" "January 5, 2022" "\ \&" "\ \&"
|
||||
.TH "MINIFLUX" "1" "August 29, 2022" "\ \&" "\ \&"
|
||||
|
||||
.SH NAME
|
||||
miniflux \- Minimalist and opinionated feed reader
|
||||
|
@ -283,6 +283,26 @@ List of networks allowed to access the metrics endpoint (comma-separated values)
|
|||
.br
|
||||
Default is 127.0.0.1/8\&.
|
||||
.TP
|
||||
.B METRICS_USERNAME
|
||||
Metrics endpoint username for basic HTTP authentication\&.
|
||||
.br
|
||||
Default is emtpty\&.
|
||||
.TP
|
||||
.B METRICS_USERNAME_FILE
|
||||
Path to a file that contains the username for the metrics endpoint HTTP authentication\&.
|
||||
.br
|
||||
Default is emtpty\&.
|
||||
.TP
|
||||
.B METRICS_PASSWORD
|
||||
Metrics endpoint password for basic HTTP authentication\&.
|
||||
.br
|
||||
Default is emtpty\&.
|
||||
.TP
|
||||
.B METRICS_PASSWORD_FILE
|
||||
Path to a file that contains the password for the metrics endpoint HTTP authentication\&.
|
||||
.br
|
||||
Default is emtpty\&.
|
||||
.TP
|
||||
.B OAUTH2_PROVIDER
|
||||
Possible values are "google" or "oidc"\&.
|
||||
.br
|
||||
|
@ -365,11 +385,26 @@ Path to a secret key exposed as a file, it should contain $POCKET_CONSUMER_KEY v
|
|||
.br
|
||||
Default is empty\&.
|
||||
.TP
|
||||
.B PROXY_IMAGES
|
||||
Avoids mixed content warnings for external images: http-only, all, or none\&.
|
||||
.B PROXY_OPTION
|
||||
Avoids mixed content warnings for external media: http-only, all, or none\&.
|
||||
.br
|
||||
Default is http-only\&.
|
||||
.TP
|
||||
.B PROXY_MEDIA_TYPES
|
||||
A list of media types to proxify (comma-separated values): image, audio, video\&.
|
||||
.br
|
||||
Default is image only\&.
|
||||
.TP
|
||||
.B PROXY_HTTP_CLIENT_TIMEOUT
|
||||
Time limit in seconds before the proxy HTTP client cancel the request\&.
|
||||
.br
|
||||
Default is 120 seconds\&.
|
||||
.TP
|
||||
.B PROXY_URL
|
||||
Sets a server to proxy media through\&.
|
||||
.br
|
||||
Default is empty, miniflux does the proxying\&.
|
||||
.TP
|
||||
.B HTTP_CLIENT_TIMEOUT
|
||||
Time limit in seconds before the HTTP client cancel the request\&.
|
||||
.br
|
||||
|
@ -392,6 +427,11 @@ When empty, Miniflux uses a default User-Agent that includes the Miniflux versio
|
|||
.br
|
||||
Default is empty.
|
||||
.TP
|
||||
.B HTTP_SERVER_TIMEOUT
|
||||
Time limit in seconds before the HTTP client cancel the request\&.
|
||||
.br
|
||||
Default is 300 seconds\&.
|
||||
.TP
|
||||
.B AUTH_PROXY_HEADER
|
||||
Proxy authentication HTTP header\&.
|
||||
.br
|
||||
|
@ -421,6 +461,11 @@ Enabled by default\&.
|
|||
Set a custom invidious instance to use\&.
|
||||
.br
|
||||
Default is yewtu.be\&.
|
||||
.TP
|
||||
.B PROXY_PRIVATE_KEY
|
||||
Set a custom custom private key used to sign proxified media url\&.
|
||||
.br
|
||||
Default is randomly generated at startup\&.
|
||||
|
||||
.SH AUTHORS
|
||||
.P
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright 2022 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package model // import "miniflux.app/model"
|
||||
|
||||
func CategoriesSortingOptions() map[string]string {
|
||||
return map[string]string{
|
||||
"unread_count": "form.prefs.select.unread_count",
|
||||
"alphabetical": "form.prefs.select.alphabetical",
|
||||
}
|
||||
}
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package model contains all data structures used by the application.
|
||||
|
||||
*/
|
||||
package model // import "miniflux.app/model"
|
||||
|
|
|
@ -37,6 +37,7 @@ type Entry struct {
|
|||
ReadingTime int `json:"reading_time"`
|
||||
Enclosures EnclosureList `json:"enclosures"`
|
||||
Feed *Feed `json:"feed,omitempty"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// Entries represents a list of entries.
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2017 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package model // import "miniflux.app/model"
|
||||
|
||||
// HomePages returns the list of available home pages.
|
||||
func HomePages() map[string]string {
|
||||
return map[string]string{
|
||||
"unread": "menu.unread",
|
||||
"starred": "menu.starred",
|
||||
"history": "menu.history",
|
||||
"feeds": "menu.feeds",
|
||||
"categories": "menu.categories",
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ type Integration struct {
|
|||
GoogleReaderUsername string
|
||||
GoogleReaderPassword string
|
||||
WallabagEnabled bool
|
||||
WallabagOnlyURL bool
|
||||
WallabagURL string
|
||||
WallabagClientID string
|
||||
WallabagClientSecret string
|
||||
|
@ -42,4 +43,9 @@ type Integration struct {
|
|||
LinkdingEnabled bool
|
||||
LinkdingURL string
|
||||
LinkdingAPIKey string
|
||||
MatrixBotEnabled bool
|
||||
MatrixBotUser string
|
||||
MatrixBotPassword string
|
||||
MatrixBotURL string
|
||||
MatrixBotChatID string
|
||||
}
|
||||
|
|
|
@ -12,26 +12,29 @@ import (
|
|||
|
||||
// User represents a user in the system.
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"-"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
Timezone string `json:"timezone"`
|
||||
EntryDirection string `json:"entry_sorting_direction"`
|
||||
EntryOrder string `json:"entry_sorting_order"`
|
||||
Stylesheet string `json:"stylesheet"`
|
||||
GoogleID string `json:"google_id"`
|
||||
OpenIDConnectID string `json:"openid_connect_id"`
|
||||
EntriesPerPage int `json:"entries_per_page"`
|
||||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
DefaultReadingSpeed int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed int `json:"cjk_reading_speed"`
|
||||
ID int64 `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"-"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
Theme string `json:"theme"`
|
||||
Language string `json:"language"`
|
||||
Timezone string `json:"timezone"`
|
||||
EntryDirection string `json:"entry_sorting_direction"`
|
||||
EntryOrder string `json:"entry_sorting_order"`
|
||||
Stylesheet string `json:"stylesheet"`
|
||||
GoogleID string `json:"google_id"`
|
||||
OpenIDConnectID string `json:"openid_connect_id"`
|
||||
EntriesPerPage int `json:"entries_per_page"`
|
||||
KeyboardShortcuts bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime bool `json:"show_reading_time"`
|
||||
EntrySwipe bool `json:"entry_swipe"`
|
||||
GestureNav string `json:"gesture_nav"`
|
||||
LastLoginAt *time.Time `json:"last_login_at"`
|
||||
DisplayMode string `json:"display_mode"`
|
||||
DefaultReadingSpeed int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage string `json:"default_home_page"`
|
||||
CategoriesSortingOrder string `json:"categories_sorting_order"`
|
||||
}
|
||||
|
||||
// UserCreationRequest represents the request to create a user.
|
||||
|
@ -45,24 +48,27 @@ type UserCreationRequest struct {
|
|||
|
||||
// UserModificationRequest represents the request to update a user.
|
||||
type UserModificationRequest struct {
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
Theme *string `json:"theme"`
|
||||
Language *string `json:"language"`
|
||||
Timezone *string `json:"timezone"`
|
||||
EntryDirection *string `json:"entry_sorting_direction"`
|
||||
EntryOrder *string `json:"entry_sorting_order"`
|
||||
Stylesheet *string `json:"stylesheet"`
|
||||
GoogleID *string `json:"google_id"`
|
||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
DisplayMode *string `json:"display_mode"`
|
||||
DefaultReadingSpeed *int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed *int `json:"cjk_reading_speed"`
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
Theme *string `json:"theme"`
|
||||
Language *string `json:"language"`
|
||||
Timezone *string `json:"timezone"`
|
||||
EntryDirection *string `json:"entry_sorting_direction"`
|
||||
EntryOrder *string `json:"entry_sorting_order"`
|
||||
Stylesheet *string `json:"stylesheet"`
|
||||
GoogleID *string `json:"google_id"`
|
||||
OpenIDConnectID *string `json:"openid_connect_id"`
|
||||
EntriesPerPage *int `json:"entries_per_page"`
|
||||
IsAdmin *bool `json:"is_admin"`
|
||||
KeyboardShortcuts *bool `json:"keyboard_shortcuts"`
|
||||
ShowReadingTime *bool `json:"show_reading_time"`
|
||||
EntrySwipe *bool `json:"entry_swipe"`
|
||||
GestureNav *string `json:"gesture_nav"`
|
||||
DisplayMode *string `json:"display_mode"`
|
||||
DefaultReadingSpeed *int `json:"default_reading_speed"`
|
||||
CJKReadingSpeed *int `json:"cjk_reading_speed"`
|
||||
DefaultHomePage *string `json:"default_home_page"`
|
||||
CategoriesSortingOrder *string `json:"categories_sorting_order"`
|
||||
}
|
||||
|
||||
// Patch updates the User object with the modification request.
|
||||
|
@ -127,6 +133,10 @@ func (u *UserModificationRequest) Patch(user *User) {
|
|||
user.EntrySwipe = *u.EntrySwipe
|
||||
}
|
||||
|
||||
if u.GestureNav != nil {
|
||||
user.GestureNav = *u.GestureNav
|
||||
}
|
||||
|
||||
if u.DisplayMode != nil {
|
||||
user.DisplayMode = *u.DisplayMode
|
||||
}
|
||||
|
@ -138,6 +148,14 @@ func (u *UserModificationRequest) Patch(user *User) {
|
|||
if u.CJKReadingSpeed != nil {
|
||||
user.CJKReadingSpeed = *u.CJKReadingSpeed
|
||||
}
|
||||
|
||||
if u.DefaultHomePage != nil {
|
||||
user.DefaultHomePage = *u.DefaultHomePage
|
||||
}
|
||||
|
||||
if u.CategoriesSortingOrder != nil {
|
||||
user.CategoriesSortingOrder = *u.CategoriesSortingOrder
|
||||
}
|
||||
}
|
||||
|
||||
// UseTimezone converts last login date to the given timezone.
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
|
||||
Package oauth2 abstracts different OAuth2 providers.
|
||||
|
||||
*/
|
||||
package oauth2 // import "miniflux.app/oauth2"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
PKG_ARCH=`dpkg --print-architecture`
|
||||
PKG_DATE=`date -R`
|
||||
PKG_VERSION=`cd /src && git describe --tags --abbrev=0`
|
||||
PKG_ARCH=$(dpkg --print-architecture)
|
||||
PKG_DATE=$(date -R)
|
||||
PKG_VERSION=$(cd /src && git describe --tags --abbrev=0)
|
||||
|
||||
echo "PKG_VERSION=$PKG_VERSION"
|
||||
echo "PKG_ARCH=$PKG_ARCH"
|
||||
|
@ -22,6 +22,7 @@ cd /src && \
|
|||
cp /src/packaging/debian/miniflux.manpages /build/debian/miniflux.manpages && \
|
||||
cp /src/packaging/debian/miniflux.postinst /build/debian/miniflux.postinst && \
|
||||
cp /src/packaging/debian/rules /build/debian/rules && \
|
||||
cp /src/packaging/debian/miniflux.dirs /build/debian/miniflux.dirs && \
|
||||
echo "miniflux ($PKG_VERSION) experimental; urgency=low" > /build/debian/changelog && \
|
||||
echo " * Miniflux version $PKG_VERSION" >> /build/debian/changelog && \
|
||||
echo " -- Frédéric Guillot <f@miniflux.net> $PKG_DATE" >> /build/debian/changelog && \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Source: miniflux
|
||||
Maintainer: Frédéric Guillot <f@miniflux.net>
|
||||
Maintainer: Frederic Guillot <f@miniflux.net>
|
||||
Build-Depends: debhelper (>= 9), dh-systemd
|
||||
|
||||
Package: miniflux
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Files: *
|
||||
Copyright: 2017-2020 Frédéric Guillot
|
||||
Copyright: 2017-2020 Frederic Guillot
|
||||
License: Apache
|
|
@ -0,0 +1,2 @@
|
|||
etc
|
||||
usr/bin
|
|
@ -9,8 +9,6 @@ override_dh_auto_clean:
|
|||
override_dh_auto_test:
|
||||
override_dh_auto_build:
|
||||
override_dh_auto_install:
|
||||
mkdir -p $(DESTDIR)/etc
|
||||
mkdir -p $(DESTDIR)/usr/bin
|
||||
cp miniflux.conf $(DESTDIR)/etc/miniflux.conf
|
||||
cp miniflux $(DESTDIR)/usr/bin/miniflux
|
||||
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright 2020 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy // import "miniflux.app/proxy"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/reader/sanitizer"
|
||||
"miniflux.app/url"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// ImageProxyRewriter replaces image URLs with internal proxy URLs.
|
||||
func ImageProxyRewriter(router *mux.Router, data string) string {
|
||||
proxyImages := config.Opts.ProxyImages()
|
||||
if proxyImages == "none" {
|
||||
return data
|
||||
}
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
|
||||
doc.Find("img").Each(func(i int, img *goquery.Selection) {
|
||||
if srcAttrValue, ok := img.Attr("src"); ok {
|
||||
if !isDataURL(srcAttrValue) && (proxyImages == "all" || !url.IsHTTPS(srcAttrValue)) {
|
||||
img.SetAttr("src", ProxifyURL(router, srcAttrValue))
|
||||
}
|
||||
}
|
||||
|
||||
if srcsetAttrValue, ok := img.Attr("srcset"); ok {
|
||||
proxifySourceSet(img, router, proxyImages, srcsetAttrValue)
|
||||
}
|
||||
})
|
||||
|
||||
doc.Find("picture source").Each(func(i int, sourceElement *goquery.Selection) {
|
||||
if srcsetAttrValue, ok := sourceElement.Attr("srcset"); ok {
|
||||
proxifySourceSet(sourceElement, router, proxyImages, srcsetAttrValue)
|
||||
}
|
||||
})
|
||||
|
||||
output, err := doc.Find("body").First().Html()
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func proxifySourceSet(element *goquery.Selection, router *mux.Router, proxyImages, srcsetAttrValue string) {
|
||||
imageCandidates := sanitizer.ParseSrcSetAttribute(srcsetAttrValue)
|
||||
|
||||
for _, imageCandidate := range imageCandidates {
|
||||
if !isDataURL(imageCandidate.ImageURL) && (proxyImages == "all" || !url.IsHTTPS(imageCandidate.ImageURL)) {
|
||||
imageCandidate.ImageURL = ProxifyURL(router, imageCandidate.ImageURL)
|
||||
}
|
||||
}
|
||||
|
||||
element.SetAttr("srcset", imageCandidates.String())
|
||||
}
|
||||
|
||||
func isDataURL(s string) bool {
|
||||
return strings.HasPrefix(s, "data:")
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2020 Frédéric Guillot. All rights reserved.
|
||||
// Use of this source code is governed by the Apache 2.0
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy // import "miniflux.app/proxy"
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"miniflux.app/config"
|
||||
"miniflux.app/reader/sanitizer"
|
||||
"miniflux.app/url"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type urlProxyRewriter func(router *mux.Router, url string) string
|
||||
|
||||
// ProxyRewriter replaces media URLs with internal proxy URLs.
|
||||
func ProxyRewriter(router *mux.Router, data string) string {
|
||||
return genericProxyRewriter(router, ProxifyURL, data)
|
||||
}
|
||||
|
||||
// AbsoluteProxyRewriter do the same as ProxyRewriter except it uses absolute URLs.
|
||||
func AbsoluteProxyRewriter(router *mux.Router, host, data string) string {
|
||||
proxifyFunction := func(router *mux.Router, url string) string {
|
||||
return AbsoluteProxifyURL(router, host, url)
|
||||
}
|
||||
return genericProxyRewriter(router, proxifyFunction, data)
|
||||
}
|
||||
|
||||
func genericProxyRewriter(router *mux.Router, proxifyFunction urlProxyRewriter, data string) string {
|
||||
proxyOption := config.Opts.ProxyOption()
|
||||
if proxyOption == "none" {
|
||||
return data
|
||||
}
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
|
||||
for _, mediaType := range config.Opts.ProxyMediaTypes() {
|
||||
switch mediaType {
|
||||
case "image":
|
||||
doc.Find("img").Each(func(i int, img *goquery.Selection) {
|
||||
if srcAttrValue, ok := img.Attr("src"); ok {
|
||||
if !isDataURL(srcAttrValue) && (proxyOption == "all" || !url.IsHTTPS(srcAttrValue)) {
|
||||
img.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||
}
|
||||
}
|
||||
|
||||
if srcsetAttrValue, ok := img.Attr("srcset"); ok {
|
||||
proxifySourceSet(img, router, proxifyFunction, proxyOption, srcsetAttrValue)
|
||||
}
|
||||
})
|
||||
|
||||
doc.Find("picture source").Each(func(i int, sourceElement *goquery.Selection) {
|
||||
if srcsetAttrValue, ok := sourceElement.Attr("srcset"); ok {
|
||||
proxifySourceSet(sourceElement, router, proxifyFunction, proxyOption, srcsetAttrValue)
|
||||
}
|
||||
})
|
||||
|
||||
case "audio":
|
||||
doc.Find("audio").Each(func(i int, audio *goquery.Selection) {
|
||||
if srcAttrValue, ok := audio.Attr("src"); ok {
|
||||
if !isDataURL(srcAttrValue) && (proxyOption == "all" || !url.IsHTTPS(srcAttrValue)) {
|
||||
audio.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
doc.Find("audio source").Each(func(i int, sourceElement *goquery.Selection) {
|
||||
if srcAttrValue, ok := sourceElement.Attr("src"); ok {
|
||||
if !isDataURL(srcAttrValue) && (proxyOption == "all" || !url.IsHTTPS(srcAttrValue)) {
|
||||
sourceElement.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
case "video":
|
||||
doc.Find("video").Each(func(i int, video *goquery.Selection) {
|
||||
if srcAttrValue, ok := video.Attr("src"); ok {
|
||||
if !isDataURL(srcAttrValue) && (proxyOption == "all" || !url.IsHTTPS(srcAttrValue)) {
|
||||
video.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
doc.Find("video source").Each(func(i int, sourceElement *goquery.Selection) {
|
||||
if srcAttrValue, ok := sourceElement.Attr("src"); ok {
|
||||
if !isDataURL(srcAttrValue) && (proxyOption == "all" || !url.IsHTTPS(srcAttrValue)) {
|
||||
sourceElement.SetAttr("src", proxifyFunction(router, srcAttrValue))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
output, err := doc.Find("body").First().Html()
|
||||
if err != nil {
|
||||
return data
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func proxifySourceSet(element *goquery.Selection, router *mux.Router, proxifyFunction urlProxyRewriter, proxyOption, srcsetAttrValue string) {
|
||||
imageCandidates := sanitizer.ParseSrcSetAttribute(srcsetAttrValue)
|
||||
|
||||
for _, imageCandidate := range imageCandidates {
|
||||
if !isDataURL(imageCandidate.ImageURL) && (proxyOption == "all" || !url.IsHTTPS(imageCandidate.ImageURL)) {
|
||||
imageCandidate.ImageURL = proxifyFunction(router, imageCandidate.ImageURL)
|
||||
}
|
||||
}
|
||||
|
||||
element.SetAttr("srcset", imageCandidates.String())
|
||||
}
|
||||
|
||||
func isDataURL(s string) bool {
|
||||
return strings.HasPrefix(s, "data:")
|
||||
}
|
|
@ -15,7 +15,9 @@ import (
|
|||
|
||||
func TestProxyFilterWithHttpDefault(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "http-only")
|
||||
os.Setenv("PROXY_OPTION", "http-only")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -25,11 +27,11 @@ func TestProxyFilterWithHttpDefault(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
||||
|
@ -38,7 +40,8 @@ func TestProxyFilterWithHttpDefault(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpsDefault(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "http-only")
|
||||
os.Setenv("PROXY_OPTION", "http-only")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -48,10 +51,10 @@ func TestProxyFilterWithHttpsDefault(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -61,7 +64,7 @@ func TestProxyFilterWithHttpsDefault(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpNever(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "none")
|
||||
os.Setenv("PROXY_OPTION", "none")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -71,10 +74,10 @@ func TestProxyFilterWithHttpNever(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := input
|
||||
|
||||
if expected != output {
|
||||
|
@ -84,7 +87,7 @@ func TestProxyFilterWithHttpNever(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpsNever(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "none")
|
||||
os.Setenv("PROXY_OPTION", "none")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -94,10 +97,10 @@ func TestProxyFilterWithHttpsNever(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := input
|
||||
|
||||
if expected != output {
|
||||
|
@ -107,7 +110,9 @@ func TestProxyFilterWithHttpsNever(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpAlways(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -117,11 +122,11 @@ func TestProxyFilterWithHttpAlways(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
||||
|
@ -130,7 +135,9 @@ func TestProxyFilterWithHttpAlways(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpsAlways(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -140,11 +147,36 @@ func TestProxyFilterWithHttpsAlways(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/LdPNR1GBDigeeNp2ArUQRyZsVqT_PWLfHGjYFrrWWIY=/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyFilterWithHttpsAlwaysAndCustomProxyServer(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_URL", "https://proxy-example/proxy")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
config.Opts, err = parser.ParseEnvironmentVariables()
|
||||
if err != nil {
|
||||
t.Fatalf(`Parsing failure: %v`, err)
|
||||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="https://proxy-example/proxy/aHR0cHM6Ly93ZWJzaXRlL2ZvbGRlci9pbWFnZS5wbmc=" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
||||
|
@ -153,7 +185,8 @@ func TestProxyFilterWithHttpsAlways(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpInvalid(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "invalid")
|
||||
os.Setenv("PROXY_OPTION", "invalid")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -163,11 +196,11 @@ func TestProxyFilterWithHttpInvalid(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got "%s" instead of "%s"`, output, expected)
|
||||
|
@ -176,7 +209,8 @@ func TestProxyFilterWithHttpInvalid(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithHttpsInvalid(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "invalid")
|
||||
os.Setenv("PROXY_OPTION", "invalid")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -186,10 +220,10 @@ func TestProxyFilterWithHttpsInvalid(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
output := ProxyRewriter(r, input)
|
||||
expected := `<p><img src="https://website/folder/image.png" alt="Test"/></p>`
|
||||
|
||||
if expected != output {
|
||||
|
@ -199,7 +233,9 @@ func TestProxyFilterWithHttpsInvalid(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithSrcset(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -209,11 +245,11 @@ func TestProxyFilterWithSrcset(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" srcset="http://website/folder/image2.png 656w, http://website/folder/image3.png 360w" alt="test"></p>`
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w" alt="test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/QgAmrJWiAud_nNAsz3F8OTxaIofwAiO36EDzH_YfMzo=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w" alt="test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got %s`, output)
|
||||
|
@ -222,7 +258,9 @@ func TestProxyFilterWithSrcset(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithEmptySrcset(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -232,11 +270,11 @@ func TestProxyFilterWithEmptySrcset(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<p><img src="http://website/folder/image.png" srcset="" alt="test"></p>`
|
||||
expected := `<p><img src="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="" alt="test"/></p>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<p><img src="/proxy/okK5PsdNY8F082UMQEAbLPeUFfbe2WnNfInNmR9T4WA=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlLnBuZw==" srcset="" alt="test"/></p>`
|
||||
output := ProxyRewriter(r, input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got %s`, output)
|
||||
|
@ -245,7 +283,9 @@ func TestProxyFilterWithEmptySrcset(t *testing.T) {
|
|||
|
||||
func TestProxyFilterWithPictureSource(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -255,11 +295,11 @@ func TestProxyFilterWithPictureSource(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<picture><source srcset="http://website/folder/image2.png 656w, http://website/folder/image3.png 360w, https://website/some,image.png 2x"></picture>`
|
||||
expected := `<picture><source srcset="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w, /proxy/aHR0cHM6Ly93ZWJzaXRlL3NvbWUsaW1hZ2UucG5n 2x"/></picture>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<picture><source srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, /proxy/QgAmrJWiAud_nNAsz3F8OTxaIofwAiO36EDzH_YfMzo=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMy5wbmc= 360w, /proxy/ZIw0hv8WhSTls5aSqhnFaCXlUrKIqTnBRaY0-NaLnds=/aHR0cHM6Ly93ZWJzaXRlL3NvbWUsaW1hZ2UucG5n 2x"/></picture>`
|
||||
output := ProxyRewriter(r, input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got %s`, output)
|
||||
|
@ -268,7 +308,9 @@ func TestProxyFilterWithPictureSource(t *testing.T) {
|
|||
|
||||
func TestProxyFilterOnlyNonHTTPWithPictureSource(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "https")
|
||||
os.Setenv("PROXY_OPTION", "https")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
os.Setenv("PROXY_PRIVATE_KEY", "test")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -278,20 +320,21 @@ func TestProxyFilterOnlyNonHTTPWithPictureSource(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<picture><source srcset="http://website/folder/image2.png 656w, https://website/some,image.png 2x"></picture>`
|
||||
expected := `<picture><source srcset="/proxy/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, https://website/some,image.png 2x"/></picture>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
expected := `<picture><source srcset="/proxy/aY5Hb4urDnUCly2vTJ7ExQeeaVS-52O7kjUr2v9VrAs=/aHR0cDovL3dlYnNpdGUvZm9sZGVyL2ltYWdlMi5wbmc= 656w, https://website/some,image.png 2x"/></picture>`
|
||||
output := ProxyRewriter(r, input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got %s`, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageProxyWithImageDataURL(t *testing.T) {
|
||||
func TestProxyWithImageDataURL(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -301,20 +344,21 @@ func TestImageProxyWithImageDataURL(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<img src="data:image/gif;base64,test">`
|
||||
expected := `<img src="data:image/gif;base64,test"/>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
output := ProxyRewriter(r, input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got %s`, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageProxyWithImageSourceDataURL(t *testing.T) {
|
||||
func TestProxyWithImageSourceDataURL(t *testing.T) {
|
||||
os.Clearenv()
|
||||
os.Setenv("PROXY_IMAGES", "all")
|
||||
os.Setenv("PROXY_OPTION", "all")
|
||||
os.Setenv("PROXY_MEDIA_TYPES", "image")
|
||||
|
||||
var err error
|
||||
parser := config.NewParser()
|
||||
|
@ -324,11 +368,11 @@ func TestImageProxyWithImageSourceDataURL(t *testing.T) {
|
|||
}
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/proxy/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
r.HandleFunc("/proxy/{encodedDigest}/{encodedURL}", func(w http.ResponseWriter, r *http.Request) {}).Name("proxy")
|
||||
|
||||
input := `<picture><source srcset="data:image/gif;base64,test"/></picture>`
|
||||
expected := `<picture><source srcset="data:image/gif;base64,test"/></picture>`
|
||||
output := ImageProxyRewriter(r, input)
|
||||
output := ProxyRewriter(r, input)
|
||||
|
||||
if expected != output {
|
||||
t.Errorf(`Not expected output: got %s`, output)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue