Это четырнадцатая часть серии мега-учебника Flask, в которой я собираюсь добавить функцию перевода на живой язык, используя службу переводов Microsoft и немного JavaScript.

Оглавление

В этой статье я собираюсь отойти от "безопасной зоны" серверной разработки и поработать над функцией, которая имеет одинаково важные серверные и клиентские компоненты. Вы видели ссылки "Перевести", которые некоторые сайты показывают рядом с пользовательским контентом? Это ссылки, которые запускают автоматический перевод контента, который не на родном языке пользователя, в режиме реального времени. Переведенный контент обычно вставляется под оригинальной версией. Google показывает его в результатах поиска на иностранных языках. Facebook делает это для сообщений. Twitter делает это для твитов. Сегодня я собираюсь показать вам, как добавить ту же функцию в микроблог!

Ссылки на GitHub для этой главы: BrowseZipDiff.

Сторона сервера против стороны клиента

В традиционной серверной модели, которой я следовал до сих пор, есть клиент (веб-браузер, которым управляет пользователь), отправляющий HTTP-запросы к серверу приложения. Запрос может просто запрашивать HTML-страницу, например, когда вы нажимаете ссылку "Профиль", или он может инициировать действие, например, когда вы нажимаете кнопку "Отправить" после редактирования информации вашего профиля. В обоих типах запросов сервер завершает запрос, отправляя клиенту новую веб-страницу либо напрямую, либо путем перенаправления. Затем клиент заменяет текущую страницу новой. Этот цикл повторяется до тех пор, пока пользователь остается на веб-сайте приложения. В этой модели сервер выполняет всю работу, в то время как клиент просто отображает веб-страницы и принимает вводимые пользователем данные.

Существует другая модель, в которой клиент играет более активную роль. В этой модели клиент отправляет запрос серверу, и сервер отвечает веб-страницей, но, в отличие от предыдущего случая, не все данные страницы являются HTML, есть также разделы страницы с кодом, обычно написанным на JavaScript. Как только клиент получает страницу, он отображает части HTML и выполняет код, который часто запускается не сразу, а просто настраивает обработчики событий для запуска в браузере в ответ на действия, инициированные пользователем. С этого момента у вас есть активный клиент, который может выполнять работу самостоятельно, практически не контактируя с сервером. В строгом клиентском приложении все приложение загружается на клиент вместе с запросом начальной страницы, а затем приложение полностью запускается на клиенте, связываясь с сервером только для извлечения или сохранения данных и внося динамические изменения в внешний вид этой первой и единственной веб-страницы. Приложения этого типа называются одностраничными приложениями или SPA.

Большинство приложений представляют собой гибрид двух моделей и сочетают в себе методы обеих. Мое приложение для микроблогов в основном является серверным приложением, но сегодня я добавлю в него немного действий на стороне клиента. Для выполнения переводов сообщений пользователей в режиме реального времени клиентский браузер будет отправлять асинхронные запросы на сервер, на которые сервер будет отвечать, не вызывая обновления страницы. Затем клиент динамически вставит переводы в текущую страницу. Этот метод известен как Ajax, что является сокращением от асинхронного JavaScript и XML (хотя в наши дни XML часто заменяют JSON).

Рабочий процесс перевода в реальном времени

Приложение имеет хорошую поддержку иностранных языков благодаря Flask-Babel, которая позволяет поддерживать языки, для которых я могу найти переводчиков. Но, конечно, не хватает одного элемента. Пользователи собираются писать сообщения в блогах на своих родных языках, поэтому вполне возможно, что пользователь наткнется на сообщения других пользователей, написанные на неизвестных ему языках. Качество автоматических переводов не всегда отличное, но в большинстве случаев его достаточно, если все, что вы хотите, - это иметь базовое представление о том, что означает текст на другом языке.

Это идеальная функция для реализации в виде Ajax-сервиса. Учтите, что на страницах индексации или изучения может отображаться несколько сообщений, некоторые из которых могут быть на иностранных языках. Если я реализую перевод с использованием традиционных серверных методов, запрос на перевод приведет к замене исходной страницы новой страницей. Дело в том, что запрос перевода для одной из многих отображаемых записей в блогах не является достаточно масштабным действием, требующим полного обновления страницы, эта функция работает намного лучше, если переведенный текст динамически вставляется под исходный текст, оставляя остальную часть страницы нетронутой.

Для реализации автоматического перевода в реальном времени требуется несколько шагов. Во-первых, мне нужен способ определить исходный язык текста для перевода. Мне также нужно знать предпочтительный язык для каждого пользователя, потому что я хочу показывать ссылку "Перевести" только для сообщений, написанных на других языках. Когда будет предложена ссылка на перевод и пользователь нажмет на нее, мне нужно будет отправить Ajax-запрос на сервер, и сервер свяжется со сторонним API перевода. Как только сервер отправит ответ с переведенным текстом, клиентский JavaScript-код динамически вставит этот текст на страницу. Как вы наверняка заметили, здесь есть несколько нетривиальных проблем. Я собираюсь рассмотреть их одну за другой.

Идентификация языка

Первая проблема заключается в определении того, на каком языке был написан пост. Это не точная наука, поскольку не всегда возможно однозначно определить язык текста, но в большинстве случаев автоматическое определение работает довольно хорошо. В Python есть хорошая библиотека определения языка под названием langdetect.

(venv) $ pip install langdetect

План состоит в том, чтобы загружать каждое сообщение в блог в этот пакет, чтобы попытаться определить язык. Поскольку выполнение этого анализа отнимает много времени, я не хочу повторять эту работу каждый раз, когда сообщение отображается на странице. Что я собираюсь сделать, так это установить обнаруженный язык сообщения во время отправки сообщения. Затем обнаруженный язык будет сохранен в таблице posts .

Первый шаг - добавить поле language в модель Post:

app/models.py: Добавьте обнаруженный язык в модель Post.

class Post(db.Model):
    # ...
    language: so.Mapped[Optional[str]] = so.mapped_column(sa.String(5))

Как вы помните, каждый раз, когда в модели базы данных вносятся изменения, необходимо выполнять миграцию базы данных:

(venv) $ flask db migrate -m "add language to posts"
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added column 'post.language'
  Generating migrations/versions/2b017edaa91f_add_language_to_posts.py ... done

Затем миграцию необходимо применить к базе данных:

(venv) $ flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Upgrade ae346256b650 -> 2b017edaa91f, add language to posts

Теперь я могу определять и сохранять язык при отправке сообщения:

app/routes.py: Сохранение языка для новых сообщений.

from langdetect import detect, LangDetectException

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    form = PostForm()
    if form.validate_on_submit():
        try:
            language = detect(form.post.data)
        except LangDetectException:
            language = ''
        post = Post(body=form.post.data, author=current_user,
                    language=language)
        # ...

Теперь с этим изменением каждый раз, когда отправляется сообщение, я прогоняю текст через функцию detect(), чтобы попытаться определить язык. Если язык не может быть идентифицирован, пакет langdetect выдает исключение типа LangDetectException. В этом случае я перестраховываюсь и сохраняю пустую строку в базе данных. Я собираюсь принять соглашение о том, что любые сообщения, для которых в поле языка установлена пустая строка, имеют неизвестный язык.

Отображение ссылки "Перевести"

Второй шаг включает в себя добавление ссылки "Перевести" рядом с любыми публикациями, написанными не на том языке, который активен для текущего пользователя.

app/templates/_post.html: Добавляем ссылку для перевода публикации.

{% if post.language and post.language != g.locale %}
<br><br>
<a href="#">{{ _('Translate') }}</a>
{% endif %}

Я делаю это во вложенном шаблоне _post.html, чтобы эта функциональность появлялась на любой странице, отображающей записи в блоге. Ссылка на перевод будет отображаться только в сообщениях, для которых был определен язык, и этот язык не соответствует языку, выбранному функцией locale_selector расширения Flask-Babel . Напомним из главы 13, что выбранная локаль записывается как g.locale в обработчике before_request. Текст ссылки необходимо добавить таким образом, чтобы его можно было перевести с помощью Flask-Babel, поэтому я использовал функцию _(), когда определял ее.

Обратите внимание, что с этой ссылкой еще не связано действие. Сначала я хочу разобраться, как выполнять перевод.

Использование стороннего сервиса перевода

Двумя основными сервисами перевода являются Google Cloud Translation API и Microsoft Translator Text API. Оба являются платными сервисами, и в обоих есть опция начального уровня для небольших объемов переводов, которая является бесплатной. В этой главе я собираюсь реализовать решение Microsoft, но я собираюсь сделать это таким образом, чтобы при желании было легко заменить службу перевода.

