lunes, 12 de febrero de 2024

27- Internacionalización con Django

 Internacionalización con Django

Django ofrece un completo soporte de internacionalización y localización. Te permite traducir tu aplicación a múltiples idiomas y manejar el formato específico para fechas, horas, números y zonas horarias según la configuración local. Vamos a aclarar la diferencia entre internacionalización y localización:

- Internacionalización (frecuentemente abreviada como i18n) es el proceso de adaptar el software para el potencial uso de diferentes idiomas y configuraciones locales, de modo que no esté fijado a un idioma o configuración local específica.

- Localización (abreviada como l10n) es el proceso de traducir el software y adaptarlo a una configuración local particular. Django mismo está traducido a más de 50 idiomas utilizando su marco de internacionalización.

El marco de internacionalización te permite marcar fácilmente cadenas para la traducción, tanto en el código Python como en tus plantillas. Se basa en la herramienta GNU gettext para generar y gestionar archivos de mensajes. Un archivo de mensaje es un archivo de texto plano que representa un idioma. Contiene una parte o todas las cadenas de traducción encontradas en tu aplicación y sus respectivas traducciones para un único idioma. Los archivos de mensaje tienen la extensión .po. Una vez que la traducción está hecha, los archivos de mensajes se compilan para ofrecer un acceso rápido a las cadenas traducidas. Los archivos de traducción compilados tienen la extensión .mo.


Configuración de internacionalización y localización


Django proporciona varios ajustes para la internacionalización. Los siguientes ajustes son los más relevantes:

- USE_I18N: Un booleano que especifica si el sistema de traducción de Django está habilitado. Esto es Verdadero por defecto.

- USE_L10N: Un booleano que indica si está habilitado el formato localizado. Cuando está activo, se utilizan formatos localizados para representar fechas y números. Esto es Falso por defecto.

- USE_TZ: Un booleano que especifica si las fechas y horas son conscientes de la zona horaria. Cuando creas un proyecto con el comando startproject, esto se establece en Verdadero.

- LANGUAGE_CODE: El código de idioma predeterminado para el proyecto. Está en el formato estándar de ID de idioma, por ejemplo, 'en-us' para inglés americano o 'en-gb' para inglés británico. Esta configuración requiere que USE_I18N esté establecido en Verdadero para que tenga efecto. Puedes encontrar una lista de ID de idiomas válidos en http://www.i18nguy.com/unicode/language-identifiers.html.

- LANGUAGES: Una tupla que contiene los idiomas disponibles para el proyecto. Vienen en dos tuplas de un código de idioma y un nombre de idioma. Puedes ver la lista de idiomas disponibles en django.conf.global_settings. Cuando elijas en qué idiomas estará disponible tu sitio, estableces LANGUAGES en un subconjunto de esa lista.

- LOCALE_PATHS: Una lista de directorios donde Django busca archivos de mensajes que contienen traducciones para el proyecto.

- TIME_ZONE: Una cadena que representa la zona horaria para el proyecto. Se establece en 'UTC' cuando creas un nuevo proyecto usando el comando startproject. Puedes establecerlo en cualquier otra zona horaria, como 'Europe/Madrid'.


Estos son algunos de los ajustes de internacionalización y localización disponibles. Puedes encontrar la lista completa en https://docs.djangoproject.com/en/5.0/ref/settings/#globalization-i18n-l10n.


Comandos de gestión de internacionalización


Django incluye los siguientes comandos de gestión para gestionar las traducciones:

- makemessages: Esto recorre el árbol de origen para encontrar todas las cadenas marcadas para traducción y crea o actualiza los archivos de mensajes .po en el directorio local. Se crea un único archivo .po para cada idioma.

- compilemessages: Esto compila los archivos de mensajes .po existentes en archivos .mo, que se utilizan para recuperar traducciones.


Instalación del kit de herramientas gettext


Necesitarás el kit de herramientas gettext para poder crear, actualizar y compilar archivos de mensajes. La mayoría de las distribuciones de Linux incluyen el kit de herramientas gettext. Si estás usando macOS, la forma más sencilla de instalarlo es a través de Homebrew, en https://brew.sh/, con el siguiente comando:

brew install gettext

También es posible que necesites vincularlo forzosamente con el siguiente comando:

brew link --force gettext

Si estás usando Windows, sigue los pasos en https://docs.djangoproject.com/en/5.0/topics/i18n/translation/#gettext-on-windows. Puedes descargar un instalador binario precompilado de gettext para Windows desde https://mlocati.github.io/articles/gettext-iconv-windows.html.


Cómo añadir traducciones a un proyecto Django


Veamos el proceso de internacionalizar tu proyecto. Necesitarás hacer lo siguiente:

1. Marcar las cadenas para traducción en tu código Python y en tus plantillas.

2. Ejecutar el comando makemessages para crear o actualizar archivos de mensajes que incluyan todas las cadenas de traducción de tu código.

3. Traducir las cadenas contenidas en los archivos de mensajes y compilarlos usando el comando de gestión compilemessages.


Cómo determina Django el idioma actual


Django viene con un middleware que determina el idioma actual basado en los datos de la solicitud. Este es el middleware LocaleMiddleware que reside en django.middleware.locale.LocaleMiddleware, que realiza las siguientes tareas:

1. Si estás usando i18n_patterns, es decir, estás usando patrones de URL traducidos, busca un prefijo de idioma en la URL solicitada para determinar el idioma actual.

2. Si no se encuentra ningún prefijo de idioma, busca un LANGUAGE_SESSION_KEY existente en la sesión del usuario actual.

3. Si el idioma no está configurado en la sesión, busca una cookie existente con el idioma actual. Se puede proporcionar un nombre personalizado para esta cookie en el ajuste LANGUAGE_COOKIE_NAME. Por defecto, el nombre de esta cookie es django_language.

4. Si no se encuentra ninguna cookie, busca en la cabecera Accept-Language de la solicitud.

5. Si la cabecera Accept-Language no especifica un idioma, Django utiliza el idioma definido en el ajuste LANGUAGE_CODE.


Por defecto, Django utilizará el idioma definido en el ajuste LANGUAGE_CODE a menos que estés usando LocaleMiddleware. El proceso descrito aquí solo se aplica al usar este middleware.


Preparando tu proyecto para la internacionalización


A continuación vamos a preparar nuestro proyecto para que se pueda usar diferentes lenguas. Vamos a crear una versión en Inglés y en Español de la tienda. Edita el archivo settings.py de la aplicación principal y añade la siguiente configuración al parámetro LANGUAGES. Pon lo al lado de la configuración LANGUAGE_CODE de esta forma:

PracticaDjango/PracticaDjango/settings.py

#...
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/

LANGUAGE_CODE = 'es'

LANGUAGES = [
    ('es', 'Español'),
    ('en', 'English'),
]

TIME_ZONE = 'Europe/Madrid'

USE_I18N = True

USE_TZ = True
La configuración LANGUAGES contiene dos tuplas que consisten en un código de idioma y un nombre. Los códigos de idioma pueden ser específicos de la localidad, como en-us o en-gb, o genéricos, como "en". Con esta configuración, especificas que tu aplicación solo estará disponible en inglés y español. Si no defines una configuración de LANGUAGES personalizada, el sitio estará disponible en todos los idiomas en los que Django esté traducido.

Haz que tu configuración LANGUAGE_CODE se vea como sigue:

LANGUAGE_CODE = 'es'

Agrega 'django.middleware.locale.LocaleMiddleware' a la configuración MIDDLEWARE. Asegúrate de que este middleware venga después de SessionMiddleware porque LocaleMiddleware necesita usar datos de sesión. También debe colocarse antes de CommonMiddleware porque este último necesita un idioma activo para resolver la URL solicitada. La configuración MIDDLEWARE debería verse ahora como sigue:

PracticaDjango/PracticaDjango/settings.py

#...
SITE_ID = 1  # define the site id

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'PracticaDjango.urls'
NOTA: El orden de las clases de middleware es muy importante porque cada middleware puede depender de los datos establecidos por otro middleware que se ejecutó previamente. El middleware se aplica a las solicitudes en el orden de aparición en MIDDLEWARE, y en orden inverso para las respuestas.

Crea la siguiente estructura de directorios dentro del directorio principal del proyecto, junto al archivo manage.py:

locale/
    es/
    en/
   
El directorio "locale" es el lugar donde residirán los archivos de mensajes para tu aplicación. Edita el archivo de configuración settings.py nuevamente y agrega la siguiente configuración debajo de LANGUAGES:


PracticaDjango/PracticaDjango/settings.py

LOCALE_PATHS = [
    BASE_DIR / 'locale',
]

La configuración LOCALE_PATHS especifica los directorios donde Django debe buscar archivos de traducción. Los directorios de localización que aparecen primero tienen la mayor precedencia.

Cuando uses el comando makemessages desde el directorio de tu proyecto, los archivos de mensajes se generarán en la ruta "locale/" que creaste. Sin embargo, para aplicaciones que contienen un directorio "locale/", los archivos de mensajes se generarán en ese directorio.

En resumen. Le estamos indicando a Django que utilice el LocaleMiddleware el cual se encargará de escoger el lenguaje adecuado para cada usuario basado en información extraída del request que normalmente será el idioma local del ordenador del usuario (Es importante ubicar este middleware entre SessionMiddleware y CommonMiddleware si estás usando ambos, de lo contrario podría no funcionar bien). También especificamos mediante la variable LANGUAGES qué lenguajes va a soportar nuestro proyecto y por último, especificamos la ruta de la carpeta en la cual se guardarán los archivos de traducción mediante la variable LOCALE_PATHS. 


Traduciendo código Python.


Para traducir literales en tu código Python, puedes marcar cadenas para la traducción usando la función gettext() incluida en django.utils.translation. Esta función traduce el mensaje y devuelve una cadena. La convención es importar esta función como un alias más corto llamado _ (el carácter de subrayado). Puedes encontrar toda la documentación sobre traducciones en https://docs.djangoproject.com/en/5.0/ topics/i18n/translation/.

Traducciones estándar

El siguiente código muestra cómo marcar una cadena para traducción:

from django.utils.translation import gettext as _
output = _('Texto a ser traducido.')

Traducciones Lazy


Django incluye versiones perezosas para todas sus funciones de traducción, que tienen el sufijo _lazy(). Cuando se usan las funciones lazy o perezosas, las cadenas se traducen cuando se accede al valor, en lugar de cuando se llama a la función (por eso se traducen de forma perezosa). Las funciones de traducción perezosa son útiles cuando las cadenas marcadas para traducción están en rutas que se ejecutan cuando se cargan los módulos.

Nota: Usar gettext_lazy() en lugar de gettext() significa que las cadenas se traducen cuando se accede al valor. Django ofrece una versión perezosa para todas las funciones de traducción.

