Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 73a0a25b6c | |||
| 2786b8f163 | |||
| 9292d5d604 | |||
| 3ba280e10c | |||
| 4eceeaaca4 | |||
| 205aef595b | |||
| a006a93a04 | |||
| 30e80c675e | |||
| dda9114692 | |||
| 16c2dc4a8c | |||
| 7b0bfd9308 | |||
| c6fd9eb9b1 | |||
| 0fb87eba3f | |||
| 1e70ca1a19 |
@@ -1,3 +1,13 @@
|
||||
Version 2.0.3 (Feb 19, 2018)
|
||||
----------------------------
|
||||
|
||||
* Add Polish translation
|
||||
* Change color of <q> tags for black theme
|
||||
* Add database indexes (don't forget to run database migrations)
|
||||
* Handle Atom feeds with HTML title
|
||||
* Strip invalid XML characters to avoid parsing errors
|
||||
* Improve error handling for HTTP client
|
||||
|
||||
Version 2.0.2 (Feb 5, 2018)
|
||||
---------------------------
|
||||
|
||||
|
||||
+37
-2
@@ -7,21 +7,36 @@ package http
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miniflux/miniflux/errors"
|
||||
"github.com/miniflux/miniflux/logger"
|
||||
"github.com/miniflux/miniflux/timer"
|
||||
"github.com/miniflux/miniflux/version"
|
||||
)
|
||||
|
||||
const requestTimeout = 300
|
||||
const maxBodySize = 1024 * 1024 * 15
|
||||
const (
|
||||
// 20 seconds max.
|
||||
requestTimeout = 20
|
||||
|
||||
// 15MB max.
|
||||
maxBodySize = 1024 * 1024 * 15
|
||||
)
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// Client is a HTTP Client :)
|
||||
type Client struct {
|
||||
@@ -77,6 +92,26 @@ func (c *Client) executeRequest(request *http.Request) (*Response, error) {
|
||||
client := c.buildClient()
|
||||
resp, err := client.Do(request)
|
||||
if err != nil {
|
||||
if uerr, ok := err.(*url.Error); ok {
|
||||
switch uerr.Err.(type) {
|
||||
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)
|
||||
}
|
||||
case net.Error:
|
||||
nerr := uerr.Err.(net.Error)
|
||||
if nerr.Timeout() {
|
||||
err = errors.NewLocalizedError(errRequestTimeout, requestTimeout)
|
||||
} else if nerr.Temporary() {
|
||||
err = errors.NewLocalizedError(errTemporaryNetworkOperation, nerr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
Regular → Executable
+1
@@ -30,5 +30,6 @@ func AvailableLanguages() map[string]string {
|
||||
"en_US": "English",
|
||||
"fr_FR": "Français",
|
||||
"de_DE": "Deutsch",
|
||||
"pl_PL": "Polski",
|
||||
}
|
||||
}
|
||||
|
||||
Regular → Executable
+249
-16
@@ -1,13 +1,13 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-02-01 14:52:01.163022 +0100 CET m=+0.026333224
|
||||
// 2018-02-19 22:49:00.3105786 +0100 STD m=+0.012978001
|
||||
|
||||
package locale
|
||||
|
||||
var translations = map[string]string{
|
||||
"de_DE": `{
|
||||
"plural.feed.error_count": [
|
||||
"%d Error",
|
||||
"%d Errors"
|
||||
"%d Fehler",
|
||||
"%d Fehler"
|
||||
],
|
||||
"plural.categories.feed_count": [
|
||||
"Es gibt %d Abonnement.",
|
||||
@@ -90,8 +90,8 @@ var translations = map[string]string{
|
||||
"Last Login": "Letzte Anmeldung",
|
||||
"Yes": "Ja",
|
||||
"No": "Nein",
|
||||
"This feed already exists (%s).": "Diese Abonnement existiert bereits (%s).",
|
||||
"Unable to fetch feed (statusCode=%d).": "Abonnement konnte nicht abgerufen werden (code=%d).",
|
||||
"This feed already exists (%s)": "Diese Abonnement existiert bereits (%s)",
|
||||
"Unable to fetch feed (statusCode=%d)": "Abonnement konnte nicht abgerufen werden (code=%d)",
|
||||
"Unable to open this link: %v": "Dieser Link konnte nicht geöffnet werden: %v",
|
||||
"Unable to analyze this page: %v": "Diese Seite konnte nicht analysiert werden: %v",
|
||||
"Unable to find any subscription.": "Es wurden keine Abonnements gefunden.",
|
||||
@@ -114,14 +114,14 @@ var translations = map[string]string{
|
||||
"Invalid username or password.": "Benutzername oder Passwort ungültig.",
|
||||
"Never": "Niemals",
|
||||
"Unable to execute request: %v": "Diese Anfrage konnte nicht ausgeführt werden: %v",
|
||||
"Last Parsing Error": "Letzter Analyse Error",
|
||||
"Last Parsing Error": "Letzter Analysefehler",
|
||||
"There is a problem with this feed": "Es gibt ein Problem mit diesem Abonnement",
|
||||
"Unable to parse OPML file: %v.": "OPML Datei konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse RSS feed: %v.": "RSS Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse Atom feed: %v.": "Atom Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse JSON feed: %v.": "JSON Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse RDF feed: %v.": "RDF Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to normalize encoding: %v.": "Zeichenkodierung konnte nicht normalisiert werden: %v.",
|
||||
"Unable to normalize encoding: %v": "Zeichenkodierung konnte nicht normalisiert werden: %v",
|
||||
"Unable to create this category.": "Diese Kategorie konnte nicht angelegt werden.",
|
||||
"yes": "ja",
|
||||
"no": "nein",
|
||||
@@ -139,7 +139,7 @@ var translations = map[string]string{
|
||||
"Sign in with Google": "Anmeldung mit Google",
|
||||
"Unlink my Google account": "Google Konto abmelden",
|
||||
"Link my Google account": "Google Konto verknüpfen",
|
||||
"Category not found for this user.": "Diese Kategorie existiert nicht für diesen Benutzer.",
|
||||
"Category not found for this user": "Diese Kategorie existiert nicht für diesen Benutzer",
|
||||
"Invalid theme.": "Dieses Thema ist fehlerhaft.",
|
||||
"Entry Sorting": "Sortierung der Artikel",
|
||||
"Older entries first": "Älteste Artikel zuerst",
|
||||
@@ -215,7 +215,12 @@ var translations = map[string]string{
|
||||
"Fever API endpoint:": "Fever API Endpunkt:",
|
||||
"Miniflux API": "Miniflux API",
|
||||
"API Endpoint": "API Endpunkt",
|
||||
"Your account password": "Ihr Konto Passwort"
|
||||
"Your account password": "Ihr Konto Passwort",
|
||||
"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)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Webseite nicht erreichbar, die Anfrage endete nach %d Sekunden"
|
||||
}
|
||||
`,
|
||||
"en_US": `{
|
||||
@@ -314,8 +319,8 @@ var translations = map[string]string{
|
||||
"Last Login": "Dernière connexion",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"This feed already exists (%s).": "Cet abonnement existe déjà (%s).",
|
||||
"Unable to fetch feed (statusCode=%d).": "Impossible de récupérer cet abonnement (code=%d).",
|
||||
"This feed already exists (%s)": "Cet abonnement existe déjà (%s)",
|
||||
"Unable to fetch feed (statusCode=%d)": "Impossible de récupérer cet abonnement (code=%d)",
|
||||
"Unable to open this link: %v": "Impossible d'ouvrir ce lien : %v",
|
||||
"Unable to analyze this page: %v": "Impossible d'analyzer cette page : %v",
|
||||
"Unable to find any subscription.": "Impossible de trouver un abonnement.",
|
||||
@@ -345,7 +350,7 @@ var translations = map[string]string{
|
||||
"Unable to parse Atom feed: %v.": "Impossible de lire ce flux Atom: %v.",
|
||||
"Unable to parse JSON feed: %v.": "Impossible de lire ce flux JSON: %v.",
|
||||
"Unable to parse RDF feed: %v.": "Impossible de lire ce flux RDF: %v.",
|
||||
"Unable to normalize encoding: %v.": "Impossible de normaliser l'encodage : %v.",
|
||||
"Unable to normalize encoding: %v": "Impossible de normaliser l'encodage : %v",
|
||||
"Unable to create this category.": "Impossible de créer cette catégorie.",
|
||||
"yes": "oui",
|
||||
"no": "non",
|
||||
@@ -363,7 +368,7 @@ var translations = map[string]string{
|
||||
"Sign in with Google": "Se connecter avec Google",
|
||||
"Unlink my Google account": "Dissocier mon compte Google",
|
||||
"Link my Google account": "Associer mon compte Google",
|
||||
"Category not found for this user.": "Cette catégorie n'existe pas pour cet utilisateur.",
|
||||
"Category not found for this user": "Cette catégorie n'existe pas pour cet utilisateur",
|
||||
"Invalid theme.": "Le thème est invalide.",
|
||||
"Entry Sorting": "Ordre des éléments",
|
||||
"Older entries first": "Ancien éléments en premier",
|
||||
@@ -414,6 +419,7 @@ var translations = map[string]string{
|
||||
"Items Navigation": "Naviguation entre les éléments",
|
||||
"Go to previous item": "Élément précédent",
|
||||
"Go to next item": "Élément suivant",
|
||||
"Pages Navigation": "Naviguation entre les pages",
|
||||
"Go to previous page": "Page précédente",
|
||||
"Go to next page": "Page suivante",
|
||||
"Open selected item": "Ouvrir élément sélectionné",
|
||||
@@ -438,13 +444,240 @@ var translations = map[string]string{
|
||||
"Fever API endpoint:": "Point de terminaison de l'API Fever :",
|
||||
"Miniflux API": "API de Miniflux",
|
||||
"API Endpoint": "Point de terminaison de l'API",
|
||||
"Your account password": "Le mot de passe de votre compte"
|
||||
"Your account password": "Le mot de passe de votre compte",
|
||||
"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)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes"
|
||||
}
|
||||
`,
|
||||
"pl_PL": `{
|
||||
"plural.feed.error_count": [
|
||||
"%d błąd",
|
||||
"%d błąd",
|
||||
"%d błędów"
|
||||
],
|
||||
"plural.categories.feed_count": [
|
||||
"Jest %d kanał.",
|
||||
"Są %d kanały.",
|
||||
"Jest %d kanałów."
|
||||
],
|
||||
"Username": "Nazwa użytkownika",
|
||||
"Password": "Hasło",
|
||||
"Unread": "Nieprzeczytane",
|
||||
"History": "Historia",
|
||||
"Feeds": "Kanały",
|
||||
"Categories": "Kategorie",
|
||||
"Settings": "Ustawienia",
|
||||
"Logout": "Wyloguj się",
|
||||
"Next": "Następny",
|
||||
"Previous": "Poprzedni",
|
||||
"New Subscription": "Nowa subskrypcja",
|
||||
"Import": "Importuj",
|
||||
"Export": "Eksportuj",
|
||||
"There is no category. You must have at least one category.": "Nie ma żadnej kategorii. Musisz mieć co najmniej jedną kategorię",
|
||||
"URL": "URL",
|
||||
"Category": "Kategoria",
|
||||
"Find a subscription": "Znajdź subskrypcję",
|
||||
"Loading...": "Ładowanie...",
|
||||
"Create a category": "Utwórz kategorię",
|
||||
"There is no category.": "Nie masz żadnej kategorii",
|
||||
"Edit": "Edytuj",
|
||||
"Remove": "Usuń",
|
||||
"No feed.": "Brak kanałów.",
|
||||
"There is no article in this category.": "W tej kategorii nie ma żadnych artykułów",
|
||||
"Original": "Oryginalny artykuł",
|
||||
"Mark this page as read": "Oznacz jako przeczytane",
|
||||
"not yet": "jeszcze nie",
|
||||
"just now": "przed chwilą",
|
||||
"1 minute ago": "minutę temu",
|
||||
"%d minutes ago": "%d minut temu",
|
||||
"1 hour ago": "godzinę temu",
|
||||
"%d hours ago": "%d godzin temu",
|
||||
"yesterday": "wczoraj",
|
||||
"%d days ago": "%d dni temu",
|
||||
"%d weeks ago": "%d tygodni temu",
|
||||
"%d months ago": "%d miesięcy temu",
|
||||
"%d years ago": "%d lat temu",
|
||||
"Date": "Data",
|
||||
"IP Address": "Adres IP",
|
||||
"User Agent": "User Agent",
|
||||
"Actions": "Działania",
|
||||
"Current session": "Bieżąca sesja",
|
||||
"Sessions": "Sesje",
|
||||
"Users": "Użytkownicy",
|
||||
"Add user": "Dodaj użytkownika",
|
||||
"Choose a Subscription": "Wybierz subskrypcję",
|
||||
"Subscribe": "Subskrypcja",
|
||||
"New Category": "Nowa kategoria",
|
||||
"Title": "Tytuł",
|
||||
"Save": "Zapisz",
|
||||
"or": "lub",
|
||||
"cancel": "anuluj",
|
||||
"New User": "Nowy użytkownik",
|
||||
"Confirmation": "Potwierdź",
|
||||
"Administrator": "Administrator",
|
||||
"Edit Category: %s": "Edycja Kategorii: %s",
|
||||
"Update": "Zaktualizuj",
|
||||
"Edit Feed: %s": "Edytuj kanał: %s",
|
||||
"There is no category!": "Nie ma żadnej kategorii!",
|
||||
"Edit user: %s": "Edytuj użytkownika: %s",
|
||||
"There is no article for this feed.": "Nie ma artykułu dla tego kanału.",
|
||||
"Add subscription": "Dodaj subskrypcję",
|
||||
"You don't have any subscription.": "Nie masz żadnej subskrypcji",
|
||||
"Last check:": "Ostatnia aktualizacja:",
|
||||
"Refresh": "Odśwież",
|
||||
"There is no history at the moment.": "Obecnie nie ma żadnej historii.",
|
||||
"OPML file": "Plik OPML",
|
||||
"Sign In": "Zaloguj się",
|
||||
"Sign in": "Zaloguj się",
|
||||
"Theme": "Wygląd",
|
||||
"Timezone": "Strefa czasowa",
|
||||
"Language": "Język",
|
||||
"There is no unread article.": "Nie ma żadnych nieprzeczytanych artykułów.",
|
||||
"You are the only user.": "Jesteś jedynym użytkownikiem.",
|
||||
"Last Login": "Ostatnie logowanie",
|
||||
"Yes": "Tak",
|
||||
"No": "Nie",
|
||||
"This feed already exists (%s)": "Ten kanał już istnieje (%s)",
|
||||
"Unable to fetch feed (statusCode=%d)": "Kanał nie mógł zostać pobrany (kod=%d)",
|
||||
"Unable to open this link: %v": "Nie można było otworzyć tego linku: %v",
|
||||
"Unable to analyze this page: %v": "Nie można przeanalizować tej strony: %v",
|
||||
"Unable to find any subscription.": "Nie znaleziono żadnych subskrypcji.",
|
||||
"The URL and the category are mandatory.": "URL i kategoria są obowiązkowe.",
|
||||
"All fields are mandatory.": "Wszystkie pola są obowiązkowe.",
|
||||
"Passwords are not the same.": "Hasła nie są identyczne.",
|
||||
"You must use at least 6 characters.": "Musisz użyć co najmniej 6 znaków.",
|
||||
"The username is mandatory.": "Nazwa użytkownika jest obowiązkowa.",
|
||||
"The username, theme, language and timezone fields are mandatory.": "Pola nazwy użytkownika, tematu, języka i strefy czasowej są obowiązkowe.",
|
||||
"The title is mandatory.": "Tytuł jest obowiązkowy.",
|
||||
"About": "O stronie",
|
||||
"version": "Wersja",
|
||||
"Version:": "Wersja :",
|
||||
"Build Date:": "Data opracowania:",
|
||||
"Author:": "Autor:",
|
||||
"Authors": "Autorzy",
|
||||
"License:": "Licencja:",
|
||||
"Attachments": "Załączniki",
|
||||
"Download": "Pobierz",
|
||||
"Invalid username or password.": "Nieprawidłowa nazwa użytkownika lub hasło.",
|
||||
"Never": "Nigdy",
|
||||
"Unable to execute request: %v": "To polecenie nie mogło zostać wykonane: %v",
|
||||
"Last Parsing Error": "Ostatni błąd analizy",
|
||||
"There is a problem with this feed": "Z tym kanałem jest problem",
|
||||
"Unable to parse OPML file: %v.": "Plik OPML nie mógł zostać odczytany: %v.",
|
||||
"Unable to parse RSS feed: %v.": "Nie można było odczytać kanału RSS: %v.",
|
||||
"Unable to parse Atom feed: %v.": "Nie można było odczytać kanału Atom: %v.",
|
||||
"Unable to parse JSON feed: %v.": "Nie można było odczytać kanału JSON: %v.",
|
||||
"Unable to parse RDF feed: %v.": "Nie można było odczytać kanału RDF: %v.",
|
||||
"Unable to normalize encoding: %v": "Kodowanie znaków nie mogło zostać znormalizowane: %v",
|
||||
"Unable to create this category.": "Ta kategoria nie mogła zostać utworzona.",
|
||||
"yes": "tak",
|
||||
"no": "nie",
|
||||
"Are you sure?": "Czy jesteś pewny?",
|
||||
"Work in progress...": "W toku...",
|
||||
"This user already exists.": "Ten użytkownik już istnieje.",
|
||||
"This category already exists.": "Ta kategoria już istnieje.",
|
||||
"Unable to update this category.": "Ta kategoria nie mogła zostać zaktualizowana.",
|
||||
"Integrations": "Usługi",
|
||||
"Bookmarklet": "Bookmarklet",
|
||||
"Drag and drop this link to your bookmarks.": "Przeciągnij i upuść to łącze do zakładek.",
|
||||
"This special link allows you to subscribe to a website directly by using a bookmark in your web browser.": "Ten link umożliwia subskrypcję strony internetowej bezpośrednio za pomocą zakładki w przeglądarce internetowej",
|
||||
"Add to Miniflux": "Dodaj do Miniflux",
|
||||
"Refresh all feeds in background": "Odśwież wszystkie subskrypcje w tle",
|
||||
"Sign in with Google": "Zaloguj przez Google",
|
||||
"Unlink my Google account": "Odłącz moje konto Google",
|
||||
"Link my Google account": "Połącz z moim kontem Google",
|
||||
"Category not found for this user": "Kategoria nie znaleziona dla tego użytkownika",
|
||||
"Invalid theme.": "Ten temat jest nieprawidłowy.",
|
||||
"Entry Sorting": "Sortowanie artykułów",
|
||||
"Older entries first": "Najstarsze wpisy jako pierwsze",
|
||||
"Recent entries first": "Najnowsze wpisy jako pierwsze",
|
||||
"Saving...": "Zapisywanie...",
|
||||
"Done!": "Gotowe!",
|
||||
"Save this article": "Zapisz ten artykuł",
|
||||
"Mark bookmark as unread": "Zaznacz zakładkę jako nieprzeczytaną",
|
||||
"Pinboard Tags": "Pinboard Tags",
|
||||
"Pinboard API Token": "Token Pinboard API",
|
||||
"Save articles to Pinboard": "Zapisz artykuł w Pinboard",
|
||||
"Save articles to Instapaper": "Zapisz artykuł w Instapaper",
|
||||
"Instapaper Username": "Login do Instapaper",
|
||||
"Instapaper Password": "Hasło do Instapaper",
|
||||
"Activate Fever API": "Aktywuj Fever API",
|
||||
"Fever Username": "Login do Fever",
|
||||
"Fever Password": "Hasło do Fever",
|
||||
"Fetch original content": "Pobierz oryginalną treść",
|
||||
"Scraper Rules": "Zasady ekstrakcji",
|
||||
"Rewrite Rules": "Reguły zapisu",
|
||||
"Preferences saved!": "Ustawienia zapisane!",
|
||||
"Your external account is now linked !": "Twoje zewnętrzne konto jest teraz połączone!",
|
||||
"Save articles to Wallabag": "Zapisz artykuły do Wallabag",
|
||||
"Wallabag API Endpoint": "Wallabag URL",
|
||||
"Wallabag Client ID": "Wallabag Client-ID",
|
||||
"Wallabag Client Secret": "Wallabag Client Secret",
|
||||
"Wallabag Username": "Login do Wallabag",
|
||||
"Wallabag Password": "Hasło do Wallabag",
|
||||
"Keyboard Shortcut: %s": "Skróty klawiszowe: %s",
|
||||
"Favorites": "Ulubione",
|
||||
"Star": "Oznacz gwiazdką",
|
||||
"Unstar": "Usuń gwiazdkę",
|
||||
"Starred": "Oznaczone gwiazdką",
|
||||
"There is no bookmark at the moment.": "Obecnie nie ma żadnych zakładek.",
|
||||
"Last checked:": "Ostatnio sprawdzone:",
|
||||
"ETag header:": "Nagłówek ETag:",
|
||||
"LastModified header:": "Ostatnio zmienione:",
|
||||
"None": "Brak",
|
||||
"Keyboard Shortcuts": "Skróty klawiszowe",
|
||||
"Sections Navigation": "Nawigacja między punktami menu",
|
||||
"Go to unread": "Przejdź do nieprzeczytanych artykułów",
|
||||
"Go to bookmarks": "Przejdź do zakładek",
|
||||
"Go to history": "Przejdź do historii",
|
||||
"Go to feeds": "Przejdź do kanałów",
|
||||
"Go to categories": "Przejdź do kategorii",
|
||||
"Go to settings": "Przejdź do ustawień",
|
||||
"Show keyboard shortcuts": "Pokaż listę skrótów klawiszowych",
|
||||
"Items Navigation": "Nawigacja między artykułami",
|
||||
"Go to previous item": "Przejdź do poprzedniego artykułu",
|
||||
"Go to next item": "Przejdź do następnego punktu artykułu",
|
||||
"Pages Navigation": "Nawigacja między stronami",
|
||||
"Go to previous page": "Przejdź do poprzedniej strony",
|
||||
"Go to next page": "Przejdź do następnej strony",
|
||||
"Open selected item": "Otwórz zaznaczony artykuł",
|
||||
"Open original link": "Otwórz oryginalny artykuł",
|
||||
"Toggle read/unread": "Oznacz jako przeczytane/nieprzeczytane",
|
||||
"Mark current page as read": "Zaznacz aktualną stronę jako przeczytaną",
|
||||
"Download original content": "Pobierz oryginalną zawartość",
|
||||
"Toggle bookmark": "Dodaj/usuń zakładki",
|
||||
"Close modal dialog": "Zamknij listę skrótów klawiszowych",
|
||||
"Save article": "Zapisz artykuł",
|
||||
"There is already someone associated with this provider!": "Już ktoś jest powiązany z tym dostawcą!",
|
||||
"There is already someone else with the same Fever username!": "Już ktoś inny używa tej nazwy użytkownika Fever!",
|
||||
"Mark all as read": "Oznacz wszystko jako przeczytane",
|
||||
"This feed is empty": "Ten kanał jest pusty",
|
||||
"Flush history": "Usuń historię",
|
||||
"Site URL": "URL strony",
|
||||
"Feed URL": "URL kanału",
|
||||
"Logged as %s": "Zalogowany jako %s",
|
||||
"Unread Items": "Nieprzeczytane",
|
||||
"Change entry status": "Zmień status artykułu",
|
||||
"Read": "Przeczytane",
|
||||
"Fever API endpoint:": "Fever API endpoint:",
|
||||
"Miniflux API": "Miniflux API",
|
||||
"API Endpoint": "API endpoint",
|
||||
"Your account password": "Hasło konta",
|
||||
"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)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Strona internetowa nieosiągalna, żądanie wygasło po %d sekundach"
|
||||
}
|
||||
`,
|
||||
}
|
||||
|
||||
var translationsChecksums = map[string]string{
|
||||
"de_DE": "f4a66bffedb7bf99294281da5a1fa78901509db67a9fba15a160b656feb1425a",
|
||||
"de_DE": "53f7637318ac418ce0e3dd483923dab39ff4f4062b4909e5e03efcc3b693e5d6",
|
||||
"en_US": "6fe95384260941e8a5a3c695a655a932e0a8a6a572c1e45cb2b1ae8baa01b897",
|
||||
"fr_FR": "e6305fd54508a4f54d630e3ef231a8eadc2335ed0ffac7b5266b2ec751824e28",
|
||||
"fr_FR": "ae61f82ac14bc2c6c6a3c2d38cf1ad8309ac2eef19b0726b2969ac155ccddc14",
|
||||
"pl_PL": "9c10899ec62f97ebb6d5d4d88cde8c68ac584e514ce840b51ba3aeff9ea3efe3",
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"plural.feed.error_count": [
|
||||
"%d Error",
|
||||
"%d Errors"
|
||||
"%d Fehler",
|
||||
"%d Fehler"
|
||||
],
|
||||
"plural.categories.feed_count": [
|
||||
"Es gibt %d Abonnement.",
|
||||
@@ -84,8 +84,8 @@
|
||||
"Last Login": "Letzte Anmeldung",
|
||||
"Yes": "Ja",
|
||||
"No": "Nein",
|
||||
"This feed already exists (%s).": "Diese Abonnement existiert bereits (%s).",
|
||||
"Unable to fetch feed (statusCode=%d).": "Abonnement konnte nicht abgerufen werden (code=%d).",
|
||||
"This feed already exists (%s)": "Diese Abonnement existiert bereits (%s)",
|
||||
"Unable to fetch feed (statusCode=%d)": "Abonnement konnte nicht abgerufen werden (code=%d)",
|
||||
"Unable to open this link: %v": "Dieser Link konnte nicht geöffnet werden: %v",
|
||||
"Unable to analyze this page: %v": "Diese Seite konnte nicht analysiert werden: %v",
|
||||
"Unable to find any subscription.": "Es wurden keine Abonnements gefunden.",
|
||||
@@ -108,14 +108,14 @@
|
||||
"Invalid username or password.": "Benutzername oder Passwort ungültig.",
|
||||
"Never": "Niemals",
|
||||
"Unable to execute request: %v": "Diese Anfrage konnte nicht ausgeführt werden: %v",
|
||||
"Last Parsing Error": "Letzter Analyse Error",
|
||||
"Last Parsing Error": "Letzter Analysefehler",
|
||||
"There is a problem with this feed": "Es gibt ein Problem mit diesem Abonnement",
|
||||
"Unable to parse OPML file: %v.": "OPML Datei konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse RSS feed: %v.": "RSS Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse Atom feed: %v.": "Atom Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse JSON feed: %v.": "JSON Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to parse RDF feed: %v.": "RDF Feed konnte nicht gelesen werden: %v.",
|
||||
"Unable to normalize encoding: %v.": "Zeichenkodierung konnte nicht normalisiert werden: %v.",
|
||||
"Unable to normalize encoding: %v": "Zeichenkodierung konnte nicht normalisiert werden: %v",
|
||||
"Unable to create this category.": "Diese Kategorie konnte nicht angelegt werden.",
|
||||
"yes": "ja",
|
||||
"no": "nein",
|
||||
@@ -133,7 +133,7 @@
|
||||
"Sign in with Google": "Anmeldung mit Google",
|
||||
"Unlink my Google account": "Google Konto abmelden",
|
||||
"Link my Google account": "Google Konto verknüpfen",
|
||||
"Category not found for this user.": "Diese Kategorie existiert nicht für diesen Benutzer.",
|
||||
"Category not found for this user": "Diese Kategorie existiert nicht für diesen Benutzer",
|
||||
"Invalid theme.": "Dieses Thema ist fehlerhaft.",
|
||||
"Entry Sorting": "Sortierung der Artikel",
|
||||
"Older entries first": "Älteste Artikel zuerst",
|
||||
@@ -209,5 +209,10 @@
|
||||
"Fever API endpoint:": "Fever API Endpunkt:",
|
||||
"Miniflux API": "Miniflux API",
|
||||
"API Endpoint": "API Endpunkt",
|
||||
"Your account password": "Ihr Konto Passwort"
|
||||
"Your account password": "Ihr Konto Passwort",
|
||||
"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)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Webseite nicht erreichbar, die Anfrage endete nach %d Sekunden"
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@
|
||||
"Last Login": "Dernière connexion",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"This feed already exists (%s).": "Cet abonnement existe déjà (%s).",
|
||||
"Unable to fetch feed (statusCode=%d).": "Impossible de récupérer cet abonnement (code=%d).",
|
||||
"This feed already exists (%s)": "Cet abonnement existe déjà (%s)",
|
||||
"Unable to fetch feed (statusCode=%d)": "Impossible de récupérer cet abonnement (code=%d)",
|
||||
"Unable to open this link: %v": "Impossible d'ouvrir ce lien : %v",
|
||||
"Unable to analyze this page: %v": "Impossible d'analyzer cette page : %v",
|
||||
"Unable to find any subscription.": "Impossible de trouver un abonnement.",
|
||||
@@ -115,7 +115,7 @@
|
||||
"Unable to parse Atom feed: %v.": "Impossible de lire ce flux Atom: %v.",
|
||||
"Unable to parse JSON feed: %v.": "Impossible de lire ce flux JSON: %v.",
|
||||
"Unable to parse RDF feed: %v.": "Impossible de lire ce flux RDF: %v.",
|
||||
"Unable to normalize encoding: %v.": "Impossible de normaliser l'encodage : %v.",
|
||||
"Unable to normalize encoding: %v": "Impossible de normaliser l'encodage : %v",
|
||||
"Unable to create this category.": "Impossible de créer cette catégorie.",
|
||||
"yes": "oui",
|
||||
"no": "non",
|
||||
@@ -133,7 +133,7 @@
|
||||
"Sign in with Google": "Se connecter avec Google",
|
||||
"Unlink my Google account": "Dissocier mon compte Google",
|
||||
"Link my Google account": "Associer mon compte Google",
|
||||
"Category not found for this user.": "Cette catégorie n'existe pas pour cet utilisateur.",
|
||||
"Category not found for this user": "Cette catégorie n'existe pas pour cet utilisateur",
|
||||
"Invalid theme.": "Le thème est invalide.",
|
||||
"Entry Sorting": "Ordre des éléments",
|
||||
"Older entries first": "Ancien éléments en premier",
|
||||
@@ -184,6 +184,7 @@
|
||||
"Items Navigation": "Naviguation entre les éléments",
|
||||
"Go to previous item": "Élément précédent",
|
||||
"Go to next item": "Élément suivant",
|
||||
"Pages Navigation": "Naviguation entre les pages",
|
||||
"Go to previous page": "Page précédente",
|
||||
"Go to next page": "Page suivante",
|
||||
"Open selected item": "Ouvrir élément sélectionné",
|
||||
@@ -208,5 +209,10 @@
|
||||
"Fever API endpoint:": "Point de terminaison de l'API Fever :",
|
||||
"Miniflux API": "API de Miniflux",
|
||||
"API Endpoint": "Point de terminaison de l'API",
|
||||
"Your account password": "Le mot de passe de votre compte"
|
||||
"Your account password": "Le mot de passe de votre compte",
|
||||
"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)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Site web injoignable, la requête à échouée après %d secondes"
|
||||
}
|
||||
|
||||
Executable
+220
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"plural.feed.error_count": [
|
||||
"%d błąd",
|
||||
"%d błąd",
|
||||
"%d błędów"
|
||||
],
|
||||
"plural.categories.feed_count": [
|
||||
"Jest %d kanał.",
|
||||
"Są %d kanały.",
|
||||
"Jest %d kanałów."
|
||||
],
|
||||
"Username": "Nazwa użytkownika",
|
||||
"Password": "Hasło",
|
||||
"Unread": "Nieprzeczytane",
|
||||
"History": "Historia",
|
||||
"Feeds": "Kanały",
|
||||
"Categories": "Kategorie",
|
||||
"Settings": "Ustawienia",
|
||||
"Logout": "Wyloguj się",
|
||||
"Next": "Następny",
|
||||
"Previous": "Poprzedni",
|
||||
"New Subscription": "Nowa subskrypcja",
|
||||
"Import": "Importuj",
|
||||
"Export": "Eksportuj",
|
||||
"There is no category. You must have at least one category.": "Nie ma żadnej kategorii. Musisz mieć co najmniej jedną kategorię",
|
||||
"URL": "URL",
|
||||
"Category": "Kategoria",
|
||||
"Find a subscription": "Znajdź subskrypcję",
|
||||
"Loading...": "Ładowanie...",
|
||||
"Create a category": "Utwórz kategorię",
|
||||
"There is no category.": "Nie masz żadnej kategorii",
|
||||
"Edit": "Edytuj",
|
||||
"Remove": "Usuń",
|
||||
"No feed.": "Brak kanałów.",
|
||||
"There is no article in this category.": "W tej kategorii nie ma żadnych artykułów",
|
||||
"Original": "Oryginalny artykuł",
|
||||
"Mark this page as read": "Oznacz jako przeczytane",
|
||||
"not yet": "jeszcze nie",
|
||||
"just now": "przed chwilą",
|
||||
"1 minute ago": "minutę temu",
|
||||
"%d minutes ago": "%d minut temu",
|
||||
"1 hour ago": "godzinę temu",
|
||||
"%d hours ago": "%d godzin temu",
|
||||
"yesterday": "wczoraj",
|
||||
"%d days ago": "%d dni temu",
|
||||
"%d weeks ago": "%d tygodni temu",
|
||||
"%d months ago": "%d miesięcy temu",
|
||||
"%d years ago": "%d lat temu",
|
||||
"Date": "Data",
|
||||
"IP Address": "Adres IP",
|
||||
"User Agent": "User Agent",
|
||||
"Actions": "Działania",
|
||||
"Current session": "Bieżąca sesja",
|
||||
"Sessions": "Sesje",
|
||||
"Users": "Użytkownicy",
|
||||
"Add user": "Dodaj użytkownika",
|
||||
"Choose a Subscription": "Wybierz subskrypcję",
|
||||
"Subscribe": "Subskrypcja",
|
||||
"New Category": "Nowa kategoria",
|
||||
"Title": "Tytuł",
|
||||
"Save": "Zapisz",
|
||||
"or": "lub",
|
||||
"cancel": "anuluj",
|
||||
"New User": "Nowy użytkownik",
|
||||
"Confirmation": "Potwierdź",
|
||||
"Administrator": "Administrator",
|
||||
"Edit Category: %s": "Edycja Kategorii: %s",
|
||||
"Update": "Zaktualizuj",
|
||||
"Edit Feed: %s": "Edytuj kanał: %s",
|
||||
"There is no category!": "Nie ma żadnej kategorii!",
|
||||
"Edit user: %s": "Edytuj użytkownika: %s",
|
||||
"There is no article for this feed.": "Nie ma artykułu dla tego kanału.",
|
||||
"Add subscription": "Dodaj subskrypcję",
|
||||
"You don't have any subscription.": "Nie masz żadnej subskrypcji",
|
||||
"Last check:": "Ostatnia aktualizacja:",
|
||||
"Refresh": "Odśwież",
|
||||
"There is no history at the moment.": "Obecnie nie ma żadnej historii.",
|
||||
"OPML file": "Plik OPML",
|
||||
"Sign In": "Zaloguj się",
|
||||
"Sign in": "Zaloguj się",
|
||||
"Theme": "Wygląd",
|
||||
"Timezone": "Strefa czasowa",
|
||||
"Language": "Język",
|
||||
"There is no unread article.": "Nie ma żadnych nieprzeczytanych artykułów.",
|
||||
"You are the only user.": "Jesteś jedynym użytkownikiem.",
|
||||
"Last Login": "Ostatnie logowanie",
|
||||
"Yes": "Tak",
|
||||
"No": "Nie",
|
||||
"This feed already exists (%s)": "Ten kanał już istnieje (%s)",
|
||||
"Unable to fetch feed (statusCode=%d)": "Kanał nie mógł zostać pobrany (kod=%d)",
|
||||
"Unable to open this link: %v": "Nie można było otworzyć tego linku: %v",
|
||||
"Unable to analyze this page: %v": "Nie można przeanalizować tej strony: %v",
|
||||
"Unable to find any subscription.": "Nie znaleziono żadnych subskrypcji.",
|
||||
"The URL and the category are mandatory.": "URL i kategoria są obowiązkowe.",
|
||||
"All fields are mandatory.": "Wszystkie pola są obowiązkowe.",
|
||||
"Passwords are not the same.": "Hasła nie są identyczne.",
|
||||
"You must use at least 6 characters.": "Musisz użyć co najmniej 6 znaków.",
|
||||
"The username is mandatory.": "Nazwa użytkownika jest obowiązkowa.",
|
||||
"The username, theme, language and timezone fields are mandatory.": "Pola nazwy użytkownika, tematu, języka i strefy czasowej są obowiązkowe.",
|
||||
"The title is mandatory.": "Tytuł jest obowiązkowy.",
|
||||
"About": "O stronie",
|
||||
"version": "Wersja",
|
||||
"Version:": "Wersja :",
|
||||
"Build Date:": "Data opracowania:",
|
||||
"Author:": "Autor:",
|
||||
"Authors": "Autorzy",
|
||||
"License:": "Licencja:",
|
||||
"Attachments": "Załączniki",
|
||||
"Download": "Pobierz",
|
||||
"Invalid username or password.": "Nieprawidłowa nazwa użytkownika lub hasło.",
|
||||
"Never": "Nigdy",
|
||||
"Unable to execute request: %v": "To polecenie nie mogło zostać wykonane: %v",
|
||||
"Last Parsing Error": "Ostatni błąd analizy",
|
||||
"There is a problem with this feed": "Z tym kanałem jest problem",
|
||||
"Unable to parse OPML file: %v.": "Plik OPML nie mógł zostać odczytany: %v.",
|
||||
"Unable to parse RSS feed: %v.": "Nie można było odczytać kanału RSS: %v.",
|
||||
"Unable to parse Atom feed: %v.": "Nie można było odczytać kanału Atom: %v.",
|
||||
"Unable to parse JSON feed: %v.": "Nie można było odczytać kanału JSON: %v.",
|
||||
"Unable to parse RDF feed: %v.": "Nie można było odczytać kanału RDF: %v.",
|
||||
"Unable to normalize encoding: %v": "Kodowanie znaków nie mogło zostać znormalizowane: %v",
|
||||
"Unable to create this category.": "Ta kategoria nie mogła zostać utworzona.",
|
||||
"yes": "tak",
|
||||
"no": "nie",
|
||||
"Are you sure?": "Czy jesteś pewny?",
|
||||
"Work in progress...": "W toku...",
|
||||
"This user already exists.": "Ten użytkownik już istnieje.",
|
||||
"This category already exists.": "Ta kategoria już istnieje.",
|
||||
"Unable to update this category.": "Ta kategoria nie mogła zostać zaktualizowana.",
|
||||
"Integrations": "Usługi",
|
||||
"Bookmarklet": "Bookmarklet",
|
||||
"Drag and drop this link to your bookmarks.": "Przeciągnij i upuść to łącze do zakładek.",
|
||||
"This special link allows you to subscribe to a website directly by using a bookmark in your web browser.": "Ten link umożliwia subskrypcję strony internetowej bezpośrednio za pomocą zakładki w przeglądarce internetowej",
|
||||
"Add to Miniflux": "Dodaj do Miniflux",
|
||||
"Refresh all feeds in background": "Odśwież wszystkie subskrypcje w tle",
|
||||
"Sign in with Google": "Zaloguj przez Google",
|
||||
"Unlink my Google account": "Odłącz moje konto Google",
|
||||
"Link my Google account": "Połącz z moim kontem Google",
|
||||
"Category not found for this user": "Kategoria nie znaleziona dla tego użytkownika",
|
||||
"Invalid theme.": "Ten temat jest nieprawidłowy.",
|
||||
"Entry Sorting": "Sortowanie artykułów",
|
||||
"Older entries first": "Najstarsze wpisy jako pierwsze",
|
||||
"Recent entries first": "Najnowsze wpisy jako pierwsze",
|
||||
"Saving...": "Zapisywanie...",
|
||||
"Done!": "Gotowe!",
|
||||
"Save this article": "Zapisz ten artykuł",
|
||||
"Mark bookmark as unread": "Zaznacz zakładkę jako nieprzeczytaną",
|
||||
"Pinboard Tags": "Pinboard Tags",
|
||||
"Pinboard API Token": "Token Pinboard API",
|
||||
"Save articles to Pinboard": "Zapisz artykuł w Pinboard",
|
||||
"Save articles to Instapaper": "Zapisz artykuł w Instapaper",
|
||||
"Instapaper Username": "Login do Instapaper",
|
||||
"Instapaper Password": "Hasło do Instapaper",
|
||||
"Activate Fever API": "Aktywuj Fever API",
|
||||
"Fever Username": "Login do Fever",
|
||||
"Fever Password": "Hasło do Fever",
|
||||
"Fetch original content": "Pobierz oryginalną treść",
|
||||
"Scraper Rules": "Zasady ekstrakcji",
|
||||
"Rewrite Rules": "Reguły zapisu",
|
||||
"Preferences saved!": "Ustawienia zapisane!",
|
||||
"Your external account is now linked !": "Twoje zewnętrzne konto jest teraz połączone!",
|
||||
"Save articles to Wallabag": "Zapisz artykuły do Wallabag",
|
||||
"Wallabag API Endpoint": "Wallabag URL",
|
||||
"Wallabag Client ID": "Wallabag Client-ID",
|
||||
"Wallabag Client Secret": "Wallabag Client Secret",
|
||||
"Wallabag Username": "Login do Wallabag",
|
||||
"Wallabag Password": "Hasło do Wallabag",
|
||||
"Keyboard Shortcut: %s": "Skróty klawiszowe: %s",
|
||||
"Favorites": "Ulubione",
|
||||
"Star": "Oznacz gwiazdką",
|
||||
"Unstar": "Usuń gwiazdkę",
|
||||
"Starred": "Oznaczone gwiazdką",
|
||||
"There is no bookmark at the moment.": "Obecnie nie ma żadnych zakładek.",
|
||||
"Last checked:": "Ostatnio sprawdzone:",
|
||||
"ETag header:": "Nagłówek ETag:",
|
||||
"LastModified header:": "Ostatnio zmienione:",
|
||||
"None": "Brak",
|
||||
"Keyboard Shortcuts": "Skróty klawiszowe",
|
||||
"Sections Navigation": "Nawigacja między punktami menu",
|
||||
"Go to unread": "Przejdź do nieprzeczytanych artykułów",
|
||||
"Go to bookmarks": "Przejdź do zakładek",
|
||||
"Go to history": "Przejdź do historii",
|
||||
"Go to feeds": "Przejdź do kanałów",
|
||||
"Go to categories": "Przejdź do kategorii",
|
||||
"Go to settings": "Przejdź do ustawień",
|
||||
"Show keyboard shortcuts": "Pokaż listę skrótów klawiszowych",
|
||||
"Items Navigation": "Nawigacja między artykułami",
|
||||
"Go to previous item": "Przejdź do poprzedniego artykułu",
|
||||
"Go to next item": "Przejdź do następnego punktu artykułu",
|
||||
"Pages Navigation": "Nawigacja między stronami",
|
||||
"Go to previous page": "Przejdź do poprzedniej strony",
|
||||
"Go to next page": "Przejdź do następnej strony",
|
||||
"Open selected item": "Otwórz zaznaczony artykuł",
|
||||
"Open original link": "Otwórz oryginalny artykuł",
|
||||
"Toggle read/unread": "Oznacz jako przeczytane/nieprzeczytane",
|
||||
"Mark current page as read": "Zaznacz aktualną stronę jako przeczytaną",
|
||||
"Download original content": "Pobierz oryginalną zawartość",
|
||||
"Toggle bookmark": "Dodaj/usuń zakładki",
|
||||
"Close modal dialog": "Zamknij listę skrótów klawiszowych",
|
||||
"Save article": "Zapisz artykuł",
|
||||
"There is already someone associated with this provider!": "Już ktoś jest powiązany z tym dostawcą!",
|
||||
"There is already someone else with the same Fever username!": "Już ktoś inny używa tej nazwy użytkownika Fever!",
|
||||
"Mark all as read": "Oznacz wszystko jako przeczytane",
|
||||
"This feed is empty": "Ten kanał jest pusty",
|
||||
"Flush history": "Usuń historię",
|
||||
"Site URL": "URL strony",
|
||||
"Feed URL": "URL kanału",
|
||||
"Logged as %s": "Zalogowany jako %s",
|
||||
"Unread Items": "Nieprzeczytane",
|
||||
"Change entry status": "Zmień status artykułu",
|
||||
"Read": "Przeczytane",
|
||||
"Fever API endpoint:": "Fever API endpoint:",
|
||||
"Miniflux API": "Miniflux API",
|
||||
"API Endpoint": "API endpoint",
|
||||
"Your account password": "Hasło konta",
|
||||
"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)",
|
||||
"Website unreachable, the request timed out after %d seconds": "Strona internetowa nieosiągalna, żądanie wygasło po %d sekundach"
|
||||
}
|
||||
+14
-2
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/miniflux/miniflux/logger"
|
||||
"github.com/miniflux/miniflux/model"
|
||||
"github.com/miniflux/miniflux/reader/date"
|
||||
"github.com/miniflux/miniflux/reader/sanitizer"
|
||||
"github.com/miniflux/miniflux/url"
|
||||
)
|
||||
|
||||
@@ -28,7 +29,7 @@ type atomFeed struct {
|
||||
|
||||
type atomEntry struct {
|
||||
ID string `xml:"id"`
|
||||
Title string `xml:"title"`
|
||||
Title atomContent `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Links []atomLink `xml:"link"`
|
||||
Summary string `xml:"summary"`
|
||||
@@ -97,7 +98,7 @@ func (a *atomEntry) Transform() *model.Entry {
|
||||
entry.Author = getAuthor(a.Author)
|
||||
entry.Hash = getHash(a)
|
||||
entry.Content = getContent(a)
|
||||
entry.Title = strings.TrimSpace(a.Title)
|
||||
entry.Title = getTitle(a)
|
||||
entry.Enclosures = getEnclosures(a)
|
||||
return entry
|
||||
}
|
||||
@@ -160,6 +161,17 @@ func getContent(a *atomEntry) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func getTitle(a *atomEntry) string {
|
||||
title := ""
|
||||
if a.Title.Type == "xhtml" {
|
||||
title = a.Title.XML
|
||||
} else {
|
||||
title = a.Title.Data
|
||||
}
|
||||
|
||||
return strings.TrimSpace(sanitizer.StripTags(title))
|
||||
}
|
||||
|
||||
func getHash(a *atomEntry) string {
|
||||
for _, value := range []string{a.ID, getURL(a.Links)} {
|
||||
if value != "" {
|
||||
|
||||
@@ -206,6 +206,84 @@ func TestParseEntryTitleWithWhitespaces(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEntryTitleWithHTMLAndCDATA(t *testing.T) {
|
||||
data := `<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>Example Feed</title>
|
||||
<link href="http://example.org/"/>
|
||||
|
||||
<entry>
|
||||
<title type="html"><![CDATA[Test “Test”]]></title>
|
||||
<link href="http://example.org/2003/12/13/atom03"/>
|
||||
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
|
||||
<updated>2003-12-13T18:30:02Z</updated>
|
||||
<summary>Some text.</summary>
|
||||
</entry>
|
||||
|
||||
</feed>`
|
||||
|
||||
feed, err := Parse(bytes.NewBufferString(data))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if feed.Entries[0].Title != "Test “Test”" {
|
||||
t.Errorf("Incorrect entry title, got: %q", feed.Entries[0].Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEntryTitleWithHTML(t *testing.T) {
|
||||
data := `<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>Example Feed</title>
|
||||
<link href="http://example.org/"/>
|
||||
|
||||
<entry>
|
||||
<title type="html"><code>Test</code> Test</title>
|
||||
<link href="http://example.org/2003/12/13/atom03"/>
|
||||
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
|
||||
<updated>2003-12-13T18:30:02Z</updated>
|
||||
<summary>Some text.</summary>
|
||||
</entry>
|
||||
|
||||
</feed>`
|
||||
|
||||
feed, err := Parse(bytes.NewBufferString(data))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if feed.Entries[0].Title != "Test Test" {
|
||||
t.Errorf("Incorrect entry title, got: %q", feed.Entries[0].Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEntryTitleWithXHTML(t *testing.T) {
|
||||
data := `<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>Example Feed</title>
|
||||
<link href="http://example.org/"/>
|
||||
|
||||
<entry>
|
||||
<title type="xhtml"><code>Test</code> Test</title>
|
||||
<link href="http://example.org/2003/12/13/atom03"/>
|
||||
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
|
||||
<updated>2003-12-13T18:30:02Z</updated>
|
||||
<summary>Some text.</summary>
|
||||
</entry>
|
||||
|
||||
</feed>`
|
||||
|
||||
feed, err := Parse(bytes.NewBufferString(data))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if feed.Entries[0].Title != "Test Test" {
|
||||
t.Errorf("Incorrect entry title, got: %q", feed.Entries[0].Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEntryWithAuthorName(t *testing.T) {
|
||||
data := `<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
|
||||
+14
-5
@@ -20,11 +20,11 @@ import (
|
||||
|
||||
var (
|
||||
errRequestFailed = "Unable to execute request: %v"
|
||||
errServerFailure = "Unable to fetch feed (statusCode=%d)."
|
||||
errDuplicate = "This feed already exists (%s)."
|
||||
errServerFailure = "Unable to fetch feed (statusCode=%d)"
|
||||
errDuplicate = "This feed already exists (%s)"
|
||||
errNotFound = "Feed %d not found"
|
||||
errEncoding = "Unable to normalize encoding: %v."
|
||||
errCategoryNotFound = "Category not found for this user."
|
||||
errEncoding = "Unable to normalize encoding: %v"
|
||||
errCategoryNotFound = "Category not found for this user"
|
||||
errEmptyFeed = "This feed is empty"
|
||||
)
|
||||
|
||||
@@ -44,6 +44,9 @@ func (h *Handler) CreateFeed(userID, categoryID int64, url string, crawler bool)
|
||||
client := http.NewClient(url)
|
||||
response, err := client.Get()
|
||||
if err != nil {
|
||||
if _, ok := err.(errors.LocalizedError); ok {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.NewLocalizedError(errRequestFailed, err)
|
||||
}
|
||||
|
||||
@@ -120,7 +123,13 @@ func (h *Handler) RefreshFeed(userID, feedID int64) error {
|
||||
client := http.NewClientWithCacheHeaders(originalFeed.FeedURL, originalFeed.EtagHeader, originalFeed.LastModifiedHeader)
|
||||
response, err := client.Get()
|
||||
if err != nil {
|
||||
customErr := errors.NewLocalizedError(errRequestFailed, err)
|
||||
var customErr errors.LocalizedError
|
||||
if lerr, ok := err.(errors.LocalizedError); ok {
|
||||
customErr = lerr
|
||||
} else {
|
||||
customErr = errors.NewLocalizedError(errRequestFailed, err)
|
||||
}
|
||||
|
||||
originalFeed.ParsingErrorCount++
|
||||
originalFeed.ParsingErrorMsg = customErr.Error()
|
||||
h.store.UpdateFeed(originalFeed)
|
||||
|
||||
+30
-2
@@ -12,6 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miniflux/miniflux/logger"
|
||||
"github.com/miniflux/miniflux/model"
|
||||
"github.com/miniflux/miniflux/reader/atom"
|
||||
"github.com/miniflux/miniflux/reader/encoding"
|
||||
@@ -69,9 +70,13 @@ func parseFeed(r io.Reader) (*model.Feed, error) {
|
||||
defer timer.ExecutionTime(time.Now(), "[Feed:ParseFeed]")
|
||||
|
||||
var buffer bytes.Buffer
|
||||
io.Copy(&buffer, r)
|
||||
size, _ := io.Copy(&buffer, r)
|
||||
if size == 0 {
|
||||
return nil, errors.New("This feed is empty")
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(buffer.Bytes())
|
||||
str := stripInvalidXMLCharacters(buffer.String())
|
||||
reader := strings.NewReader(str)
|
||||
format := DetectFeedFormat(reader)
|
||||
reader.Seek(0, io.SeekStart)
|
||||
|
||||
@@ -88,3 +93,26 @@ func parseFeed(r io.Reader) (*model.Feed, error) {
|
||||
return nil, errors.New("Unsupported feed format")
|
||||
}
|
||||
}
|
||||
|
||||
func stripInvalidXMLCharacters(input string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if isInCharacterRange(r) {
|
||||
return r
|
||||
}
|
||||
|
||||
logger.Debug("Strip invalid XML characters: %U", r)
|
||||
return -1
|
||||
}, input)
|
||||
}
|
||||
|
||||
// Decide whether the given rune is in the XML Character Range, per
|
||||
// the Char production of http://www.xml.com/axml/testaxml.htm,
|
||||
// Section 2.2 Characters.
|
||||
func isInCharacterRange(r rune) (inrange bool) {
|
||||
return r == 0x09 ||
|
||||
r == 0x0A ||
|
||||
r == 0x0D ||
|
||||
r >= 0x20 && r <= 0xDF77 ||
|
||||
r >= 0xE000 && r <= 0xFFFD ||
|
||||
r >= 0x10000 && r <= 0x10FFFF
|
||||
}
|
||||
|
||||
@@ -205,3 +205,10 @@ func TestParseUnknownFeed(t *testing.T) {
|
||||
t.Error("ParseFeed must returns an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEmptyFeed(t *testing.T) {
|
||||
_, err := parseFeed(bytes.NewBufferString(""))
|
||||
if err == nil {
|
||||
t.Error("ParseFeed must returns an error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
var (
|
||||
errConnectionFailure = "Unable to open this link: %v"
|
||||
errUnreadableDoc = "Unable to analyze this page: %v"
|
||||
errEmptyBody = "This web page is empty"
|
||||
)
|
||||
|
||||
// FindSubscriptions downloads and try to find one or more subscriptions from an URL.
|
||||
@@ -32,16 +33,28 @@ func FindSubscriptions(websiteURL string) (Subscriptions, error) {
|
||||
client := http.NewClient(websiteURL)
|
||||
response, err := client.Get()
|
||||
if err != nil {
|
||||
if _, ok := err.(errors.LocalizedError); ok {
|
||||
return nil, err
|
||||
}
|
||||
return nil, errors.NewLocalizedError(errConnectionFailure, err)
|
||||
}
|
||||
|
||||
// Content-Length = -1 when no Content-Length header is sent
|
||||
if response.ContentLength == 0 {
|
||||
return nil, errors.NewLocalizedError(errEmptyBody)
|
||||
}
|
||||
|
||||
body, err := response.NormalizeBodyEncoding()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
io.Copy(&buffer, body)
|
||||
size, _ := io.Copy(&buffer, body)
|
||||
if size == 0 {
|
||||
return nil, errors.NewLocalizedError(errEmptyBody)
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(buffer.Bytes())
|
||||
|
||||
if format := feed.DetectFeedFormat(reader); format != feed.FormatUnknown {
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
create index entries_user_status_idx on entries(user_id, status);
|
||||
create index feeds_user_category_idx on feeds(user_id, category_id);
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// 2018-01-31 21:53:31.179267411 -0800 PST m=+0.003439489
|
||||
// 2018-02-10 22:26:57.088648586 -0800 PST m=+0.004853992
|
||||
|
||||
package sql
|
||||
|
||||
@@ -123,6 +123,9 @@ alter table integrations add column wallabag_client_secret text default '';
|
||||
alter table integrations add column wallabag_username text default '';
|
||||
alter table integrations add column wallabag_password text default '';`,
|
||||
"schema_version_12": `alter table entries add column starred bool default 'f';`,
|
||||
"schema_version_13": `create index entries_user_status_idx on entries(user_id, status);
|
||||
create index feeds_user_category_idx on feeds(user_id, category_id);
|
||||
`,
|
||||
"schema_version_2": `create extension if not exists hstore;
|
||||
alter table users add column extra hstore;
|
||||
create index users_extra_idx on users using gin(extra);
|
||||
@@ -166,6 +169,7 @@ var SqlMapChecksums = map[string]string{
|
||||
"schema_version_10": "8faf15ddeff7c8cc305e66218face11ed92b97df2bdc2d0d7944d61441656795",
|
||||
"schema_version_11": "dc5bbc302e01e425b49c48ddcd8e29e3ab2bb8e73a6cd1858a6ba9fbec0b5243",
|
||||
"schema_version_12": "a95abab6cdf64811fc744abd37457e2928939d999c5ef00d2bdd9398e16f32fb",
|
||||
"schema_version_13": "9073fae1e796936f4a43a8120ebdb4218442fe7d346ace6387556a357c2d7edf",
|
||||
"schema_version_2": "e8e9ff32478df04fcddad10a34cba2e8bb1e67e7977b5bd6cdc4c31ec94282b4",
|
||||
"schema_version_3": "a54745dbc1c51c000f74d4e5068f1e2f43e83309f023415b1749a47d5c1e0f12",
|
||||
"schema_version_4": "216ea3a7d3e1704e40c797b5dc47456517c27dbb6ca98bf88812f4f63d74b5d9",
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/miniflux/miniflux/sql"
|
||||
)
|
||||
|
||||
const schemaVersion = 12
|
||||
const schemaVersion = 13
|
||||
|
||||
// Migrate run database migrations.
|
||||
func (s *Storage) Migrate() {
|
||||
|
||||
+3
-3
File diff suppressed because one or more lines are too long
@@ -214,6 +214,10 @@ article.feed-parsing-error {
|
||||
border-color: #888;
|
||||
}
|
||||
|
||||
.entry-content q {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.entry-enclosure {
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user