mirror of
https://github.com/LibreTranslate/LibreTranslate.git
synced 2024-11-29 03:11:01 +00:00
commit
cb3ee55eb4
23 changed files with 2385 additions and 206 deletions
2
.github/workflows/publish-package.yml
vendored
2
.github/workflows/publish-package.yml
vendored
|
@ -26,6 +26,7 @@ jobs:
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install pytest flake8
|
pip install pytest flake8
|
||||||
pip install .
|
pip install .
|
||||||
|
python compile_locales.py
|
||||||
|
|
||||||
- name: Check code style with flake8 (lint)
|
- name: Check code style with flake8 (lint)
|
||||||
run: |
|
run: |
|
||||||
|
@ -60,5 +61,6 @@ jobs:
|
||||||
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||||
run: |
|
run: |
|
||||||
|
python compile_locales.py
|
||||||
python setup.py sdist bdist_wheel
|
python setup.py sdist bdist_wheel
|
||||||
twine upload dist/*
|
twine upload dist/*
|
||||||
|
|
|
@ -14,11 +14,11 @@ RUN python -mvenv venv && ./venv/bin/pip install --upgrade pip
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Install package from source code
|
# Install package from source code, compile translations
|
||||||
RUN ./venv/bin/pip install . \
|
RUN ./venv/bin/pip install Babel==2.11.0 && ./venv/bin/python compile_locales.py \
|
||||||
|
&& ./venv/bin/pip install . \
|
||||||
&& ./venv/bin/pip cache purge
|
&& ./venv/bin/pip cache purge
|
||||||
|
|
||||||
|
|
||||||
FROM python:3.8.14-slim-bullseye
|
FROM python:3.8.14-slim-bullseye
|
||||||
|
|
||||||
ARG with_models=false
|
ARG with_models=false
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
1.3.8
|
1.3.9
|
||||||
|
|
2
babel.cfg
Normal file
2
babel.cfg
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[python: **.py]
|
||||||
|
[jinja2: **/templates/**]
|
16
compile_locales.py
Executable file
16
compile_locales.py
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from babel.messages.frontend import main as pybabel
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
locales_dir = os.path.join("libretranslate", "locales")
|
||||||
|
if not os.path.isdir(locales_dir):
|
||||||
|
os.makedirs(locales_dir)
|
||||||
|
|
||||||
|
print("Compiling locales")
|
||||||
|
sys.argv = ["", "compile", "-f", "-d", locales_dir]
|
||||||
|
pybabel()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ RUN if [ "$with_models" = "true" ]; then \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install package from source code
|
# Install package from source code
|
||||||
RUN pip3 install . \
|
RUN pip3 install Babel==2.11.0 && python3 compile_locales.py \
|
||||||
|
&& pip3 install . \
|
||||||
&& pip3 cache purge
|
&& pip3 cache purge
|
||||||
|
|
||||||
# Depending on your cuda install you may need to uncomment this line to allow the container to access the cuda libraries
|
# Depending on your cuda install you may need to uncomment this line to allow the container to access the cuda libraries
|
||||||
|
|
|
@ -9,15 +9,19 @@ from timeit import default_timer
|
||||||
import argostranslatefiles
|
import argostranslatefiles
|
||||||
from argostranslatefiles import get_supported_formats
|
from argostranslatefiles import get_supported_formats
|
||||||
from flask import (abort, Blueprint, Flask, jsonify, render_template, request,
|
from flask import (abort, Blueprint, Flask, jsonify, render_template, request,
|
||||||
Response, send_file, url_for)
|
Response, send_file, url_for, session)
|
||||||
from flask_swagger import swagger
|
from flask_swagger import swagger
|
||||||
from flask_swagger_ui import get_swaggerui_blueprint
|
from flask_swagger_ui import get_swaggerui_blueprint
|
||||||
|
from flask_session import Session
|
||||||
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
|
||||||
|
|
||||||
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 (_, _lazy, get_available_locales, get_available_locale_codes, gettext_escaped,
|
||||||
|
gettext_html, lazy_swag, get_alternate_locale_links)
|
||||||
|
|
||||||
from .api_keys import Database, RemoteDatabase
|
from .api_keys import Database, RemoteDatabase
|
||||||
from .suggestions import Database as SuggestionsDatabase
|
from .suggestions import Database as SuggestionsDatabase
|
||||||
|
@ -53,7 +57,7 @@ def get_req_api_key():
|
||||||
def get_json_dict(request):
|
def get_json_dict(request):
|
||||||
d = request.get_json()
|
d = request.get_json()
|
||||||
if not isinstance(d, dict):
|
if not isinstance(d, dict):
|
||||||
abort(400, description="Invalid JSON format")
|
abort(400, description=_("Invalid JSON format"))
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,7 +125,7 @@ def create_app(args):
|
||||||
# Map userdefined frontend languages to argos language object.
|
# Map userdefined frontend languages to argos language object.
|
||||||
if args.frontend_language_source == "auto":
|
if args.frontend_language_source == "auto":
|
||||||
frontend_argos_language_source = type(
|
frontend_argos_language_source = type(
|
||||||
"obj", (object,), {"code": "auto", "name": "Auto Detect"}
|
"obj", (object,), {"code": "auto", "name": _("Auto Detect")}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
frontend_argos_language_source = next(
|
frontend_argos_language_source = next(
|
||||||
|
@ -186,7 +190,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)
|
||||||
|
@ -204,7 +208,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()
|
||||||
|
@ -213,16 +217,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,
|
||||||
|
@ -262,7 +266,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):
|
||||||
|
@ -274,6 +278,10 @@ def create_app(args):
|
||||||
if args.disable_web_ui:
|
if args.disable_web_ui:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
langcode = request.args.get('lang')
|
||||||
|
if langcode and langcode in get_available_locale_codes(not args.debug):
|
||||||
|
session.update(preferred_lang=langcode)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
gaId=args.ga_id,
|
gaId=args.ga_id,
|
||||||
|
@ -283,16 +291,20 @@ def create_app(args):
|
||||||
web_version=os.environ.get("LT_WEB") is not None,
|
web_version=os.environ.get("LT_WEB") is not None,
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
swagger_url=SWAGGER_URL,
|
swagger_url=SWAGGER_URL,
|
||||||
url_prefix=args.url_prefix
|
available_locales=[{'code': l['code'], 'name': _lazy(l['name'])} for l in get_available_locales(not args.debug)],
|
||||||
|
current_locale=get_locale(),
|
||||||
|
alternate_locales=get_alternate_locale_links()
|
||||||
)
|
)
|
||||||
|
|
||||||
@bp.get("/javascript-licenses")
|
@bp.route("/js/app.js")
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
def javascript_licenses():
|
def appjs():
|
||||||
if args.disable_web_ui:
|
if args.disable_web_ui:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
return render_template("javascript-licenses.html")
|
return render_template("app.js.template",
|
||||||
|
url_prefix=args.url_prefix,
|
||||||
|
get_api_key_link=args.get_api_key_link)
|
||||||
|
|
||||||
@bp.get("/languages")
|
@bp.get("/languages")
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
|
@ -323,7 +335,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
|
||||||
|
@ -452,11 +464,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)
|
||||||
|
|
||||||
|
@ -465,8 +477,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:
|
||||||
|
@ -478,8 +489,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":
|
||||||
|
@ -512,18 +522,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:
|
||||||
|
@ -531,7 +541,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))
|
||||||
|
@ -555,7 +565,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))
|
||||||
|
@ -576,7 +586,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
|
||||||
|
@ -663,36 +673,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)
|
||||||
|
@ -717,7 +727,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:
|
||||||
|
@ -725,7 +735,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:
|
||||||
|
@ -818,9 +828,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")
|
||||||
|
@ -828,7 +835,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))
|
||||||
|
|
||||||
|
@ -901,11 +908,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),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -969,7 +976,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")
|
||||||
|
@ -977,18 +984,23 @@ 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})
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
app.config["SESSION_TYPE"] = "filesystem"
|
||||||
|
app.config["SESSION_FILE_DIR"] = os.path.join("db", "sessions")
|
||||||
|
Session(app)
|
||||||
|
|
||||||
if args.debug:
|
if args.debug:
|
||||||
app.config["TEMPLATES_AUTO_RELOAD"] = True
|
app.config["TEMPLATES_AUTO_RELOAD"] = True
|
||||||
if args.url_prefix:
|
if args.url_prefix:
|
||||||
|
@ -1002,11 +1014,21 @@ def create_app(args):
|
||||||
swag["info"]["version"] = get_version()
|
swag["info"]["version"] = get_version()
|
||||||
swag["info"]["title"] = "LibreTranslate"
|
swag["info"]["title"] = "LibreTranslate"
|
||||||
|
|
||||||
|
|
||||||
@app.route(API_URL)
|
@app.route(API_URL)
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
def spec():
|
def spec():
|
||||||
return jsonify(swag)
|
return jsonify(lazy_swag(swag))
|
||||||
|
|
||||||
|
app.config["BABEL_TRANSLATION_DIRECTORIES"] = 'locales'
|
||||||
|
babel = Babel(app)
|
||||||
|
@babel.localeselector
|
||||||
|
def get_locale():
|
||||||
|
override_lang = request.headers.get('X-Override-Accept-Language')
|
||||||
|
if override_lang and override_lang in get_available_locale_codes():
|
||||||
|
return override_lang
|
||||||
|
return session.get('preferred_lang', request.accept_languages.best_match(get_available_locale_codes()))
|
||||||
|
|
||||||
|
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
|
||||||
swaggerui_blueprint = get_swaggerui_blueprint(SWAGGER_URL, API_URL)
|
swaggerui_blueprint = get_swaggerui_blueprint(SWAGGER_URL, API_URL)
|
||||||
|
|
84
libretranslate/locales.py
Normal file
84
libretranslate/locales.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
from functools import lru_cache
|
||||||
|
from flask_babel import gettext as _
|
||||||
|
from flask_babel import lazy_gettext as _lazy
|
||||||
|
|
||||||
|
from markupsafe import escape, Markup
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def get_available_locales(only_reviewed=True):
|
||||||
|
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
|
||||||
|
dirs = [os.path.join(locales_dir, d) for d in os.listdir(locales_dir)]
|
||||||
|
|
||||||
|
res = [{'code': 'en', 'name': 'English'}]
|
||||||
|
|
||||||
|
for d in dirs:
|
||||||
|
meta_file = os.path.join(d, 'meta.json')
|
||||||
|
if os.path.isdir(os.path.join(d, 'LC_MESSAGES')) and os.path.isfile(meta_file):
|
||||||
|
try:
|
||||||
|
with open(meta_file) as f:
|
||||||
|
j = json.loads(f.read())
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if j.get('reviewed') or not only_reviewed:
|
||||||
|
res.append({'code': os.path.basename(d), 'name': j.get('name', '')})
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def get_available_locale_codes(only_reviewed=True):
|
||||||
|
return [l['code'] for l in get_available_locales(only_reviewed=only_reviewed)]
|
||||||
|
|
||||||
|
@lru_cache(maxsize=None)
|
||||||
|
def get_alternate_locale_links():
|
||||||
|
tmpl = os.environ.get("LT_LOCALE_LINK_TEMPLATE")
|
||||||
|
if tmpl is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
locales = get_available_locale_codes()
|
||||||
|
result = []
|
||||||
|
for l in locales:
|
||||||
|
link = tmpl.replace("{LANG}", l)
|
||||||
|
if l == 'en':
|
||||||
|
link = link.replace("en.", "")
|
||||||
|
result.append({ 'link': link,'lang': l })
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Javascript code should use _e instead of _
|
||||||
|
def gettext_escaped(text, **variables):
|
||||||
|
return json.dumps(_(text, **variables))
|
||||||
|
|
||||||
|
# HTML should be escaped using _h instead of _
|
||||||
|
def gettext_html(text, **variables):
|
||||||
|
# Translate text without args
|
||||||
|
s = str(escape(_(text)))
|
||||||
|
|
||||||
|
v = {}
|
||||||
|
if variables:
|
||||||
|
for k in variables:
|
||||||
|
if hasattr(variables[k], 'unescape'):
|
||||||
|
v[k] = variables[k].unescape()
|
||||||
|
else:
|
||||||
|
v[k] = Markup(variables[k])
|
||||||
|
|
||||||
|
# Variables are assumed to be already escaped and thus safe
|
||||||
|
return Markup(s if not v else s % v)
|
||||||
|
|
||||||
|
def swag_eval(swag, func):
|
||||||
|
# Traverse the swag spec structure
|
||||||
|
# and call func on summary and description keys
|
||||||
|
for k in swag:
|
||||||
|
if k in ['summary', 'description'] and isinstance(swag[k], str) and swag[k] != "":
|
||||||
|
swag[k] = func(swag[k])
|
||||||
|
elif k == 'tags' and isinstance(swag[k], list):
|
||||||
|
swag[k] = [func(v) for v in swag[k]]
|
||||||
|
elif isinstance(swag[k], dict):
|
||||||
|
swag_eval(swag[k], func)
|
||||||
|
|
||||||
|
return swag
|
||||||
|
|
||||||
|
def lazy_swag(swag):
|
||||||
|
return swag_eval(swag, _lazy)
|
3
libretranslate/locales/.gitignore
vendored
Normal file
3
libretranslate/locales/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
**/*.mo
|
||||||
|
.langs.py
|
||||||
|
.swag.py
|
590
libretranslate/locales/de/LC_MESSAGES/messages.po
Normal file
590
libretranslate/locales/de/LC_MESSAGES/messages.po
Normal file
|
@ -0,0 +1,590 @@
|
||||||
|
# German translations for LibreTranslate.
|
||||||
|
# Copyright (C) 2023 LibreTranslate Authors
|
||||||
|
# This file is distributed under the same license as the LibreTranslate
|
||||||
|
# project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: LibreTranslate 1.3.9\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2023-01-06 14:26-0500\n"
|
||||||
|
"PO-Revision-Date: 2023-01-06 14:26-0500\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language: de\n"
|
||||||
|
"Language-Team: de <LL@li.org>\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Generated-By: Babel 2.11.0\n"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:60
|
||||||
|
msgid "Invalid JSON format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:128 libretranslate/templates/app.js.template:427
|
||||||
|
msgid "Auto Detect"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:193
|
||||||
|
msgid "Unauthorized"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:211
|
||||||
|
msgid "Too many request limits violations"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:220
|
||||||
|
msgid "Invalid API key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:227
|
||||||
|
msgid "Please contact the server operator to get an API key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:229
|
||||||
|
#, python-format
|
||||||
|
msgid "Visit %(url)s to get an API key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:269
|
||||||
|
msgid "Slowdown:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:467 libretranslate/app.py:469
|
||||||
|
#: libretranslate/app.py:471 libretranslate/app.py:683
|
||||||
|
#: libretranslate/app.py:685 libretranslate/app.py:687
|
||||||
|
#: libretranslate/app.py:838 libretranslate/app.py:987
|
||||||
|
#: libretranslate/app.py:989 libretranslate/app.py:991
|
||||||
|
#: libretranslate/app.py:993
|
||||||
|
#, python-format
|
||||||
|
msgid "Invalid request: missing %(name)s parameter"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:480 libretranslate/app.py:492
|
||||||
|
#, python-format
|
||||||
|
msgid "Invalid request: request (%(size)s) exceeds text limit (%(limit)s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:525 libretranslate/app.py:530
|
||||||
|
#: libretranslate/app.py:700 libretranslate/app.py:705
|
||||||
|
#, python-format
|
||||||
|
msgid "%(lang)s is not supported"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:536
|
||||||
|
#, python-format
|
||||||
|
msgid "%(format)s format is not supported"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:544 libretranslate/app.py:568
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"%(tname)s (%(tcode)s) is not available as a target language from "
|
||||||
|
"%(sname)s (%(scode)s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:589
|
||||||
|
#, python-format
|
||||||
|
msgid "Cannot translate text: %(text)s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:676 libretranslate/app.py:730
|
||||||
|
msgid "Files translation are disabled on this server."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:690
|
||||||
|
msgid "Invalid request: empty file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:693
|
||||||
|
msgid "Invalid request: file format not supported"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:738
|
||||||
|
msgid "Invalid filename"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/app.py:979
|
||||||
|
msgid "Suggestions are disabled on this server."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:1
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:2
|
||||||
|
msgid "Arabic"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:3
|
||||||
|
msgid "Azerbaijani"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:4
|
||||||
|
msgid "Chinese"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:5
|
||||||
|
msgid "Czech"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:6
|
||||||
|
msgid "Danish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:7
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:8
|
||||||
|
msgid "Esperanto"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:9
|
||||||
|
msgid "Finnish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:10
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:11
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:12
|
||||||
|
msgid "Greek"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:13
|
||||||
|
msgid "Hebrew"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:14
|
||||||
|
msgid "Hindi"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:15
|
||||||
|
msgid "Hungarian"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:16
|
||||||
|
msgid "Indonesian"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:17
|
||||||
|
msgid "Irish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:18
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:19
|
||||||
|
msgid "Japanese"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:20
|
||||||
|
msgid "Korean"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:21
|
||||||
|
msgid "Persian"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:22
|
||||||
|
msgid "Polish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:23
|
||||||
|
msgid "Portuguese"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:24
|
||||||
|
msgid "Russian"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:25
|
||||||
|
msgid "Slovak"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:26
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:27
|
||||||
|
msgid "Swedish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:28
|
||||||
|
msgid "Turkish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:29
|
||||||
|
msgid "Ukranian"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:30
|
||||||
|
msgid "Vietnamese"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:1
|
||||||
|
msgid "Retrieve list of supported languages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:2
|
||||||
|
msgid "List of languages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:3
|
||||||
|
msgid "translate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:4
|
||||||
|
msgid "Translate text from a language to another"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:5 libretranslate/templates/index.html:219
|
||||||
|
msgid "Translated text"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:6
|
||||||
|
msgid "Invalid request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:7
|
||||||
|
msgid "Translation error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:8
|
||||||
|
msgid "Slow down"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:9
|
||||||
|
msgid "Banned"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:10
|
||||||
|
msgid "Translate file from a language to another"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:11
|
||||||
|
msgid "Translated file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:12
|
||||||
|
msgid "Detect the language of a single text"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:13
|
||||||
|
msgid "Detections"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:14
|
||||||
|
msgid "Detection error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:15
|
||||||
|
msgid "Retrieve frontend specific settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:16
|
||||||
|
msgid "frontend settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:17
|
||||||
|
msgid "frontend"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:18
|
||||||
|
msgid "Submit a suggestion to improve a translation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:19
|
||||||
|
msgid "Success"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:20
|
||||||
|
msgid "Not authorized"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:21
|
||||||
|
msgid "feedback"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:22
|
||||||
|
msgid "Language code"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:23
|
||||||
|
msgid "Human-readable language name (in English)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:24
|
||||||
|
msgid "Supported target language codes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:25
|
||||||
|
msgid "Translated text(s)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:26
|
||||||
|
msgid "Error message"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:27
|
||||||
|
msgid "Reason for slow down"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:28
|
||||||
|
msgid "Translated file url"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:29
|
||||||
|
msgid "Confidence value"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:30
|
||||||
|
msgid "Character input limit for this language (-1 indicates no limit)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:31
|
||||||
|
msgid "Frontend translation timeout"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:32
|
||||||
|
msgid "Whether the API key database is enabled."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:33
|
||||||
|
msgid "Whether an API key is required."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:34
|
||||||
|
msgid "Whether submitting suggestions is enabled."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:35
|
||||||
|
msgid "Supported files format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:36
|
||||||
|
msgid "Whether submission was successful"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:31
|
||||||
|
#: libretranslate/templates/app.js.template:275
|
||||||
|
#: libretranslate/templates/app.js.template:279
|
||||||
|
msgid "Copy text"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: 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
|
||||||
|
msgid "Cannot load %(url)s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:253
|
||||||
|
#: libretranslate/templates/app.js.template:323
|
||||||
|
#: libretranslate/templates/app.js.template:385
|
||||||
|
#: libretranslate/templates/app.js.template:395
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:276
|
||||||
|
msgid "Copied"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:320
|
||||||
|
msgid ""
|
||||||
|
"Thanks for your correction. Note the suggestion will not take effect "
|
||||||
|
"right away."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:423
|
||||||
|
msgid "No languages available. Did you install the models correctly?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:479
|
||||||
|
#, python-format
|
||||||
|
msgid "Type in your API Key. If you need an API key, %(instructions)s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:479
|
||||||
|
msgid "press the \"Get API Key\" link."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:479
|
||||||
|
msgid "contact the server operator."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:8 libretranslate/templates/index.html:25
|
||||||
|
#: libretranslate/templates/index.html:333
|
||||||
|
msgid "Free and Open Source Machine Translation API"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:10
|
||||||
|
#: libretranslate/templates/index.html:29
|
||||||
|
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 ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:11
|
||||||
|
msgid "translation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:11
|
||||||
|
msgid "api"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:64
|
||||||
|
msgid "API Docs"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:66
|
||||||
|
msgid "Get API Key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:68
|
||||||
|
msgid "GitHub"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:70
|
||||||
|
msgid "Set API Key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:72
|
||||||
|
msgid "Change language"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:78
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:154
|
||||||
|
msgid "Dismiss"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:168
|
||||||
|
msgid "Translation API"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:172
|
||||||
|
msgid "Translate Text"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:176
|
||||||
|
msgid "Translate Files"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:182
|
||||||
|
msgid "Translate from"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:192
|
||||||
|
msgid "Swap source and target languages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:195
|
||||||
|
msgid "Translate into"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:207
|
||||||
|
msgid "Text to translate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:210
|
||||||
|
msgid "Delete text"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:223
|
||||||
|
msgid "Suggest translation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:227
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:230
|
||||||
|
msgid "Send"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:246
|
||||||
|
msgid "Supported file formats:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:250
|
||||||
|
msgid "File"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:265
|
||||||
|
msgid "Remove file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:272
|
||||||
|
msgid "Translate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:273
|
||||||
|
#: libretranslate/templates/index.html:317
|
||||||
|
msgid "Download"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:292
|
||||||
|
msgid "Request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:297
|
||||||
|
msgid "Response"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:312
|
||||||
|
msgid "Open Source Machine Translation API"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:313
|
||||||
|
msgid "Self-Hosted. Offline Capable. Easy to Setup."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:332
|
||||||
|
msgid "LibreTranslate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:334
|
||||||
|
msgid "License:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
#, 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 ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
msgid "host your own server"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
msgid "get an API key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:345
|
||||||
|
#, python-format
|
||||||
|
msgid "Made with %(heart)s by %(contributors)s and powered by %(engine)s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:345
|
||||||
|
#, python-format
|
||||||
|
msgid "%(libretranslate)s Contributors"
|
||||||
|
msgstr ""
|
||||||
|
|
4
libretranslate/locales/de/meta.json
Normal file
4
libretranslate/locales/de/meta.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "German",
|
||||||
|
"reviewed": false
|
||||||
|
}
|
606
libretranslate/locales/fr/LC_MESSAGES/messages.po
Normal file
606
libretranslate/locales/fr/LC_MESSAGES/messages.po
Normal file
|
@ -0,0 +1,606 @@
|
||||||
|
# French translations for LibreTranslate.
|
||||||
|
# Copyright (C) 2023 LibreTranslate Authors
|
||||||
|
# This file is distributed under the same license as the LibreTranslate
|
||||||
|
# project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: LibreTranslate 1.3.9\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2023-01-06 14:26-0500\n"
|
||||||
|
"PO-Revision-Date: 2023-01-06 14:26-0500\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: fr <LL@li.org>\n"
|
||||||
|
"Language: fr\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
"Generated-By: Babel 2.11.0\n"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:60
|
||||||
|
msgid "Invalid JSON format"
|
||||||
|
msgstr "Format JSON invalide"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:128 libretranslate/templates/app.js.template:427
|
||||||
|
msgid "Auto Detect"
|
||||||
|
msgstr "Auto Detect"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:193
|
||||||
|
msgid "Unauthorized"
|
||||||
|
msgstr "Non autorisé"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:211
|
||||||
|
msgid "Too many request limits violations"
|
||||||
|
msgstr "Trop de demandes limitent les violations"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:220
|
||||||
|
msgid "Invalid API key"
|
||||||
|
msgstr "Clé API invalide"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:227
|
||||||
|
msgid "Please contact the server operator to get an API key"
|
||||||
|
msgstr "Veuillez contacter l'opérateur du serveur pour obtenir une clé API"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:229
|
||||||
|
#, python-format
|
||||||
|
msgid "Visit %(url)s to get an API key"
|
||||||
|
msgstr "Visite %(url)s pour obtenir une clé API"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:269
|
||||||
|
msgid "Slowdown:"
|
||||||
|
msgstr "Ralentissement :"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:467 libretranslate/app.py:469
|
||||||
|
#: libretranslate/app.py:471 libretranslate/app.py:683
|
||||||
|
#: libretranslate/app.py:685 libretranslate/app.py:687
|
||||||
|
#: libretranslate/app.py:838 libretranslate/app.py:987
|
||||||
|
#: libretranslate/app.py:989 libretranslate/app.py:991
|
||||||
|
#: libretranslate/app.py:993
|
||||||
|
#, python-format
|
||||||
|
msgid "Invalid request: missing %(name)s parameter"
|
||||||
|
msgstr "Demande invalide: manquante %(name)s paramètre"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:480 libretranslate/app.py:492
|
||||||
|
#, python-format
|
||||||
|
msgid "Invalid request: request (%(size)s) exceeds text limit (%(limit)s)"
|
||||||
|
msgstr ""
|
||||||
|
"Demande non valide : demande (%(size)s) dépasse la limite de texte "
|
||||||
|
"(%(limit)s)"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:525 libretranslate/app.py:530
|
||||||
|
#: libretranslate/app.py:700 libretranslate/app.py:705
|
||||||
|
#, python-format
|
||||||
|
msgid "%(lang)s is not supported"
|
||||||
|
msgstr "%(lang)s n &apos; est pas soutenue"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:536
|
||||||
|
#, python-format
|
||||||
|
msgid "%(format)s format is not supported"
|
||||||
|
msgstr "%(format)s format n'est pas supporté"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:544 libretranslate/app.py:568
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"%(tname)s (%(tcode)s) is not available as a target language from %(sname)s "
|
||||||
|
"(%(scode)s)"
|
||||||
|
msgstr ""
|
||||||
|
"%(tname)s (%(tcode)s) n'est pas disponible comme langue cible de %(sname)s "
|
||||||
|
"(%(scode)s)"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:589
|
||||||
|
#, python-format
|
||||||
|
msgid "Cannot translate text: %(text)s"
|
||||||
|
msgstr "Impossible de traduire le texte: %(text)s"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:676 libretranslate/app.py:730
|
||||||
|
msgid "Files translation are disabled on this server."
|
||||||
|
msgstr "La traduction de fichiers est désactivée sur ce serveur."
|
||||||
|
|
||||||
|
#: libretranslate/app.py:690
|
||||||
|
msgid "Invalid request: empty file"
|
||||||
|
msgstr "Demande invalide: fichier vide"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:693
|
||||||
|
msgid "Invalid request: file format not supported"
|
||||||
|
msgstr "Demande invalide: format de fichier non supporté"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:738
|
||||||
|
msgid "Invalid filename"
|
||||||
|
msgstr "Nom de fichier invalide"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:979
|
||||||
|
msgid "Suggestions are disabled on this server."
|
||||||
|
msgstr "Les suggestions sont désactivées sur ce serveur."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:1
|
||||||
|
msgid "English"
|
||||||
|
msgstr "Anglais"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:2
|
||||||
|
msgid "Arabic"
|
||||||
|
msgstr "Arabe"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:3
|
||||||
|
msgid "Azerbaijani"
|
||||||
|
msgstr "Azerbaïdjan"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:4
|
||||||
|
msgid "Chinese"
|
||||||
|
msgstr "Chinois"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:5
|
||||||
|
msgid "Czech"
|
||||||
|
msgstr "Tchèque"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:6
|
||||||
|
msgid "Danish"
|
||||||
|
msgstr "Danish"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:7
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr "Néerlandais"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:8
|
||||||
|
msgid "Esperanto"
|
||||||
|
msgstr "Esperanto"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:9
|
||||||
|
msgid "Finnish"
|
||||||
|
msgstr "Finland"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:10
|
||||||
|
msgid "French"
|
||||||
|
msgstr "Français"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:11
|
||||||
|
msgid "German"
|
||||||
|
msgstr "Allemand"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:12
|
||||||
|
msgid "Greek"
|
||||||
|
msgstr "Grec"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:13
|
||||||
|
msgid "Hebrew"
|
||||||
|
msgstr "Hébreux"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:14
|
||||||
|
msgid "Hindi"
|
||||||
|
msgstr "Hindi"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:15
|
||||||
|
msgid "Hungarian"
|
||||||
|
msgstr "Hongrois"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:16
|
||||||
|
msgid "Indonesian"
|
||||||
|
msgstr "Indonésien"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:17
|
||||||
|
msgid "Irish"
|
||||||
|
msgstr "Irish"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:18
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr "Italien"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:19
|
||||||
|
msgid "Japanese"
|
||||||
|
msgstr "Japonais"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:20
|
||||||
|
msgid "Korean"
|
||||||
|
msgstr "Corée"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:21
|
||||||
|
msgid "Persian"
|
||||||
|
msgstr "Perse"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:22
|
||||||
|
msgid "Polish"
|
||||||
|
msgstr "Polonais"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:23
|
||||||
|
msgid "Portuguese"
|
||||||
|
msgstr "Portugais"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:24
|
||||||
|
msgid "Russian"
|
||||||
|
msgstr "Russe"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:25
|
||||||
|
msgid "Slovak"
|
||||||
|
msgstr "Slovaquie"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:26
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr "Espagnol"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:27
|
||||||
|
msgid "Swedish"
|
||||||
|
msgstr "Suédois"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:28
|
||||||
|
msgid "Turkish"
|
||||||
|
msgstr "Turque"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:29
|
||||||
|
msgid "Ukranian"
|
||||||
|
msgstr "Ukranian"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.langs.py:30
|
||||||
|
msgid "Vietnamese"
|
||||||
|
msgstr "Vietnam"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:1
|
||||||
|
msgid "Retrieve list of supported languages"
|
||||||
|
msgstr "Liste des langues supportées"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:2
|
||||||
|
msgid "List of languages"
|
||||||
|
msgstr "Liste des langues"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:3
|
||||||
|
msgid "translate"
|
||||||
|
msgstr "traduire"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:4
|
||||||
|
msgid "Translate text from a language to another"
|
||||||
|
msgstr "Traduire le texte d'une langue à une autre"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:5 libretranslate/templates/index.html:219
|
||||||
|
msgid "Translated text"
|
||||||
|
msgstr "Texte traduit"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:6
|
||||||
|
msgid "Invalid request"
|
||||||
|
msgstr "Demande non valide"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:7
|
||||||
|
msgid "Translation error"
|
||||||
|
msgstr "Erreur de traduction"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:8
|
||||||
|
msgid "Slow down"
|
||||||
|
msgstr "Doucement"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:9
|
||||||
|
msgid "Banned"
|
||||||
|
msgstr "Banned"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:10
|
||||||
|
msgid "Translate file from a language to another"
|
||||||
|
msgstr "Translate file from a language to another"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:11
|
||||||
|
msgid "Translated file"
|
||||||
|
msgstr "Fichier traduit"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:12
|
||||||
|
msgid "Detect the language of a single text"
|
||||||
|
msgstr "Detect the language of a single text"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:13
|
||||||
|
msgid "Detections"
|
||||||
|
msgstr "Détections"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:14
|
||||||
|
msgid "Detection error"
|
||||||
|
msgstr "Erreur de détection"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:15
|
||||||
|
msgid "Retrieve frontend specific settings"
|
||||||
|
msgstr "Récupérer les paramètres spécifiques du frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:16
|
||||||
|
msgid "frontend settings"
|
||||||
|
msgstr "paramètres de frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:17
|
||||||
|
msgid "frontend"
|
||||||
|
msgstr "frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:18
|
||||||
|
msgid "Submit a suggestion to improve a translation"
|
||||||
|
msgstr "Soumettre une suggestion pour améliorer la traduction"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:19
|
||||||
|
msgid "Success"
|
||||||
|
msgstr "Succès"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:20
|
||||||
|
msgid "Not authorized"
|
||||||
|
msgstr "Non autorisé"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:21
|
||||||
|
msgid "feedback"
|
||||||
|
msgstr "rétroaction"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:22
|
||||||
|
msgid "Language code"
|
||||||
|
msgstr "Code de langue"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:23
|
||||||
|
msgid "Human-readable language name (in English)"
|
||||||
|
msgstr "Nom de langue lisible (en anglais)"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:24
|
||||||
|
msgid "Supported target language codes"
|
||||||
|
msgstr "Codes linguistiques ciblés appuyés"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:25
|
||||||
|
msgid "Translated text(s)"
|
||||||
|
msgstr "Texte(s) traduit(s)"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:26
|
||||||
|
msgid "Error message"
|
||||||
|
msgstr "Message d ' erreur"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:27
|
||||||
|
msgid "Reason for slow down"
|
||||||
|
msgstr "Raison de ralentir"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:28
|
||||||
|
msgid "Translated file url"
|
||||||
|
msgstr "Fichier traduit url"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:29
|
||||||
|
msgid "Confidence value"
|
||||||
|
msgstr "Valeur de confiance"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:30
|
||||||
|
msgid "Character input limit for this language (-1 indicates no limit)"
|
||||||
|
msgstr ""
|
||||||
|
"Limite d'entrée de caractères pour cette langue (-1 n'indique aucune limite)"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:31
|
||||||
|
msgid "Frontend translation timeout"
|
||||||
|
msgstr "Délai de traduction de Frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:32
|
||||||
|
msgid "Whether the API key database is enabled."
|
||||||
|
msgstr "Que la base de données clé API soit activée."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:33
|
||||||
|
msgid "Whether an API key is required."
|
||||||
|
msgstr "Si une clé API est requise."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:34
|
||||||
|
msgid "Whether submitting suggestions is enabled."
|
||||||
|
msgstr "La possibilité de soumettre des suggestions est activée."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:35
|
||||||
|
msgid "Supported files format"
|
||||||
|
msgstr "Format des fichiers supportés"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:36
|
||||||
|
msgid "Whether submission was successful"
|
||||||
|
msgstr "Que la soumission soit réussie"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:31
|
||||||
|
#: libretranslate/templates/app.js.template:275
|
||||||
|
#: libretranslate/templates/app.js.template:279
|
||||||
|
msgid "Copy text"
|
||||||
|
msgstr "Copie du texte"
|
||||||
|
|
||||||
|
#: 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
|
||||||
|
msgid "Cannot load %(url)s"
|
||||||
|
msgstr "Charge %(url)s"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:253
|
||||||
|
#: libretranslate/templates/app.js.template:323
|
||||||
|
#: libretranslate/templates/app.js.template:385
|
||||||
|
#: libretranslate/templates/app.js.template:395
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr "Erreur inconnue"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:276
|
||||||
|
msgid "Copied"
|
||||||
|
msgstr "Copied"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:320
|
||||||
|
msgid ""
|
||||||
|
"Thanks for your correction. Note the suggestion will not take effect right "
|
||||||
|
"away."
|
||||||
|
msgstr ""
|
||||||
|
"Merci pour votre correction. Notez que la suggestion ne prendra pas effet "
|
||||||
|
"immédiatement."
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:423
|
||||||
|
msgid "No languages available. Did you install the models correctly?"
|
||||||
|
msgstr ""
|
||||||
|
"Pas de langues disponibles. Avez-vous installé les modèles correctement ?"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:479
|
||||||
|
#, python-format
|
||||||
|
msgid "Type in your API Key. If you need an API key, %(instructions)s"
|
||||||
|
msgstr ""
|
||||||
|
"Entrez votre clé API. Si vous avez besoin d'une clé API, %(instructions)s"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:479
|
||||||
|
msgid "press the \"Get API Key\" link."
|
||||||
|
msgstr "appuyez sur le lien \"Get API Key\"."
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:479
|
||||||
|
msgid "contact the server operator."
|
||||||
|
msgstr "contactez l'opérateur du serveur."
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:8
|
||||||
|
#: libretranslate/templates/index.html:25
|
||||||
|
#: libretranslate/templates/index.html:333
|
||||||
|
msgid "Free and Open Source Machine Translation API"
|
||||||
|
msgstr "API de Traduction Automatique gratuite et Open Source"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:10
|
||||||
|
#: libretranslate/templates/index.html:29
|
||||||
|
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 de Traduction Automatique et Open Source. Auto-hostée, hors ligne "
|
||||||
|
"capable et facile à installer. Exécutez votre propre serveur API en quelques"
|
||||||
|
" minutes."
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:11
|
||||||
|
msgid "translation"
|
||||||
|
msgstr "traduction"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:11
|
||||||
|
msgid "api"
|
||||||
|
msgstr "api"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:64
|
||||||
|
msgid "API Docs"
|
||||||
|
msgstr "API Docs"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:66
|
||||||
|
msgid "Get API Key"
|
||||||
|
msgstr "Obtenir API Key"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:68
|
||||||
|
msgid "GitHub"
|
||||||
|
msgstr "GitHub"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:70
|
||||||
|
msgid "Set API Key"
|
||||||
|
msgstr "Set API Key"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:72
|
||||||
|
msgid "Change language"
|
||||||
|
msgstr "Changer de langue"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:78
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr "Edit"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:154
|
||||||
|
msgid "Dismiss"
|
||||||
|
msgstr "Dismiss"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:168
|
||||||
|
msgid "Translation API"
|
||||||
|
msgstr "API de traduction"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:172
|
||||||
|
msgid "Translate Text"
|
||||||
|
msgstr "Texte traduit"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:176
|
||||||
|
msgid "Translate Files"
|
||||||
|
msgstr "Translate Files"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:182
|
||||||
|
msgid "Translate from"
|
||||||
|
msgstr "Translate from"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:192
|
||||||
|
msgid "Swap source and target languages"
|
||||||
|
msgstr "Inverser la source et les langues cibles"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:195
|
||||||
|
msgid "Translate into"
|
||||||
|
msgstr "Translate into"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:207
|
||||||
|
msgid "Text to translate"
|
||||||
|
msgstr "Texte pour traduire"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:210
|
||||||
|
msgid "Delete text"
|
||||||
|
msgstr "Supprimer le texte"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:223
|
||||||
|
msgid "Suggest translation"
|
||||||
|
msgstr "Traduction suggérée"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:227
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Annuler"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:230
|
||||||
|
msgid "Send"
|
||||||
|
msgstr "Envoyer"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:246
|
||||||
|
msgid "Supported file formats:"
|
||||||
|
msgstr "Formats de fichiers supportés:"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:250
|
||||||
|
msgid "File"
|
||||||
|
msgstr "Fichier"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:265
|
||||||
|
msgid "Remove file"
|
||||||
|
msgstr "Supprimer le fichier"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:272
|
||||||
|
msgid "Translate"
|
||||||
|
msgstr "Traduire"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:273
|
||||||
|
#: libretranslate/templates/index.html:317
|
||||||
|
msgid "Download"
|
||||||
|
msgstr "Télécharger"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:292
|
||||||
|
msgid "Request"
|
||||||
|
msgstr "Demande"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:297
|
||||||
|
msgid "Response"
|
||||||
|
msgstr "Réponse"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:312
|
||||||
|
msgid "Open Source Machine Translation API"
|
||||||
|
msgstr "Open Source API de Traduction automatique"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:313
|
||||||
|
msgid "Self-Hosted. Offline Capable. Easy to Setup."
|
||||||
|
msgstr "Auto-Hosted. Offline Capable. Facile à installer."
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:332
|
||||||
|
msgid "LibreTranslate"
|
||||||
|
msgstr "LibreTranslate"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:334
|
||||||
|
msgid "License:"
|
||||||
|
msgstr "Licence:"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
#, 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 ""
|
||||||
|
"Cette API publique devrait être utilisée pour les tests, l'utilisation "
|
||||||
|
"personnelle ou occasionnelle. Si vous allez exécuter une demande en "
|
||||||
|
"production, s'il vous plaît %(host_server)s ou %(get_api_key)s."
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
msgid "host your own server"
|
||||||
|
msgstr "hôte de votre propre serveur"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
msgid "get an API key"
|
||||||
|
msgstr "obtenir une clé API"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:345
|
||||||
|
#, python-format
|
||||||
|
msgid "Made with %(heart)s by %(contributors)s and powered by %(engine)s"
|
||||||
|
msgstr ""
|
||||||
|
"Fabriqué avec %(heart)s by %(contributors)s et alimenté par %(engine)s"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:345
|
||||||
|
#, python-format
|
||||||
|
msgid "%(libretranslate)s Contributors"
|
||||||
|
msgstr "%(libretranslate)s Contributeurs"
|
4
libretranslate/locales/fr/meta.json
Normal file
4
libretranslate/locales/fr/meta.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "French",
|
||||||
|
"reviewed": false
|
||||||
|
}
|
606
libretranslate/locales/it/LC_MESSAGES/messages.po
Normal file
606
libretranslate/locales/it/LC_MESSAGES/messages.po
Normal file
|
@ -0,0 +1,606 @@
|
||||||
|
# Italian translations for LibreTranslate.
|
||||||
|
# Copyright (C) 2023 LibreTranslate Authors
|
||||||
|
# This file is distributed under the same license as the LibreTranslate
|
||||||
|
# project.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: LibreTranslate 1.3.9\n"
|
||||||
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
|
"POT-Creation-Date: 2023-01-06 14:26-0500\n"
|
||||||
|
"PO-Revision-Date: 2023-01-06 14:26-0500\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: it <LL@li.org>\n"
|
||||||
|
"Language: it\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"Generated-By: Babel 2.11.0\n"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:60
|
||||||
|
msgid "Invalid JSON format"
|
||||||
|
msgstr "Formato JSON non valido"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:128 libretranslate/templates/app.js.template:427
|
||||||
|
msgid "Auto Detect"
|
||||||
|
msgstr "Rilevamento automatico"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:193
|
||||||
|
msgid "Unauthorized"
|
||||||
|
msgstr "Non autorizzato"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:211
|
||||||
|
msgid "Too many request limits violations"
|
||||||
|
msgstr "Troppe richieste limitano le violazioni"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:220
|
||||||
|
msgid "Invalid API key"
|
||||||
|
msgstr "Chiave API non valida"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:227
|
||||||
|
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:229
|
||||||
|
#, python-format
|
||||||
|
msgid "Visit %(url)s to get an API key"
|
||||||
|
msgstr "Visita %(url)s per ottenere una chiave API"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:269
|
||||||
|
msgid "Slowdown:"
|
||||||
|
msgstr "Rallenta:"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:467 libretranslate/app.py:469
|
||||||
|
#: libretranslate/app.py:471 libretranslate/app.py:683
|
||||||
|
#: libretranslate/app.py:685 libretranslate/app.py:687
|
||||||
|
#: libretranslate/app.py:838 libretranslate/app.py:987
|
||||||
|
#: libretranslate/app.py:989 libretranslate/app.py:991
|
||||||
|
#: libretranslate/app.py:993
|
||||||
|
#, python-format
|
||||||
|
msgid "Invalid request: missing %(name)s parameter"
|
||||||
|
msgstr "Richiesta non valida: mancante %(name)s parametro"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:480 libretranslate/app.py:492
|
||||||
|
#, 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:525 libretranslate/app.py:530
|
||||||
|
#: libretranslate/app.py:700 libretranslate/app.py:705
|
||||||
|
#, python-format
|
||||||
|
msgid "%(lang)s is not supported"
|
||||||
|
msgstr "%(lang)s non è supportato"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:536
|
||||||
|
#, python-format
|
||||||
|
msgid "%(format)s format is not supported"
|
||||||
|
msgstr "%(format)s formato non è supportato"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:544 libretranslate/app.py:568
|
||||||
|
#, 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:589
|
||||||
|
#, python-format
|
||||||
|
msgid "Cannot translate text: %(text)s"
|
||||||
|
msgstr "Non può tradurre il testo: %(text)s"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:676 libretranslate/app.py:730
|
||||||
|
msgid "Files translation are disabled on this server."
|
||||||
|
msgstr "La traduzione dei file è disabilitata su questo server."
|
||||||
|
|
||||||
|
#: libretranslate/app.py:690
|
||||||
|
msgid "Invalid request: empty file"
|
||||||
|
msgstr "Richiesta non valida: file vuoto"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:693
|
||||||
|
msgid "Invalid request: file format not supported"
|
||||||
|
msgstr "Richiesta non valida: formato file non supportato"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:738
|
||||||
|
msgid "Invalid filename"
|
||||||
|
msgstr "Invalid filename"
|
||||||
|
|
||||||
|
#: libretranslate/app.py:979
|
||||||
|
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/locales/.swag.py:1
|
||||||
|
msgid "Retrieve list of supported languages"
|
||||||
|
msgstr "Recuperare l'elenco delle lingue supportate"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:2
|
||||||
|
msgid "List of languages"
|
||||||
|
msgstr "Elenco delle lingue"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:3
|
||||||
|
msgid "translate"
|
||||||
|
msgstr "tradurre"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:4
|
||||||
|
msgid "Translate text from a language to another"
|
||||||
|
msgstr "Tradurre testo da una lingua a un'altra"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:5 libretranslate/templates/index.html:219
|
||||||
|
msgid "Translated text"
|
||||||
|
msgstr "Tradotto testo"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:6
|
||||||
|
msgid "Invalid request"
|
||||||
|
msgstr "Richiesta non valida"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:7
|
||||||
|
msgid "Translation error"
|
||||||
|
msgstr "Errore di traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:8
|
||||||
|
msgid "Slow down"
|
||||||
|
msgstr "Rallenta"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:9
|
||||||
|
msgid "Banned"
|
||||||
|
msgstr "Banati"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:10
|
||||||
|
msgid "Translate file from a language to another"
|
||||||
|
msgstr "Tradurre file da una lingua a un'altra"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:11
|
||||||
|
msgid "Translated file"
|
||||||
|
msgstr "Tradotto file"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:12
|
||||||
|
msgid "Detect the language of a single text"
|
||||||
|
msgstr "Rileva la lingua di un singolo testo"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:13
|
||||||
|
msgid "Detections"
|
||||||
|
msgstr "Rilevazioni"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:14
|
||||||
|
msgid "Detection error"
|
||||||
|
msgstr "Errore di rilevamento"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:15
|
||||||
|
msgid "Retrieve frontend specific settings"
|
||||||
|
msgstr "Recuperare le impostazioni specifiche di frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:16
|
||||||
|
msgid "frontend settings"
|
||||||
|
msgstr "impostazioni di frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:17
|
||||||
|
msgid "frontend"
|
||||||
|
msgstr "fronte"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:18
|
||||||
|
msgid "Submit a suggestion to improve a translation"
|
||||||
|
msgstr "Inviare un suggerimento per migliorare una traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:19
|
||||||
|
msgid "Success"
|
||||||
|
msgstr "Successo"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:20
|
||||||
|
msgid "Not authorized"
|
||||||
|
msgstr "Non autorizzato"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:21
|
||||||
|
msgid "feedback"
|
||||||
|
msgstr "feedback"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:22
|
||||||
|
msgid "Language code"
|
||||||
|
msgstr "Codice linguistico"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:23
|
||||||
|
msgid "Human-readable language name (in English)"
|
||||||
|
msgstr "Nome di lingua leggibile dall'uomo (in inglese)"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:24
|
||||||
|
msgid "Supported target language codes"
|
||||||
|
msgstr "Codici di lingua target supportati"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:25
|
||||||
|
msgid "Translated text(s)"
|
||||||
|
msgstr "Tradotto testo(i)"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:26
|
||||||
|
msgid "Error message"
|
||||||
|
msgstr "Messaggio di errore"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:27
|
||||||
|
msgid "Reason for slow down"
|
||||||
|
msgstr "Ragione per rallentare"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:28
|
||||||
|
msgid "Translated file url"
|
||||||
|
msgstr "Tradotto file url"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:29
|
||||||
|
msgid "Confidence value"
|
||||||
|
msgstr "Valore di fiducia"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:30
|
||||||
|
msgid "Character input limit for this language (-1 indicates no limit)"
|
||||||
|
msgstr "Limite di ingresso per questa lingua (-1 non indica limiti)"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:31
|
||||||
|
msgid "Frontend translation timeout"
|
||||||
|
msgstr "Tempo di traduzione Frontend"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:32
|
||||||
|
msgid "Whether the API key database is enabled."
|
||||||
|
msgstr "Se il database chiave API è abilitato."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:33
|
||||||
|
msgid "Whether an API key is required."
|
||||||
|
msgstr "Se è richiesta una chiave API."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:34
|
||||||
|
msgid "Whether submitting suggestions is enabled."
|
||||||
|
msgstr "Se presentare suggerimenti è abilitato."
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:35
|
||||||
|
msgid "Supported files format"
|
||||||
|
msgstr "Formato file supportato"
|
||||||
|
|
||||||
|
#: libretranslate/locales/.swag.py:36
|
||||||
|
msgid "Whether submission was successful"
|
||||||
|
msgstr "Se la presentazione è stata di successo"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:31
|
||||||
|
#: libretranslate/templates/app.js.template:275
|
||||||
|
#: libretranslate/templates/app.js.template:279
|
||||||
|
msgid "Copy text"
|
||||||
|
msgstr "Copia testo"
|
||||||
|
|
||||||
|
#: 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
|
||||||
|
msgid "Cannot load %(url)s"
|
||||||
|
msgstr "Non riesco a caricare %(url)s"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:253
|
||||||
|
#: libretranslate/templates/app.js.template:323
|
||||||
|
#: libretranslate/templates/app.js.template:385
|
||||||
|
#: libretranslate/templates/app.js.template:395
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr "Errore sconosciuto"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:276
|
||||||
|
msgid "Copied"
|
||||||
|
msgstr "Copie"
|
||||||
|
|
||||||
|
#: libretranslate/templates/app.js.template:320
|
||||||
|
msgid ""
|
||||||
|
"Thanks for your correction. Note the suggestion will not take effect right "
|
||||||
|
"away."
|
||||||
|
msgstr ""
|
||||||
|
"Grazie per la sua correzione. Si noti che il suggerimento non avrà effetto "
|
||||||
|
"subito."
|
||||||
|
|
||||||
|
#: 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:8
|
||||||
|
#: libretranslate/templates/index.html:25
|
||||||
|
#: libretranslate/templates/index.html:333
|
||||||
|
msgid "Free and Open Source Machine Translation API"
|
||||||
|
msgstr "API di traduzione automatica gratuita e open source"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:10
|
||||||
|
#: libretranslate/templates/index.html:29
|
||||||
|
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:11
|
||||||
|
msgid "translation"
|
||||||
|
msgstr "traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:11
|
||||||
|
msgid "api"
|
||||||
|
msgstr "api"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:64
|
||||||
|
msgid "API Docs"
|
||||||
|
msgstr "API"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:66
|
||||||
|
msgid "Get API Key"
|
||||||
|
msgstr "Ottieni API Chiave"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:68
|
||||||
|
msgid "GitHub"
|
||||||
|
msgstr "GitHub"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:70
|
||||||
|
msgid "Set API Key"
|
||||||
|
msgstr "Set API Chiave"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:72
|
||||||
|
msgid "Change language"
|
||||||
|
msgstr "Cambia la lingua"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:78
|
||||||
|
msgid "Edit"
|
||||||
|
msgstr "Modifica"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:154
|
||||||
|
msgid "Dismiss"
|
||||||
|
msgstr "Oggetto"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:168
|
||||||
|
msgid "Translation API"
|
||||||
|
msgstr "API di traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:172
|
||||||
|
msgid "Translate Text"
|
||||||
|
msgstr "Traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:176
|
||||||
|
msgid "Translate Files"
|
||||||
|
msgstr "Traduci file"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:182
|
||||||
|
msgid "Translate from"
|
||||||
|
msgstr "Traduttore da"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:192
|
||||||
|
msgid "Swap source and target languages"
|
||||||
|
msgstr "Swap sorgente e lingue di destinazione"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:195
|
||||||
|
msgid "Translate into"
|
||||||
|
msgstr "Traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:207
|
||||||
|
msgid "Text to translate"
|
||||||
|
msgstr "Testo da tradurre"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:210
|
||||||
|
msgid "Delete text"
|
||||||
|
msgstr "Eliminare il testo"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:223
|
||||||
|
msgid "Suggest translation"
|
||||||
|
msgstr "Suggerisci la traduzione"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:227
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Annulla"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:230
|
||||||
|
msgid "Send"
|
||||||
|
msgstr "Invia"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:246
|
||||||
|
msgid "Supported file formats:"
|
||||||
|
msgstr "Formati di file supportati:"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:250
|
||||||
|
msgid "File"
|
||||||
|
msgstr "File"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:265
|
||||||
|
msgid "Remove file"
|
||||||
|
msgstr "Rimuovi file"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:272
|
||||||
|
msgid "Translate"
|
||||||
|
msgstr "Traduttore"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:273
|
||||||
|
#: libretranslate/templates/index.html:317
|
||||||
|
msgid "Download"
|
||||||
|
msgstr "Scarica"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:292
|
||||||
|
msgid "Request"
|
||||||
|
msgstr "Richiesta"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:297
|
||||||
|
msgid "Response"
|
||||||
|
msgstr "Risposta"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:312
|
||||||
|
msgid "Open Source Machine Translation API"
|
||||||
|
msgstr "API di traduzione automatica Open Source"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:313
|
||||||
|
msgid "Self-Hosted. Offline Capable. Easy to Setup."
|
||||||
|
msgstr "Ossessionato. Offline Capable. Facile da configurare."
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:332
|
||||||
|
msgid "LibreTranslate"
|
||||||
|
msgstr "LibreTranslate"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:334
|
||||||
|
msgid "License:"
|
||||||
|
msgstr "Licenza:"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
#, 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:337
|
||||||
|
msgid "host your own server"
|
||||||
|
msgstr "host tuo server"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:337
|
||||||
|
msgid "get an API key"
|
||||||
|
msgstr "ottenere una chiave API"
|
||||||
|
|
||||||
|
#: libretranslate/templates/index.html:345
|
||||||
|
#, 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:345
|
||||||
|
#, python-format
|
||||||
|
msgid "%(libretranslate)s Contributors"
|
||||||
|
msgstr "%(libretranslate)s Contributori"
|
4
libretranslate/locales/it/meta.json
Normal file
4
libretranslate/locales/it/meta.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "Italian",
|
||||||
|
"reviewed": false
|
||||||
|
}
|
|
@ -52,7 +52,7 @@
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select, select#locales{
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background: #111;
|
background: #111;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ a {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.noline{
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
min-height: 80vh;
|
min-height: 80vh;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +39,88 @@ h3.header {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-nav .locale-panel{
|
||||||
|
position: absolute;
|
||||||
|
top: 64px;
|
||||||
|
height: 68px;
|
||||||
|
right: 0;
|
||||||
|
padding: 0 16px;
|
||||||
|
width: 240px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
box-shadow: 0 2px 2px 0 rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 12%), 0px 4px 5px 0 rgb(0 0 0 / 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.locale-panel{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav:hover .change-language:hover + .locale-panel,
|
||||||
|
#nav-mobile:hover .change-language:hover + .locale-panel,
|
||||||
|
.change-language.clicked + .locale-panel{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav:hover .locale-panel:hover,
|
||||||
|
#nav-mobile .locale-panel:hover{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.locale-panel select{
|
||||||
|
display: block;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #fff;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locale-panel a{
|
||||||
|
line-height: normal;
|
||||||
|
font-size: 90%;
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 6px;
|
||||||
|
text-align: right;
|
||||||
|
text-decoration: none;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locale-panel a:hover{
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locale-panel a i.material-icons{
|
||||||
|
display: inline-block;
|
||||||
|
line-height: initial;
|
||||||
|
line-height: 14px;
|
||||||
|
font-size: 100%;
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.locale-panel a:hover{
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-mobile .locale-panel{
|
||||||
|
color: rgba(0,0,0,0.87);
|
||||||
|
padding: 0 32px;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-mobile a, #nav-mobile a i.material-icons{
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-mobile .locale-panel a{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-mobile .locale-panel a i.material-icons{
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
.language-select {
|
.language-select {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
|
||||||
// API host/endpoint
|
// API host/endpoint
|
||||||
var BaseUrl = window.location.protocol + "//" + window.location.host + url_prefix ;
|
var BaseUrl = window.location.protocol + "//" + window.location.host + "{{ url_prefix }}" ;
|
||||||
var htmlRegex = /<(.*)>.*?|<(.*)\/>/;
|
var htmlRegex = /<(.*)>.*?|<(.*)\/>/;
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
document.addEventListener('DOMContentLoaded', function(){
|
||||||
var sidenavElems = document.querySelectorAll('.sidenav');
|
var sidenavElems = document.querySelectorAll('.sidenav');
|
||||||
|
@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
|
||||||
detectedLangText: "",
|
detectedLangText: "",
|
||||||
|
|
||||||
copyTextLabel: "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 = "Cannot load /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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -272,11 +272,11 @@ document.addEventListener('DOMContentLoaded', function(){
|
||||||
this.$refs.translatedTextarea.setSelectionRange(0, 9999999); /* For mobile devices */
|
this.$refs.translatedTextarea.setSelectionRange(0, 9999999); /* For mobile devices */
|
||||||
document.execCommand("copy");
|
document.execCommand("copy");
|
||||||
|
|
||||||
if (this.copyTextLabel === "Copy text"){
|
if (this.copyTextLabel === {{ _e("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);
|
|
@ -1,21 +1,20 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="{{ current_locale }}">
|
||||||
<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>LibreTranslate - Free and Open Source Machine Translation API</title>
|
{% for al in alternate_locales %}<link rel="alternate" hreflang="{{ al.lang }}" href="{{ al.link }}" />
|
||||||
|
{% endfor %}
|
||||||
|
<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="Free and Open Source Machine Translation API. 100% 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="translation,api">
|
<meta name="keywords" content="{{ _h('translation') }},{{ _h('api') }}">
|
||||||
<script type="text/javascript">
|
|
||||||
var url_prefix = "{{ url_prefix }}"
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<link rel="preload" href="{{ url_for('static', filename='icon.svg') }}" as="image" />
|
<link rel="preload" href="{{ url_for('static', filename='icon.svg') }}" as="image" />
|
||||||
<link rel="preload" href="{{ url_for('static', filename='js/vue@2.js') }}" as="script">
|
<link rel="preload" href="{{ url_for('static', filename='js/vue@2.js') }}" as="script">
|
||||||
<link rel="preload" href="{{ url_for('static', filename='js/materialize.min.js') }}" as="script">
|
<link rel="preload" href="{{ url_for('static', filename='js/materialize.min.js') }}" as="script">
|
||||||
<link rel="preload" href="{{ url_for('static', filename='js/prism.min.js') }}" as="script">
|
<link rel="preload" href="{{ url_for('static', filename='js/prism.min.js') }}" as="script">
|
||||||
<link rel="preload" href="{{ url_for('static', filename='js/app.js') }}?v={{ version }}" as="script">
|
<link rel="preload" href="js/app.js?v={{ version }}" as="script">
|
||||||
|
|
||||||
<link rel="preload" href="{{ url_for('static', filename='css/materialize.min.css') }}" as="style"/>
|
<link rel="preload" href="{{ url_for('static', filename='css/materialize.min.css') }}" as="style"/>
|
||||||
<link rel="preload" href="{{ url_for('static', filename='css/material-icons.css') }}" as="style"/>
|
<link rel="preload" href="{{ url_for('static', filename='css/material-icons.css') }}" as="style"/>
|
||||||
|
@ -23,11 +22,11 @@
|
||||||
<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="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" />
|
||||||
<meta property="og:description" name="description" class="swiftype" content="Free and Open Source Machine Translation API. 100% self-hosted, no limits, no ties to proprietary services. Run your own API server in just a few minutes."/>
|
<meta property="og:description" name="description" class="swiftype" 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.') }}"/>
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='js/vue@2.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/vue@2.js') }}"></script>
|
||||||
|
|
||||||
|
@ -60,28 +59,64 @@
|
||||||
<img src="{{ url_for('static', filename='icon.svg') }}" alt="" class="logo">
|
<img src="{{ url_for('static', filename='icon.svg') }}" alt="" class="logo">
|
||||||
<span>LibreTranslate</span>
|
<span>LibreTranslate</span>
|
||||||
</a>
|
</a>
|
||||||
<ul class="right hide-on-med-and-down">
|
<ul id="nav" class="right hide-on-med-and-down top-nav position-relative">
|
||||||
<li><a href="{{ swagger_url }}">API Docs</a></li>
|
{% set menulinks %}
|
||||||
{% if get_api_key_link %}
|
<li><a href="{{ swagger_url }}">{{ _h("API Docs") }}</a></li>
|
||||||
<li><a href="{{ get_api_key_link }}">Get API Key</a></li>
|
{% if get_api_key_link %}
|
||||||
<script>window.getApiKeyLink = "{{ get_api_key_link }}";</script>
|
<li><a href="{{ get_api_key_link }}">{{ _h("Get API Key") }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">GitHub</a></li>
|
<li><a href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">{{ _h("GitHub") }}</a></li>
|
||||||
{% if api_keys %}
|
{% if api_keys %}
|
||||||
<li><a href="javascript:setApiKey()" title="Set API Key" aria-label="Set API Key"><i class="material-icons">vpn_key</i></a></li>
|
<li><a class="noline" href="javascript:setApiKey()" title="{{ _h('Set API Key') }}" aria-label="{{ _h('Set API Key') }}"><i class="material-icons">vpn_key</i></a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<li class="change-language"><a class="noline" href="javascript:void(0)" title="{{ _h('Change language') }}"><i class="material-icons">language</i></a>
|
||||||
|
</li>
|
||||||
|
<li class="locale-panel blue darken-3">
|
||||||
|
<select id="locales" onchange="changeLocale(this)">
|
||||||
|
{% for l in available_locales %}<option value="{{ l['code'] }}" {{ 'selected' if current_locale == l['code'] else ''}}>{{ l['name'] }}</option>{% endfor %}
|
||||||
|
</select>
|
||||||
|
<a href="#TODO">{{ _h("Edit") }}<i class="material-icons">create</i></a>
|
||||||
|
</li>
|
||||||
|
{% endset %}
|
||||||
|
{{ menulinks }}
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul id="nav-mobile" class="sidenav blue darken-3">
|
||||||
<ul id="nav-mobile" class="sidenav">
|
{{ menulinks }}
|
||||||
<li><a href="{{ swagger_url }}">API Docs</a></li>
|
|
||||||
{% if get_api_key_link %}
|
|
||||||
<li><a href="{{ get_api_key_link }}">Get API Key</a></li>
|
|
||||||
{% endif %}
|
|
||||||
<li><a href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">GitHub</a></li>
|
|
||||||
{% if api_keys %}
|
|
||||||
<li><a href="javascript:setApiKey()" title="Set API Key" aria-label="Set API Key"><i class="material-icons">vpn_key</i></a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
<script>
|
||||||
|
var localeLinks = {
|
||||||
|
{% for al in alternate_locales %}"{{ al.lang }}": "{{ al.link }}"{% if not loop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
};
|
||||||
|
|
||||||
|
function changeLocale(slt){
|
||||||
|
var lang = slt.value;
|
||||||
|
if (localeLinks[lang]) location.href = localeLinks[lang];
|
||||||
|
else location.href = '?lang=' + slt.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var btnChangeLangs = document.getElementsByClassName("change-language");
|
||||||
|
var localePanels = document.getElementsByClassName("locale-panel");
|
||||||
|
console.log(btnChangeLangs);
|
||||||
|
for (var i = 0; i < btnChangeLangs.length; i++){
|
||||||
|
(function(btn){
|
||||||
|
btn.addEventListener('click', function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
btn.classList.toggle('clicked');
|
||||||
|
});
|
||||||
|
})(btnChangeLangs[i]);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < localePanels.length; i++){
|
||||||
|
localePanels[i].addEventListener('click', function(e){
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.addEventListener('click', function(){
|
||||||
|
for (var i = 0; i < btnChangeLangs.length; i++){
|
||||||
|
btnChangeLangs[i].classList.remove('clicked');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
@ -116,7 +151,7 @@
|
||||||
<i class="material-icons">warning</i><p> [[ error ]]</p>
|
<i class="material-icons">warning</i><p> [[ error ]]</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<a href="#" @click="dismissError">Dismiss</a>
|
<a href="#" @click="dismissError">{{ _h("Dismiss") }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -130,22 +165,22 @@
|
||||||
<div class="section no-pad-bot">
|
<div class="section no-pad-bot">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h3 class="header center">Translation API</h3>
|
<h3 class="header center">{{ _h("Translation API") }}</h3>
|
||||||
<div id="translation-type-btns" class="s12 center" v-if="filesTranslation === true">
|
<div id="translation-type-btns" class="s12 center" v-if="filesTranslation === true">
|
||||||
<button type="button" class="btn btn-switch-type" @click="switchType('text')" :class="{'active': translationType === 'text'}">
|
<button type="button" class="btn btn-switch-type" @click="switchType('text')" :class="{'active': translationType === 'text'}">
|
||||||
<i aria-hidden="true" class="material-icons">title</i>
|
<i aria-hidden="true" class="material-icons">title</i>
|
||||||
<span class="btn-text">Translate Text</span>
|
<span class="btn-text">{{ _h("Translate Text") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-switch-type" @click="switchType('files')" :class="{'active': translationType === 'files'}">
|
<button type="button" class="btn btn-switch-type" @click="switchType('files')" :class="{'active': translationType === 'files'}">
|
||||||
<i aria-hidden="true" class="material-icons">description</i>
|
<i aria-hidden="true" class="material-icons">description</i>
|
||||||
<span class="btn-text">Translate Files</span>
|
<span class="btn-text">{{ _h("Translate Files") }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form id="translation-form" class="col s12">
|
<form id="translation-form" class="col s12">
|
||||||
<div class="row mb-0">
|
<div class="row mb-0">
|
||||||
<div class="col s6 language-select">
|
<div class="col s6 language-select">
|
||||||
<span id="sourceLangLabel">Translate from</span>
|
<span id="sourceLangLabel">{{ _h("Translate from") }}</span>
|
||||||
<span v-if="detectedLangText !== ''">[[ detectedLangText ]]</span>
|
<span v-if="detectedLangText !== ''">[[ detectedLangText ]]</span>
|
||||||
<select aria-labelledby="sourceLangLabel" class="browser-default" v-model="sourceLang" ref="sourceLangDropdown" @change="handleInput">
|
<select aria-labelledby="sourceLangLabel" class="browser-default" v-model="sourceLang" ref="sourceLangDropdown" @change="handleInput">
|
||||||
<template v-for="option in langs">
|
<template v-for="option in langs">
|
||||||
<option :value="option.code">[[ option.name ]]</option>
|
<option :value="option.code">[[ option.name ]]</option>
|
||||||
|
@ -154,10 +189,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col s6 language-select">
|
<div class="col s6 language-select">
|
||||||
<a href="javascript:void(0)" @click="swapLangs" class="btn-switch-language" aria-label="Swap source and target languages">
|
<a href="javascript:void(0)" @click="swapLangs" class="btn-switch-language" aria-label="{{ _h('Swap source and target languages') }}">
|
||||||
<i class="material-icons">swap_horiz</i>
|
<i class="material-icons">swap_horiz</i>
|
||||||
</a>
|
</a>
|
||||||
<span id="targetLangLabel">Translate into</span>
|
<span id="targetLangLabel">{{ _h("Translate into") }}</span>
|
||||||
<select aria-labelledby="targetLangLabel" class="browser-default" v-model="targetLang" ref="targetLangDropdown" @change="handleInput">
|
<select aria-labelledby="targetLangLabel" class="browser-default" v-model="targetLang" ref="targetLangDropdown" @change="handleInput">
|
||||||
<template v-for="option in targetLangs">
|
<template v-for="option in targetLangs">
|
||||||
<option v-if="option.code !== 'auto'" :value="option.code">[[ option.name ]]</option>
|
<option v-if="option.code !== 'auto'" :value="option.code">[[ option.name ]]</option>
|
||||||
|
@ -169,10 +204,10 @@
|
||||||
<div class="row" v-if="translationType === 'text'">
|
<div class="row" v-if="translationType === 'text'">
|
||||||
<div class="input-field textarea-container col s6">
|
<div class="input-field textarea-container col s6">
|
||||||
<label for="textarea1" class="sr-only">
|
<label for="textarea1" class="sr-only">
|
||||||
Text to translate
|
{{ _h("Text to translate") }}
|
||||||
</label>
|
</label>
|
||||||
<textarea id="textarea1" v-model="inputText" @input="handleInput" ref="inputTextarea" dir="auto"></textarea>
|
<textarea id="textarea1" v-model="inputText" @input="handleInput" ref="inputTextarea" dir="auto"></textarea>
|
||||||
<button class="btn-delete-text" title="Delete text" aria-label="Delete text" @click="deleteText">
|
<button class="btn-delete-text" title="{{ _h('Delete text') }}" aria-label="{{ _h('Delete text') }}" aria-label="Delete text" @click="deleteText">
|
||||||
<i class="material-icons">close</i>
|
<i class="material-icons">close</i>
|
||||||
</button>
|
</button>
|
||||||
<div class="characters-limit-container" v-if="charactersLimit !== -1">
|
<div class="characters-limit-container" v-if="charactersLimit !== -1">
|
||||||
|
@ -181,23 +216,23 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="input-field textarea-container col s6">
|
<div class="input-field textarea-container col s6">
|
||||||
<label for="textarea2" class="sr-only">
|
<label for="textarea2" class="sr-only">
|
||||||
Translated text
|
{{ _h("Translated text") }}
|
||||||
</label>
|
</label>
|
||||||
<textarea id="textarea2" v-model="translatedText" ref="translatedTextarea" dir="auto" v-bind:readonly="suggestions && !isSuggesting"></textarea>
|
<textarea id="textarea2" v-model="translatedText" ref="translatedTextarea" dir="auto" v-bind:readonly="suggestions && !isSuggesting"></textarea>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button v-if="suggestions && !loadingTranslation && inputText.length && !isSuggesting" class="btn-action" @click="suggestTranslation" aria-label="Suggest translation">
|
<button v-if="suggestions && !loadingTranslation && inputText.length && !isSuggesting" class="btn-action" @click="suggestTranslation" aria-label="{{ _h('Suggest translation') }}">
|
||||||
<i class="material-icons">edit</i>
|
<i class="material-icons">edit</i>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="suggestions && !loadingTranslation && inputText.length && isSuggesting" class="btn-action btn-blue" @click="closeSuggestTranslation">
|
<button v-if="suggestions && !loadingTranslation && inputText.length && isSuggesting" class="btn-action btn-blue" @click="closeSuggestTranslation">
|
||||||
<span>Cancel</span>
|
<span>{{ _h("Cancel") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="suggestions && !loadingTranslation && inputText.length && isSuggesting" :disabled="!canSendSuggestion" class="btn-action btn-blue" @click="sendSuggestion">
|
<button v-if="suggestions && !loadingTranslation && inputText.length && isSuggesting" :disabled="!canSendSuggestion" class="btn-action btn-blue" @click="sendSuggestion">
|
||||||
<span>Send</span>
|
<span>{{ _h("Send") }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button v-if="!isSuggesting" class="btn-action btn-copy-translated" @click="copyText">
|
<button v-if="!isSuggesting" class="btn-action btn-copy-translated" @click="copyText">
|
||||||
<span>[[ copyTextLabel ]]</span> <i class="material-icons" aria-hidden="true">content_copy</i>
|
<span>[[ copyTextLabel ]]</span> <i class="material-icons" aria-hidden="true">content_copy</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="position-relative">
|
<div class="position-relative">
|
||||||
<div class="progress translate" v-if="loadingTranslation">
|
<div class="progress translate" v-if="loadingTranslation">
|
||||||
<div class="indeterminate"></div>
|
<div class="indeterminate"></div>
|
||||||
|
@ -205,43 +240,43 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row" v-if="translationType === 'files'">
|
<div class="row" v-if="translationType === 'files'">
|
||||||
<div class="file-dropzone">
|
<div class="file-dropzone">
|
||||||
<div v-if="inputFile === false" class="dropzone-content">
|
<div v-if="inputFile === false" class="dropzone-content">
|
||||||
<span>Supported file formats: [[ supportedFilesFormatFormatted ]]</span>
|
<span>{{ _h("Supported file formats:") }} [[ supportedFilesFormatFormatted ]]</span>
|
||||||
<form action="#">
|
<form action="#">
|
||||||
<div class="file-field input-field">
|
<div class="file-field input-field">
|
||||||
<div class="btn">
|
<div class="btn">
|
||||||
<span id="fileLabel">File</span>
|
<span id="fileLabel">{{ _h("File") }}</span>
|
||||||
<input aria-labelledby="fileLabel" type="file" :accept="supportedFilesFormatFormatted" @change="handleInputFile" ref="fileInputRef">
|
<input aria-labelledby="fileLabel" type="file" :accept="supportedFilesFormatFormatted" @change="handleInputFile" ref="fileInputRef">
|
||||||
</div>
|
</div>
|
||||||
<div class="file-path-wrapper hidden">
|
<div class="file-path-wrapper hidden">
|
||||||
<input class="file-path validate" type="text">
|
<input class="file-path validate" type="text">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="inputFile !== false" class="dropzone-content">
|
<div v-if="inputFile !== false" class="dropzone-content">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="row mb-0">
|
<div class="row mb-0">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
[[ inputFile.name ]]
|
[[ inputFile.name ]]
|
||||||
<button v-if="loadingFileTranslation !== true" @click="removeFile" class="btn-flat" aria-label="Remove file">
|
<button v-if="loadingFileTranslation !== true" @click="removeFile" class="btn-flat" aria-label="{{ _h('Remove file') }}">
|
||||||
<i class="material-icons">close</i>
|
<i class="material-icons">close</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button @click="translateFile" v-if="translatedFileUrl === false && loadingFileTranslation === false" class="btn">Translate</button>
|
|
||||||
<a v-if="translatedFileUrl !== false" :href="translatedFileUrl" class="btn">Download</a>
|
|
||||||
<div class="progress" v-if="loadingFileTranslation">
|
|
||||||
<div class="indeterminate"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button @click="translateFile" v-if="translatedFileUrl === false && loadingFileTranslation === false" class="btn">{{ _h("Translate") }}</button>
|
||||||
|
<a v-if="translatedFileUrl !== false" :href="translatedFileUrl" class="btn">{{ _h("Download") }}</a>
|
||||||
|
<div class="progress" v-if="loadingFileTranslation">
|
||||||
|
<div class="indeterminate"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,12 +289,12 @@
|
||||||
|
|
||||||
<div class="row center">
|
<div class="row center">
|
||||||
<div class="col s12 m12 l6 left-align">
|
<div class="col s12 m12 l6 left-align">
|
||||||
<p class="mb-0">Request</p>
|
<p class="mb-0">{{ _h("Request") }}</p>
|
||||||
<pre class="code mt-0"><code class="language-javascript" v-html="$options.filters.highlight(requestCode)">
|
<pre class="code mt-0"><code class="language-javascript" v-html="$options.filters.highlight(requestCode)">
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="col s12 m12 l6 left-align">
|
<div class="col s12 m12 l6 left-align">
|
||||||
<p class="mb-0">Response</p>
|
<p class="mb-0">{{ _h("Response") }}</p>
|
||||||
<pre class="code mt-0"><code class="language-javascript" v-html="$options.filters.highlight(output)">
|
<pre class="code mt-0"><code class="language-javascript" v-html="$options.filters.highlight(output)">
|
||||||
</code></pre>
|
</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
@ -274,12 +309,12 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row center">
|
<div class="row center">
|
||||||
<div class="col s12 m12">
|
<div class="col s12 m12">
|
||||||
<h3 class="header">Open Source Machine Translation API</h3>
|
<h3 class="header">{{ _h("Open Source Machine Translation API") }}</h3>
|
||||||
<h4 class="header">100% Self-Hosted. Offline Capable. Easy to Setup.</h4>
|
<h4 class="header">{{ _h("Self-Hosted. Offline Capable. Easy to Setup.") }}</h4>
|
||||||
<div id="download-btn-wrapper">
|
<div id="download-btn-wrapper">
|
||||||
<a id="download-btn" class="waves-effect waves-light btn btn-large teal darken-2" href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">
|
<a id="download-btn" class="waves-effect waves-light btn btn-large teal darken-2" href="https://github.com/LibreTranslate/LibreTranslate" rel="noopener noreferrer">
|
||||||
<i aria-hidden="true" class="material-icons">cloud_download</i>
|
<i aria-hidden="true" class="material-icons">cloud_download</i>
|
||||||
<span class="btn-text">Download</span>
|
<span class="btn-text">{{ _h("Download") }}</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -294,13 +329,12 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col l12 s12">
|
<div class="col l12 s12">
|
||||||
<h5 class="white-text">LibreTranslate</h5>
|
<h5 class="white-text">{{ _h("LibreTranslate") }}</h5>
|
||||||
<p class="grey-text text-lighten-4">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>
|
||||||
<p><a class="grey-text text-lighten-4" href="/javascript-licenses" rel="jslicense">JavaScript license information</a></p>
|
|
||||||
{% if web_version %}
|
{% if web_version %}
|
||||||
<p>
|
<p>
|
||||||
This public API should be used for testing, personal or infrequent use. If you're going to run an application in production, please <a href="https://github.com/LibreTranslate/LibreTranslate" class="grey-text text-lighten-4" rel="noopener noreferrer">host your own server</a> or <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">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>') }}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -308,7 +342,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="footer-copyright center">
|
<div class="footer-copyright center">
|
||||||
<p class="white-text">
|
<p class="white-text">
|
||||||
Made with ❤ by <a class="white-text" href="https://github.com/LibreTranslate/LibreTranslate/graphs/contributors" rel="noopener noreferrer">LibreTranslate Contributors</a> and powered by <a class="white-text text-lighten-3" href="https://github.com/argosopentech/argos-translate/" rel="noopener noreferrer">Argos Translate</a>
|
{{ _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>
|
||||||
|
@ -320,7 +354,7 @@
|
||||||
window.Prism.manual = true;
|
window.Prism.manual = true;
|
||||||
// @license-end
|
// @license-end
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='js/prism.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/prism.min.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/app.js') }}?v={{ version }}"></script>
|
<script src="js/app.js?v={{ version }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>jslicense-labels1 for LibreTranslate</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h3>Weblabels</h3>
|
|
||||||
<table id="jslicense-labels1" border="1">
|
|
||||||
<tr>
|
|
||||||
<td><a href="{{ url_for('static', filename='js/vue@2.js') }}">Vue.js</a></td>
|
|
||||||
<td><a href="http://www.jclark.com/xml/copying.txt">Expat</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="{{ url_for('static', filename='js/prism.min.js') }}">prism.min.js</a></td>
|
|
||||||
<td><a href="http://www.jclark.com/xml/copying.txt">Expat</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="{{ url_for('static', filename='js/materialize.min.js') }}">materialize.min.js</a></td>
|
|
||||||
<td><a href="http://www.jclark.com/xml/copying.txt">Expat</a></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -3,6 +3,8 @@ Flask==2.2.2
|
||||||
flask-swagger==0.2.14
|
flask-swagger==0.2.14
|
||||||
flask-swagger-ui==4.11.1
|
flask-swagger-ui==4.11.1
|
||||||
Flask-Limiter==2.6.3
|
Flask-Limiter==2.6.3
|
||||||
|
Flask-Babel==2.0.0
|
||||||
|
Flask-Session==0.4.0
|
||||||
waitress==2.1.2
|
waitress==2.1.2
|
||||||
expiringdict==1.2.2
|
expiringdict==1.2.2
|
||||||
LTpycld2==0.42
|
LTpycld2==0.42
|
||||||
|
@ -16,3 +18,4 @@ Werkzeug==2.2.2
|
||||||
requests==2.28.1
|
requests==2.28.1
|
||||||
redis==4.3.4
|
redis==4.3.4
|
||||||
prometheus-client==0.15.0
|
prometheus-client==0.15.0
|
||||||
|
polib==1.1.1
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -13,7 +13,7 @@ setup(
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
# packages=find_packages(include=['openpredict']),
|
# packages=find_packages(include=['openpredict']),
|
||||||
# package_dir={'openpredict': 'openpredict'},
|
# package_dir={'openpredict': 'openpredict'},
|
||||||
package_data={'': ['static/*', 'static/**/*', 'templates/*']},
|
package_data={'': ['static/*', 'static/**/*', 'templates/*', 'locales/**/meta.json', 'locales/**/**/*.mo']},
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
|
|
136
update_locales.py
Executable file
136
update_locales.py
Executable file
|
@ -0,0 +1,136 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import polib
|
||||||
|
import json
|
||||||
|
from babel.messages.frontend import main as pybabel
|
||||||
|
from libretranslate.language import load_languages, improve_translation_formatting
|
||||||
|
from libretranslate.locales import get_available_locale_codes, swag_eval
|
||||||
|
from translatehtml import translate_html
|
||||||
|
from libretranslate.app import get_version, create_app
|
||||||
|
from libretranslate.main import get_args
|
||||||
|
from flask_swagger import swagger
|
||||||
|
|
||||||
|
# Update strings
|
||||||
|
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")
|
||||||
|
if not os.path.isdir(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)
|
||||||
|
|
||||||
|
# Dump swagger strings
|
||||||
|
args = get_args()
|
||||||
|
app = create_app(args)
|
||||||
|
swag = swagger(app)
|
||||||
|
|
||||||
|
swag_strings = []
|
||||||
|
def add_swag_string(s):
|
||||||
|
if not s in swag_strings:
|
||||||
|
swag_strings.append(s)
|
||||||
|
swag_eval(swag, add_swag_string)
|
||||||
|
|
||||||
|
swag_file = os.path.join(locales_dir, ".swag.py")
|
||||||
|
with open(swag_file, 'w') as f:
|
||||||
|
for ss in swag_strings:
|
||||||
|
f.write("_(%s)\n" % json.dumps(ss))
|
||||||
|
print("Wrote %s" % swag_file)
|
||||||
|
|
||||||
|
messagespot = os.path.join(locales_dir, "messages.pot")
|
||||||
|
print("Updating %s" % messagespot)
|
||||||
|
sys.argv = ["", "extract", "-F", "babel.cfg", "-k", "_e _h",
|
||||||
|
"--copyright-holder", "LibreTranslate Authors",
|
||||||
|
"--project", "LibreTranslate",
|
||||||
|
"--version", get_version(),
|
||||||
|
"-o", messagespot, "libretranslate"]
|
||||||
|
pybabel()
|
||||||
|
|
||||||
|
lang_codes = [l.code for l in languages if l != "en"]
|
||||||
|
|
||||||
|
# Init/update
|
||||||
|
for l in lang_codes:
|
||||||
|
cmd = "init"
|
||||||
|
if os.path.isdir(os.path.join(locales_dir, l, "LC_MESSAGES")):
|
||||||
|
cmd = "update"
|
||||||
|
|
||||||
|
sys.argv = ["", cmd, "-i", messagespot, "-d", locales_dir, "-l", l]
|
||||||
|
pybabel()
|
||||||
|
|
||||||
|
meta_file = os.path.join(locales_dir, l, "meta.json")
|
||||||
|
if not os.path.isfile(meta_file):
|
||||||
|
with open(meta_file, 'w') as f:
|
||||||
|
f.write(json.dumps({
|
||||||
|
'name': next((lang.name for lang in languages if lang.code == l)),
|
||||||
|
'reviewed': False
|
||||||
|
}, indent=4))
|
||||||
|
print("Wrote %s" % meta_file)
|
||||||
|
|
||||||
|
# Automatically translate strings with libretranslate
|
||||||
|
# when a language model is available and a string is empty
|
||||||
|
|
||||||
|
locales = get_available_locale_codes(only_reviewed=False)
|
||||||
|
print(locales)
|
||||||
|
for locale in locales:
|
||||||
|
if locale == 'en':
|
||||||
|
continue
|
||||||
|
|
||||||
|
tgt_lang = next((l for l in languages if l.code == locale), None)
|
||||||
|
|
||||||
|
if tgt_lang is None:
|
||||||
|
# We cannot translate
|
||||||
|
continue
|
||||||
|
|
||||||
|
translator = en_lang.get_translation(tgt_lang)
|
||||||
|
|
||||||
|
messages_file = os.path.join(locales_dir, locale, "LC_MESSAGES", 'messages.po')
|
||||||
|
if os.path.isfile(messages_file):
|
||||||
|
print("Translating '%s'" % locale)
|
||||||
|
pofile = polib.pofile(messages_file)
|
||||||
|
c = 0
|
||||||
|
|
||||||
|
for entry in pofile.untranslated_entries():
|
||||||
|
text = entry.msgid
|
||||||
|
|
||||||
|
# Extract placeholders
|
||||||
|
placeholders = re.findall(r'%\(?[^\)]*\)?s', text)
|
||||||
|
|
||||||
|
for p in range(0, len(placeholders)):
|
||||||
|
text = text.replace(placeholders[p], "<x>%s</x>" % p)
|
||||||
|
|
||||||
|
if len(placeholders) > 0:
|
||||||
|
translated = str(translate_html(translator, text))
|
||||||
|
else:
|
||||||
|
translated = improve_translation_formatting(text, translator.translate(text))
|
||||||
|
|
||||||
|
# Restore placeholders
|
||||||
|
for p in range(0, len(placeholders)):
|
||||||
|
tag = "<x>%s</x>" % p
|
||||||
|
if tag in translated:
|
||||||
|
translated = translated.replace(tag, placeholders[p])
|
||||||
|
else:
|
||||||
|
# Meh, append
|
||||||
|
translated += " " + placeholders[p]
|
||||||
|
|
||||||
|
print(entry.msgid, " --> ", translated)
|
||||||
|
entry.msgstr = translated
|
||||||
|
c += 1
|
||||||
|
|
||||||
|
if c > 0:
|
||||||
|
pofile.save(messages_file)
|
||||||
|
print("Saved %s" % messages_file)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue