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

Flask procesamiento multilenguaje, conmutación y la excepción 404 Page Not Found

En este post discutimos varias condiciones a la hora de procesar el idioma en la url, usando un idioma por defecto y una lista de idiomas disponibles.

13 noviembre 2019 Actualizado 15 noviembre 2019
post main image
https://unsplash.com/@sadswim

Cómo implementar el multilenguaje Flask se explica en los documentos de Flask , ver enlaces a continuación. Pero esto es sólo un punto de partida. Necesita un entendimiento más profundo del proceso para manejar casos especiales como volver a un idioma predeterminado, cambiar de idioma y la excepción de 404 páginas no encontradas.

Suposiciones

En el resto de este post estamos usando un código de idioma, 'lang_code', que está disponible en la url, es la primera parte de la url, por ejemplo:

https://www.example.com/en

https://www.example.com/en/login

También estamos utilizando Flask-Babel para las traducciones. Utilizo blueprints y los registro en create_app() como sigue:

    # authentication (shared)
    from shared.blueprints.auth.views import auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/<lang_code>/auth')

    # pages (shared)
    from shared.blueprints.pages.views import pages_blueprint
    app.register_blueprint(pages_blueprint, url_prefix='/<lang_code>')

Utilizamos g.lang_code como valor del idioma seleccionado. No utilizo una variable de sesión para recordar el idioma, sino que confío en la disponibilidad del idioma en cada solicitud.

Flask tratamiento multilingüe

La forma en que Flask maneja el multilenguaje en una solicitud es la siguiente:

Primero, se llama url_value_preprocessor() para extraer el lenguaje del ejemplo de la url:

@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
    # pop lang_code from url and set g.lang_code
    ...
    if 'lang_code' in values:
        g.lang_code = values.pop('lang_code')

A continuación, se llama a Babels locale_selector() para proporcionar las traducciones de la página, por ejemplo:

@babel.localeselector
def get_locale():
    return g.get('lang_code')

Finalmente url_defaults() es llamado antes de que la página sea construida para reemplazar <lang_code> con lang_code en las urls, por ejemplo:

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

Esto es bastante sencillo, pero como puede ver, pueden darse condiciones en las que el código de idioma cambia, no está disponible o incluso no es válido. Lo más importante es asegurarse de que g.lang_code siempre esté configurado en un idioma válido antes de llamar a localeselector() y url_defaults(). A continuación se analizan una serie de condiciones.

Flask Tratamiento multilingüe: el visitante cambia de idioma.

Para cambiar a otro idioma podemos usar un GET a la petición actual con un parámetro adicional lang_code:

<a href="{{ request.script_root  +  request.path }}?lc=en">English</a>
<a href="{{ request.script_root  +  request.path }}?lc=de">Deutsch</a>

Debemos extender la funcionalidad del url_value_preprocessor() para soportar el cambio de idioma. Simplificado, este código extra tiene el siguiente aspecto:

@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
    ...
    request_lang_code = request.args.get('lc')
    if request_lang_code:
        g.lang_code = request_lang_code
    ...

Flask procesamiento multilenguaje: falta el idioma en la url y el idioma por defecto.

Esto puede no ser un error porque puede que quieras que el visitante escriba la URL de tu dominio y que sea"redirigido" a las páginas en el idioma por defecto. Pero esto también puede ocurrir si un visitante escribe una url incorrecta, un bot (de búsqueda) llama a una url incorrecta. De nuevo podemos manejar esto en el url_value_preprocessor(). En este caso fijamos el lang_code al lang_code del idioma por defecto:

@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
    ...
    if g.get('lang_code') is  None:
        g.lang_code = default_lang_code

Flask Tratamiento multilingüe: El idioma no es un idioma soportado.

Nuestra aplicación soporta sólo un número limitado de idiomas, por ejemplo:

    available_lang_codes = ['en', 'de']

De nuevo podemos manejar el caso de idioma no válido en el url_value_preprocessor(). Si el idioma no es válido, establecemos el lang_code en el lang_code del idioma por defecto:

@pages_blueprint.url_value_preprocessor
def pull_lang_code(endpoint, values):
    ...
    if 'lang_code' in values:
        lang_code = values.pop('lang_code')
        if lang_code not in available_lang_codes:
            g.lang_code = default_lang_code

Flask tratamiento multilingüe: Error de página No encontrado (404)

Este me dio algunos dolores de cabeza, me llevó un poco de depuración para ver que el flujo es diferente en este caso. Lo que sucede aquí es que si ninguno de los blueprints coincide con la url de la petición, NUNCA se llama url_value_preprocessor(). Por ejemplo, con el blueprints mostrado anteriormente, esta es una url válida:

http://127.0.0.1:8000/en/auth/login

pero esta url da una excepción 404:

http://127.0.0.1:8000/en/auth/login/something

¿Qué hacer aquí? La respuesta es procesar esta condición en la solicitud Flask @before_request. En un flujo normal, before_request() se llama después de (!) url_value_preprocessor():

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

    @app.before_request
    def before_request():
        ....

En el caso de una excepción 404, url_value_preprocessor() NO se llama, pero antes_de_solicitud() sí se llama:

    @app.before_request
    def before_request():
        ....

Normalmente url_value_preprocessor() establecerá g.lang_code a un valor, un código de idioma. Pero en un 404, url_value_preprocessor() no se llama y g.lang_code no está configurado. En before_request() comprobamos el valor de g.lang_code. Si no está configurado podemos procesar la url de la petición nosotros mismos. Si la primera parte es un código de idioma válido, asumimos que esto es lo que necesitamos y establecemos g.lang_code. De lo contrario, estableceremos g.lang_code en el idioma por defecto. Entonces, cuando se llama al manejador 404, la página se puede mostrar en el idioma apropiado.

Resumen

No utilicé una variable de sesión para almacenar el idioma seleccionado, sino que confié en el idioma de la url. Esto funciona bien si manejamos todo tipo de condiciones como la falta de un idioma. Lo más importante es ajustar el idioma en g antes de realizar cualquier otro proceso.

Enlaces / créditos

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

Flask-Babel
https://pythonhosted.org/Flask-Babel/

Using URL Processors
https://flask.palletsprojects.com/en/1.1.x/patterns/urlprocessors/

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.