angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Создание мультиязычного веб-сайта с помощью.. Flask.Babel

Не так уж много примеров для мультиязычного языка. Здесь мы следуем рекомендациям из Flask документации.

7 августа 2019
В Babel
post main image
Original photo unsplash.com/@jantined.

С единым языком проблем не возникает, мы просто забываем об остальном мире и создаем наше Flask приложение для работы с одним языком. Мы начинаем испытывать головную боль, когда веб-сайт должен поддерживать несколько языков. Что такое веб-сайт, поддерживающий несколько языков? Сколько языков будет поддерживаться и какие языки? Для английского языка существуют, например, en-GB и en-US. Какие разделы сайта должны быть доступны на всех языках? Какие части сайта вообще существуют? Я ограничусь здесь ответом на самые тривиальные вопросы. Для удобства ознакомления вы также можете ознакомиться, например, с руководством по подключаемым модулям Wordpress Polylang, см. ниже.

Когда вы создаете веб-сайт без поддержки нескольких языков, вы используете urls для своего контента, когда вы хотите добавить дополнительные языки, вы должны поддерживать все urls, т.е. то, что уже было создано. С точки зрения SEO вам лучше начать со всех мультиязычных компонентов, даже если вы сомневаетесь, что когда-либо будете добавлять другой язык.

Выбор языка

Когда язык находится в куки (сессии) правильный просмотр языка может быть проблемой, когда куки отключены. С языком в домене / URL эта проблема устранена, а также сайт является более SEO дружественным.

Вариант 1: язык расширения домена

example.com
example.es
example.pl

Пример: Тойота
https://www.toyota.de
https://www.toyota.es

Вариант 2: язык субдомена

Э...example.com
э... э...example.com
э... э... э... э... э... э... э... э...example.com

Пример: cnn
https://edition.cnn.com
https://cnnespanol.cnn.com

Вариант 3: язык в урльском варианте

example.com/en
example.com/es /pl /en /es
example.com/pl

Переведенные урны

Переведенные урны очень дружелюбны, но также представляют большую сложность:

https://edition.cnn.comРазвлечения / секс-работы / секс-индустрия /восприятие /принятие/

Некоторые веб-сайты не имеют переведенных писем:

https://www.tesla.com/nl_NL/model3/design#battery
https://www.tesla.com/de_CH/model3/design#battery

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

@pages.route('/<lang_code>/about-us', methods=['GET', 'POST'])
@pages.route('/<lang_code>/uber-uns', methods=['GET', 'POST'])
def about():
    return render_template(...)

Конечно, мы хотим, чтобы это было как-то автоматизировано... сложно.

Делая выбор

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

Вы можете захотеть сайт с основным языком без "языкового id" в URL пути и другие языки с "языковым id" в URL пути. Я не знаю, как это сделать Flask в данный момент, и считаю, что это очень сильно усложняет ситуацию, а также думаю о SEO. Резюме: Я думаю, не плохая идея начать с языка на пути урны.

В первой реализации я использовал cookie для выбора языка, теперь я должен удалить его и использовать язык в url. К счастью, Flask документация дает хорошую информацию о интернационализации. Я предлагаю вам прочитать это (я делал это много раз). У меня их много, для просмотра главной страницы.py я добавляю следующее в просмотр:

...
home_blueprint = Blueprint('home', __name__)

# lang_code in urls
@home_blueprint.url_defaults
def add_language_code(endpoint, values):
    values.setdefault('lang_code', g.lang_code)

@home_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):

    url_lang_code_items_values = get_url_lang_code_items_values()
    url_lang_code_default_item_value = get_url_lang_code_default_item_value()

    g.lang_code = url_lang_code_default_item_value
    if values:
        if 'lang_code' in values:
            if values['lang_code'] in url_lang_code_items_values:
                g.lang_code = values.pop('lang_code', None)
            else:
                pass
...

Функция get_url_lang_lang_code_items_values() возвращает список языковых кодов: en, nl, es, and function get_url_lang_code_default_item_value() возвращает en, поэтому английский язык является языком по умолчанию. Затем в __init__.py я регистрирую дом :

    from .blueprints.home.views import home_blueprint
    app.register_blueprint(home_blueprint, url_prefix='/<lang_code>')

Что произойдет, если мы напечатаем урну без пути или совершенно случайный урл? Появится сообщение об ошибке:

TypeError: homepage() got an unexpected keyword argument 'lang_code'