Прежде чем вы сможете использовать Microsoft Translator API, вам необходимо создать учетную запись в Azure, облачном сервисе Microsoft. Вы можете зарегистрироваться на их бесплатном уровне. В процессе регистрации вас попросят указать номер кредитной карты, но с вашей карты не будут списываться средства, пока вы остаетесь на этом уровне обслуживания.

После создания учетной записи Azure перейдите на портал Azure и нажмите кнопку "Создать ресурс", введите "translator" в поле поиска и нажмите Enter. Найдите ресурс Translator в результатах поиска и нажмите кнопку "Создать". Теперь вам будет представлена форма, в которой вы можете указать новый ресурс translator, который будет добавлен в вашу учетную запись. Заполните форму следующим образом:

  • Подписка: выберите "Оплачивать по мере поступления".

  • Группа ресурсов: нажмите "Создать новую" и введите имя "microblog-translator".

  • Регион: выберите ближайший к вам регион

  • Имя: введите "microblog"

  • Уровень цен: выберите "Бесплатный F0 (перевод до 2 МЛН символов в месяц)".

Нажмите кнопку "Просмотреть + создать", чтобы перейти на следующую страницу, на которой вы увидите краткое описание выбранных вами опций. Нажмите кнопку "Создать", чтобы подтвердить создание ресурса перевода. Если вы подождете несколько секунд, в верхней панели появится уведомление о том, что ресурс translator был развернут. Нажмите кнопку "Перейти к ресурсу", а затем на опцию "Ключи и конечная точка" на левой боковой панели. Теперь вы увидите две клавиши с надписями "Ключ 1" и "Ключ 2". Скопируйте любой из ключей в буфер обмена, а затем введите его в переменную окружения в вашем терминале (если вы используете Microsoft Windows, замените export на set):

(venv) $ export MS_TRANSLATOR_KEY=<paste-your-key-here>


Этот ключ используется для аутентификации в службе перевода, поэтому его необходимо добавить в конфигурацию приложения:

config.py: Добавляем ключ API Microsoft Translator в конфигурацию.

class Config:
    # ...
    MS_TRANSLATOR_KEY = os.environ.get('MS_TRANSLATOR_KEY')

Как всегда в случае со значениями конфигурации, я предпочитаю устанавливать их в переменные среды и импортировать оттуда в конфигурацию Flask. Это особенно важно при работе с конфиденциальной информацией, такой как ключи или пароли, которые обеспечивают доступ к сторонним сервисам. Вы определенно не захотите писать их явно в коде.

Microsoft Translator API - это веб-сервис, который принимает HTTP-запросы. На Python существует несколько HTTP-клиентов, но наиболее популярным и простым в использовании является пакет requests. Итак, давайте установим это в виртуальную среду:

(venv) $ pip install requests

Ниже вы можете увидеть функцию, которую я написал для перевода текста с помощью Microsoft Translator API. Я добавляю ее в новый модуль app/translate.py:

app/translate.py: Функция перевода текста.

import requests
from flask_babel import _
from app import app

def translate(text, source_language, dest_language):
    if 'MS_TRANSLATOR_KEY' not in app.config or \
            not app.config['MS_TRANSLATOR_KEY']:
        return _('Error: the translation service is not configured.')
    auth = {
        'Ocp-Apim-Subscription-Key': app.config['MS_TRANSLATOR_KEY'],
        'Ocp-Apim-Subscription-Region': 'westus',
    }
    r = requests.post(
        'https://api.cognitive.microsofttranslator.com'
        '/translate?api-version=3.0&from={}&to={}'.format(
            source_language, dest_language), headers=auth, json=[{'Text': text}])
    if r.status_code != 200:
        return _('Error: the translation service failed.')
    return r.json()[0]['translations'][0]['text']

Функция принимает текст для перевода, а также коды языков источника и назначения в качестве аргументов и возвращает строку с переведенным текстом. Он начинается с проверки наличия ключа для службы перевода в конфигурации, возвращая ошибку, если ключ не был определен. Ошибка также является строкой, поэтому снаружи это будет выглядеть как переведенный текст. Это гарантирует, что в случае ошибки пользователь увидит осмысленное сообщение об ошибке.

Метод post() из пакета requests отправляет HTTP-запрос методом POST на URL, указанный в качестве первого аргумента. Я использую базовый URL, который отображается на странице "Ключи и конечная точка" ресурса translator, который является https://api.cognitive.microsofttranslator.com/. Путь к конечной точке перевода - /translate, как указано в документации.

