Tagged all strings

This commit is contained in:
Piero Toffanin 2023-01-05 13:12:35 -05:00
parent 19c3c04ca6
commit edd3d6931d
8 changed files with 492 additions and 102 deletions

View file

@ -9,7 +9,7 @@ if __name__ == "__main__":
os.makedirs(locales_dir) os.makedirs(locales_dir)
print("Compiling locales") print("Compiling locales")
sys.argv = ["", "compile", "-d", locales_dir] sys.argv = ["", "compile", "-f", "-d", locales_dir]
pybabel() pybabel()

View file

@ -15,11 +15,11 @@ from flask_swagger_ui import get_swaggerui_blueprint
from translatehtml import translate_html from translatehtml import translate_html
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
from flask_babel import Babel, gettext as _ from flask_babel import Babel
from libretranslate import flood, remove_translated_files, security from libretranslate import flood, remove_translated_files, security
from libretranslate.language import detect_languages, improve_translation_formatting from libretranslate.language import detect_languages, improve_translation_formatting
from libretranslate.locales import get_available_locales, gettext_escaped, gettext_html from libretranslate.locales import _, _lazy, get_available_locales, gettext_escaped, gettext_html
from .api_keys import Database, RemoteDatabase from .api_keys import Database, RemoteDatabase
from .suggestions import Database as SuggestionsDatabase from .suggestions import Database as SuggestionsDatabase
@ -188,7 +188,7 @@ def create_app(args):
if args.metrics_auth_token: if args.metrics_auth_token:
authorization = request.headers.get('Authorization') authorization = request.headers.get('Authorization')
if authorization != "Bearer " + args.metrics_auth_token: if authorization != "Bearer " + args.metrics_auth_token:
abort(401, description="Unauthorized") abort(401, description=_("Unauthorized"))
registry = CollectorRegistry() registry = CollectorRegistry()
multiprocess.MultiProcessCollector(registry) multiprocess.MultiProcessCollector(registry)
@ -206,7 +206,7 @@ def create_app(args):
ip = get_remote_address() ip = get_remote_address()
if flood.is_banned(ip): if flood.is_banned(ip):
abort(403, description="Too many request limits violations") abort(403, description=_("Too many request limits violations"))
if args.api_keys: if args.api_keys:
ak = get_req_api_key() ak = get_req_api_key()
@ -215,16 +215,16 @@ def create_app(args):
): ):
abort( abort(
403, 403,
description="Invalid API key", description=_("Invalid API key"),
) )
elif ( elif (
args.require_api_key_origin args.require_api_key_origin
and api_keys_db.lookup(ak) is None and api_keys_db.lookup(ak) is None
and request.headers.get("Origin") != args.require_api_key_origin and request.headers.get("Origin") != args.require_api_key_origin
): ):
description = "Please contact the server operator to get an API key" description = _("Please contact the server operator to get an API key")
if args.get_api_key_link: if args.get_api_key_link:
description = "Visit %s to get an API key" % args.get_api_key_link description = _("Visit %(url)s to get an API key", url=args.get_api_key_link)
abort( abort(
403, 403,
description=description, description=description,
@ -264,7 +264,7 @@ def create_app(args):
@bp.errorhandler(429) @bp.errorhandler(429)
def slow_down_error(e): def slow_down_error(e):
flood.report(get_remote_address()) flood.report(get_remote_address())
return jsonify({"error": "Slowdown: " + str(e.description)}), 429 return jsonify({"error": _("Slowdown:") + " " + str(e.description)}), 429
@bp.errorhandler(403) @bp.errorhandler(403)
def denied(e): def denied(e):
@ -294,7 +294,8 @@ def create_app(args):
if args.disable_web_ui: if args.disable_web_ui:
abort(404) abort(404)
return render_template("app.js.template") return render_template("app.js.template",
get_api_key_link=args.get_api_key_link)
@bp.get("/languages") @bp.get("/languages")
@limiter.exempt @limiter.exempt
@ -325,7 +326,7 @@ def create_app(args):
type: string type: string
description: Supported target language codes description: Supported target language codes
""" """
return jsonify([{"code": l.code, "name": l.name, "targets": language_pairs.get(l.code, [])} for l in languages]) return jsonify([{"code": l.code, "name": _lazy(l.name), "targets": language_pairs.get(l.code, [])} for l in languages])
# Add cors # Add cors
@bp.after_request @bp.after_request
@ -454,11 +455,11 @@ def create_app(args):
text_format = request.values.get("format") text_format = request.values.get("format")
if not q: if not q:
abort(400, description="Invalid request: missing q parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='q'))
if not source_lang: if not source_lang:
abort(400, description="Invalid request: missing source parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='source'))
if not target_lang: if not target_lang:
abort(400, description="Invalid request: missing target parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='target'))
batch = isinstance(q, list) batch = isinstance(q, list)
@ -467,8 +468,7 @@ def create_app(args):
if args.batch_limit < batch_size: if args.batch_limit < batch_size:
abort( abort(
400, 400,
description="Invalid request: Request (%d) exceeds text limit (%d)" description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=batch_size, limit=args.batch_limit),
% (batch_size, args.batch_limit),
) )
if args.char_limit != -1: if args.char_limit != -1:
@ -480,8 +480,7 @@ def create_app(args):
if args.char_limit < chars: if args.char_limit < chars:
abort( abort(
400, 400,
description="Invalid request: Request (%d) exceeds character limit (%d)" description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=chars, limit=args.char_limit),
% (chars, args.char_limit),
) )
if source_lang == "auto": if source_lang == "auto":
@ -514,18 +513,18 @@ def create_app(args):
for idx, lang in enumerate(src_langs): for idx, lang in enumerate(src_langs):
if lang is None: if lang is None:
abort(400, description="%s is not supported" % source_langs[idx]) abort(400, description=_("%(lang)s is not supported", lang=source_langs[idx]))
tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None) tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None)
if tgt_lang is None: if tgt_lang is None:
abort(400, description="%s is not supported" % target_lang) abort(400, description=_("%(lang)s is not supported",lang=target_lang))
if not text_format: if not text_format:
text_format = "text" text_format = "text"
if text_format not in ["text", "html"]: if text_format not in ["text", "html"]:
abort(400, description="%s format is not supported" % text_format) abort(400, description=_("%(format)s format is not supported", format=text_format))
try: try:
if batch: if batch:
@ -533,7 +532,7 @@ def create_app(args):
for idx, text in enumerate(q): for idx, text in enumerate(q):
translator = src_langs[idx].get_translation(tgt_lang) translator = src_langs[idx].get_translation(tgt_lang)
if translator is None: if translator is None:
abort(400, description="%s (%s) is not available as a target language from %s (%s)" % (tgt_lang.name, tgt_lang.code, src_langs[idx].name, src_langs[idx].code)) abort(400, description=_("%(tname)s (%(tcode)s) is not available as a target language from %(sname)s (%(scode)s)", tname=_lazy(tgt_lang.name), tcode=tgt_lang.code, sname=_lazy(src_langs[idx].name), scode=src_langs[idx].code))
if text_format == "html": if text_format == "html":
translated_text = str(translate_html(translator, text)) translated_text = str(translate_html(translator, text))
@ -557,7 +556,7 @@ def create_app(args):
else: else:
translator = src_langs[0].get_translation(tgt_lang) translator = src_langs[0].get_translation(tgt_lang)
if translator is None: if translator is None:
abort(400, description="%s (%s) is not available as a target language from %s (%s)" % (tgt_lang.name, tgt_lang.code, src_langs[0].name, src_langs[0].code)) abort(400, description=_("%(tname)s (%(tcode)s) is not available as a target language from %(sname)s (%(scode)s)", tname=_lazy(tgt_lang.name), tcode=tgt_lang.code, sname=_lazy(src_langs[0].name), scode=src_langs[0].code))
if text_format == "html": if text_format == "html":
translated_text = str(translate_html(translator, q)) translated_text = str(translate_html(translator, q))
@ -578,7 +577,7 @@ def create_app(args):
} }
) )
except Exception as e: except Exception as e:
abort(500, description="Cannot translate text: %s" % str(e)) abort(500, description=_("Cannot translate text: %(text)s", text=str(e)))
@bp.post("/translate_file") @bp.post("/translate_file")
@access_check @access_check
@ -665,36 +664,36 @@ def create_app(args):
description: Error message description: Error message
""" """
if args.disable_files_translation: if args.disable_files_translation:
abort(403, description="Files translation are disabled on this server.") abort(403, description=_("Files translation are disabled on this server."))
source_lang = request.form.get("source") source_lang = request.form.get("source")
target_lang = request.form.get("target") target_lang = request.form.get("target")
file = request.files['file'] file = request.files['file']
if not file: if not file:
abort(400, description="Invalid request: missing file parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='file'))
if not source_lang: if not source_lang:
abort(400, description="Invalid request: missing source parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='source'))
if not target_lang: if not target_lang:
abort(400, description="Invalid request: missing target parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='target'))
if file.filename == '': if file.filename == '':
abort(400, description="Invalid request: empty file") abort(400, description=_("Invalid request: empty file"))
if os.path.splitext(file.filename)[1] not in frontend_argos_supported_files_format: if os.path.splitext(file.filename)[1] not in frontend_argos_supported_files_format:
abort(400, description="Invalid request: file format not supported") abort(400, description=_("Invalid request: file format not supported"))
source_langs = [source_lang] source_langs = [source_lang]
src_langs = [next(iter([l for l in languages if l.code == source_lang]), None) for source_lang in source_langs] src_langs = [next(iter([l for l in languages if l.code == source_lang]), None) for source_lang in source_langs]
for idx, lang in enumerate(src_langs): for idx, lang in enumerate(src_langs):
if lang is None: if lang is None:
abort(400, description="%s is not supported" % source_langs[idx]) abort(400, description=_("%(lang)s is not supported", lang=source_langs[idx]))
tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None) tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None)
if tgt_lang is None: if tgt_lang is None:
abort(400, description="%s is not supported" % target_lang) abort(400, description=_("%(lang)s is not supported", lang=target_lang))
try: try:
filename = str(uuid.uuid4()) + '.' + secure_filename(file.filename) filename = str(uuid.uuid4()) + '.' + secure_filename(file.filename)
@ -719,7 +718,7 @@ def create_app(args):
Download a translated file Download a translated file
""" """
if args.disable_files_translation: if args.disable_files_translation:
abort(400, description="Files translation are disabled on this server.") abort(400, description=_("Files translation are disabled on this server."))
filepath = os.path.join(get_upload_dir(), filename) filepath = os.path.join(get_upload_dir(), filename)
try: try:
@ -727,7 +726,7 @@ def create_app(args):
if os.path.isfile(checked_filepath): if os.path.isfile(checked_filepath):
filepath = checked_filepath filepath = checked_filepath
except security.SuspiciousFileOperation: except security.SuspiciousFileOperation:
abort(400, description="Invalid filename") abort(400, description=_("Invalid filename"))
return_data = io.BytesIO() return_data = io.BytesIO()
with open(filepath, 'rb') as fo: with open(filepath, 'rb') as fo:
@ -820,9 +819,6 @@ def create_app(args):
type: string type: string
description: Error message description: Error message
""" """
if flood.is_banned(get_remote_address()):
abort(403, description="Too many request limits violations")
if request.is_json: if request.is_json:
json = get_json_dict(request) json = get_json_dict(request)
q = json.get("q") q = json.get("q")
@ -830,7 +826,7 @@ def create_app(args):
q = request.values.get("q") q = request.values.get("q")
if not q: if not q:
abort(400, description="Invalid request: missing q parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='q'))
return jsonify(detect_languages(q)) return jsonify(detect_languages(q))
@ -903,11 +899,11 @@ def create_app(args):
"language": { "language": {
"source": { "source": {
"code": frontend_argos_language_source.code, "code": frontend_argos_language_source.code,
"name": frontend_argos_language_source.name, "name": _lazy(frontend_argos_language_source.name),
}, },
"target": { "target": {
"code": frontend_argos_language_target.code, "code": frontend_argos_language_target.code,
"name": frontend_argos_language_target.name, "name": _lazy(frontend_argos_language_target.name),
}, },
}, },
} }
@ -971,7 +967,7 @@ def create_app(args):
description: Error message description: Error message
""" """
if not args.suggestions: if not args.suggestions:
abort(403, description="Suggestions are disabled on this server.") abort(403, description=_("Suggestions are disabled on this server."))
q = request.values.get("q") q = request.values.get("q")
s = request.values.get("s") s = request.values.get("s")
@ -979,13 +975,13 @@ def create_app(args):
target_lang = request.values.get("target") target_lang = request.values.get("target")
if not q: if not q:
abort(400, description="Invalid request: missing q parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='q'))
if not s: if not s:
abort(400, description="Invalid request: missing s parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='s'))
if not source_lang: if not source_lang:
abort(400, description="Invalid request: missing source parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='source'))
if not target_lang: if not target_lang:
abort(400, description="Invalid request: missing target parameter") abort(400, description=_("Invalid request: missing %(name)s parameter", name='target'))
SuggestionsDatabase().add(q, s, source_lang, target_lang) SuggestionsDatabase().add(q, s, source_lang, target_lang)
return jsonify({"success": True}) return jsonify({"success": True})
@ -1016,8 +1012,6 @@ def create_app(args):
def get_locale(): def get_locale():
return request.accept_languages.best_match(get_available_locales()) return request.accept_languages.best_match(get_available_locales())
app.jinja_env.globals.update(_e=gettext_escaped, _h=gettext_html) app.jinja_env.globals.update(_e=gettext_escaped, _h=gettext_html)
# Call factory function to create our blueprint # Call factory function to create our blueprint

View file

@ -1,6 +1,9 @@
import os import os
import json
from functools import cache from functools import cache
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_babel import lazy_gettext as _lazy
from markupsafe import escape, Markup from markupsafe import escape, Markup
@cache @cache
@ -12,7 +15,7 @@ def get_available_locales():
# Javascript code should use _e instead of _ # Javascript code should use _e instead of _
def gettext_escaped(text, **variables): def gettext_escaped(text, **variables):
return _(text, **variables).replace("'", "\\'") return json.dumps(_(text, **variables))
# HTML should be escaped using _h instead of _ # HTML should be escaped using _h instead of _
def gettext_html(text, **variables): def gettext_html(text, **variables):

View file

@ -1 +1,2 @@
**/*.mo **/*.mo
.langs.py

View file

@ -1,52 +1,309 @@
# Italian translations for PROJECT. # Italian translations for LibreTranslate.
# Copyright (C) 2023 ORGANIZATION # Copyright (C) 2023 LibreTranslate Authors
# This file is distributed under the same license as the PROJECT project. # This file is distributed under the same license as the LibreTranslate
# project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023. # FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: LibreTranslate 1.3.8\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2023-01-04 16:34-0500\n" "POT-Creation-Date: 2023-01-05 13:11-0500\n"
"PO-Revision-Date: 2023-01-04 12:27-0500\n" "PO-Revision-Date: 2023-01-05 13:11-0500\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: it\n"
"Language-Team: it <LL@li.org>\n" "Language-Team: it <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Language: it\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Generated-By: Babel 2.11.0\n" "Generated-By: Babel 2.11.0\n"
#: libretranslate/app.py:58 #: libretranslate/app.py:58
msgid "Invalid JSON format" msgid "Invalid JSON format"
msgstr "Formato JSON non valido" msgstr "Formato JSON non valido"
#: libretranslate/app.py:126 #: libretranslate/app.py:126 libretranslate/templates/app.js.template:427
msgid "Auto Detect" msgid "Auto Detect"
msgstr "Rilevamento automatico" msgstr "Rilevamento automatico"
#: libretranslate/app.py:191
msgid "Unauthorized"
msgstr "Non autorizzato"
#: libretranslate/app.py:209
msgid "Too many request limits violations"
msgstr "Troppe richieste limitano le violazioni"
#: libretranslate/app.py:218
msgid "Invalid API key"
msgstr "Chiave API non valida"
#: libretranslate/app.py:225
msgid "Please contact the server operator to get an API key"
msgstr ""
"Si prega di contattare l'operatore del server per ottenere una chiave API"
#: libretranslate/app.py:227
#, python-format
msgid "Visit %(url)s to get an API key"
msgstr "Visita %(url)s per ottenere una chiave API"
#: libretranslate/app.py:267
msgid "Slowdown:"
msgstr "Rallenta:"
#: libretranslate/app.py:458 libretranslate/app.py:460
#: libretranslate/app.py:462 libretranslate/app.py:674
#: libretranslate/app.py:676 libretranslate/app.py:678
#: libretranslate/app.py:829 libretranslate/app.py:978
#: libretranslate/app.py:980 libretranslate/app.py:982
#: libretranslate/app.py:984
#, python-format
msgid "Invalid request: missing %(name)s parameter"
msgstr "Richiesta non valida: mancante %(name)s parametro"
#: libretranslate/app.py:471 libretranslate/app.py:483
#, python-format
msgid "Invalid request: request (%(size)s) exceeds text limit (%(limit)s)"
msgstr ""
"Richiesta non valida: richiesta (%(size)s) supera il limite di testo "
"(%(limit)s)"
#: libretranslate/app.py:516 libretranslate/app.py:521
#: libretranslate/app.py:691 libretranslate/app.py:696
#, python-format
msgid "%(lang)s is not supported"
msgstr "%(lang)s non è supportato"
#: libretranslate/app.py:527
#, python-format
msgid "%(format)s format is not supported"
msgstr "%(format)s formato non è supportato"
#: libretranslate/app.py:535 libretranslate/app.py:559
#, python-format
msgid ""
"%(tname)s (%(tcode)s) is not available as a target language from %(sname)s "
"(%(scode)s)"
msgstr ""
"%(tname)s (%(tcode)s) non è disponibile come lingua di destinazione "
"%(sname)s (%(scode)s)"
#: libretranslate/app.py:580
#, python-format
msgid "Cannot translate text: %(text)s"
msgstr "Non può tradurre il testo: %(text)s"
#: libretranslate/app.py:667 libretranslate/app.py:721
msgid "Files translation are disabled on this server."
msgstr "La traduzione dei file è disabilitata su questo server."
#: libretranslate/app.py:681
msgid "Invalid request: empty file"
msgstr "Richiesta non valida: file vuoto"
#: libretranslate/app.py:684
msgid "Invalid request: file format not supported"
msgstr "Richiesta non valida: formato file non supportato"
#: libretranslate/app.py:729
msgid "Invalid filename"
msgstr "Invalid filename"
#: libretranslate/app.py:970
msgid "Suggestions are disabled on this server."
msgstr "I suggerimenti sono disabilitati su questo server."
#: libretranslate/locales/.langs.py:1
msgid "English"
msgstr "Inglese"
#: libretranslate/locales/.langs.py:2
msgid "Arabic"
msgstr "Arabo"
#: libretranslate/locales/.langs.py:3
msgid "Azerbaijani"
msgstr "Azerbaigian"
#: libretranslate/locales/.langs.py:4
msgid "Chinese"
msgstr "Cinese"
#: libretranslate/locales/.langs.py:5
msgid "Czech"
msgstr "Ceco"
#: libretranslate/locales/.langs.py:6
msgid "Danish"
msgstr "Danese"
#: libretranslate/locales/.langs.py:7
msgid "Dutch"
msgstr "Paesi Bassi"
#: libretranslate/locales/.langs.py:8
msgid "Esperanto"
msgstr "Esperanto"
#: libretranslate/locales/.langs.py:9
msgid "Finnish"
msgstr "Finlandia"
#: libretranslate/locales/.langs.py:10
msgid "French"
msgstr "Francese"
#: libretranslate/locales/.langs.py:11
msgid "German"
msgstr "Germania"
#: libretranslate/locales/.langs.py:12
msgid "Greek"
msgstr "Greco"
#: libretranslate/locales/.langs.py:13
msgid "Hebrew"
msgstr "Ebraico"
#: libretranslate/locales/.langs.py:14
msgid "Hindi"
msgstr "Hindi"
#: libretranslate/locales/.langs.py:15
msgid "Hungarian"
msgstr "Ungherese"
#: libretranslate/locales/.langs.py:16
msgid "Indonesian"
msgstr "Indonesiano"
#: libretranslate/locales/.langs.py:17
msgid "Irish"
msgstr "Irlanda"
#: libretranslate/locales/.langs.py:18
msgid "Italian"
msgstr "Italiano"
#: libretranslate/locales/.langs.py:19
msgid "Japanese"
msgstr "Giappone"
#: libretranslate/locales/.langs.py:20
msgid "Korean"
msgstr "Coreano"
#: libretranslate/locales/.langs.py:21
msgid "Persian"
msgstr "Persiano"
#: libretranslate/locales/.langs.py:22
msgid "Polish"
msgstr "Polacco"
#: libretranslate/locales/.langs.py:23
msgid "Portuguese"
msgstr "Portoghese"
#: libretranslate/locales/.langs.py:24
msgid "Russian"
msgstr "Russo"
#: libretranslate/locales/.langs.py:25
msgid "Slovak"
msgstr "Slovacchia"
#: libretranslate/locales/.langs.py:26
msgid "Spanish"
msgstr "Spagnolo"
#: libretranslate/locales/.langs.py:27
msgid "Swedish"
msgstr "Svezia"
#: libretranslate/locales/.langs.py:28
msgid "Turkish"
msgstr "Turco"
#: libretranslate/locales/.langs.py:29
msgid "Ukranian"
msgstr "Ucraina"
#: libretranslate/locales/.langs.py:30
msgid "Vietnamese"
msgstr "Vietnamita"
#: libretranslate/templates/app.js.template:31 #: libretranslate/templates/app.js.template:31
#: libretranslate/templates/app.js.template:279
msgid "Copy text" msgid "Copy text"
msgstr "Copia testo" msgstr "Copia testo"
#: libretranslate/templates/app.js.template:72 #: libretranslate/templates/app.js.template:72
#: libretranslate/templates/app.js.template:78
#: libretranslate/templates/app.js.template:83
#: libretranslate/templates/app.js.template:262
#: libretranslate/templates/app.js.template:332
#: libretranslate/templates/app.js.template:402
#: libretranslate/templates/app.js.template:447
#, python-format #, python-format
msgid "Cannot load %(url)s" msgid "Cannot load %(url)s"
msgstr "Non riesco a caricare %(url)s" msgstr "Non riesco a caricare %(url)s"
#: libretranslate/templates/index.html:6 libretranslate/templates/index.html:26 #: libretranslate/templates/app.js.template:253
msgid "LibreTranslate - Free and Open Source Machine Translation API" #: libretranslate/templates/app.js.template:323
msgstr "API di traduzione automatica open source < ' a" #: libretranslate/templates/app.js.template:385
#: libretranslate/templates/app.js.template:395
msgid "Unknown error"
msgstr "Errore sconosciuto"
#: libretranslate/templates/index.html:8 libretranslate/templates/index.html:30 #: libretranslate/templates/app.js.template:276
msgid "Copied"
msgstr "Copie"
#: libretranslate/templates/app.js.template:320
msgid "" msgid ""
"Free and Open Source Machine Translation API. Self-hosted, offline " "Thanks for your correction. Note the suggestion will not take effect right "
"capable and easy to setup. Run your own API server in just a few minutes." "away."
msgstr "" msgstr ""
"API di traduzione automatica gratuita e open source. Auto-hosted, offline" "Grazie per la sua correzione. Si noti che il suggerimento non avrà effetto "
" in grado e facile da configurare. Eseguire il proprio server API in " "subito."
"pochi minuti."
#: libretranslate/templates/app.js.template:423
msgid "No languages available. Did you install the models correctly?"
msgstr "Nessuna lingua disponibile. Hai installato correttamente i modelli?"
#: libretranslate/templates/app.js.template:479
#, python-format
msgid "Type in your API Key. If you need an API key, %(instructions)s"
msgstr ""
"Digitare nella chiave API. Se hai bisogno di una chiave API, "
"%(instructions)s"
#: libretranslate/templates/app.js.template:479
msgid "press the \"Get API Key\" link."
msgstr "premere il link \"Get API Key\"."
#: libretranslate/templates/app.js.template:479
msgid "contact the server operator."
msgstr "contattare l'operatore del server."
#: libretranslate/templates/index.html:6
#: libretranslate/templates/index.html:26
#: libretranslate/templates/index.html:297
msgid "Free and Open Source Machine Translation API"
msgstr "API di traduzione automatica gratuita e open source"
#: libretranslate/templates/index.html:8
#: libretranslate/templates/index.html:30
msgid ""
"Free and Open Source Machine Translation API. Self-hosted, offline capable "
"and easy to setup. Run your own API server in just a few minutes."
msgstr ""
"API di traduzione automatica gratuita e open source. Auto-hosted, offline in"
" grado e facile da configurare. Eseguire il proprio server API in pochi "
"minuti."
#: libretranslate/templates/index.html:9 #: libretranslate/templates/index.html:9
msgid "translation" msgid "translation"
@ -56,3 +313,132 @@ msgstr "traduzione"
msgid "api" msgid "api"
msgstr "api" msgstr "api"
#: libretranslate/templates/index.html:64
#: libretranslate/templates/index.html:75
msgid "API Docs"
msgstr "API"
#: libretranslate/templates/index.html:66
#: libretranslate/templates/index.html:77
msgid "Get API Key"
msgstr "Ottieni API Chiave"
#: libretranslate/templates/index.html:68
#: libretranslate/templates/index.html:79
msgid "GitHub"
msgstr "GitHub"
#: libretranslate/templates/index.html:81
msgid "Set API Key"
msgstr "Set API Chiave"
#: libretranslate/templates/index.html:118
msgid "Dismiss"
msgstr "Oggetto"
#: libretranslate/templates/index.html:132
msgid "Translation API"
msgstr "API di traduzione"
#: libretranslate/templates/index.html:136
msgid "Translate Text"
msgstr "Traduzione"
#: libretranslate/templates/index.html:140
msgid "Translate Files"
msgstr "Traduci file"
#: libretranslate/templates/index.html:146
msgid "Translate from"
msgstr "Traduttore da"
#: libretranslate/templates/index.html:159
msgid "Translate into"
msgstr "Traduzione"
#: libretranslate/templates/index.html:171
msgid "Text to translate"
msgstr "Testo da tradurre"
#: libretranslate/templates/index.html:183
msgid "Translated text"
msgstr "Tradotto testo"
#: libretranslate/templates/index.html:191
msgid "Cancel"
msgstr "Annulla"
#: libretranslate/templates/index.html:194
msgid "Send"
msgstr "Invia"
#: libretranslate/templates/index.html:210
msgid "Supported file formats:"
msgstr "Formati di file supportati:"
#: libretranslate/templates/index.html:214
msgid "File"
msgstr "File"
#: libretranslate/templates/index.html:236
msgid "Translate"
msgstr "Traduttore"
#: libretranslate/templates/index.html:237
#: libretranslate/templates/index.html:281
msgid "Download"
msgstr "Scarica"
#: libretranslate/templates/index.html:256
msgid "Request"
msgstr "Richiesta"
#: libretranslate/templates/index.html:261
msgid "Response"
msgstr "Risposta"
#: libretranslate/templates/index.html:276
msgid "Open Source Machine Translation API"
msgstr "API di traduzione automatica Open Source"
#: libretranslate/templates/index.html:277
msgid "Self-Hosted. Offline Capable. Easy to Setup."
msgstr "Ossessionato. Offline Capable. Facile da configurare."
#: libretranslate/templates/index.html:296
msgid "LibreTranslate"
msgstr "LibreTranslate"
#: libretranslate/templates/index.html:298
msgid "License:"
msgstr "Licenza:"
#: libretranslate/templates/index.html:301
#, python-format
msgid ""
"This public API should be used for testing, personal or infrequent use. If "
"you're going to run an application in production, please %(host_server)s or "
"%(get_api_key)s."
msgstr ""
"Questa API pubblica dovrebbe essere utilizzata per il test, uso personale o "
"infrequente. Se hai intenzione di eseguire un'applicazione in produzione, "
"per favore %(host_server)s o %(get_api_key)s."
#: libretranslate/templates/index.html:301
msgid "host your own server"
msgstr "host tuo server"
#: libretranslate/templates/index.html:301
msgid "get an API key"
msgstr "ottenere una chiave API"
#: libretranslate/templates/index.html:309
#, python-format
msgid "Made with %(heart)s by %(contributors)s and powered by %(engine)s"
msgstr ""
"Realizzato con %(heart)s di %(contributors)s e alimentato da %(engine)s"
#: libretranslate/templates/index.html:309
#, python-format
msgid "%(libretranslate)s Contributors"
msgstr "%(libretranslate)s Contributori"

View file

@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', function(){
detectedLangText: "", detectedLangText: "",
copyTextLabel: '{{ _e("Copy text") }}', copyTextLabel: {{ _e("Copy text") }},
suggestions: false, suggestions: false,
isSuggesting: false, isSuggesting: false,
@ -69,18 +69,18 @@ document.addEventListener('DOMContentLoaded', function(){
} }
} }
} else { } else {
self.error = '{{ _e("Cannot load %(url)s", url="/frontend/settings") }}'; self.error = {{ _e("Cannot load %(url)s", url="/frontend/settings") }};
self.loading = false; self.loading = false;
} }
}; };
settingsRequest.onerror = function() { settingsRequest.onerror = function() {
self.error = "Error while calling /frontend/settings"; self.error = {{ _e("Cannot load %(url)s", url="/frontend/settings") }};
self.loading = false; self.loading = false;
}; };
langsRequest.onerror = function() { langsRequest.onerror = function() {
self.error = "Error while calling /languages"; self.error = {{ _e("Cannot load %(url)s", url="/languages") }};
self.loading = false; self.loading = false;
}; };
@ -250,7 +250,7 @@ document.addEventListener('DOMContentLoaded', function(){
self.detectedLangText = ": " + (lang !== undefined ? lang.name : res.detectedLanguage.language) + " (" + res.detectedLanguage.confidence + "%)"; self.detectedLangText = ": " + (lang !== undefined ? lang.name : res.detectedLanguage.language) + " (" + res.detectedLanguage.confidence + "%)";
} }
} else{ } else{
throw new Error(res.error || "Unknown error"); throw new Error(res.error || {{ _e("Unknown error") }});
} }
} catch (e) { } catch (e) {
self.error = e.message; self.error = e.message;
@ -259,7 +259,7 @@ document.addEventListener('DOMContentLoaded', function(){
}; };
request.onerror = function() { request.onerror = function() {
self.error = "Error while calling /translate"; self.error = {{ _e("Cannot load %(url)s", url="/translate") }};
self.loadingTranslation = false; self.loadingTranslation = false;
}; };
@ -273,10 +273,10 @@ document.addEventListener('DOMContentLoaded', function(){
document.execCommand("copy"); document.execCommand("copy");
if (this.copyTextLabel === "Copy text"){ if (this.copyTextLabel === "Copy text"){
this.copyTextLabel = "Copied"; this.copyTextLabel = {{ _e("Copied") }};
var self = this; var self = this;
setTimeout(function(){ setTimeout(function(){
self.copyTextLabel = "Copy text"; self.copyTextLabel = {{ _e("Copy text") }};
}, 1500); }, 1500);
} }
}, },
@ -317,10 +317,10 @@ document.addEventListener('DOMContentLoaded', function(){
try{ try{
var res = JSON.parse(this.response); var res = JSON.parse(this.response);
if (res.success){ if (res.success){
M.toast({html: 'Thanks for your correction. Note the suggestion will not take effect right away.'}) M.toast({html: {{ _e("Thanks for your correction. Note the suggestion will not take effect right away.") }} })
self.closeSuggestTranslation(e) self.closeSuggestTranslation(e)
}else{ }else{
throw new Error(res.error || "Unknown error"); throw new Error(res.error || {{ _e("Unknown error") }});
} }
}catch(e){ }catch(e){
self.error = e.message; self.error = e.message;
@ -329,7 +329,7 @@ document.addEventListener('DOMContentLoaded', function(){
}; };
request.onerror = function() { request.onerror = function() {
self.error = "Error while calling /suggest"; self.error = {{ _e("Cannot load %(url)s", url="/suggest") }};
self.loadingTranslation = false; self.loadingTranslation = false;
}; };
@ -382,7 +382,7 @@ document.addEventListener('DOMContentLoaded', function(){
link.href = self.translatedFileUrl; link.href = self.translatedFileUrl;
link.click(); link.click();
}else{ }else{
throw new Error(res.error || "Unknown error"); throw new Error(res.error || {{ _e("Unknown error") }});
} }
}catch(e){ }catch(e){
@ -392,14 +392,14 @@ document.addEventListener('DOMContentLoaded', function(){
} }
}else{ }else{
let res = JSON.parse(this.response); let res = JSON.parse(this.response);
self.error = res.error || "Unknown error"; self.error = res.error || {{ _e("Unknown error") }};
self.loadingFileTranslation = false; self.loadingFileTranslation = false;
self.inputFile = false; self.inputFile = false;
} }
} }
translateFileRequest.onerror = function() { translateFileRequest.onerror = function() {
self.error = "Error while calling /translate_file"; self.error = {{ _e("Cannot load %(url)s", url="/translate_file") }};
self.loadingFileTranslation = false; self.loadingFileTranslation = false;
self.inputFile = false; self.inputFile = false;
}; };
@ -420,11 +420,11 @@ function handleLangsResponse(self, response) {
if (self.langs.length === 0){ if (self.langs.length === 0){
self.loading = false; self.loading = false;
self.error = "No languages available. Did you install the models correctly?" self.error = {{ _e("No languages available. Did you install the models correctly?") }};
return; return;
} }
self.langs.push({ name: "Auto Detect", code: "auto", targets: self.langs.map(l => l.code)}) self.langs.push({ name: {{ _e("Auto Detect") }}, code: "auto", targets: self.langs.map(l => l.code)})
const sourceLanguage = self.langs.find(l => l.code === self.getQueryParam("source")) const sourceLanguage = self.langs.find(l => l.code === self.getQueryParam("source"))
const targetLanguage = self.langs.find(l => l.code === self.getQueryParam("target")) const targetLanguage = self.langs.find(l => l.code === self.getQueryParam("target"))
@ -444,7 +444,7 @@ function handleLangsResponse(self, response) {
self.handleInput(new Event('none')) self.handleInput(new Event('none'))
} }
} else { } else {
self.error = "Cannot load /languages"; self.error = {{ _e("Cannot load %(url)s", url="/languages") }};
} }
self.loading = false; self.loading = false;
@ -476,9 +476,7 @@ function getTextWidth(text) {
function setApiKey(){ function setApiKey(){
var prevKey = localStorage.getItem("api_key") || ""; var prevKey = localStorage.getItem("api_key") || "";
var newKey = ""; var newKey = "";
var instructions = "contact the server operator."; newKey = window.prompt({{ _e("Type in your API Key. If you need an API key, %(instructions)s", instructions=_e("press the \"Get API Key\" link.") if get_api_key_link else _e("contact the server operator.")) }}, prevKey);
if (window.getApiKeyLink) instructions = "press the \"Get API Key\" link."
newKey = window.prompt("Type in your API Key. If you need an API key, " + instructions, prevKey);
if (newKey === null) newKey = ""; if (newKey === null) newKey = "";
localStorage.setItem("api_key", newKey); localStorage.setItem("api_key", newKey);

View file

@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ _h("LibreTranslate - Free and Open Source Machine Translation API") }}</title> <title>LibreTranslate - {{ _h("Free and Open Source Machine Translation API") }}</title>
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"> <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
<meta name="description" content="{{ _h('Free and Open Source Machine Translation API. Self-hosted, offline capable and easy to setup. Run your own API server in just a few minutes.') }}"> <meta name="description" content="{{ _h('Free and Open Source Machine Translation API. Self-hosted, offline capable and easy to setup. Run your own API server in just a few minutes.') }}">
<meta name="keywords" content="{{ _h('translation') }},{{ _h('api') }}"> <meta name="keywords" content="{{ _h('translation') }},{{ _h('api') }}">
@ -23,7 +23,7 @@
<link rel="preload" href="{{ url_for('static', filename='css/main.css') }}?v={{ version }}" as="style"/> <link rel="preload" href="{{ url_for('static', filename='css/main.css') }}?v={{ version }}" as="style"/>
<link rel="preload" href="{{ url_for('static', filename='css/dark-theme.css') }}" as="style"/> <link rel="preload" href="{{ url_for('static', filename='css/dark-theme.css') }}" as="style"/>
<meta property="og:title" content="{{ _h('LibreTranslate - Free and Open Source Machine Translation API') }}" /> <meta property="og:title" content="LibreTranslate - {{ _h('Free and Open Source Machine Translation API') }}" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://libretranslate.com" /> <meta property="og:url" content="https://libretranslate.com" />
<meta property="og:image" content="https://user-images.githubusercontent.com/1951843/102724116-32a6df00-42db-11eb-8cc0-129ab39cdfb5.png" /> <meta property="og:image" content="https://user-images.githubusercontent.com/1951843/102724116-32a6df00-42db-11eb-8cc0-129ab39cdfb5.png" />
@ -57,14 +57,13 @@
<div class="nav-wrapper container"> <div class="nav-wrapper container">
<button data-target="nav-mobile" class="sidenav-trigger"><i class="material-icons">menu</i></button> <button data-target="nav-mobile" class="sidenav-trigger"><i class="material-icons">menu</i></button>
<a id="logo-container" href="/" class="brand-logo"> <a id="logo-container" href="/" class="brand-logo">
<img src="{{ url_for('static', filename='icon.svg') }}" alt="Logo for LibreTranslate" class="logo"> <img src="{{ url_for('static', filename='icon.svg') }}" alt="LibreTranslate" class="logo">
<span>{{ _h("LibreTranslate") }}</span> <span>LibreTranslate</span>
</a> </a>
<ul class="right hide-on-med-and-down"> <ul class="right hide-on-med-and-down">
<li><a href="{{ swagger_url }}">{{ _h("API Docs") }}</a></li> <li><a href="{{ swagger_url }}">{{ _h("API Docs") }}</a></li>
{% if get_api_key_link %} {% if get_api_key_link %}
<li><a href="{{ get_api_key_link }}">{{ _h("Get API Key") }}</a></li> <li><a href="{{ get_api_key_link }}">{{ _h("Get API Key") }}</a></li>
<script>window.getApiKeyLink = "{{ get_api_key_link }}";</script>
{% endif %} {% endif %}
<li><a href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">{{ _h("GitHub") }}</a></li> <li><a href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">{{ _h("GitHub") }}</a></li>
{% if api_keys %} {% if api_keys %}
@ -296,7 +295,7 @@
<div class="col l12 s12"> <div class="col l12 s12">
<h5 class="white-text">{{ _h("LibreTranslate") }}</h5> <h5 class="white-text">{{ _h("LibreTranslate") }}</h5>
<p class="grey-text text-lighten-4">{{ _h("Free and Open Source Machine Translation API") }}</p> <p class="grey-text text-lighten-4">{{ _h("Free and Open Source Machine Translation API") }}</p>
<p>License: <a class="grey-text text-lighten-4" href="https://www.gnu.org/licenses/agpl-3.0.en.html" rel="noopener noreferrer">AGPLv3</a></p> <p>{{ _h("License:") }} <a class="grey-text text-lighten-4" href="https://www.gnu.org/licenses/agpl-3.0.en.html" rel="noopener noreferrer">AGPLv3</a></p>
{% if web_version %} {% if web_version %}
<p> <p>
{{ _h("This public API should be used for testing, personal or infrequent use. If you're going to run an application in production, please %(host_server)s or %(get_api_key)s.", host_server='<a href="https://github.com/LibreTranslate/LibreTranslate" class="grey-text text-lighten-4" rel="noopener noreferrer">' + _h("host your own server") + '</a>', get_api_key='<a class="grey-text text-lighten-4" href="' + (get_api_key_link if get_api_key_link else "https://github.com/LibreTranslate/LibreTranslate#mirrors") + '" rel="noopener noreferrer">' + _h("get an API key") + '</a>') }} {{ _h("This public API should be used for testing, personal or infrequent use. If you're going to run an application in production, please %(host_server)s or %(get_api_key)s.", host_server='<a href="https://github.com/LibreTranslate/LibreTranslate" class="grey-text text-lighten-4" rel="noopener noreferrer">' + _h("host your own server") + '</a>', get_api_key='<a class="grey-text text-lighten-4" href="' + (get_api_key_link if get_api_key_link else "https://github.com/LibreTranslate/LibreTranslate#mirrors") + '" rel="noopener noreferrer">' + _h("get an API key") + '</a>') }}
@ -307,7 +306,7 @@
</div> </div>
<div class="footer-copyright center"> <div class="footer-copyright center">
<p class="white-text"> <p class="white-text">
{{ _h("Made with %(heart)s by %(contributors)s and powered by %(engine)s", heart='❤', contributors='<a class="white-text" href="https://github.com/LibreTranslate/LibreTranslate/graphs/contributors" rel="noopener noreferrer">%s</a>' % _h("LibreTranslate Contributors"), engine='<a class="white-text text-lighten-3" href="https://github.com/argosopentech/argos-translate/" rel="noopener noreferrer">%s</a>' % _h('Argos Translate')) }} {{ _h("Made with %(heart)s by %(contributors)s and powered by %(engine)s", heart='❤', contributors='<a class="white-text" href="https://github.com/LibreTranslate/LibreTranslate/graphs/contributors" rel="noopener noreferrer">%s</a>' % _h("%(libretranslate)s Contributors", libretranslate="LibreTranslate"), engine='<a class="white-text text-lighten-3" href="https://github.com/argosopentech/argos-translate/" rel="noopener noreferrer">Argos Translate</a>') }}
</p> </p>
</div> </div>
</footer> </footer>

View file

@ -3,6 +3,7 @@ import sys
import os import os
import re import re
import polib import polib
import json
from babel.messages.frontend import main as pybabel from babel.messages.frontend import main as pybabel
from libretranslate.language import load_languages, improve_translation_formatting from libretranslate.language import load_languages, improve_translation_formatting
from libretranslate.locales import get_available_locales from libretranslate.locales import get_available_locales
@ -11,10 +12,24 @@ from libretranslate.app import get_version
# Update strings # Update strings
if __name__ == "__main__": if __name__ == "__main__":
print("Loading languages")
languages = load_languages()
en_lang = next((l for l in languages if l.code == 'en'), None)
if en_lang is None:
print("Error: English model not found. You need it to run this script.")
exit(1)
locales_dir = os.path.join("libretranslate", "locales") locales_dir = os.path.join("libretranslate", "locales")
if not os.path.isdir(locales_dir): if not os.path.isdir(locales_dir):
os.makedirs(locales_dir) os.makedirs(locales_dir)
# Dump language list so it gets picked up by pybabel
langs_file = os.path.join(locales_dir, ".langs.py")
with open(langs_file, 'w') as f:
for l in languages:
f.write("_(%s)\n" % json.dumps(l.name))
print("Wrote %s" % langs_file)
messagespot = os.path.join(locales_dir, "messages.pot") messagespot = os.path.join(locales_dir, "messages.pot")
print("Updating %s" % messagespot) print("Updating %s" % messagespot)
sys.argv = ["", "extract", "-F", "babel.cfg", "-k", "_e _h", sys.argv = ["", "extract", "-F", "babel.cfg", "-k", "_e _h",
@ -24,13 +39,7 @@ if __name__ == "__main__":
"-o", messagespot, "libretranslate"] "-o", messagespot, "libretranslate"]
pybabel() pybabel()
# Load list of languages
print("Loading languages")
languages = load_languages()
en_lang = next((l for l in languages if l.code == 'en'), None)
if en_lang is None:
print("Error: English model not found. You need it to run this script.")
exit(1)
lang_codes = [l.code for l in languages if l != "en"] lang_codes = [l.code for l in languages if l != "en"]
lang_codes = ["it"] # TODO REMOVE lang_codes = ["it"] # TODO REMOVE
@ -70,7 +79,7 @@ if __name__ == "__main__":
text = entry.msgid text = entry.msgid
# Extract placeholders # Extract placeholders
placeholders = re.findall(r'%\(?.*?\)?s', text) placeholders = re.findall(r'%\(?[^\)]*\)?s', text)
for p in range(0, len(placeholders)): for p in range(0, len(placeholders)):
text = text.replace(placeholders[p], "<x>%s</x>" % p) text = text.replace(placeholders[p], "<x>%s</x>" % p)
@ -79,7 +88,7 @@ if __name__ == "__main__":
translated = str(translate_html(translator, text)) translated = str(translate_html(translator, text))
else: else:
translated = improve_translation_formatting(text, translator.translate(text)) translated = improve_translation_formatting(text, translator.translate(text))
# Restore placeholders # Restore placeholders
for p in range(0, len(placeholders)): for p in range(0, len(placeholders)):
tag = "<x>%s</x>" % p tag = "<x>%s</x>" % p