mirror of
https://github.com/LibreTranslate/LibreTranslate.git
synced 2024-05-23 19:48:13 +00:00
Merge 1de8d84817
into a403ba7e3a
This commit is contained in:
commit
1b53abbce6
|
@ -47,7 +47,8 @@ def get_version():
|
||||||
|
|
||||||
|
|
||||||
def get_upload_dir():
|
def get_upload_dir():
|
||||||
upload_dir = os.path.join(tempfile.gettempdir(), "libretranslate-files-translate")
|
upload_dir = os.path.join(tempfile.gettempdir(),
|
||||||
|
"libretranslate-files-translate")
|
||||||
|
|
||||||
if not os.path.isdir(upload_dir):
|
if not os.path.isdir(upload_dir):
|
||||||
os.mkdir(upload_dir)
|
os.mkdir(upload_dir)
|
||||||
|
@ -64,6 +65,7 @@ def get_req_api_key():
|
||||||
|
|
||||||
return ak
|
return ak
|
||||||
|
|
||||||
|
|
||||||
def get_req_secret():
|
def get_req_secret():
|
||||||
if request.is_json:
|
if request.is_json:
|
||||||
json = get_json_dict(request)
|
json = get_json_dict(request)
|
||||||
|
@ -130,8 +132,8 @@ def get_routes_limits(args, api_keys_db):
|
||||||
|
|
||||||
def hourly_limits(n):
|
def hourly_limits(n):
|
||||||
def func():
|
def func():
|
||||||
decay = (0.75 ** (n - 1))
|
decay = (0.75 ** (n - 1))
|
||||||
return "{} per {} hour".format(get_req_limits(args.hourly_req_limit * n, api_keys_db, int(os.environ.get("LT_HOURLY_REQ_LIMIT_MULTIPLIER", 60) * n), decay), n)
|
return "{} per {} hour".format(get_req_limits(args.hourly_req_limit * n, api_keys_db, int(os.environ.get("LT_HOURLY_REQ_LIMIT_MULTIPLIER", 60) * n), decay), n)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def daily_limits():
|
def daily_limits():
|
||||||
|
@ -140,8 +142,8 @@ def get_routes_limits(args, api_keys_db):
|
||||||
res = [minute_limits]
|
res = [minute_limits]
|
||||||
|
|
||||||
if args.hourly_req_limit > 0:
|
if args.hourly_req_limit > 0:
|
||||||
for n in range(1, args.hourly_req_limit_decay + 2):
|
for n in range(1, args.hourly_req_limit_decay + 2):
|
||||||
res.append(hourly_limits(n))
|
res.append(hourly_limits(n))
|
||||||
|
|
||||||
if args.daily_req_limit > 0:
|
if args.daily_req_limit > 0:
|
||||||
res.append(daily_limits)
|
res.append(daily_limits)
|
||||||
|
@ -149,6 +151,12 @@ def get_routes_limits(args, api_keys_db):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def clean_text(text):
|
||||||
|
pattern = re.compile(r'(?<=[^.!?])\n')
|
||||||
|
cleaned_text = re.sub(pattern, ' ', text)
|
||||||
|
return cleaned_text
|
||||||
|
|
||||||
|
|
||||||
def create_app(args):
|
def create_app(args):
|
||||||
from libretranslate.init import boot
|
from libretranslate.init import boot
|
||||||
|
|
||||||
|
@ -168,7 +176,8 @@ def create_app(args):
|
||||||
languages = load_languages()
|
languages = load_languages()
|
||||||
language_pairs = {}
|
language_pairs = {}
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
language_pairs[lang.code] = sorted([l.to_lang.code for l in lang.translations_from])
|
language_pairs[lang.code] = sorted(
|
||||||
|
[l.to_lang.code for l in lang.translations_from])
|
||||||
|
|
||||||
# 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":
|
||||||
|
@ -177,33 +186,36 @@ def create_app(args):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
frontend_argos_language_source = next(
|
frontend_argos_language_source = next(
|
||||||
iter([l for l in languages if l.code == args.frontend_language_source]),
|
iter([l for l in languages if l.code ==
|
||||||
|
args.frontend_language_source]),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if frontend_argos_language_source is None:
|
if frontend_argos_language_source is None:
|
||||||
frontend_argos_language_source = languages[0]
|
frontend_argos_language_source = languages[0]
|
||||||
|
|
||||||
|
language_target_fallback = languages[1] if len(
|
||||||
language_target_fallback = languages[1] if len(languages) >= 2 else languages[0]
|
languages) >= 2 else languages[0]
|
||||||
|
|
||||||
if args.frontend_language_target == "locale":
|
if args.frontend_language_target == "locale":
|
||||||
def resolve_language_locale():
|
def resolve_language_locale():
|
||||||
loc = get_locale()
|
loc = get_locale()
|
||||||
language_target = next(
|
language_target = next(
|
||||||
iter([l for l in languages if l.code == loc]), None
|
iter([l for l in languages if l.code == loc]), None
|
||||||
)
|
)
|
||||||
if language_target is None:
|
if language_target is None:
|
||||||
language_target = language_target_fallback
|
language_target = language_target_fallback
|
||||||
return language_target
|
return language_target
|
||||||
|
|
||||||
frontend_argos_language_target = resolve_language_locale
|
frontend_argos_language_target = resolve_language_locale
|
||||||
else:
|
else:
|
||||||
language_target = next(
|
language_target = next(
|
||||||
iter([l for l in languages if l.code == args.frontend_language_target]), None
|
iter([l for l in languages if l.code ==
|
||||||
)
|
args.frontend_language_target]), None
|
||||||
if language_target is None:
|
)
|
||||||
language_target = language_target_fallback
|
if language_target is None:
|
||||||
frontend_argos_language_target = lambda: language_target
|
language_target = language_target_fallback
|
||||||
|
|
||||||
|
def frontend_argos_language_target(): return language_target
|
||||||
|
|
||||||
frontend_argos_supported_files_format = []
|
frontend_argos_supported_files_format = []
|
||||||
|
|
||||||
|
@ -216,16 +228,17 @@ def create_app(args):
|
||||||
if args.req_limit > 0 or args.api_keys or args.daily_req_limit > 0 or args.hourly_req_limit > 0:
|
if args.req_limit > 0 or args.api_keys or args.daily_req_limit > 0 or args.hourly_req_limit > 0:
|
||||||
api_keys_db = None
|
api_keys_db = None
|
||||||
if args.api_keys:
|
if args.api_keys:
|
||||||
api_keys_db = RemoteDatabase(args.api_keys_remote) if args.api_keys_remote else Database(args.api_keys_db_path)
|
api_keys_db = RemoteDatabase(
|
||||||
|
args.api_keys_remote) if args.api_keys_remote else Database(args.api_keys_db_path)
|
||||||
|
|
||||||
from flask_limiter import Limiter
|
from flask_limiter import Limiter
|
||||||
|
|
||||||
def limits_cost():
|
def limits_cost():
|
||||||
req_cost = getattr(request, 'req_cost', 1)
|
req_cost = getattr(request, 'req_cost', 1)
|
||||||
if args.req_time_cost > 0:
|
if args.req_time_cost > 0:
|
||||||
return max(req_cost, int(math.ceil(getattr(request, 'duration', 0) / args.req_time_cost)))
|
return max(req_cost, int(math.ceil(getattr(request, 'duration', 0) / args.req_time_cost)))
|
||||||
else:
|
else:
|
||||||
return req_cost
|
return req_cost
|
||||||
|
|
||||||
limiter = Limiter(
|
limiter = Limiter(
|
||||||
key_func=get_remote_address,
|
key_func=get_remote_address,
|
||||||
|
@ -233,7 +246,8 @@ def create_app(args):
|
||||||
args, api_keys_db
|
args, api_keys_db
|
||||||
),
|
),
|
||||||
storage_uri=args.req_limit_storage,
|
storage_uri=args.req_limit_storage,
|
||||||
default_limits_deduct_when=lambda req: True, # Force cost to be called after the request
|
# Force cost to be called after the request
|
||||||
|
default_limits_deduct_when=lambda req: True,
|
||||||
default_limits_cost=limits_cost
|
default_limits_cost=limits_cost
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -242,8 +256,8 @@ def create_app(args):
|
||||||
limiter = Limiter()
|
limiter = Limiter()
|
||||||
|
|
||||||
if not "gunicorn" in os.environ.get("SERVER_SOFTWARE", ""):
|
if not "gunicorn" in os.environ.get("SERVER_SOFTWARE", ""):
|
||||||
# Gunicorn starts the scheduler in the master process
|
# Gunicorn starts the scheduler in the master process
|
||||||
scheduler.setup(args)
|
scheduler.setup(args)
|
||||||
|
|
||||||
flood.setup(args)
|
flood.setup(args)
|
||||||
secret.setup(args)
|
secret.setup(args)
|
||||||
|
@ -251,31 +265,33 @@ def create_app(args):
|
||||||
measure_request = None
|
measure_request = None
|
||||||
gauge_request = None
|
gauge_request = None
|
||||||
if args.metrics:
|
if args.metrics:
|
||||||
if os.environ.get("PROMETHEUS_MULTIPROC_DIR") is None:
|
if os.environ.get("PROMETHEUS_MULTIPROC_DIR") is None:
|
||||||
default_mp_dir = os.path.abspath(os.path.join("db", "prometheus"))
|
default_mp_dir = os.path.abspath(os.path.join("db", "prometheus"))
|
||||||
if not os.path.isdir(default_mp_dir):
|
if not os.path.isdir(default_mp_dir):
|
||||||
os.mkdir(default_mp_dir)
|
os.mkdir(default_mp_dir)
|
||||||
os.environ["PROMETHEUS_MULTIPROC_DIR"] = default_mp_dir
|
os.environ["PROMETHEUS_MULTIPROC_DIR"] = default_mp_dir
|
||||||
|
|
||||||
from prometheus_client import CONTENT_TYPE_LATEST, CollectorRegistry, Gauge, Summary, generate_latest, multiprocess
|
from prometheus_client import CONTENT_TYPE_LATEST, CollectorRegistry, Gauge, Summary, generate_latest, multiprocess
|
||||||
|
|
||||||
@bp.route("/metrics")
|
@bp.route("/metrics")
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
def prometheus_metrics():
|
def prometheus_metrics():
|
||||||
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)
|
||||||
return Response(generate_latest(registry), mimetype=CONTENT_TYPE_LATEST)
|
return Response(generate_latest(registry), mimetype=CONTENT_TYPE_LATEST)
|
||||||
|
|
||||||
measure_request = Summary('libretranslate_http_request_duration_seconds', 'Time spent on request', ['endpoint', 'status', 'request_ip', 'api_key'])
|
measure_request = Summary('libretranslate_http_request_duration_seconds', 'Time spent on request', [
|
||||||
measure_request.labels('/translate', 200, '127.0.0.1', '')
|
'endpoint', 'status', 'request_ip', 'api_key'])
|
||||||
|
measure_request.labels('/translate', 200, '127.0.0.1', '')
|
||||||
|
|
||||||
gauge_request = Gauge('libretranslate_http_requests_in_flight', 'Active requests', ['endpoint', 'request_ip', 'api_key'], multiprocess_mode='livesum')
|
gauge_request = Gauge('libretranslate_http_requests_in_flight', 'Active requests', [
|
||||||
gauge_request.labels('/translate', '127.0.0.1', '')
|
'endpoint', 'request_ip', 'api_key'], multiprocess_mode='livesum')
|
||||||
|
gauge_request.labels('/translate', '127.0.0.1', '')
|
||||||
|
|
||||||
def access_check(f):
|
def access_check(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -293,59 +309,62 @@ def create_app(args):
|
||||||
description=_("Invalid API key"),
|
description=_("Invalid API key"),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
need_key = False
|
need_key = False
|
||||||
key_missing = api_keys_db.lookup(ak) is None
|
key_missing = api_keys_db.lookup(ak) is None
|
||||||
|
|
||||||
if (args.require_api_key_origin
|
if (args.require_api_key_origin
|
||||||
and key_missing
|
and key_missing
|
||||||
and not re.match(args.require_api_key_origin, request.headers.get("Origin", ""))
|
and not re.match(args.require_api_key_origin, request.headers.get("Origin", ""))
|
||||||
):
|
):
|
||||||
need_key = True
|
need_key = True
|
||||||
|
|
||||||
if (args.require_api_key_secret
|
if (args.require_api_key_secret
|
||||||
and key_missing
|
and key_missing
|
||||||
and not secret.secret_match(get_req_secret())
|
and not secret.secret_match(get_req_secret())
|
||||||
):
|
):
|
||||||
need_key = True
|
need_key = True
|
||||||
|
|
||||||
if need_key:
|
if need_key:
|
||||||
description = _("Please contact the server operator to get an API key")
|
description = _(
|
||||||
if args.get_api_key_link:
|
"Please contact the server operator to get an API key")
|
||||||
description = _("Visit %(url)s to get an API key", url=args.get_api_key_link)
|
if args.get_api_key_link:
|
||||||
abort(
|
description = _(
|
||||||
400,
|
"Visit %(url)s to get an API key", url=args.get_api_key_link)
|
||||||
description=description,
|
abort(
|
||||||
)
|
400,
|
||||||
|
description=description,
|
||||||
|
)
|
||||||
return f(*a, **kw)
|
return f(*a, **kw)
|
||||||
|
|
||||||
if args.metrics:
|
if args.metrics:
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def measure_func(*a, **kw):
|
def measure_func(*a, **kw):
|
||||||
start_t = default_timer()
|
start_t = default_timer()
|
||||||
status = 200
|
status = 200
|
||||||
ip = get_remote_address()
|
ip = get_remote_address()
|
||||||
ak = get_req_api_key() or ''
|
ak = get_req_api_key() or ''
|
||||||
g = gauge_request.labels(request.path, ip, ak)
|
g = gauge_request.labels(request.path, ip, ak)
|
||||||
try:
|
try:
|
||||||
g.inc()
|
g.inc()
|
||||||
return func(*a, **kw)
|
return func(*a, **kw)
|
||||||
except HTTPException as e:
|
except HTTPException as e:
|
||||||
status = e.code
|
status = e.code
|
||||||
raise e
|
raise e
|
||||||
finally:
|
finally:
|
||||||
request.duration = max(default_timer() - start_t, 0)
|
request.duration = max(default_timer() - start_t, 0)
|
||||||
measure_request.labels(request.path, status, ip, ak).observe(request.duration)
|
measure_request.labels(
|
||||||
g.dec()
|
request.path, status, ip, ak).observe(request.duration)
|
||||||
return measure_func
|
g.dec()
|
||||||
|
return measure_func
|
||||||
else:
|
else:
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def time_func(*a, **kw):
|
def time_func(*a, **kw):
|
||||||
start_t = default_timer()
|
start_t = default_timer()
|
||||||
try:
|
try:
|
||||||
return func(*a, **kw)
|
return func(*a, **kw)
|
||||||
finally:
|
finally:
|
||||||
request.duration = max(default_timer() - start_t, 0)
|
request.duration = max(default_timer() - start_t, 0)
|
||||||
return time_func
|
return time_func
|
||||||
|
|
||||||
@bp.errorhandler(400)
|
@bp.errorhandler(400)
|
||||||
def invalid_api(e):
|
def invalid_api(e):
|
||||||
|
@ -383,7 +402,8 @@ 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,
|
||||||
available_locales=[{'code': l['code'], 'name': _lazy(l['name'])} for l in get_available_locales(not args.debug)],
|
available_locales=[{'code': l['code'], 'name': _lazy(
|
||||||
|
l['name'])} for l in get_available_locales(not args.debug)],
|
||||||
current_locale=get_locale(),
|
current_locale=get_locale(),
|
||||||
alternate_locales=get_alternate_locale_links()
|
alternate_locales=get_alternate_locale_links()
|
||||||
)
|
)
|
||||||
|
@ -391,21 +411,21 @@ def create_app(args):
|
||||||
@bp.route("/js/app.js")
|
@bp.route("/js/app.js")
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
def appjs():
|
def appjs():
|
||||||
if args.disable_web_ui:
|
if args.disable_web_ui:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
response = Response(render_template("app.js.template",
|
response = Response(render_template("app.js.template",
|
||||||
url_prefix=args.url_prefix,
|
url_prefix=args.url_prefix,
|
||||||
get_api_key_link=args.get_api_key_link,
|
get_api_key_link=args.get_api_key_link,
|
||||||
api_secret=secret.get_current_secret() if args.require_api_key_secret else ""), content_type='application/javascript; charset=utf-8')
|
api_secret=secret.get_current_secret() if args.require_api_key_secret else ""), content_type='application/javascript; charset=utf-8')
|
||||||
|
|
||||||
if args.require_api_key_secret:
|
if args.require_api_key_secret:
|
||||||
response.headers['Last-Modified'] = http_date(datetime.now())
|
response.headers['Last-Modified'] = http_date(datetime.now())
|
||||||
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
|
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0'
|
||||||
response.headers['Pragma'] = 'no-cache'
|
response.headers['Pragma'] = 'no-cache'
|
||||||
response.headers['Expires'] = '-1'
|
response.headers['Expires'] = '-1'
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@bp.get("/languages")
|
@bp.get("/languages")
|
||||||
@limiter.exempt
|
@limiter.exempt
|
||||||
|
@ -565,17 +585,17 @@ 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 %(name)s parameter", name='q'))
|
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 %(name)s parameter", name='source'))
|
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 %(name)s parameter", name='target'))
|
abort(400, description=_(
|
||||||
|
"Invalid request: missing %(name)s parameter", name='target'))
|
||||||
|
|
||||||
if not request.is_json:
|
if not request.is_json:
|
||||||
# Normalize line endings to UNIX style (LF) only so we can consistently
|
q = clean_text(q)
|
||||||
# enforce character limits.
|
|
||||||
# https://www.rfc-editor.org/rfc/rfc2046#section-4.1.1
|
|
||||||
q = "\n".join(q.splitlines())
|
|
||||||
|
|
||||||
char_limit = get_char_limit(args.char_limit, api_keys_db)
|
char_limit = get_char_limit(args.char_limit, api_keys_db)
|
||||||
|
|
||||||
|
@ -586,7 +606,8 @@ def create_app(args):
|
||||||
if args.batch_limit < batch_size:
|
if args.batch_limit < batch_size:
|
||||||
abort(
|
abort(
|
||||||
400,
|
400,
|
||||||
description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=batch_size, limit=args.batch_limit),
|
description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)",
|
||||||
|
size=batch_size, limit=args.batch_limit),
|
||||||
)
|
)
|
||||||
|
|
||||||
src_texts = q if batch else [q]
|
src_texts = q if batch else [q]
|
||||||
|
@ -596,7 +617,8 @@ def create_app(args):
|
||||||
if len(text) > char_limit:
|
if len(text) > char_limit:
|
||||||
abort(
|
abort(
|
||||||
400,
|
400,
|
||||||
description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=len(text), limit=char_limit),
|
description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=len(
|
||||||
|
text), limit=char_limit),
|
||||||
)
|
)
|
||||||
|
|
||||||
if batch:
|
if batch:
|
||||||
|
@ -608,21 +630,24 @@ def create_app(args):
|
||||||
else:
|
else:
|
||||||
detected_src_lang = {"confidence": 100.0, "language": source_lang}
|
detected_src_lang = {"confidence": 100.0, "language": source_lang}
|
||||||
|
|
||||||
src_lang = next(iter([l for l in languages if l.code == detected_src_lang["language"]]), None)
|
src_lang = next(
|
||||||
|
iter([l for l in languages if l.code == detected_src_lang["language"]]), None)
|
||||||
|
|
||||||
if src_lang is None:
|
if src_lang is None:
|
||||||
abort(400, description=_("%(lang)s is not supported", lang=source_lang))
|
abort(400, description=_("%(lang)s is not supported", lang=source_lang))
|
||||||
|
|
||||||
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=_("%(lang)s is not supported",lang=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=_("%(format)s format is not supported", format=text_format))
|
abort(400, description=_(
|
||||||
|
"%(format)s format is not supported", format=text_format))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if batch:
|
if batch:
|
||||||
|
@ -630,12 +655,14 @@ def create_app(args):
|
||||||
for text in q:
|
for text in q:
|
||||||
translator = src_lang.get_translation(tgt_lang)
|
translator = src_lang.get_translation(tgt_lang)
|
||||||
if translator is None:
|
if translator is None:
|
||||||
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_lang.name), scode=src_lang.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_lang.name), scode=src_lang.code))
|
||||||
|
|
||||||
if text_format == "html":
|
if text_format == "html":
|
||||||
translated_text = str(translate_html(translator, text))
|
translated_text = str(translate_html(translator, text))
|
||||||
else:
|
else:
|
||||||
translated_text = improve_translation_formatting(text, translator.translate(text))
|
translated_text = improve_translation_formatting(
|
||||||
|
text, translator.translate(text))
|
||||||
|
|
||||||
results.append(unescape(translated_text))
|
results.append(unescape(translated_text))
|
||||||
if source_lang == "auto":
|
if source_lang == "auto":
|
||||||
|
@ -647,19 +674,21 @@ def create_app(args):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{
|
{
|
||||||
"translatedText": results
|
"translatedText": results
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
translator = src_lang.get_translation(tgt_lang)
|
translator = src_lang.get_translation(tgt_lang)
|
||||||
if translator is None:
|
if translator is None:
|
||||||
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_lang.name), scode=src_lang.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_lang.name), scode=src_lang.code))
|
||||||
|
|
||||||
if text_format == "html":
|
if text_format == "html":
|
||||||
translated_text = str(translate_html(translator, q))
|
translated_text = str(translate_html(translator, q))
|
||||||
else:
|
else:
|
||||||
translated_text = improve_translation_formatting(q, translator.translate(q))
|
translated_text = improve_translation_formatting(
|
||||||
|
q, translator.translate(q))
|
||||||
|
|
||||||
if source_lang == "auto":
|
if source_lang == "auto":
|
||||||
return jsonify(
|
return jsonify(
|
||||||
|
@ -676,7 +705,8 @@ def create_app(args):
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
abort(500, description=_("Cannot translate text: %(text)s", text=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
|
||||||
|
@ -763,7 +793,8 @@ 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")
|
||||||
|
@ -771,11 +802,14 @@ def create_app(args):
|
||||||
char_limit = get_char_limit(args.char_limit, api_keys_db)
|
char_limit = get_char_limit(args.char_limit, api_keys_db)
|
||||||
|
|
||||||
if not file:
|
if not file:
|
||||||
abort(400, description=_("Invalid request: missing %(name)s parameter", name='file'))
|
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 %(name)s parameter", name='source'))
|
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 %(name)s parameter", name='target'))
|
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"))
|
||||||
|
@ -783,12 +817,14 @@ def create_app(args):
|
||||||
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"))
|
||||||
|
|
||||||
src_lang = next(iter([l for l in languages if l.code == source_lang]), None)
|
src_lang = next(
|
||||||
|
iter([l for l in languages if l.code == source_lang]), None)
|
||||||
|
|
||||||
if src_lang is None:
|
if src_lang is None:
|
||||||
abort(400, description=_("%(lang)s is not supported", lang=source_lang))
|
abort(400, description=_("%(lang)s is not supported", lang=source_lang))
|
||||||
|
|
||||||
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=_("%(lang)s is not supported", lang=target_lang))
|
abort(400, description=_("%(lang)s is not supported", lang=target_lang))
|
||||||
|
@ -805,9 +841,11 @@ def create_app(args):
|
||||||
# roughly equivalent to a batch process of N batches assuming
|
# roughly equivalent to a batch process of N batches assuming
|
||||||
# each batch uses all available limits
|
# each batch uses all available limits
|
||||||
if char_limit > 0:
|
if char_limit > 0:
|
||||||
request.req_cost = max(1, int(os.path.getsize(filepath) / char_limit))
|
request.req_cost = max(
|
||||||
|
1, int(os.path.getsize(filepath) / char_limit))
|
||||||
|
|
||||||
translated_file_path = argostranslatefiles.translate_file(src_lang.get_translation(tgt_lang), filepath)
|
translated_file_path = argostranslatefiles.translate_file(
|
||||||
|
src_lang.get_translation(tgt_lang), filepath)
|
||||||
translated_filename = os.path.basename(translated_file_path)
|
translated_filename = os.path.basename(translated_file_path)
|
||||||
|
|
||||||
return jsonify(
|
return jsonify(
|
||||||
|
@ -824,11 +862,13 @@ 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:
|
||||||
checked_filepath = security.path_traversal_check(filepath, get_upload_dir())
|
checked_filepath = security.path_traversal_check(
|
||||||
|
filepath, get_upload_dir())
|
||||||
if os.path.isfile(checked_filepath):
|
if os.path.isfile(checked_filepath):
|
||||||
filepath = checked_filepath
|
filepath = checked_filepath
|
||||||
except security.SuspiciousFileOperationError:
|
except security.SuspiciousFileOperationError:
|
||||||
|
@ -932,7 +972,8 @@ 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 %(name)s parameter", name='q'))
|
abort(400, description=_(
|
||||||
|
"Invalid request: missing %(name)s parameter", name='q'))
|
||||||
|
|
||||||
return jsonify(detect_languages(q))
|
return jsonify(detect_languages(q))
|
||||||
|
|
||||||
|
@ -1089,13 +1130,17 @@ 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 %(name)s parameter", name='q'))
|
abort(400, description=_(
|
||||||
|
"Invalid request: missing %(name)s parameter", name='q'))
|
||||||
if not s:
|
if not s:
|
||||||
abort(400, description=_("Invalid request: missing %(name)s parameter", name='s'))
|
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 %(name)s parameter", name='source'))
|
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 %(name)s parameter", name='target'))
|
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})
|
||||||
|
|
Loading…
Reference in a new issue