Исходный и конечный языки должны быть указаны в качестве аргументов строки запроса в URL-адресе с именами from и to соответственно. API также требует, чтобы аргумент api-version=3.0 указывался в строке запроса. Текст для перевода должен быть предоставлен в формате JSON в теле запроса с указанием формата {"Text": "the text to translate here"}.

Для аутентификации в сервисе мне нужно передать ключ, который я добавил в конфигурацию. Этот ключ должен быть указан в пользовательском HTTP-заголовке с именем Ocp-Apim-Subscription-Key. Регион, в котором был развернут ресурс translator, также необходимо указать в заголовке с названием Ocp-Apim-Subscription-Region. Имя, которое вам нужно указать для региона, показано на странице "Ключи и конечная точка", прямо под двумя ключами. В моем случае это westus для West US, выбранного мной региона, но у вас все будет по-другому, если вы выберете другой регион. Я создал словарь auth с этими двумя заголовками, а затем передал его в requests в аргументе headers.

Метод requests.post() возвращает объект ответа, который содержит все сведения, предоставленные сервисом. Сначала мне нужно проверить, что код состояния равен 200, что является кодом успешного запроса. Если я получу какие-либо другие коды, я знаю, что произошла ошибка, поэтому в этом случае я возвращаю строку ошибки. Если код состояния равен 200, то тело ответа содержит строку в кодировке JSON с переводом, поэтому все, что мне нужно сделать, это использовать метод json() из объекта response для декодирования JSON в строку Python, которую я могу использовать. Ответ JSON представляет собой список переводов, но поскольку мы переводим один текст, я могу получить первый элемент и найти фактически переведенный текст в структуре перевода.

Ниже вы можете увидеть сеанс консоли Python, в котором я использую новую функцию translate():

>>> from app.translate import translate
>>> translate('Hi, how are you today?', 'en', 'es')  # English to Spanish
'Hola, ¿cómo estás hoy?'
>>> translate('Hi, how are you today?', 'en', 'de')  # English to German
'Are Hallo, how you heute?'
>>> translate('Hi, how are you today?', 'en', 'it')  # English to Italian
'Ciao, come stai oggi?'
>>> translate('Hi, how are you today?', 'en', 'fr')  # English to French
"Salut, comment allez-vous aujourd'hui ?"

Довольно круто, не так ли? Теперь пришло время интегрировать эту функциональность в приложение.

Ajax на стороне сервера

Я собираюсь начать с реализации серверной части. Когда пользователь нажимает на ссылку "Перевести", которая появляется под сообщением, на сервер отправляется асинхронный HTTP-запрос. Я покажу вам, как это сделать, в следующем разделе, поэтому сейчас я собираюсь сосредоточиться на реализации обработки этого запроса сервером.

Асинхронный (или Ajax) запрос аналогичен функциям маршрутов и просмотра, которые я создал в приложении, с той лишь разницей, что вместо возврата HTML или перенаправления он просто возвращает данные, отформатированные как XML или, чаще, как JSON. Ниже вы можете увидеть функцию просмотра перевода, которая вызывает Microsoft Translator API и затем возвращает переведенный текст в формате JSON:

app/routes.py: Функция просмотра перевода текста.

from app.translate import translate

@app.route('/translate', methods=['POST'])
@login_required
def translate_text():
    data = request.get_json()
    return {'text': translate(data['text'],
                              data['source_language'],
                              data['dest_language'])}

Как вы можете видеть, он очень короткий. Я реализовал этот маршрут как запрос POST, который является предпочтительным форматом, когда клиенту необходимо отправить данные на сервер.

Метод request.get_json() возвращает словарь с данными, которые клиент отправил в формате JSON. Что я делаю в этой функции, так это вызываю функцию translate() из предыдущего раздела, передавая три аргумента непосредственно из данных JSON, которые были отправлены вместе с запросом. Результат включается в словарь с помощью единственного ключа с именем text, который возвращается в качестве ответа. Flask автоматически переводит словари, возвращаемые функциями просмотра, в формат JSON.

Например, если клиент хотел перевести строку Hello, World! на испанский, ответ на этот запрос имел бы следующую полезную нагрузку:

{ "text": "Hola, Mundo!" }

Ajax на стороне клиента

Итак, теперь, когда сервер может предоставлять переводы по URL-адресу /translate, мне нужно вызывать этот URL-адрес, когда пользователь нажимает ссылку "Перевести", которую я добавил выше, передавая текст для перевода, а также языки источника и назначения. Если вы не знакомы с работой с JavaScript в браузере, это будет хорошим опытом обучения.

