rebase localization onto latest main
Agent-Logs-Url: https://github.com/wasi-master/13ft/sessions/85227ed8-0556-4ae4-a47b-92bfed6cfe67 Co-authored-by: wasi-master <63045920+wasi-master@users.noreply.github.com>
This commit is contained in:
co-authored by
wasi-master
parent
6e50c9c182
commit
1cefe387ab
@@ -168,7 +168,7 @@ WantedBy=multi-user.target
|
||||
|
||||
## Localisation
|
||||
|
||||
The UI strings can be localised by setting the `LOCALE` environment variable before starting the app. Locale files live in `/home/runner/work/13ft/13ft/app/locales/<locale>.json`.
|
||||
The UI strings can be localised by setting the `LOCALE` environment variable before starting the app. Locale files live in `app/locales/<locale>.json`.
|
||||
|
||||
Built-in locales:
|
||||
|
||||
@@ -179,14 +179,14 @@ Built-in locales:
|
||||
Examples:
|
||||
|
||||
```sh
|
||||
# from /home/runner/work/13ft/13ft/app
|
||||
# from app
|
||||
LOCALE=de python portable.py
|
||||
|
||||
# or with gunicorn
|
||||
LOCALE=fr python -m gunicorn 'portable:app'
|
||||
```
|
||||
|
||||
To add a new locale, copy `/home/runner/work/13ft/13ft/app/locales/en.json` to a new file such as `/home/runner/work/13ft/13ft/app/locales/es.json` and translate the values.
|
||||
To add a new locale, copy `app/locales/en.json` to a new file such as `app/locales/es.json` and translate the values.
|
||||
|
||||
## Screenshots
|
||||
|
||||
|
||||
Binary file not shown.
+2
-2
@@ -22,6 +22,6 @@
|
||||
"challenge_error": "Die Website hat eine Anti-Bot-Prüfung (Cloudflare oder ähnlich) ausgeliefert und es wurde kein brauchbarer Schnappschuss auf archive.org oder archive.today gefunden. Diese Website blockiert aktiv automatisierte Anfragen.",
|
||||
"timeout_error": "Die Website hat zu lange zum Antworten gebraucht (30s-Timeout). Bitte versuchen Sie es später erneut.",
|
||||
"connection_error": "Die Verbindung zur Website konnte nicht hergestellt werden. Prüfen Sie die URL und versuchen Sie es erneut.",
|
||||
"request_error": "Seite konnte nicht geladen werden: {error}",
|
||||
"unexpected_error": "Unerwarteter Fehler: {error}"
|
||||
"request_error": "Die Seite konnte nicht geladen werden.",
|
||||
"unexpected_error": "Unerwarteter Fehler beim Laden der Seite."
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,6 +22,6 @@
|
||||
"challenge_error": "The site served an anti-bot challenge (Cloudflare or similar) and no usable snapshot was found on archive.org or archive.today. This site is actively blocking automated requests.",
|
||||
"timeout_error": "The website took too long to respond (30s timeout). Try again later.",
|
||||
"connection_error": "Could not connect to the website. Check the URL and try again.",
|
||||
"request_error": "Failed to fetch the page: {error}",
|
||||
"unexpected_error": "Unexpected error: {error}"
|
||||
"request_error": "Failed to fetch the page.",
|
||||
"unexpected_error": "Unexpected error while fetching the page."
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,6 +22,6 @@
|
||||
"challenge_error": "Le site a présenté une protection anti-bot (Cloudflare ou similaire) et aucun instantané exploitable n'a été trouvé sur archive.org ou archive.today. Ce site bloque activement les requêtes automatisées.",
|
||||
"timeout_error": "Le site web a mis trop de temps à répondre (délai de 30 s). Veuillez réessayer plus tard.",
|
||||
"connection_error": "Impossible de se connecter au site web. Vérifiez l'URL et réessayez.",
|
||||
"request_error": "Échec de récupération de la page : {error}",
|
||||
"unexpected_error": "Erreur inattendue : {error}"
|
||||
"request_error": "Impossible de récupérer la page.",
|
||||
"unexpected_error": "Erreur inattendue lors du chargement de la page."
|
||||
}
|
||||
|
||||
+46
-16
@@ -6,6 +6,7 @@ import requests
|
||||
import uuid
|
||||
import threading
|
||||
import time
|
||||
from functools import lru_cache
|
||||
from urllib.parse import urljoin, urlparse, quote
|
||||
from flask import request, Response
|
||||
from bs4 import BeautifulSoup
|
||||
@@ -44,16 +45,20 @@ _DEFAULT_STRINGS = {
|
||||
),
|
||||
"timeout_error": "The website took too long to respond (30s timeout). Try again later.",
|
||||
"connection_error": "Could not connect to the website. Check the URL and try again.",
|
||||
"request_error": "Failed to fetch the page: {error}",
|
||||
"unexpected_error": "Unexpected error: {error}",
|
||||
"request_error": "Failed to fetch the page.",
|
||||
"unexpected_error": "Unexpected error while fetching the page.",
|
||||
}
|
||||
|
||||
|
||||
def load_strings():
|
||||
def get_locale():
|
||||
locale = os.environ.get("LOCALE", "en")
|
||||
if not re.match(r"^[a-zA-Z0-9_-]+$", locale):
|
||||
locale = "en"
|
||||
return locale
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def load_strings_for_locale(locale):
|
||||
strings = dict(_DEFAULT_STRINGS)
|
||||
locale_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "locales")
|
||||
locale_file = os.path.join(locale_dir, f"{locale}.json")
|
||||
@@ -63,10 +68,17 @@ def load_strings():
|
||||
with open(locale_file, "r", encoding="utf-8") as file:
|
||||
strings.update(json.load(file))
|
||||
except (FileNotFoundError, json.JSONDecodeError, OSError):
|
||||
strings = dict(_DEFAULT_STRINGS)
|
||||
locale = "en"
|
||||
|
||||
strings["lang"] = locale
|
||||
return strings
|
||||
|
||||
|
||||
def load_strings():
|
||||
return dict(load_strings_for_locale(get_locale()))
|
||||
|
||||
|
||||
googlebot_headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.119 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
|
||||
}
|
||||
@@ -74,6 +86,12 @@ googlebot_headers = {
|
||||
jobs = {}
|
||||
jobs_lock = threading.Lock()
|
||||
|
||||
|
||||
class UserFacingError(Exception):
|
||||
def __init__(self, user_message):
|
||||
super().__init__(user_message)
|
||||
self.user_message = user_message
|
||||
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ lang }}">
|
||||
@@ -692,7 +710,7 @@ def bypass_paywall(url, strings, job_id=None):
|
||||
msg = strings["medium_challenge_error"]
|
||||
else:
|
||||
msg = strings["challenge_error"]
|
||||
raise RuntimeError(msg)
|
||||
raise UserFacingError(msg)
|
||||
|
||||
set_step(job_id, 'process')
|
||||
result = add_base_tag(html_text, final_url)
|
||||
@@ -717,15 +735,15 @@ def fetch_worker(job_id, url, strings):
|
||||
jobs[job_id]['step'] = 'error'
|
||||
except requests.exceptions.RequestException as e:
|
||||
with jobs_lock:
|
||||
jobs[job_id]['error'] = strings["request_error"].format(error=e)
|
||||
jobs[job_id]['error'] = strings["request_error"]
|
||||
jobs[job_id]['step'] = 'error'
|
||||
except RuntimeError as e:
|
||||
except UserFacingError as error:
|
||||
with jobs_lock:
|
||||
jobs[job_id]['error'] = str(e)
|
||||
jobs[job_id]['error'] = error.user_message
|
||||
jobs[job_id]['step'] = 'error'
|
||||
except Exception as e:
|
||||
with jobs_lock:
|
||||
jobs[job_id]['error'] = strings["unexpected_error"].format(error=e)
|
||||
jobs[job_id]['error'] = strings["unexpected_error"]
|
||||
jobs[job_id]['step'] = 'error'
|
||||
|
||||
|
||||
@@ -782,10 +800,16 @@ def show_article():
|
||||
strings = load_strings()
|
||||
try:
|
||||
return bypass_paywall(link, strings)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return strings["request_error"].format(error=e), 400
|
||||
except Exception as e:
|
||||
raise e
|
||||
except requests.exceptions.Timeout:
|
||||
return strings["timeout_error"], 400
|
||||
except requests.exceptions.ConnectionError:
|
||||
return strings["connection_error"], 400
|
||||
except requests.exceptions.RequestException:
|
||||
return strings["request_error"], 400
|
||||
except UserFacingError as error:
|
||||
return error.user_message, 400
|
||||
except Exception:
|
||||
return strings["unexpected_error"], 500
|
||||
|
||||
|
||||
@app.route("/", defaults={"path": ""})
|
||||
@@ -798,10 +822,16 @@ def get_article(path):
|
||||
actual_url = "https://" + parts[4].lstrip("/")
|
||||
try:
|
||||
return bypass_paywall(actual_url, strings)
|
||||
except requests.exceptions.RequestException as e:
|
||||
return strings["request_error"].format(error=e), 400
|
||||
except Exception as e:
|
||||
raise e
|
||||
except requests.exceptions.Timeout:
|
||||
return strings["timeout_error"], 400
|
||||
except requests.exceptions.ConnectionError:
|
||||
return strings["connection_error"], 400
|
||||
except requests.exceptions.RequestException:
|
||||
return strings["request_error"], 400
|
||||
except UserFacingError as error:
|
||||
return error.user_message, 400
|
||||
except Exception:
|
||||
return strings["unexpected_error"], 500
|
||||
else:
|
||||
return strings["invalid_url"], 400
|
||||
|
||||
|
||||
Reference in New Issue
Block a user