Translations ============ The Smarter web console is translated with `gettext `__, the same way as Django itself. English is the source language of every message and the default. Each other language is a pair of ``.po`` message catalogs, which a translator fills in. The console is translated into Spanish and French. The Spanish catalogs are written in Latin American Spanish, under the generic ``es`` code, so that a browser that asks for any variant of Spanish (``es-MX``, ``es-419``, ``es-ES``) gets them. Django's ``LANG_INFO`` has no ``es-419``, so the language menu would describe an ``es-419`` language as ``es``, and post ``es`` back. The console's Django templates, its Python views, its React apps and its plain JavaScript all share this one pipeline, so a translator works with one set of files, in one format. The documentation at docs.smarter.sh, and the Smarter api's responses, are not translated. How a Page Chooses Its Language ------------------------------- Django's ``LocaleMiddleware`` activates a language for each request: the one in the language cookie, which the language menu sets, or else the best match for the browser's ``Accept-Language`` header, or else ``settings.LANGUAGE_CODE``, English. Only the languages in ``settings.LANGUAGES`` can be chosen. The page then renders in that language: - **Django templates** mark their text with ``{% translate %}`` and ``{% blocktranslate %}``, after ``{% load i18n %}``. ``base.html`` sets ```` to the active language. - **Python code** marks messages that it shows in the console, such as sign-in errors, with ``gettext()``, imported as ``_``. Api responses are not marked: their consumers are programs. - **The React apps** call ``gettext()`` and friends from ``@smarter/common``. - **Plain JavaScript** in ``smarter/static/js`` calls the global ``gettext()``. The last two get their messages from Django's `JavaScriptCatalog `__ view, at ``/jsi18n/``, which ``base.html`` loads before any other script. It serves the active language's ``djangojs`` messages, and defines ``window.django.gettext()``, ``ngettext()``, ``pgettext()``, ``interpolate()`` and the global functions of the same names. In tests and in Storybook there is no catalog, and ``@smarter/common`` falls back to the English source text. :doc:`Smarter Chat <../smarter-chat>`, the LLMClient prompt engineering workbench, is its own npm package, which also runs on websites that Django didn't render, so it has its own catalogs, in the same format and the same languages, in the smarter-chat repository. It follows the console's language through the ```` attribute of ``base.html``. See :doc:`Developing Smarter Chat <../smarter-chat/development>`. The language menu, in the user menu of the console's navbar, lists each language in its own name, and posts the choice to Django's ``set_language`` view at ``/i18n/setlang/``. It is shown only when ``settings.LANGUAGES`` has more than one language. The Message Catalogs -------------------- The catalogs are in ``smarter/smarter/locale//LC_MESSAGES/``: .. list-table:: :header-rows: 1 * - File - Messages * - ``django.po`` - The Django templates, and the Python code. * - ``djangojs.po`` - The React apps in ``smarter/react/packages``, and ``smarter/static/js``. Only the ``.po`` files are committed. The Docker image compiles them to the ``.mo`` files that Django reads, with ``manage.py compilemessages``. The English catalogs have empty translations, because gettext shows the source text of an untranslated message. Django's ``makemessages`` finds the messages in templates and Python code. The ``xgettext`` that it runs can't parse TypeScript and JSX, so ``smarter/react/i18n/extract.ts`` finds the React apps' and the static scripts' calls with the TypeScript compiler instead, and writes each distinct message as one plain JavaScript call to ``smarter/react/i18n/messages.js``. ``makemessages`` reads that file for the ``djangojs`` domain. Each message is preceded by a ``Translators:`` comment that lists the files that use it, which appears in ``djangojs.po``. A test, ``smarter/react/i18n/extract.test.ts``, fails when ``messages.js`` is out of date. Marking Text for Translation ---------------------------- Every message is a complete English sentence or label, written as a string literal, so that it can be extracted. Put values into it with named placeholders, after translating it, so that a translator can move them: .. code-block:: html+django {% load i18n %}

{% translate "API Keys" %}

{% blocktranslate with email=branding.support_email %}Email: {{ email }}{% endblocktranslate %}

.. code-block:: python from django.utils.translation import gettext as _ error_message = _("Invalid login attempt. Unknown user %(email)s") % {"email": email} .. code-block:: tsx import { gettext, interpolate, interpolateNodes, ngettext } from "@smarter/common";