При работе с JavaScript в браузере отображаемая в данный момент страница внутренне представлена в объектной модели документа или DOM. Это иерархическая структура, которая ссылается на все элементы, существующие на странице. Код JavaScript, выполняемый в этом контексте, может вносить изменения в DOM, чтобы вызвать изменения на странице.

Давайте сначала обсудим, как мой JavaScript-код, запущенный в браузере, может получить три аргумента, которые мне нужно отправить функции translate(), которая выполняется на сервере. Чтобы получить текст, мне нужно найти узел в DOM, который содержит текст сообщения в блоге, и прочитать его содержимое. Чтобы упростить идентификацию DOM-узлов, содержащих записи в блоге, я собираюсь прикрепить к ним уникальный идентификатор. Если вы посмотрите на шаблон _post.html, строка, которая отображает текст публикации, просто гласит {{ post.body }}. Что я собираюсь сделать, так это обернуть это содержимое в элемент <span>. Визуально это ничего не изменит, но даст мне место, куда я могу вставить идентификатор:

app/templates/_post.html: Добавляем идентификатор к каждой записи в блоге.

<span id="post{{ post.id }}">{{ post.body }}</span>

Здесь каждому сообщению в блоге будет присвоен уникальный идентификатор в формате post1post2 и так далее, где номер соответствует идентификатору сообщения в базе данных . Теперь, когда у каждого поста в блоге есть уникальный идентификатор, я могу использовать функцию document.getElementById(), доступную в браузере, чтобы найти элемент <span> для этого поста и извлечь из него текст. Например, если бы я хотел получить текст для записи с идентификатором 123, то я бы сделал это:

document.getElementById('post123').innerText

Я также хочу иметь место, куда я буду вставлять переведенный текст, как только получу его с сервера. Для этого я собираюсь заменить ссылку "Перевести" переведенным текстом, поэтому мне также нужен уникальный идентификатор, чтобы легко ее найти:

app/templates/_post.html: Добавляем идентификатор к ссылке для перевода.

<span id="translation{{ post.id }}">
    <a href="#">{{ _('Translate') }}</a>
</span>

Итак, теперь у меня есть узел post<ID> для записи в блоге и соответствующий узел translation<ID>, где мне нужно будет заменить ссылку на перевод переведенным текстом, как только он у меня будет.

Следующий шаг - написать функцию, которая может выполнять всю работу по переводу. Эта функция будет использовать входные и выходные узлы DOM, а также языки источника и назначения. Затем она отправит асинхронный запрос серверу с тремя необходимыми аргументами и, наконец, заменит ссылку на перевод переведенным текстом, возвращаемым сервером. Это звучит как большая работа, но реализация довольно короткая. Для удобства эта функция будет добавлена в базовый шаблон внизу элемента <body>, чтобы она была доступна на всех страницах приложения.

app/templates/base.html: Функция перевода на стороне клиента.

    ...
    <script>
      async function translate(sourceElem, destElem, sourceLang, destLang) {
        document.getElementById(destElem).innerHTML =
          '<img src="{{ url_for('static', filename='loading.gif') }}">';
        const response = await fetch('/translate', {
          method: 'POST',
          headers: {'Content-Type': 'application/json; charset=utf-8'},
          body: JSON.stringify({
            text: document.getElementById(sourceElem).innerText,
            source_language: sourceLang,
            dest_language: destLang
          })
        })
        const data = await response.json();
        document.getElementById(destElem).innerText = data.text;
      }
    </script>
  </body>
</html>

Первые два аргумента функции translate() являются уникальными идентификаторами для узлов post и translation, переданных в sourceElem и destElem соответственно. Третий и четвертый аргументы являются языковыми кодами источника и назначения. Функция определяется с помощью ключевого слова async, так что она может ожидать асинхронные функции с ключевым словом await.

Первое, что делает функция, это косметическое, но очень классное решение: она добавляет изображение spinner, заменяющее ссылку на перевод, чтобы пользователь знал, что перевод выполняется. Это делается путем присвоения элементу свойства innerHTML, на который ссылается destElem, которое эффективно заменяет содержимое этого элемента новым HTML. Для значка загрузки я собираюсь использовать небольшой анимированный GIF-файл, который я добавил в каталог app/static/loading.gif, который Flask резервирует для статических файлов. Чтобы сгенерировать URL, который ссылается на это изображение, я использую функцию url_for(), передавая ей специальное имя маршрута static и указывая имя файла изображения в качестве аргумента. Вы можете найти изображение loading.gif в пакете для загрузки для этой главы.