Las traducciones que incluyen variables


Las cadenas marcadas para traducción pueden incluir marcadores de posición para incluir variables en las traducciones. El siguiente código es un ejemplo de una cadena de traducción con un marcador de posición:

from django.utils.translation import gettext as _
month = _('April')
day = '14'
output = _('Hoy es %(month)s %(day)s') % {'month': month, 'day': day}

Al usar marcadores de posición, puedes reordenar las variables de texto. Por ejemplo, una traducción al inglés del ejemplo anterior podría ser "Today is April 14", mientras que la traducción al español podría ser "hoy es 14 de abril". Siempre usa interpolación de cadenas en lugar de interpolación posicional cuando tengas más de un parámetro para la cadena de traducción. De esta manera, podrás reordenar el texto del marcador de posición.

Plurales en las traducciones.


Para formas plurales en las traducciones, puedes utilizar ngettext() y ngettext_lazy(). Estas funciones traducen formas singular y plural dependiendo de un argumento que indica el número de objetos. El siguiente ejemplo muestra cómo usarlas:

output = ngettext('hay %(count)d producto',
                  'hay %(count)d productos',
                  count) % {'count': count}

Ahora que conoces los conceptos básicos de traducción de literales en tu código Python, es hora de aplicar las traducciones a tu proyecto.

Traduciendo tu propio código.


Edita el archivo settings.py del proyecto, importa la función gettext_lazy y cambia la opción LANGUAGES de la siguiente forma para traducir el nombre de las lenguas.

PracticaDjango/PracticaDjango/settings.py

# Para la internacionalización del proyecto
from django.utils.translation import gettext_lazy as _

LANGUAGES = [
    ('es', _('Español')),
    ('en', _('English')),
]

Nota: Si en linux te da un error de que no se puede encontrar un determinado archivo vuelve a instalar gettext con sudo apt intstall gettext.

Aquí, vamos a usar la función gettext_lazy en vez de gettext para evitar una importación circular, con esto traduciremos los nombres de los lenguajes cuando acedamos a ellos.

Abre el shel y ejecuta el siguiente comando desde el directorio principal del proyecto donde esta manage.py:

$ django-admin makemessages --all
Al ejecutar este comando la salida debería ser:

processing locale en
processing locale es
Echa un vistazo al directorio que creamos antes, el directorio locale. Deberías ver una estructura de directorios como la siguiente:

en/
    LC_MESSAGES/
        django.po
es/
    LC_MESSAGES/
        django.po
Un archivo .po ha sido creado para cada lenguaje. Como nuestro código ha sido escrito en español, tendremos que hacer la traducción al ingles. Abre el archivo en/LC_MESSAGES/django.po con un editor de texto. Al final del archivo verás lo siguiente:

PracticaDjango/locale/en/LC_MESSAGES/django.po

#: PracticaDjango/settings.py:149
msgid "Español"
msgstr ""

#: PracticaDjango/settings.py:150
msgid "English"
msgstr ""
Cada cadena de traducción está precedida por un comentario que muestra detalles sobre el archivo y la línea donde fue encontrado. Cada traducción incluye dos cadenas:

• msgid: la cadena de traducción tal como aparece en el código fuente.
• msgstr: La traducción del idioma, que está vacía de forma predeterminada. Aquí es donde tienes que entrar la traducción real de la cadena dada.

Completa las traducciones de msgstr para la cadena msgid dada, de la siguiente manera:

PracticaDjango/locale/en/LC_MESSAGES/django.po

#: PracticaDjango/settings.py:149
msgid "Español"
msgstr "Spanish"

#: PracticaDjango/settings.py:150
msgid "English"
msgstr "English"
Guarda las modificaciones que hemos hecho en el archivo, abre el shell y ejecuta el siguiente comando:

$ django-admin compilemessages
Si todo ha ido bien deberías ver una salida del comando como esta:

processing locale en
processing locale es
Los archivos han sido compilados (.mo) y si observas el directorio "locale" verás ahora lo siguiente:

en/
    LC_MESSAGES/
        django.mo
        django.po
es/
    LC_MESSAGES/         django.mo        
        django.po
¡Enhorabuena! Has traducido los nombres de los idiomas. Ahora, traduzcamos los nombres de los campos un modelo de una aplicación del proyecto. Edita el archivo models.py de la aplicación "Servicios", por ejemplo, y vamos a añadir a los campos del modelo el etiquetado para su traducción de la siguiente manera:

PracticaDjango/Servicios/models.py

# Para internacionalizar el modelo
from django.utils.translation import gettext_lazy as _
#...
class Servicio(models.Model):
    titulo = models.CharField(_('titulo'),max_length=50)
    contenido = models.CharField(_('contenido'), max_length=50)
    imagen = models.ImageField(_('imagen'), upload_to='Servicios')
    created = models.DateField(auto_now_add=True)
    # Para que automaticamente actualice las fechas usamos el argumento
    # auto_now_add = True para guardar la fecha de cuando se cree el registro
    updated = models.DateField(auto_now=True)
    # auto_now = True para guardar la fecha cuando se guarde o actualice el 
    # registro.

Ha agregado nombres para los campos que se muestran cuando el administrador añada nuevos servicios que tenga nuestra tienda. Estos son titulo, contenido e imagen. Recuerda que también puedes utilizar
el atributo verbose_name para nombrar los campos.