Flask Документы не дают решения, но после некоторых головных болей (опять же) я думаю, что нашел решение этой проблемы, используя обработчик before_request. В этом обработчике я смотрю на запрос url. Путь этой урны разделен на части. Первая часть должна быть нашей речью. Если первая часть находится в нашем списке языков, то просто продолжайте. Если страница не может быть найдена, сгенерируется 404, страница не найдена, что вполне нормально. Если первая часть не является нашим списком языков, то обработчик before_request возвращает URL перенаправления на главную страницу языка по умолчанию.

После этого сайт был показан без стилей и я получил странные сообщения об ошибках в консоли инструментов разработчика. Решение заключалось в исключении статического каталога. Итак, вот что происходит в обработчике запроса before_request:

    @app.before_request
    def before_request():

        #  try to handle missing lang_code in url interceptor

        url_lang_code_items_values = get_url_lang_code_items_values()
        url_lang_code_default_item_value = get_url_lang_code_default_item_value()

        # check for a valid url = starts with /lang_code/
        request_path = request.path.strip('/')
        request_path_parts = urlparse(request_path).path.split('/')
        if request.method in ['GET'] and len(request_path_parts) > 0:
            request_path_part_0 = request_path_parts[0]

            # do nothing with static urls !!!
            if request_path_part_0 != 'static' and request_path_part_0 not in url_lang_code_items_values:
                # fucked up url
                redir_url_parts = []
                redir_url_parts.append( request.url_root.strip('/') )
                redir_url_parts.append( url_lang_code_default_item_value.strip('/') )
                redir_url = '/'.join(redir_url_parts)
                return redirect(redir_url)

Это работает, но как мы можем изменить язык?

В шаблоне base.html у меня есть выпадающий переключатель языка:

	<li class="nav-item dropdown">
		<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
		{{ language_selected }}
		</a>
		<div class="dropdown-menu" aria-labelledby="navbarDropdown">
			<a class="dropdown-item" href="{{ request.path }}?lc=en_US">{{ _('EN') }}</a>
			<a class="dropdown-item" href="{{ request.path }}?lc=nl_NL">{{ _('NL') }}</a>
			<a class="dropdown-item" href="{{ request.path }}?lc=es_ES">{{ _('ES') }}</a>
		</div>
	</li>

Затем мы активируем Babel язык при смене языка:

    @babel.localeselector
    def get_locale():
        request_lc = request.args.get('lc')
        if not request_lc:
            if not 'lang_code' in g:
                # use default
                g.lang_code = 'en'
                request_lc = 'en_US'
            else:
                if g.lang_code == 'es':
                    request_lc = 'es_ES'
                elif g.lang_code == 'nl':
                    request_lc = 'nl_NL'
                else:
                    request_lc = 'en_US'

        else:
            # set g.lang_code to the requested language
            if request_lc == 'nl_NL':
               g.lang_code = 'nl'
            elif request_lc == 'es_ES':
               g.lang_code = 'es'
            else:
                request_lc = 'en_US'
                g.lang_code = 'en'
            #sys.exit()
        session['lc'] = request_lc
        return request_lc

Хорошо, он работает сейчас, но должен быть оптимизирован. Также на данный момент все еще существуют две проблемы:

  • Когда язык изменен, он не отображается в url после этого.
    Тебе нужно перейти на новую страницу. После изменения языка с помощью выпадающего списка URL не отображает язык в URL сразу, а только на следующем щелчке мыши. Может, переместить Babel код в обработчик before_request?
  • Многие вызовы url_defaults по умолчанию для каждого вызова, который я хотел бы иметь код:
    @home_.url_defaults @home_
    .url_value_preprocessor
    в __init__.py, а не в blueprint view.py, но это не работает.
    Зачем мне это? Потому что мне не нравится дублировать один и тот же код и я вижу много обращений к @auth_.url и т.д. Я считаю, что они должны быть сделаны один раз в __init__.py, но, возможно, я ошибаюсь.

Ссылки / кредиты

Flask Series: Internationalization
https://damyanon.net/post/flask-series-internationalization/

Flask-multilang-demo
https://github.com/DusanMadar/Flask-multilang-demo

How to Easily Create a Multilingual WordPress Site
https://www.wpbeginner.com/beginners-guide/how-to-easily-create-a-multilingual-wordpress-site/

Internationalized Application URLs
https://flask.palletsprojects.com/en/1.1.x/patterns/urlprocessors/

Multilingual flask application
https://stackoverflow.com/questions/3420897/multilingual-flask-application

Set languages and locales
https://docs.microsoft.com/en-us/windows-hardware/customize/mobile/mcsf/set-languages-and-locales

Оставить комментарий

Комментируйте анонимно или войдите в систему, чтобы прокомментировать.

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

Оставьте ответ

Ответьте анонимно или войдите в систему, чтобы ответить.

avatar

From where 'get_url_lang_code_items_values()' and 'get_url_lang_code_default_item_value()' come from?