Итак, теперь у меня есть симпатичный счетчик, который заменил ссылку на перевод, так что пользователь знает, что нужно подождать, пока перевод появится через несколько минут. Следующим шагом будет отправка запроса POST на URL /translate, который я определил в предыдущем разделе. Для этого я собираюсь использовать функцию fetch(), предоставляемую браузером. Эта функция отправляет данные на сервер по URL, указанному в первом аргументе. Словарь, передаваемый в качестве второго аргумента, определяет характеристики запроса, включая используемый HTTP-метод, любые заголовки и тело запроса с данными.

Тело представляет собой строку, которая генерируется с помощью функции JSON.stringify(), которая принимает словарь с данными и возвращает полезную нагрузку JSON с этими данными. К запросу добавляется заголовок Content-Type , сообщающий серверу, что данные предоставлены в формате JSON.

Функция fetch() является асинхронной, что означает, что она возвращает объект promise. Чтобы упростить ее обработку, для ожидания завершения этой функции используется ключевое слово await. Возвращаемое значение является объектом ответа.

Сервер возвращает данные JSON в этом запросе, поэтому для преобразования их в словарь (который в JavaScript называется "объектом") используется метод response.json(). Это также асинхронная операция, поэтому ключевое await используется еще раз. Результат этого преобразования сохраняется в data.

Последняя строка функции устанавливает в свойство innerText узла destElem (у которого теперь есть изображение счетчика) текст, возвращаемый сервером, который является либо переведенной версией входного текста, либо сообщением об ошибке.

Итак, теперь единственное, что осталось, это запустить функцию translate() с правильными аргументами в результате нажатия пользователем ссылки "Перевод". Есть также несколько способов сделать это, то, что я собираюсь сделать, это просто встроить в вызов функции в атрибут ссылки href:

app/templates/_post.html: Обработка ссылки для перевода.

<span id="translation{{ post.id }}">
    <a href="javascript:translate(
                'post{{ post.id }}',
                'translation{{ post.id }}',
                '{{ post.language }}',
                '{{ g.locale }}');">{{ _('Translate') }}</a>
 </span>

Элемент ссылки href может принимать любой код JavaScript, если он имеет префикс javascript: , так что это удобный способ вызвать функцию перевода. Поскольку эта ссылка будет генерироваться на сервере, когда клиент запрашивает страницу, я могу использовать выражение {{ }} для генерации четырех аргументов функции. У каждого поста будет своя ссылка для перевода с уникальными сгенерированными аргументами.

Теперь функция перевода в реальном времени завершена! Если вы установили действующий ключ API Microsoft Translator в своей среде, теперь вы должны иметь возможность запускать переводы. Предполагая, что ваш браузер настроен на английский, вам нужно будет написать сообщение на другом языке, чтобы увидеть ссылку "Перевести". Ниже вы можете увидеть пример:

В этой главе я представил несколько новых текстов, которые необходимо перевести на все языки, поддерживаемые приложением, поэтому необходимо обновить каталоги переводов:

(venv) $ flask translate update

Для ваших собственных проектов вам нужно будет отредактировать файлы messages.po в каждом языковом репозитории, чтобы включить переводы для этих новых тестов, но я уже создал переводы на испанский в пакете загрузки для этой главы или в репозитории GitHub.

Чтобы опубликовать новые переводы, их необходимо скомпилировать:

(venv) $ flask translate compile

Комментарии (5)


  1. Stuffy_User
    17.06.2024 02:40

    А вместо API от Microsoft и Google, есть ли вариант подключить какой-нибудь русский сервис? Будет какое-нибудь дополнение по этому поводу? Например, Яндекс Переводчик API?


    1. Alex_Mer5er Автор
      17.06.2024 02:40

      Честно скажу, что рассматривал вариант Яндекс Переводчик API, но бесплатных тарифов у них сейчас нет, по завершению перевода всей серии планирую вернуться к этому вопросу.


      1. Stuffy_User
        17.06.2024 02:40

        Возможно какой-нибудь другой сервис, потому что Майкрософт или Гугл это конечно, интересно, но ситуация за окном может поменяться резко и доступ к ним пропадет. В любом случае шикарно, продолжай!


  1. Kremen644
    17.06.2024 02:40

    Жалко пример на гитхабе к статье не оставили(((


    1. Alex_Mer5er Автор
      17.06.2024 02:40

      О каком примере идёт речь?