Crea el siguiente directorio dentro de la aplicación Servicios:

locale/
    en/
    es/
Al crear un directorio local, las cadenas de traducción de esta aplicación se almacenarán en un archivo de mensajes .po, en este directorio en lugar del archivo de mensajes principal. De esta manera, podemos generar por separado archivos de traducción para cada aplicación.

Abra el shell desde el directorio del proyecto y ejecuta el siguiente comando:

$ django-admin makemessages --all
Salida:
processing locale es
processing locale en
Abra el archivo locale/en/LC_MESSAGES/django.po de la aplicación de Servicios usando un editor de texto. Verás las cadenas de traducción para el modelo de Servicios. Completa las siguientes traducciones msgstr para las cadenas dadas de msgid:

msgid "titulo"
msgstr "title"

#: Servicios/models.py:10
msgid "contenido"
msgstr "content"

#: Servicios/models.py:11
msgid "imagen"
msgstr "image"
Después de terminada la traducción, guarda el archivo. 

Además de un editor de texto, puedes utilizar Poedit para editar traducciones. Poedit es un software para editar traducciones que utiliza gettext. Está disponible para Linux, Windows y macOS. Puedes descargar Poedit desde https://poedit.net/.

También vamos a traducir los formularios del proyecto. Para ver un ejemplo vamos a traducir el formulario de contacto. Edita el archivo forms.py dentro del directorio de la aplicación de Contacto y agrega un atributo de etiqueta a los campos de la siguiente manera:

PracticaDjango/Contacto/forms.py

from django import forms

# Internacionalización
from django.utils.translation import gettext_lazy as _


class FormularioContacto(forms.Form):
    # Especificamos los campos del formulario
    nombre = forms.CharField(label=_("Nombre"), max_length=50)
    email = forms.EmailField(label=_("Email"), max_length=50)
    contenido = forms.CharField(
        label=_("Contenido"),
        max_length=400,
        widget=forms.Textarea(attrs={"cols": 45, "rows": 5}),
    )
Con esto hemos marcado el nombre de las etiquetas para su posterior traducción al inglés.

Traduciendo las plantillas.


Django ofrece las etiquetas de plantilla {% trans %} y {% blocktrans %} para traducir las cadenas en las plantillas. Para usar las etiquetas de plantilla de traducción, debes agregar {% load i18n %} en la parte superior de tu plantilla para cargarlas.

La etiqueta de plantilla {% trans %}

La etiqueta de plantilla {% trans %} te permite marcar un literal para su traducción. Internamente, Django ejecuta gettext() en el texto dado. Así es como se marca una cadena para traducción en una plantilla:

```
{% trans "Texto a ser traducido" %}
```

Puedes usar as para almacenar el contenido traducido en una variable que puedes utilizar en toda tu plantilla. El siguiente ejemplo almacena el texto traducido en una variable llamada saludo:

```
{% trans "¡Hola!" as saludo %}
<h1>{{ saludo }}</h1>
```

La etiqueta {% trans %} es útil para cadenas de traducción simples, pero no puede manejar contenido para traducción que incluya variables.

La etiqueta de plantilla {% blocktrans %}

La etiqueta de plantilla {% blocktrans %} te permite marcar contenido que incluye literales y contenido variable utilizando marcadores de posición. El siguiente ejemplo te muestra cómo usar la etiqueta {% blocktrans %}, incluyendo una variable de nombre en el contenido para traducción:

```
{% blocktrans %}¡Hola {{ nombre }}!{% endblocktrans %}
```

Puedes usar with para incluir expresiones de plantilla, como acceder a atributos de objetos o aplicar filtros de plantilla a variables. Siempre debes usar marcadores de posición para estas. No puedes acceder a expresiones o atributos de objetos dentro del bloque blocktrans. El siguiente ejemplo te muestra cómo usar with para incluir un atributo de objeto al que se ha aplicado el filtro capfirst:

```
{% blocktrans with nombre=user.nombre|capfirst %}
¡Hola {{ nombre }}!
{% endblocktrans %}
```

Traducir las plantillas de la Tienda


Edita la plantilla Tienda/tienda.html de la aplicación de la Tienda. Asegúrate de cargar la etiqueta i18n en la parte superior de la plantilla y marcar las cadenas para traducción, como se muestra a continuación. El código nuevo se resalta en azul:

PracticaDjango/Tienda/templates/Tienda/tienda.html

<!--Cargamos la plantilla base-->
{% extends "Proyecto_web_app/base.html" %}
{% load thumbnail %}
{% load i18n %}

<!-- Establecemos el titulo de la página -->
{% block title %}{% trans "Tienda" %}{% endblock %}

<!-- Definimos su contenido -->
{% block content %}
<h1 class="text-center">{% trans "Elige tu Consola." %}</h1>

<div class="container">
  <div class="bg-dark">

Usa la etiqueta {% blocktrans %} en lugar de {% trans %} cuando necesites incluir contenido variable en tu cadena de traducción.

Asegúrate de que ninguna etiqueta de plantilla se divida en varias líneas.

Igual que con esta plantilla puedes hacerlo con el resto para traducirlas.

Vamos a actualizar los archivos de mensajes para incluir las nuevas cadenas de traducción. Abre la terminal y ejecuta el siguiente comando:

```
django-admin makemessages --all
```

Los archivos .po están dentro del directorio "locale" del proyecto "PracticaDjango", y verás que la aplicación de pedidos ahora contiene todas las cadenas que marcamos para su traducción.

