mirror of
https://github.com/LibreTranslate/LibreTranslate.git
synced 2024-11-22 07:51:00 +00:00
Add file translation
This commit is contained in:
parent
82c2f4396d
commit
0b14600199
2 changed files with 195 additions and 36 deletions
209
app/app.py
209
app/app.py
|
@ -1,19 +1,22 @@
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
|
import uuid
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
import argostranslatefiles
|
||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
from argostranslatefiles import get_supported_formats
|
||||||
from flask import Flask, abort, jsonify, render_template, request
|
from flask import Flask, abort, jsonify, render_template, request
|
||||||
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 translatehtml import translate_html
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
|
||||||
from app import flood
|
from app import flood
|
||||||
from app.language import detect_languages, transliterate
|
from app.language import detect_languages, transliterate
|
||||||
|
|
||||||
from .api_keys import Database
|
from .api_keys import Database
|
||||||
from .suggestions import Database as SuggestionsDatabase
|
from .suggestions import Database as SuggestionsDatabase
|
||||||
|
|
||||||
from argostranslatefiles import get_supported_formats
|
|
||||||
from translatehtml import translate_html
|
|
||||||
|
|
||||||
def get_json_dict(request):
|
def get_json_dict(request):
|
||||||
d = request.get_json()
|
d = request.get_json()
|
||||||
|
@ -31,7 +34,7 @@ def get_remote_address():
|
||||||
return ip
|
return ip
|
||||||
|
|
||||||
|
|
||||||
def get_req_limits(default_limit, api_keys_db, multiplier = 1):
|
def get_req_limits(default_limit, api_keys_db, multiplier=1):
|
||||||
req_limit = default_limit
|
req_limit = default_limit
|
||||||
|
|
||||||
if api_keys_db:
|
if api_keys_db:
|
||||||
|
@ -45,7 +48,7 @@ def get_req_limits(default_limit, api_keys_db, multiplier = 1):
|
||||||
db_req_limit = api_keys_db.lookup(api_key)
|
db_req_limit = api_keys_db.lookup(api_key)
|
||||||
if db_req_limit is not None:
|
if db_req_limit is not None:
|
||||||
req_limit = db_req_limit * multiplier
|
req_limit = db_req_limit * multiplier
|
||||||
|
|
||||||
return req_limit
|
return req_limit
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,7 +150,7 @@ def create_app(args):
|
||||||
ak = request.values.get("api_key")
|
ak = request.values.get("api_key")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
api_keys_db.lookup(ak) is None and request.headers.get("Origin") != args.require_api_key_origin
|
api_keys_db.lookup(ak) is None and request.headers.get("Origin") != args.require_api_key_origin
|
||||||
):
|
):
|
||||||
abort(
|
abort(
|
||||||
403,
|
403,
|
||||||
|
@ -368,7 +371,7 @@ def create_app(args):
|
||||||
abort(
|
abort(
|
||||||
400,
|
400,
|
||||||
description="Invalid request: Request (%d) exceeds text limit (%d)"
|
description="Invalid request: Request (%d) exceeds text limit (%d)"
|
||||||
% (batch_size, args.batch_limit),
|
% (batch_size, args.batch_limit),
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.char_limit != -1:
|
if args.char_limit != -1:
|
||||||
|
@ -381,40 +384,40 @@ def create_app(args):
|
||||||
abort(
|
abort(
|
||||||
400,
|
400,
|
||||||
description="Invalid request: Request (%d) exceeds character limit (%d)"
|
description="Invalid request: Request (%d) exceeds character limit (%d)"
|
||||||
% (chars, args.char_limit),
|
% (chars, args.char_limit),
|
||||||
)
|
)
|
||||||
|
|
||||||
if source_lang == "auto":
|
if source_lang == "auto":
|
||||||
source_langs = []
|
source_langs = []
|
||||||
if batch:
|
if batch:
|
||||||
auto_detect_texts = q
|
auto_detect_texts = q
|
||||||
else:
|
else:
|
||||||
auto_detect_texts = [q]
|
auto_detect_texts = [q]
|
||||||
|
|
||||||
overall_candidates = detect_languages(q)
|
overall_candidates = detect_languages(q)
|
||||||
|
|
||||||
for text_to_check in auto_detect_texts:
|
|
||||||
if len(text_to_check) > 40:
|
|
||||||
candidate_langs = detect_languages(text_to_check)
|
|
||||||
else:
|
|
||||||
# Unable to accurately detect languages for short texts
|
|
||||||
candidate_langs = overall_candidates
|
|
||||||
source_langs.append(candidate_langs[0]["language"])
|
|
||||||
|
|
||||||
if args.debug:
|
for text_to_check in auto_detect_texts:
|
||||||
print(text_to_check, candidate_langs)
|
if len(text_to_check) > 40:
|
||||||
print("Auto detected: %s" % candidate_langs[0]["language"])
|
candidate_langs = detect_languages(text_to_check)
|
||||||
|
else:
|
||||||
|
# Unable to accurately detect languages for short texts
|
||||||
|
candidate_langs = overall_candidates
|
||||||
|
source_langs.append(candidate_langs[0]["language"])
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
print(text_to_check, candidate_langs)
|
||||||
|
print("Auto detected: %s" % candidate_langs[0]["language"])
|
||||||
else:
|
else:
|
||||||
if batch:
|
if batch:
|
||||||
source_langs = [source_lang for text in q]
|
source_langs = [source_lang for text in q]
|
||||||
else:
|
else:
|
||||||
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="%s is not supported" % 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)
|
||||||
|
|
||||||
|
@ -427,19 +430,18 @@ def create_app(args):
|
||||||
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="%s format is not supported" % text_format)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if batch:
|
if batch:
|
||||||
results = []
|
results = []
|
||||||
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 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 = translator.translate(transliterate(text, target_lang=source_langs[idx]))
|
translated_text = translator.translate(transliterate(text, target_lang=source_langs[idx]))
|
||||||
|
|
||||||
results.append(translated_text)
|
results.append(translated_text)
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{
|
{
|
||||||
"translatedText": results
|
"translatedText": results
|
||||||
|
@ -460,6 +462,143 @@ 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: %s" % str(e))
|
||||||
|
|
||||||
|
@app.route("/translate_file", methods=["POST"])
|
||||||
|
@access_check
|
||||||
|
def translate_file():
|
||||||
|
"""
|
||||||
|
Translate file from a language to another
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- translate
|
||||||
|
consumes:
|
||||||
|
- multipart/form-data
|
||||||
|
parameters:
|
||||||
|
- in: formData
|
||||||
|
name: file
|
||||||
|
type: file
|
||||||
|
required: true
|
||||||
|
description: file to translate
|
||||||
|
- in: formData
|
||||||
|
name: source
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: en
|
||||||
|
required: true
|
||||||
|
description: Source language code
|
||||||
|
- in: formData
|
||||||
|
name: target
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: es
|
||||||
|
required: true
|
||||||
|
description: Target language code
|
||||||
|
- in: formData
|
||||||
|
name: api_key
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||||
|
required: false
|
||||||
|
description: API key
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Translated file
|
||||||
|
schema:
|
||||||
|
id: translate
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
translatedText:
|
||||||
|
oneOf:
|
||||||
|
- type: string
|
||||||
|
- type: array
|
||||||
|
description: Translated text(s)
|
||||||
|
400:
|
||||||
|
description: Invalid request
|
||||||
|
schema:
|
||||||
|
id: error-response
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
description: Error message
|
||||||
|
500:
|
||||||
|
description: Translation error
|
||||||
|
schema:
|
||||||
|
id: error-response
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
description: Error message
|
||||||
|
429:
|
||||||
|
description: Slow down
|
||||||
|
schema:
|
||||||
|
id: error-slow-down
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
description: Reason for slow down
|
||||||
|
403:
|
||||||
|
description: Banned
|
||||||
|
schema:
|
||||||
|
id: error-response
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
description: Error message
|
||||||
|
"""
|
||||||
|
if request.is_json:
|
||||||
|
json = get_json_dict(request)
|
||||||
|
source_lang = json.get("source")
|
||||||
|
target_lang = json.get("target")
|
||||||
|
else:
|
||||||
|
source_lang = request.values.get("source")
|
||||||
|
target_lang = request.values.get("target")
|
||||||
|
|
||||||
|
file = request.files['file']
|
||||||
|
|
||||||
|
if not file:
|
||||||
|
abort(400, description="Invalid request: missing q parameter")
|
||||||
|
if not source_lang:
|
||||||
|
abort(400, description="Invalid request: missing source parameter")
|
||||||
|
if not target_lang:
|
||||||
|
abort(400, description="Invalid request: missing target parameter")
|
||||||
|
|
||||||
|
if file.filename == '':
|
||||||
|
abort(400, description="Invalid request: empty file")
|
||||||
|
|
||||||
|
if os.path.splitext(file.filename)[1] not in frontend_argos_supported_files_format:
|
||||||
|
abort(400, description="Invalid request: file format not supported")
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
for idx, lang in enumerate(src_langs):
|
||||||
|
if lang is None:
|
||||||
|
abort(400, description="%s is not supported" % source_langs[idx])
|
||||||
|
|
||||||
|
tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None)
|
||||||
|
|
||||||
|
if tgt_lang is None:
|
||||||
|
abort(400, description="%s is not supported" % target_lang)
|
||||||
|
|
||||||
|
try:
|
||||||
|
filename = str(uuid.uuid4()) + '.' + secure_filename(file.filename)
|
||||||
|
filepath = os.path.join(tempfile.gettempdir(), filename)
|
||||||
|
|
||||||
|
file.save(filepath)
|
||||||
|
|
||||||
|
translated_file_path = argostranslatefiles.translate_file(src_langs[0].get_translation(tgt_lang), filepath)
|
||||||
|
|
||||||
|
return jsonify(
|
||||||
|
{
|
||||||
|
"translatedFileUrl": translated_file_path
|
||||||
|
}
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
abort(500, description=e)
|
||||||
|
|
||||||
@app.route("/detect", methods=["POST"])
|
@app.route("/detect", methods=["POST"])
|
||||||
@access_check
|
@access_check
|
||||||
def detect():
|
def detect():
|
||||||
|
|
|
@ -33,7 +33,8 @@ document.addEventListener('DOMContentLoaded', function(){
|
||||||
|
|
||||||
supportedFilesFormat : [],
|
supportedFilesFormat : [],
|
||||||
translationType: "text",
|
translationType: "text",
|
||||||
inputFile: false
|
inputFile: false,
|
||||||
|
loadingFileTranslation: false,
|
||||||
},
|
},
|
||||||
mounted: function(){
|
mounted: function(){
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -319,6 +320,25 @@ document.addEventListener('DOMContentLoaded', function(){
|
||||||
},
|
},
|
||||||
translateFile: function(e) {
|
translateFile: function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
let translateFileRequest = new XMLHttpRequest();
|
||||||
|
|
||||||
|
translateFileRequest.open("POST", BaseUrl + "/translate_file", true);
|
||||||
|
translateFileRequest.setRequestHeader("Content-type", "multipart/form-data");
|
||||||
|
|
||||||
|
let formdata = new FormData();
|
||||||
|
formdata.append("file", this.inputFile);
|
||||||
|
|
||||||
|
translateFileRequest.send(formdata);
|
||||||
|
|
||||||
|
this.loadingFileTranslation = true
|
||||||
|
|
||||||
|
translateFileRequest.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState == 4 && xhr.status == 200) {
|
||||||
|
this.loadingFileTranslation = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue