El libro de Django 1.0

18.4. Cómo descubre Django la preferencia de idioma

Una vez que has preparado tus traducciones — o, si solo deseas usar las que están incluidas en Django — necesitarás activar el sistema de traducción para tu aplicación.

Detrás de escena, Django tiene un modelo muy flexible para decidir qué idioma se usará — determinado a nivel de la instalación, para un usuario particular, o ambas.

Para configurar una preferencia de idioma a nivel de la instalación, fija LANGUAGE_CODE en tu archivo de configuración. Django usará este idioma como la traducción por omisión — la opción a seleccionarse en último término si ningún otro traductor encuentra una traducción.

Si todo lo que deseas hacer es ejecutar Django con tu idioma nativo y hay disponible un archivo de idioma para el mismo, simplemente asigna un valor a LANGUAGE_CODE.

Si deseas permitir que cada usuario individual especifique el idioma que ella o él prefiere, usa LocaleMiddleware. LocaleMiddleware permite la selección del idioma basado en datos incluidos en la petición. Personaliza el contenido para cada usuario.

Para usar LocaleMiddleware, agrega django.middleware.locale.LocaleMiddleware a tu variable de configuración MIDDLEWARE_CLASSES. Debido a que el orden de los middlewares es relevante, deberías seguir las siguientes guías:

  • Asegúrate de que se encuentre entre las primeras clases middleware instaladas.
  • Debe estar ubicado después de SessionMiddleware, esto es debido a que LocaleMiddleware usa datos de la sesión.
  • Si usas CacheMiddleware, coloca LocaleMiddleware después de este (de otra forma los usuarios podrían recibir contenido cacheado del locale equivocado).

Por ejemplo tu MIDDLEWARE_CLASSES podría verse como esta:

MIDDLEWARE_CLASSES = (
   'django.middleware.common.CommonMiddleware',
   'django.contrib.sessions.middleware.SessionMiddleware',
   'django.middleware.locale.LocaleMiddleware'
)

LocaleMiddleware intenta determinar la preferencia de idioma del usuario siguiendo el siguiente algoritmo:

  • Primero, busca una clave django_language en la sesión del usuario actual.
  • Se eso falla, busca una cookie llamada django_language.
  • Si eso falla, busca la cabecera HTTP Accept-Language. Esta cabecera es enviada por tu navegador y le indica al servidor qué idioma(s) prefieres en orden de prioridad. Django intenta con cada idioma que aparezca en dicha cabecera hasta que encuentra uno para el que haya disponible una traducción.
  • Si eso falla, usa la variable de configuración global LANGUAGE_CODE.

En cada uno de dichas ubicaciones, el formato esperado para la preferencia de idioma es el formato estándar, como una cadena. Por ejemplo, portugués de Brasil es pt-br. Si un idioma base está disponible pero el sub-idioma especificado no, Django usará el idioma base. Por ejemplo, si un usuario especifica de-at (alemán de Austria) pero Django solo tiene disponible de, usará de.

Sólo pueden seleccionarse idiomas que se encuentren listados en la variable de configuración LANGUAGES. Si deseas restringir la selección de idiomas a un subconjunto de los idiomas provistos (debido a que tu aplicación no incluye todos esos idiomas), fija tu LANGUAGES a una lista de idiomas, por ejemplo:

LANGUAGES = (
    ('de', _('German')),
    ('en', _('English')),
)

Este ejemplo restringe los idiomas que se encuentran disponibles para su selección automática a alemán e inglés (y cualquier sub-idioma, como de-ch o en-us).

Si defines un LANGUAGES personalizado es posible marcar los idiomas como cadenas de traducción — pero usa una función gettext() "boba", no la que se encuentra en django.utils.translation. Nunca debes importar django.utils.translation desde el archivo de configuración debido a que ese módulo a su vez depende de las variables de configuración, y eso crearía una importación circular.

La solución es usar una función gettext() muy simple. A continuación un archivo de configuración de ejemplo:

_ = lambda s: s

LANGUAGES = (
      ('de', _('German')),
      ('en', _('English')),
)

Con este esquema, make-messages.py todavía será capaz de encontrar y marcar dichas cadenas para su traducción pero la misma no ocurrirá en tiempo de ejecución, de manera que tendrás que recordar envolver los idiomas con la verdadera gettext() en cualquier código que use LANGUAGES en tiempo de ejecución.

El LocaleMiddleware sólo puede seleccionar idiomas para los cuales exista una traducción base provista por Django. Si deseas ofrecer traducciones para tu aplicación que no se encuentran en el conjunto de traducciones incluidas en el código fuente de Django, querrás proveer al menos traducciones básicas para ese idioma. Por ejemplo, Django usa identificadores de mensajes técnicos para traducir formatos de fechas y de horas — así que necesitarás al menos esas traducciones para que el sistema funcione correctamente.

Un buen punto de partida es copiar el archivo .po de inglés y traducir al menos los mensajes técnicos, y quizá también los mensajes de los validadores.

Los identificadores de mensajes técnicos son fácilmente reconocibles; están completamente en mayúsculas. No necesitas traducir los identificadores de mensajes como lo haces con otros mensajes; en cambio, deber proporcionar la variante local correcta del valor provisto en inglés. Por ejemplo, con DATETIME_FORMAT (o DATE_FORMAT o TIME_FORMAT), este sería la cadena de formato que deseas usar en tu idioma. El formato es idéntico al de la cadena de formato usado por la etiqueta de plantillas now.

Una vez que el LocaleMiddleware ha determinado la preferencia del usuario, la deja disponible como request.LANGUAGE_CODE para cada objeto petición. Eres libre de leer este valor en tu código de vista. A continuación un ejemplo simple:

def hello_world(request, count):
    if request.LANGUAGE_CODE == 'de-at':
        return HttpResponse("You prefer to read Austrian German.")
    else:
        return HttpResponse("You prefer to read another language.")

Nota que con traducción estática (en otras palabras sin middleware) el idioma está en settings.LANGUAGE_CODE, mientras que con traducción dinámica (con middleware) el mismo está en request.LANGUAGE_CODE.