Edita los archivos de traducción .po del proyecto y de la aplicación de Servicios e incluye las traducciones al inglés en msgstr. 

Ejecuta el siguiente comando para compilar los archivos de traducción:

```
django-admin compilemessages
```


Se ha generado un archivo .mo que contiene las traducciones compiladas para cada archivo .po de traducción.


Utilizando la interfaz de traducción Rosetta


Rosetta es una aplicación de terceros que te permite editar traducciones utilizando la misma interfaz que el sitio de administración de Django. Rosetta facilita la edición de archivos .po y actualiza los archivos de traducción compilados. Vamos a agregarlo al proyecto.


Instala Rosetta a través de pip usando este comando:

```
pip install django-rosetta
```

Luego, agrega 'rosetta' a la configuración INSTALLED_APPS en el archivo settings.py de tu proyecto, de la siguiente manera:

```
INSTALLED_APPS = [
    # ...
    'rosetta',
]
```
Necesitas agregar las URL de Rosetta a tu configuración principal de URL. Edita el archivo urls.py principal de tu proyecto y agrega el siguiente patrón de URL resaltado en azul:

PracticaDjango/PracticaDjango/urls.py

#...
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('Proyecto_web_app.urls')),
    path('servicios/', include('Servicios.urls')),
    path('blog/', include('Blog.urls')),
    path('contacto/', include('Contacto.urls')),
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
    path('rosetta/', include('rosetta.urls')),
    path('payment/', include('Payment.urls', namespace='payment')),
    path('cupones/', include('Cupones.urls', namespace='cupones')),
    path('tienda/', include('Tienda.urls')),
    path('carro/', include('Carro.urls')),
    path('cuenta/', include('Autentificacion.urls')),
    path('social-auth/', include('social_django.urls', namespace='social')),
    path('orders/', include('Orders.urls', namespace='orders')),
    path('__debug__/', include('debug_toolbar.urls')),    
]

Asegúrate de colocarlo antes del patrón de URL de las aplicaciones de la Tienda para evitar una coincidencia no deseada de patrones.

Abre http://127.0.0.1:8000/admin/ e inicia sesión con un superusuario. Luego, navega a http://127.0.0.1:8000/rosetta/ en tu navegador. En el menú Filtro, haz clic en TERCEROS para mostrar todos los archivos de mensajes disponibles.

Deberías ver una lista de idiomas existentes, como esta:

imagen de rosetta


Haz clic en la aplicación Practicadjango de la sección Inglés para editar las traducciones al Inglés. Deberías ver una lista de las cadenas de traducción, algo parecido a esto:


cadenas traducidas



Como ves, puedes introducir las traducciones en la columna dedicada al idioma a traducir, el inglés en este caso. Si hubiésemos utilizado marcadores de posición estas aparecerían de la siguiente forma:

  %(items)s producto, $%(total)s          %(items)s item, $%(total)s 
  %(items)s productos, $%(total)s          %(items)s items, $%(total)s      
Roseta utiliza un color de fondo diferente para mostrar los marcadores de posición. Cuando los traduzcas, asegúrate de mantener los marcadores de posición inalterados.

Cuando termines de editar las traducciones, haz clic en el botón Guardar y traducir el siguiente bloque para guardar las traducciones en el archivo .po. Rosetta compila el archivo de mensajes cuando guardas las traducciones, por lo que no es necesario que ejecutes el comando compilemessages. Sin embargo, Rosetta requiere acceso de escritura a los directorios de localización para escribir los archivos de mensajes. Asegúrate de que los directorios tengan permisos válidos.

Si deseas que otros usuarios puedan editar las traducciones, abre http://127.0.0.1:8000/admin/auth/group/add/ en tu navegador y crea un nuevo grupo llamado traductores. Luego, accede a http://127.0.0.1:8000/admin/auth/user/ para editar los usuarios a quienes deseas otorgar permisos para que puedan editar las traducciones. Al editar un usuario, en la sección Permisos, añade el grupo de traductores a los Grupos elegidos para cada usuario. Rosetta solo está disponible para superusuarios o usuarios que pertenezcan al grupo de traductores.

Puedes leer la documentación de Rosetta en https://django-rosetta.readthedocs.io/.

Cuando agregues nuevas traducciones a tu entorno de producción, si usas Django con un servidor web real, deberás recargar tu servidor después de ejecutar el comando compilemessages o después de guardar las traducciones con Rosetta para que los cambios surtan efecto.


Fuzzy Translations.


Al editar traducciones en Rosetta, puedes ver una columna llamada FUZZY. Esta no es una característica de Rosetta; es proporcionada por gettext. Si la bandera FUZZY está activa para una traducción, no se incluirá en los archivos de mensajes compilados. Esta bandera marca las cadenas de traducción que necesitan ser revisadas por un traductor. Cuando los archivos .po se actualizan con nuevas cadenas de traducción, es posible que algunas cadenas de traducción se marquen automáticamente como difusas. Esto ocurre cuando gettext encuentra algún msgid que ha sido ligeramente modificado. gettext lo empareja con lo que cree que era la antigua traducción y lo marca como difuso para su revisión. El traductor debe revisar las traducciones difusas, eliminar la bandera FUZZY y compilar nuevamente el archivo de traducción.


Patrones de URL para internacionalización


Django ofrece capacidades de internacionalización para las URL. Incluye dos características principales para las URL internacionalizadas:

• Prefijo de idioma en patrones de URL: Agregar un prefijo de idioma a las URL para servir cada versión de idioma bajo una URL base diferente.
• Patrones de URL traducidos: Traducir los patrones de URL para que cada URL sea diferente para cada idioma.

Una razón para traducir las URL es optimizar tu sitio para los motores de búsqueda. Al agregar un prefijo de idioma a tus patrones, podrás indexar una URL para cada idioma en lugar de una única URL para todos ellos. Además, al traducir las URL a cada idioma, proporcionarás a los motores de búsqueda URLs que se clasificarán mejor para cada idioma.


Agregar un prefijo de idioma a los patrones de URL


Django te permite agregar un prefijo de idioma a tus patrones de URL. Por ejemplo, la versión en inglés de tu sitio puede ser servida bajo una ruta que comienza con /en/, y la versión en español bajo /es/. Para usar idiomas en los patrones de URL, debes utilizar el LocaleMiddleware proporcionado por Django. El framework lo utilizará para identificar el idioma actual a partir de la URL solicitada. Anteriormente, lo agregaste a la configuración MIDDLEWARE de tu proyecto, así que no necesitas hacerlo ahora.

Agreguemos un prefijo de idioma a tus patrones de URL. Edita el archivo urls.py principal del proyecto  y agrega i18n_patterns(), de la siguiente manera:

PracticaDjango/PracticaDjango/urls.py

from django.conf.urls.i18n import i18n_patterns
#...
urlpatterns = i18n_patterns(
    path('admin/', admin.site.urls),
    path('', include('Proyecto_web_app.urls')),
    path('servicios/', include('Servicios.urls')),
    path('blog/', include('Blog.urls')),
    path('contacto/', include('Contacto.urls')),
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
    path('rosetta/', include('rosetta.urls')),
    path('payment/', include('Payment.urls', namespace='payment')),
    path('cupones/', include('Cupones.urls', namespace='cupones')),
    path('tienda/', include('Tienda.urls')),
    path('carro/', include('Carro.urls')),
    path('cuenta/', include('Autentificacion.urls')),
    path('social-auth/', include('social_django.urls', namespace='social')),
    path('orders/', include('Orders.urls', namespace='orders')),
    path('__debug__/', include('debug_toolbar.urls')),    
)

Puedes combinar patrones de URL estándar no traducibles y patrones bajo i18n_patterns para que algunos patrones incluyan un prefijo de idioma y otros no lo hagan. Sin embargo, es mejor usar URLs traducidas únicamente para evitar la posibilidad de que una URL traducida descuidadamente coincida con un patrón de URL no traducido.

Ejecuta el servidor de desarrollo y abre http://127.0.0.1:8000/ en tu navegador. Django realizará los pasos descritos en la sección "Cómo Django determina el idioma actual" para determinar el idioma actual, y te redirigirá a la URL solicitada, incluyendo el prefijo de idioma. Echa un vistazo a la URL en tu navegador; ahora debería lucir como http://127.0.0.1:8000/es/. El idioma actual es el establecido por el encabezado Accept-Language de tu navegador si es español o inglés; de lo contrario, es el LANGUAGE_CODE predeterminado (español) definido en tu configuración.


Traducir patrones de URL


Django soporta cadenas traducidas en patrones de URL. Puedes usar una traducción diferente para cada idioma para un solo patrón de URL. Puedes marcar los patrones de URL para traducción de la misma manera que lo harías con literales, utilizando la función gettext_lazy().

Edita el archivo urls.py principal del proyecto y añade cadenas de traducción a las expresiones regulares de los patrones de URL para las aplicaciones de carrito, pedidos, pagos y cupones, de la siguiente manera:

PracticaDjango/PracticaDjango/urls.py

from django.utils.translation import gettext_lazy as _

#...

urlpatterns = i18n_patterns(
    path('admin/', admin.site.urls),
    path('', include('Proyecto_web_app.urls')),
    path(_('servicios/'), include('Servicios.urls')),
    path('blog/', include('Blog.urls')),
    path(_('contacto/'), include('Contacto.urls')),
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
    path('rosetta/', include('rosetta.urls')),
    path('payment/', include('Payment.urls', namespace='payment')),
    path(_('cupones/'), include('Cupones.urls', namespace='cupones')),
    path(_('tienda/'), include('Tienda.urls')),
    path(_('carro/'), include('Carro.urls')),
    path(_('cuenta/'), include('Autentificacion.urls')),
    path('social-auth/', include('social_django.urls', namespace='social')),
    path('orders/', include('Orders.urls', namespace='orders')),
    path('__debug__/', include('debug_toolbar.urls')),    
)
Al igual que estos patrones puedes traducir cualquier otro de otro archivo urls.py. Vamos a detenernos un momento en la aplicación Payment porque, aunque cuando se creo las variables están en inglés y por tanto no haría falta traducirlas, creo que es interesante verlo por el tema de la pasarela de pago que usa webhooks.

Edita el código del archivo urls.py de la aplicación Payment de esta forma:

PracticaDjango/Payment/urls.py

from django.urls import path
from . import views
from . import webhooks

# Para la internacionalización
from django.utils.translation import gettext_lazy as _

app_name = 'payment'

