diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml index fb9b8d7..f9eb6a1 100644 --- a/.github/workflows/publish-package.yml +++ b/.github/workflows/publish-package.yml @@ -26,6 +26,7 @@ jobs: python -m pip install --upgrade pip pip install pytest flake8 pip install . + python compile_locales.py - name: Check code style with flake8 (lint) run: | @@ -60,5 +61,6 @@ jobs: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | + python compile_locales.py python setup.py sdist bdist_wheel twine upload dist/* diff --git a/Dockerfile b/Dockerfile index 0c8034b..c68b1d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,11 +14,11 @@ RUN python -mvenv venv && ./venv/bin/pip install --upgrade pip COPY . . -# Install package from source code -RUN ./venv/bin/pip install . \ +# Install package from source code, compile translations +RUN ./venv/bin/pip install Babel==2.11.0 && ./venv/bin/python compile_locales.py \ + && ./venv/bin/pip install . \ && ./venv/bin/pip cache purge - FROM python:3.8.14-slim-bullseye ARG with_models=false diff --git a/VERSION b/VERSION index e05cb33..d4c4950 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.8 +1.3.9 diff --git a/babel.cfg b/babel.cfg new file mode 100644 index 0000000..b335d31 --- /dev/null +++ b/babel.cfg @@ -0,0 +1,2 @@ +[python: **.py] +[jinja2: **/templates/**] \ No newline at end of file diff --git a/compile_locales.py b/compile_locales.py new file mode 100755 index 0000000..edccc4c --- /dev/null +++ b/compile_locales.py @@ -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() + + + diff --git a/docker/Dockerfile.cuda b/docker/Dockerfile.cuda index 61f7f0f..bd94cf0 100644 --- a/docker/Dockerfile.cuda +++ b/docker/Dockerfile.cuda @@ -34,7 +34,8 @@ RUN if [ "$with_models" = "true" ]; then \ fi # Install package from source code -RUN pip3 install . \ +RUN pip3 install Babel==2.11.0 && python3 compile_locales.py \ + && pip3 install . \ && pip3 cache purge # Depending on your cuda install you may need to uncomment this line to allow the container to access the cuda libraries diff --git a/libretranslate/app.py b/libretranslate/app.py index 6375a05..e877579 100644 --- a/libretranslate/app.py +++ b/libretranslate/app.py @@ -9,15 +9,19 @@ from timeit import default_timer import argostranslatefiles from argostranslatefiles import get_supported_formats 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_ui import get_swaggerui_blueprint +from flask_session import Session from translatehtml import translate_html from werkzeug.utils import secure_filename from werkzeug.exceptions import HTTPException +from flask_babel import Babel from libretranslate import flood, remove_translated_files, security 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 .suggestions import Database as SuggestionsDatabase @@ -53,7 +57,7 @@ def get_req_api_key(): def get_json_dict(request): d = request.get_json() if not isinstance(d, dict): - abort(400, description="Invalid JSON format") + abort(400, description=_("Invalid JSON format")) return d @@ -121,7 +125,7 @@ def create_app(args): # Map userdefined frontend languages to argos language object. if args.frontend_language_source == "auto": frontend_argos_language_source = type( - "obj", (object,), {"code": "auto", "name": "Auto Detect"} + "obj", (object,), {"code": "auto", "name": _("Auto Detect")} ) else: frontend_argos_language_source = next( @@ -186,7 +190,7 @@ def create_app(args): if args.metrics_auth_token: authorization = request.headers.get('Authorization') if authorization != "Bearer " + args.metrics_auth_token: - abort(401, description="Unauthorized") + abort(401, description=_("Unauthorized")) registry = CollectorRegistry() multiprocess.MultiProcessCollector(registry) @@ -204,7 +208,7 @@ def create_app(args): ip = get_remote_address() 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: ak = get_req_api_key() @@ -213,16 +217,16 @@ def create_app(args): ): abort( 403, - description="Invalid API key", + description=_("Invalid API key"), ) elif ( args.require_api_key_origin and api_keys_db.lookup(ak) is None 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: - 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( 403, description=description, @@ -262,7 +266,7 @@ def create_app(args): @bp.errorhandler(429) def slow_down_error(e): flood.report(get_remote_address()) - return jsonify({"error": "Slowdown: " + str(e.description)}), 429 + return jsonify({"error": _("Slowdown:") + " " + str(e.description)}), 429 @bp.errorhandler(403) def denied(e): @@ -274,6 +278,10 @@ def create_app(args): if args.disable_web_ui: 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( "index.html", gaId=args.ga_id, @@ -283,16 +291,20 @@ def create_app(args): web_version=os.environ.get("LT_WEB") is not None, version=get_version(), 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 - def javascript_licenses(): - if args.disable_web_ui: + def appjs(): + if args.disable_web_ui: 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") @limiter.exempt @@ -323,7 +335,7 @@ def create_app(args): type: string 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 @bp.after_request @@ -452,11 +464,11 @@ def create_app(args): text_format = request.values.get("format") 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: - abort(400, description="Invalid request: missing source parameter") + abort(400, description=_("Invalid request: missing %(name)s parameter", name='source')) 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) @@ -465,8 +477,7 @@ def create_app(args): if args.batch_limit < batch_size: abort( 400, - description="Invalid request: Request (%d) exceeds text limit (%d)" - % (batch_size, args.batch_limit), + description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=batch_size, limit=args.batch_limit), ) if args.char_limit != -1: @@ -478,8 +489,7 @@ def create_app(args): if args.char_limit < chars: abort( 400, - description="Invalid request: Request (%d) exceeds character limit (%d)" - % (chars, args.char_limit), + description=_("Invalid request: request (%(size)s) exceeds text limit (%(limit)s)", size=chars, limit=args.char_limit), ) if source_lang == "auto": @@ -512,18 +522,18 @@ def create_app(args): for idx, lang in enumerate(src_langs): 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) 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: text_format = "text" 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: if batch: @@ -531,7 +541,7 @@ def create_app(args): for idx, text in enumerate(q): translator = src_langs[idx].get_translation(tgt_lang) 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": translated_text = str(translate_html(translator, text)) @@ -555,7 +565,7 @@ def create_app(args): else: translator = src_langs[0].get_translation(tgt_lang) 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": translated_text = str(translate_html(translator, q)) @@ -576,7 +586,7 @@ def create_app(args): } ) 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") @access_check @@ -663,36 +673,36 @@ def create_app(args): description: Error message """ 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") target_lang = request.form.get("target") file = request.files['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: - abort(400, description="Invalid request: missing source parameter") + abort(400, description=_("Invalid request: missing %(name)s parameter", name='source')) 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 == '': - 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: - abort(400, description="Invalid request: file format not supported") + 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]) + 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) 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: filename = str(uuid.uuid4()) + '.' + secure_filename(file.filename) @@ -717,7 +727,7 @@ def create_app(args): Download a translated file """ 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) try: @@ -725,7 +735,7 @@ def create_app(args): if os.path.isfile(checked_filepath): filepath = checked_filepath except security.SuspiciousFileOperation: - abort(400, description="Invalid filename") + abort(400, description=_("Invalid filename")) return_data = io.BytesIO() with open(filepath, 'rb') as fo: @@ -818,9 +828,6 @@ def create_app(args): type: string description: Error message """ - if flood.is_banned(get_remote_address()): - abort(403, description="Too many request limits violations") - if request.is_json: json = get_json_dict(request) q = json.get("q") @@ -828,7 +835,7 @@ def create_app(args): q = request.values.get("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)) @@ -901,11 +908,11 @@ def create_app(args): "language": { "source": { "code": frontend_argos_language_source.code, - "name": frontend_argos_language_source.name, + "name": _lazy(frontend_argos_language_source.name), }, "target": { "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 """ 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") s = request.values.get("s") @@ -977,18 +984,23 @@ def create_app(args): target_lang = request.values.get("target") 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: - abort(400, description="Invalid request: missing s parameter") + abort(400, description=_("Invalid request: missing %(name)s parameter", name='s')) 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: - 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) return jsonify({"success": True}) app = Flask(__name__) + + app.config["SESSION_TYPE"] = "filesystem" + app.config["SESSION_FILE_DIR"] = os.path.join("db", "sessions") + Session(app) + if args.debug: app.config["TEMPLATES_AUTO_RELOAD"] = True if args.url_prefix: @@ -1002,11 +1014,21 @@ def create_app(args): swag["info"]["version"] = get_version() swag["info"]["title"] = "LibreTranslate" - @app.route(API_URL) @limiter.exempt 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 swaggerui_blueprint = get_swaggerui_blueprint(SWAGGER_URL, API_URL) diff --git a/libretranslate/locales.py b/libretranslate/locales.py new file mode 100644 index 0000000..9885f7e --- /dev/null +++ b/libretranslate/locales.py @@ -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) \ No newline at end of file diff --git a/libretranslate/locales/.gitignore b/libretranslate/locales/.gitignore new file mode 100644 index 0000000..034e08a --- /dev/null +++ b/libretranslate/locales/.gitignore @@ -0,0 +1,3 @@ +**/*.mo +.langs.py +.swag.py diff --git a/libretranslate/locales/de/LC_MESSAGES/messages.po b/libretranslate/locales/de/LC_MESSAGES/messages.po new file mode 100644 index 0000000..0ad6cea --- /dev/null +++ b/libretranslate/locales/de/LC_MESSAGES/messages.po @@ -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 , 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 \n" +"Language: de\n" +"Language-Team: de \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 "" + diff --git a/libretranslate/locales/de/meta.json b/libretranslate/locales/de/meta.json new file mode 100644 index 0000000..e465af5 --- /dev/null +++ b/libretranslate/locales/de/meta.json @@ -0,0 +1,4 @@ +{ + "name": "German", + "reviewed": false +} \ No newline at end of file diff --git a/libretranslate/locales/fr/LC_MESSAGES/messages.po b/libretranslate/locales/fr/LC_MESSAGES/messages.po new file mode 100644 index 0000000..c84ccd4 --- /dev/null +++ b/libretranslate/locales/fr/LC_MESSAGES/messages.po @@ -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 , 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 \n" +"Language-Team: fr \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" diff --git a/libretranslate/locales/fr/meta.json b/libretranslate/locales/fr/meta.json new file mode 100644 index 0000000..060abee --- /dev/null +++ b/libretranslate/locales/fr/meta.json @@ -0,0 +1,4 @@ +{ + "name": "French", + "reviewed": false +} \ No newline at end of file diff --git a/libretranslate/locales/it/LC_MESSAGES/messages.po b/libretranslate/locales/it/LC_MESSAGES/messages.po new file mode 100644 index 0000000..2ee2ecb --- /dev/null +++ b/libretranslate/locales/it/LC_MESSAGES/messages.po @@ -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 , 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 \n" +"Language-Team: it \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" diff --git a/libretranslate/locales/it/meta.json b/libretranslate/locales/it/meta.json new file mode 100644 index 0000000..38ce3c5 --- /dev/null +++ b/libretranslate/locales/it/meta.json @@ -0,0 +1,4 @@ +{ + "name": "Italian", + "reviewed": false +} \ No newline at end of file diff --git a/libretranslate/static/css/dark-theme.css b/libretranslate/static/css/dark-theme.css index dcb1866..f409977 100644 --- a/libretranslate/static/css/dark-theme.css +++ b/libretranslate/static/css/dark-theme.css @@ -52,7 +52,7 @@ margin-top: 1rem; } - select { + select, select#locales{ color: #fff; background: #111; } diff --git a/libretranslate/static/css/main.css b/libretranslate/static/css/main.css index 7ee8554..7425883 100644 --- a/libretranslate/static/css/main.css +++ b/libretranslate/static/css/main.css @@ -11,6 +11,10 @@ a { text-decoration: underline; } +a.noline{ + text-decoration: none; +} + #app { min-height: 80vh; } @@ -35,6 +39,88 @@ h3.header { 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 { display: inline-flex; align-items: center; diff --git a/libretranslate/static/js/app.js b/libretranslate/templates/app.js.template similarity index 90% rename from libretranslate/static/js/app.js rename to libretranslate/templates/app.js.template index 6957b04..db436b4 100644 --- a/libretranslate/static/js/app.js +++ b/libretranslate/templates/app.js.template @@ -1,6 +1,6 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 // 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 = /<(.*)>.*?|<(.*)\/>/; document.addEventListener('DOMContentLoaded', function(){ var sidenavElems = document.querySelectorAll('.sidenav'); @@ -28,7 +28,7 @@ document.addEventListener('DOMContentLoaded', function(){ detectedLangText: "", - copyTextLabel: "Copy text", + copyTextLabel: {{ _e("Copy text") }}, suggestions: false, isSuggesting: false, @@ -69,18 +69,18 @@ document.addEventListener('DOMContentLoaded', function(){ } } } else { - self.error = "Cannot load /frontend/settings"; + self.error = {{ _e("Cannot load %(url)s", url="/frontend/settings") }}; self.loading = false; } }; settingsRequest.onerror = function() { - self.error = "Error while calling /frontend/settings"; + self.error = {{ _e("Cannot load %(url)s", url="/frontend/settings") }}; self.loading = false; }; langsRequest.onerror = function() { - self.error = "Error while calling /languages"; + self.error = {{ _e("Cannot load %(url)s", url="/languages") }}; self.loading = false; }; @@ -250,7 +250,7 @@ document.addEventListener('DOMContentLoaded', function(){ self.detectedLangText = ": " + (lang !== undefined ? lang.name : res.detectedLanguage.language) + " (" + res.detectedLanguage.confidence + "%)"; } } else{ - throw new Error(res.error || "Unknown error"); + throw new Error(res.error || {{ _e("Unknown error") }}); } } catch (e) { self.error = e.message; @@ -259,7 +259,7 @@ document.addEventListener('DOMContentLoaded', function(){ }; request.onerror = function() { - self.error = "Error while calling /translate"; + self.error = {{ _e("Cannot load %(url)s", url="/translate") }}; self.loadingTranslation = false; }; @@ -272,11 +272,11 @@ document.addEventListener('DOMContentLoaded', function(){ this.$refs.translatedTextarea.setSelectionRange(0, 9999999); /* For mobile devices */ document.execCommand("copy"); - if (this.copyTextLabel === "Copy text"){ - this.copyTextLabel = "Copied"; + if (this.copyTextLabel === {{ _e("Copy text") }}){ + this.copyTextLabel = {{ _e("Copied") }}; var self = this; setTimeout(function(){ - self.copyTextLabel = "Copy text"; + self.copyTextLabel = {{ _e("Copy text") }}; }, 1500); } }, @@ -317,10 +317,10 @@ document.addEventListener('DOMContentLoaded', function(){ try{ var res = JSON.parse(this.response); 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) }else{ - throw new Error(res.error || "Unknown error"); + throw new Error(res.error || {{ _e("Unknown error") }}); } }catch(e){ self.error = e.message; @@ -329,7 +329,7 @@ document.addEventListener('DOMContentLoaded', function(){ }; request.onerror = function() { - self.error = "Error while calling /suggest"; + self.error = {{ _e("Cannot load %(url)s", url="/suggest") }}; self.loadingTranslation = false; }; @@ -382,7 +382,7 @@ document.addEventListener('DOMContentLoaded', function(){ link.href = self.translatedFileUrl; link.click(); }else{ - throw new Error(res.error || "Unknown error"); + throw new Error(res.error || {{ _e("Unknown error") }}); } }catch(e){ @@ -392,14 +392,14 @@ document.addEventListener('DOMContentLoaded', function(){ } }else{ let res = JSON.parse(this.response); - self.error = res.error || "Unknown error"; + self.error = res.error || {{ _e("Unknown error") }}; self.loadingFileTranslation = false; self.inputFile = false; } } translateFileRequest.onerror = function() { - self.error = "Error while calling /translate_file"; + self.error = {{ _e("Cannot load %(url)s", url="/translate_file") }}; self.loadingFileTranslation = false; self.inputFile = false; }; @@ -420,11 +420,11 @@ function handleLangsResponse(self, response) { if (self.langs.length === 0){ 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; } - 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 targetLanguage = self.langs.find(l => l.code === self.getQueryParam("target")) @@ -444,7 +444,7 @@ function handleLangsResponse(self, response) { self.handleInput(new Event('none')) } } else { - self.error = "Cannot load /languages"; + self.error = {{ _e("Cannot load %(url)s", url="/languages") }}; } self.loading = false; @@ -476,9 +476,7 @@ function getTextWidth(text) { function setApiKey(){ var prevKey = localStorage.getItem("api_key") || ""; var newKey = ""; - var instructions = "contact the server operator."; - 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); + 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 (newKey === null) newKey = ""; localStorage.setItem("api_key", newKey); diff --git a/libretranslate/templates/index.html b/libretranslate/templates/index.html index f2dfe0a..8ae6092 100644 --- a/libretranslate/templates/index.html +++ b/libretranslate/templates/index.html @@ -1,21 +1,20 @@ - + - LibreTranslate - Free and Open Source Machine Translation API + {% for al in alternate_locales %} + {% endfor %} + LibreTranslate - {{ _h("Free and Open Source Machine Translation API") }} - - - + + - + @@ -23,11 +22,11 @@ - + - + @@ -60,28 +59,64 @@ LibreTranslate -
    -
  • API Docs
  • - {% if get_api_key_link %} -
  • Get API Key
  • - - {% endif %} -
  • GitHub
  • - {% if api_keys %} -
  • vpn_key
  • - {% endif %} + - -