urlpatterns = [
    path(_('process/'), views.payment_process, name='process'),
    path(_('completed/'), views.payment_completed, name='completed'),
    path(_('canceled/'), views.payment_canceled, name='canceled'),
    path('webhook/', webhooks.stripe_webhook, name='stripe-webhook'),
]
Ten en cuenta que estos patrones de URL incluirán un prefijo de idioma porque están incluidos bajo i18n_patterns() en el archivo urls.py principal del proyecto. Esto hará que cada patrón de URL tenga un URI diferente para cada idioma disponible, uno que comienza con /es/, otro con /en/, y así sucesivamente. Sin embargo, necesitamos una sola URL para que Stripe notifique eventos, y necesitamos evitar los prefijos de idioma en la URL del webhook.

Elimina el patrón de URL del webhook del archivo urls.py de la aplicación de pagos. El archivo debería verse así ahora:

PracticaDjango/Payment/urls.py

from django.urls import path
from . import views
from . import webhooks

# Para la internacionalización
from django.utils.translation import gettext_lazy as _

app_name = 'payment'

urlpatterns = [
    path(_('process/'), views.payment_process, name='process'),
    path(_('completed/'), views.payment_completed, name='completed'),
    path(_('canceled/'), views.payment_canceled, name='canceled'),
]
Luego, agrega el siguiente patrón de URL del webhook al archivo urls.py principal del proyecto PracticaDjango. El nuevo código está resaltado en negrita:

PracticaDjango/Payment/urls.py

#...
# Para la internacionalización del proyecto
from django.conf.urls.i18n import i18n_patterns
from django.utils.translation import gettext_lazy as _
from Payment import webhooks

#...
urlpatterns = i18n_patterns(
    path('admin/', admin.site.urls),
    path('', include('Proyecto_web_app.urls')),
    path(_('servicios/'), include('Servicios.urls')),
    path('blog/', include('Blog.urls')),
    path(_('contacto/'), include('Contacto.urls')),
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'),
    path('rosetta/', include('rosetta.urls')),
    path('payment/', include('Payment.urls', namespace='payment')),
    path(_('cupones/'), include('Cupones.urls', namespace='cupones')),
    path(_('tienda/'), include('Tienda.urls')),
    path(_('carro/'), include('Carro.urls')),
    path(_('cuenta/'), include('Autentificacion.urls')),
    path('social-auth/', include('social_django.urls', namespace='social')),
    path('orders/', include('Orders.urls', namespace='orders')),
    path('__debug__/', include('debug_toolbar.urls')),
)

urlpatterns += [path('payment/webhook/', webhooks.stripe_webhook, name='stripe-webhook'),
]

Hemos agregado el patrón de URL del webhook a urlpatterns fuera de i18n_patterns() para asegurarnos de mantener una única URL para las notificaciones de eventos de Stripe.

Abre la terminal y ejecuta el siguiente comando para actualizar los archivos de mensajes con las nuevas traducciones:

```
django-admin makemessages --all
```

Asegúrate de que el servidor de desarrollo esté en funcionamiento con el siguiente comando:

```
python manage.py runserver
```

Abre http://127.0.0.1:8000/es/rosetta/ en tu navegador y haz clic en el enlace de PracticaDjango bajo la sección de inglés. Haz clic en SIN TRADUCIR SOLO para ver solo las cadenas que aún no se han traducido. Ahora verás los patrones de URL para traducción, como se muestra en la Figura 11.4:


cadenas sin traducir


Añade las traducciones para cada URL. No olvides incluir la barra invertida / al final de cada URL como se muestra en la siguiente figura:


traducción de cadenas


Cuando hayas terminado haz clic en GUARDAR Y TRADUCIR SIGUIENTE BLOQUE

En el bloque A REVISAR SOLO aparecerán aquellas traducciones que han sido marcadas porque exista alguna duda sobre su corrección.


Permitir a los usuarios cambiar de idioma


Dado que estás sirviendo contenido que está disponible en varios idiomas, deberías permitir que tus usuarios cambien el idioma del sitio. Vamos a añadir un selector de idioma a nuestro sitio. El selector de idioma consistirá en una lista de idiomas disponibles mostrados utilizando enlaces.

Edita la plantilla base.html de la aplicación de la aplicacion Proyecto_web_app y añade el siguiente código:

PracticaDjango/Proyecto_web_app/templates/Proyecto_web_app/base.html

{# para internacionalizar del proyecto#}
{% load i18n %}

<header>
        <h1 class="container-fluid text-center text-white bg-dark py-5">UnikGAME TIENDA VIRTUAL</h1>
        <div style="display: flex; justify-content: space-between; align-items: center;">

            {% get_current_language as LANGUAGE_CODE %}
            {% get_available_languages as LANGUAGES %}
            {% get_language_info_list for LANGUAGES as languages %}
            <div class="languages">
                <p>{% trans "Idioma" %}:</p>
                <ul class="languages">
                    {% for language in languages %}
                    <li>
                        <a href="/{{ language.code }}/"{% if language.code == LANGUAGE_CODE %} class="selected"{% endif %}>
                            {{ language.name_local }}
                        </a>
                    </li>                    
                    {% endfor %}
                </ul>
            </div>
        </div>
        <div style="color: black; text-align: right; margin-right: 100px;">
            {% if user.is_authenticated %}
            Hola, {{user.username}}&nbsp;&nbsp;|&nbsp;&nbsp;

Para que quede algo más bonito añade lo siguiente al archivo style.css de la aplicación:

PracticaDjango/Proyecto_web_app/static/Proyecto_web_app/css/style.css

.languages ul {
     list-style:none;
     margin:0;
     padding:0;
     float:left;
 }

 .languages ul li {
    float:left;
    margin:0 0 0 10px;
    font-size:14px;
}

.languages p {
    float:left;
    margin:0;
}

.selected {
    color: black;
    background:#e1ebf1;
    border-radius:4px;
}

Asegúrate de que ninguna etiqueta de plantilla esté dividida en múltiples líneas. Así es como construyes tu selector de idioma:

1. Cargas las etiquetas de internacionalización usando {% load i18n %}.
2. Utilizas la etiqueta {% get_current_language %} para recuperar el idioma actual.
3. Obtienes los idiomas definidos en la configuración LANGUAGES utilizando la etiqueta de plantilla {% get_available_languages %}.
4. Utilizas la etiqueta {% get_language_info_list %} para proporcionar un acceso fácil a los atributos del idioma.
5. Construyes una lista HTML para mostrar todos los idiomas disponibles y añades un atributo de clase seleccionada al idioma activo actual.

En el código para el selector de idioma, utilizaste las etiquetas de plantilla proporcionadas por i18n, basadas en los idiomas disponibles en la configuración de tu proyecto. Ahora abre http://127.0.0.1:8000/ en tu navegador y echa un vistazo. Deberías ver el selector de idioma en la esquina superior derecha del sitio, como sigue:


Selección de idioma



Traduciendo los campos del modelo. 


Si necesitas traducir los objetos que se crean con el modelo puedes buscar información sobre los paquetes django-vinaigrette, django-parler o django-modeltranslation.


Localización de formato

Dependiendo de la configuración regional del usuario, es posible que desees mostrar fechas, horas y números en diferentes formatos. Esto puede activarse cambiando la configuración USE_L10N a True en el archivo settings.py de tu proyecto.

Cuando USE_L10N está habilitado, Django intentará usar un formato específico para la configuración regional cada vez que emita un valor en una plantilla. Puedes ver que los números decimales en la versión en inglés de tu sitio se muestran con un punto como separador de decimales, mientras que en la versión en español, se muestran utilizando una coma. Esto se debe a los formatos regionales especificados para la configuración regional 'es' por Django. Puedes echar un vistazo a la configuración de formato español en https://github.com/django/django/blob/stable/5.0.x/django/conf/locale/es/formats.py.

Normalmente, establecerás la configuración USE_L10N en True y dejarás que Django aplique la localización de formato para cada configuración regional. Sin embargo, puede haber situaciones en las que no quieras usar valores localizados. Esto es especialmente relevante al emitir JavaScript o JSON, que debe proporcionar un formato legible por máquina.

Django ofrece una etiqueta de plantilla {% localize %} que te permite activar/desactivar la localización para fragmentos de plantilla. Esto te da control sobre la formateación localizada. Deberás cargar las etiquetas l10n para poder usar esta etiqueta de plantilla. El siguiente es un ejemplo de cómo activar y desactivar la localización en una plantilla:

```

{% load l10n %}

{% localize on %}

{{ valor }}

{% endlocalize %}

{% localize off %}

{{ valor }}

{% endlocalize %}

```

Django también ofrece los filtros de plantilla localize y unlocalize para forzar o evitar la localización de un valor. Estos filtros se pueden aplicar de la siguiente manera:

```django

{{ valor|localize }}

{{ valor|unlocalize }}

```

También puedes crear archivos de formato personalizados para especificar el formato regional. Puedes encontrar más información sobre la localización de formato en https://docs.djangoproject.com/en/4.1/topics/i18n/formatting/.


Utilizando django-localflavor para validar campos de formulario


django-localflavor es un módulo de terceros que contiene una colección de utilidades, como campos de formulario o campos de modelo, que son específicos para cada país. Es muy útil para validar regiones locales, números de teléfono locales, números de documento de identidad, números de seguridad social, etc. El paquete está organizado en una serie de módulos nombrados según los códigos de país ISO 3166.

Instala django-localflavor usando el siguiente comando:

pip install django-localflavor

Edita el archivo settings.py de tu proyecto y añade localflavor al ajuste INSTALLED_APPS, de la siguiente manera:

```

INSTALLED_APPS = [

    # ...

    'localflavor',

]

```

Vas a añadir el campo de código postal de España para que se requiera un código postal válido de al crear un nuevo pedido.

Edita el archivo forms.py de la aplicación de pedidos y haz que se vea como sigue:

from django import forms
from localflavor.es.forms import ESPostalCodeField
from .models import Order
class OrderCreateForm(forms.ModelForm):
    codigo_postal = ESPostalCodeField()

    #...

Importas el campo ESPostalCodeField del paquete es de localflavor y lo utilizas para el campo código postal del formulario OrderCreateForm.

Ejecuta el servidor de desarrollo con el siguiente comando:

```

python manage.py runserver

```

Abre http://127.0.0.1:8000/en/orders/create/ en tu navegador. Rellena todos los campos, introduce un código postal de site números y luego envía el formulario. Obtendrás el siguiente error de validación, que es generado por ESPostalCodeField:

```

Introduce un código postal en el formato XXXXX

```

Esto es solo un breve ejemplo de cómo utilizar un campo personalizado de localflavor en tu propio proyecto con fines de validación. Los componentes locales proporcionados por localflavor son muy útiles para adaptar tu aplicación a países específicos. Puedes leer la documentación de django-localflavor y ver todos los componentes locales disponibles para cada país en https://django-localflavor.readthedocs.io/en/latest/.


No hay comentarios:

Publicar un comentario