.. index:: single: i18n single: l10n single: internationalization single: localization .. _i18n_chapter: Internationalization and Localization ===================================== :term:`Internationalization` (i18n) is the act of creating software with a user interface that can potentially be displayed in more than one language or cultural context. :term:`Localization` (l10n) is the process of displaying the user interface of an internationalized application in a *particular* language or cultural context. :app:`Pyramid` offers internationalization and localization subsystems that can be used to translate the text of buttons, error messages, and other software- and template-defined values into the native language of a user of your application. .. index:: single: translation string pair: domain; translation pair: msgid; translation single: message identifier Creating a Translation String ----------------------------- While you write your software, you can insert specialized markup into your Python code that makes it possible for the system to translate text values into the languages used by your application's users. This markup creates a :term:`translation string`. A translation string is an object that behaves mostly like a normal Unicode object, except that it also carries around extra information related to its job as part of the :app:`Pyramid` translation machinery. Using the ``TranslationString`` Class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The most primitive way to create a translation string is to use the :class:`pyramid.i18n.TranslationString` callable: .. code-block:: python :linenos: from pyramid.i18n import TranslationString ts = TranslationString('Add') This creates a Unicode-like object that is a TranslationString. .. note:: For people more familiar with :term:`Zope` i18n, a TranslationString is a lot like a ``zope.i18nmessageid.Message`` object. It is not a subclass, however. For people more familiar with :term:`Pylons` or :term:`Django` i18n, using a TranslationString is a lot like using "lazy" versions of related gettext APIs. The first argument to :class:`~pyramid.i18n.TranslationString` is the ``msgid``; it is required. It represents the key into the translation mappings provided by a particular localization. The ``msgid`` argument must be a Unicode object or an ASCII string. The msgid may optionally contain *replacement markers*. For instance: .. code-block:: python :linenos: from pyramid.i18n import TranslationString ts = TranslationString('Add ${number}') Within the string above, ``${number}`` is a replacement marker. It will be replaced by whatever is in the *mapping* for a translation string. The mapping may be supplied at the same time as the replacement marker itself: .. code-block:: python :linenos: from pyramid.i18n import TranslationString ts = TranslationString('Add ${number}', mapping={'number':1}) Any number of replacement markers can be present in the msgid value, any number of times. Only markers which can be replaced by the values in the *mapping* will be replaced at translation time. The others will not be interpolated and will be output literally. A translation string should also usually carry a *domain*. The domain represents a translation category to disambiguate it from other translations of the same msgid, in case they conflict. .. code-block:: python :linenos: from pyramid.i18n import TranslationString ts = TranslationString('Add ${number}', mapping={'number':1}, domain='form') The above translation string named a domain of ``form``. A :term:`translator` function will often use the domain to locate the right translator file on the filesystem which contains translations for a given domain. In this case, if it were trying to translate our msgid to German, it might try to find a translation from a :term:`gettext` file within a :term:`translation directory` like this one: .. code-block:: text locale/de/LC_MESSAGES/form.mo In other words, it would want to take translations from the ``form.mo`` translation file in the German language. Finally, the TranslationString constructor accepts a ``default`` argument. If a ``default`` argument is supplied, it replaces usages of the ``msgid`` as the *default value* for the translation string. When ``default`` is ``None``, the ``msgid`` value passed to a TranslationString is used as an implicit message identifier. Message identifiers are matched with translations in translation files, so it is often useful to create translation strings with "opaque" message identifiers unrelated to their default text: .. code-block:: python :linenos: from pyramid.i18n import TranslationString ts = TranslationString('add-number', default='Add ${number}', domain='form', mapping={'number':1}) When default text is used, Default text objects may contain replacement values. .. index:: single: translation string factory Using the ``TranslationStringFactory`` Class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Another way to generate a translation string is to use the :attr:`~pyramid.i18n.TranslationStringFactory` object. This object is a *translation string factory*. Basically a translation string factory presets the ``domain`` value of any :term:`translation string` generated by using it. For example: .. code-block:: python :linenos: from pyramid.i18n import TranslationStringFactory _ = TranslationStringFactory('pyramid') ts = _('add-number', default='Add ${number}', mapping={'number':1}) .. note:: We assigned the translation string factory to the name ``_``. This is a convention which will be supported by translation file generation tools. After assigning ``_`` to the result of a :func:`~pyramid.i18n.TranslationStringFactory`, the subsequent result of calling ``_`` will be a :class:`~pyramid.i18n.TranslationString` instance. Even though a ``domain`` value was not passed to ``_`` (as would have been necessary if the :class:`~pyramid.i18n.TranslationString` constructor were used instead of a translation string factory), the ``domain`` attribute of the resulting translation string will be ``pyramid``. As a result, the previous code example is completely equivalent (except for spelling) to: .. code-block:: python :linenos: from pyramid.i18n import TranslationString as _ ts = _('add-number', default='Add ${number}', mapping={'number':1}, domain='pyramid') You can set up your own translation string factory much like the one provided above by using the :class:`~pyramid.i18n.TranslationStringFactory` class. For example, if you'd like to create a translation string factory which presets the ``domain`` value of generated translation strings to ``form``, you'd do something like this: .. code-block:: python :linenos: from pyramid.i18n import TranslationStringFactory _ = TranslationStringFactory('form') ts = _('add-number', default='Add ${number}', mapping={'number':1}) Creating a unique domain for your application via a translation string factory is best practice. Using your own unique translation domain allows another person to reuse your application without needing to merge your translation files with their own. Instead they can just include your package's :term:`translation directory` via the :meth:`pyramid.config.Configurator.add_translation_dirs` method. .. note:: For people familiar with Zope internationalization, a TranslationStringFactory is a lot like a ``zope.i18nmessageid.MessageFactory`` object. It is not a subclass, however. .. index:: single: gettext single: translation directories Working with ``gettext`` Translation Files ------------------------------------------ The basis of :app:`Pyramid` translation services is GNU :term:`gettext`. Once your application source code files and templates are marked up with translation markers, you can work on translations by creating various kinds of gettext files. .. note:: The steps a developer must take to work with :term:`gettext` :term:`message catalog` files within a :app:`Pyramid` application are very similar to the steps a :term:`Pylons` developer must take to do the same. See the :ref:`Pylons Internationalization and Localization documentation ` for more information. GNU gettext uses three types of files in the translation framework, ``.pot`` files, ``.po`` files, and ``.mo`` files. ``.pot`` (Portable Object Template) files A ``.pot`` file is created by a program which searches through your project's source code and which picks out every :term:`message identifier` passed to one of the ``_()`` functions (e.g., :term:`translation string` constructions). The list of all message identifiers is placed into a ``.pot`` file, which serves as a template for creating ``.po`` files. ``.po`` (Portable Object) files The list of messages in a ``.pot`` file are translated by a human to a particular language; the result is saved as a ``.po`` file. ``.mo`` (Machine Object) files A ``.po`` file is turned into a machine-readable binary file, which is the ``.mo`` file. Compiling the translations to machine code makes the localized program start faster. The tools for working with :term:`gettext` translation files related to a :app:`Pyramid` application are :term:`Lingua` and :term:`Gettext`. Lingua can scrape i18n references out of Python and Chameleon files and create the ``.pot`` file. Gettext includes ``msgmerge`` tool to update a ``.po`` file from an updated ``.pot`` file and ``msgfmt`` to compile ``.po`` files to ``.mo`` files. .. index:: single: Gettext single: Lingua .. _installing_babel: Installing Lingua and Gettext ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order for the commands related to working with ``gettext`` translation files to work properly, you will need to have :term:`Lingua` and :term:`Gettext` installed into the same environment in which :app:`Pyramid` is installed. Installation on UNIX ++++++++++++++++++++ Gettext is often already installed on UNIX systems. You can check if it is installed by testing if the ``msgfmt`` command is available. If it is not available you can install it through the packaging system from your OS; the package name is almost always ``gettext``. For example on a Debian or Ubuntu system run this command: .. code-block:: bash $ sudo apt-get install gettext Installing Lingua is done with the Python packaging tools. If the :term:`virtual environment` into which you've installed your :app:`Pyramid` application lives at the environment variable ``$VENV``, you can install Lingua like so: .. code-block:: bash $ $VENV/bin/pip install lingua Installation on Windows +++++++++++++++++++++++ There are several ways to install Gettext on Windows: it is included in the `Cygwin `_ collection, or you can use the `installer from the GnuWin32 `_, or compile it yourself. Make sure the installation path is added to your ``$PATH``. Installing Lingua is done with the Python packaging tools. If the :term:`virtual environment` into which you've installed your :app:`Pyramid` application lives at the environment variable ``%VENV%``, you can install Lingua like so: .. code-block:: doscon c:\> %VENV%\Scripts\pip install lingua .. index:: pair: extracting; messages .. _extracting_messages: Extracting Messages from Code and Templates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once Lingua is installed, you may extract a message catalog template from the code and :term:`Chameleon` templates which reside in your :app:`Pyramid` application. You run a ``pot-create`` command to extract the messages: .. code-block:: bash $ cd /file/path/to/myapplication_setup.py $ mkdir -p myapplication/locale $ $VENV/bin/pot-create -o myapplication/locale/myapplication.pot src The message catalog ``.pot`` template will end up in ``myapplication/locale/myapplication.pot``. .. index:: pair: initializing; message catalog Initializing a Message Catalog File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once you've extracted messages into a ``.pot`` file (see :ref:`extracting_messages`), to begin localizing the messages present in the ``.pot`` file, you need to generate at least one ``.po`` file. A ``.po`` file represents translations of a particular set of messages to a particular locale. Initialize a ``.po`` file for a specific locale from a pre-generated ``.pot`` template by using the ``msginit`` command from Gettext: .. code-block:: bash $ cd /file/path/to/myapplication_setup.py $ cd myapplication/locale $ mkdir -p es/LC_MESSAGES $ msginit -l es -o es/LC_MESSAGES/myapplication.po This will create a new message catalog ``.po`` file in ``myapplication/locale/es/LC_MESSAGES/myapplication.po``. Once the file is there, it can be worked on by a human translator. One tool which may help with this is `Poedit `_. Note that :app:`Pyramid` itself ignores the existence of all ``.po`` files. For a running application to have translations available, a ``.mo`` file must exist. See :ref:`compiling_message_catalog`. .. index:: pair: updating; message catalog Updating a Catalog File ~~~~~~~~~~~~~~~~~~~~~~~ If more translation strings are added to your application, or translation strings change, you will need to update existing ``.po`` files based on changes to the ``.pot`` file, so that the new and changed messages can also be translated or re-translated. First, regenerate the ``.pot`` file as per :ref:`extracting_messages`. Then use the ``msgmerge`` command from Gettext. .. code-block:: bash $ cd /file/path/to/myapplication_setup.py $ cd myapplication/locale $ msgmerge --update es/LC_MESSAGES/myapplication.po myapplication.pot .. index:: pair: compiling; message catalog .. _compiling_message_catalog: Compiling a Message Catalog File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Finally, to prepare an application for performing actual runtime translations, compile ``.po`` files to ``.mo`` files using the ``msgfmt`` command from Gettext: .. code-block:: bash $ cd /file/path/to/myapplication_setup.py $ msgfmt -o myapplication/locale/es/LC_MESSAGES/myapplication.mo \ myapplication/locale/es/LC_MESSAGES/myapplication.po This will create a ``.mo`` file for each ``.po`` file in your application. As long as the :term:`translation directory` in which the ``.mo`` file ends up in is configured into your application (see :ref:`adding_a_translation_directory`), these translations will be available to :app:`Pyramid`. .. index:: single: localizer single: translation single: pluralization Using a Localizer ----------------- A :term:`localizer` is an object that allows you to perform translation or pluralization "by hand" in an application. You may use the :attr:`pyramid.request.Request.localizer` attribute to obtain a :term:`localizer`. The localizer object will be configured to produce translations implied by the active :term:`locale negotiator`, or a default localizer object if no explicit locale negotiator is registered. .. code-block:: python :linenos: def aview(request): localizer = request.localizer .. note:: If you need to create a localizer for a locale, use the :func:`pyramid.i18n.make_localizer` function. .. index:: single: translating (i18n) .. _performing_a_translation: Performing a Translation ~~~~~~~~~~~~~~~~~~~~~~~~ A :term:`localizer` has a ``translate`` method which accepts either a :term:`translation string` or a Unicode string and which returns a Unicode object representing the translation. Generating a translation in a view component of an application might look like so: .. code-block:: python :linenos: from pyramid.i18n import TranslationString ts = TranslationString('Add ${number}', mapping={'number':1}, domain='pyramid') def aview(request): localizer = request.localizer translated = localizer.translate(ts) # translation string # ... use translated ... The ``request.localizer`` attribute will be a :class:`pyramid.i18n.Localizer` object bound to the locale name represented by the request. The translation returned from its :meth:`pyramid.i18n.Localizer.translate` method will depend on the ``domain`` attribute of the provided translation string as well as the locale of the localizer. .. note:: If you're using :term:`Chameleon` templates, you don't need to pre-translate translation strings this way. See :ref:`chameleon_translation_strings`. .. index:: single: pluralizing (i18n) .. _performing_a_pluralization: Performing a Pluralization ~~~~~~~~~~~~~~~~~~~~~~~~~~ A :term:`localizer` has a ``pluralize`` method with the following signature: .. code-block:: python :linenos: def pluralize(singular, plural, n, domain=None, mapping=None): ... The simplest case is the ``singular`` and ``plural`` arguments being passed as Unicode literals. This returns the appropriate literal according to the locale pluralization rules for the number ``n``, and interpolates ``mapping``. .. code-block:: python :linenos: def aview(request): localizer = request.localizer translated = localizer.pluralize('Item', 'Items', 1, 'mydomain') # ... use translated ... However, for support of other languages, the ``singular`` argument should be a Unicode value representing a :term:`message identifier`. In this case the ``plural`` value is ignored. ``domain`` should be a :term:`translation domain`, and ``mapping`` should be a dictionary that is used for *replacement value* interpolation of the translated string. The value of ``n`` will be used to find the appropriate plural form for the current language, and ``pluralize`` will return a Unicode translation for the message id ``singular``. The message file must have defined ``singular`` as a translation with plural forms. The argument provided as ``singular`` may be a :term:`translation string` object, but the domain and mapping information attached is ignored. .. code-block:: python :linenos: def aview(request): localizer = request.localizer num = 1 translated = localizer.pluralize('item_plural', '${number} items', num, 'mydomain', mapping={'number':num}) The corresponding message catalog must have language plural definitions and plural alternatives set. .. code-block:: text :linenos: "Plural-Forms: nplurals=3; plural=n==0 ? 0 : n==1 ? 1 : 2;" msgid "item_plural" msgid_plural "" msgstr[0] "No items" msgstr[1] "${number} item" msgstr[2] "${number} items" More information on complex plurals can be found in the `gettext documentation `_. .. index:: single: locale name single: negotiate_locale_name .. _obtaining_the_locale_name: Obtaining the Locale Name for a Request --------------------------------------- You can obtain the locale name related to a request by using the :func:`pyramid.request.Request.locale_name` attribute of the request. .. code-block:: python :linenos: def aview(request): locale_name = request.locale_name The locale name of a request is dynamically computed; it will be the locale name negotiated by the currently active :term:`locale negotiator`, or the :term:`default locale name` if the locale negotiator returns ``None``. You can change the default locale name by changing the ``pyramid.default_locale_name`` setting. See :ref:`default_locale_name_setting`. Once :func:`~pyramid.request.Request.locale_name` is first run, the locale name is stored on the request object. Subsequent calls to :func:`~pyramid.request.Request.locale_name` will return the stored locale name without invoking the :term:`locale negotiator`. To avoid this caching, you can use the :func:`pyramid.i18n.negotiate_locale_name` function: .. code-block:: python :linenos: from pyramid.i18n import negotiate_locale_name def aview(request): locale_name = negotiate_locale_name(request) You can also obtain the locale name related to a request using the ``locale_name`` attribute of a :term:`localizer`. .. code-block:: python :linenos: def aview(request): localizer = request.localizer locale_name = localizer.locale_name Obtaining the locale name as an attribute of a localizer is equivalent to obtaining a locale name by asking for the :func:`~pyramid.request.Request.locale_name` attribute. .. index:: single: date and currency formatting (i18n) single: Babel Performing Date Formatting and Currency Formatting -------------------------------------------------- :app:`Pyramid` does not itself perform date and currency formatting for different locales. However, :term:`Babel` can help you do this via the :class:`babel.core.Locale` class. The `Babel documentation for this class `_ provides minimal information about how to perform date and currency related locale operations. See :ref:`installing_babel` for information about how to install Babel. The :class:`babel.core.Locale` class requires a :term:`locale name` as an argument to its constructor. You can use :app:`Pyramid` APIs to obtain the locale name for a request to pass to the :class:`babel.core.Locale` constructor. See :ref:`obtaining_the_locale_name`. For example: .. code-block:: python :linenos: from babel.core import Locale def aview(request): locale_name = request.locale_name locale = Locale(locale_name) .. index:: pair: translation strings; Chameleon .. _chameleon_translation_strings: Chameleon Template Support for Translation Strings -------------------------------------------------- When a :term:`translation string` is used as the subject of textual rendering by a :term:`Chameleon` template renderer, it will automatically be translated to the requesting user's language if a suitable translation exists. This is true of both the ZPT and text variants of the Chameleon template renderers. For example, in a Chameleon ZPT template, the translation string represented by "some_translation_string" in each example below will go through translation before being rendered: .. code-block:: xml :linenos: .. code-block:: xml :linenos: .. code-block:: xml :linenos: ${some_translation_string} .. code-block:: xml :linenos: Click here .. XXX the last example above appears to not yet work as of Chameleon .. 1.2.3 The features represented by attributes of the ``i18n`` namespace of Chameleon will also consult the :app:`Pyramid` translations. See http://chameleon.readthedocs.org/en/latest/reference.html#translation-i18n. .. note:: Unlike when Chameleon is used outside of :app:`Pyramid`, when it is used *within* :app:`Pyramid`, it does not support use of the ``zope.i18n`` translation framework. Applications which use :app:`Pyramid` should use the features documented in this chapter rather than ``zope.i18n``. Third party :app:`Pyramid` template renderers might not provide this support out of the box and may need special code to do an equivalent. For those, you can always use the more manual translation facility described in :ref:`performing_a_translation`. .. index:: single: Mako i18n Mako Pyramid i18n Support ------------------------- There exists a recipe within the :term:`Pyramid Community Cookbook` named :ref:`Mako Internationalization ` which explains how to add idiomatic i18n support to :term:`Mako` templates. .. index:: single: Jinja2 i18n Jinja2 Pyramid i18n Support --------------------------- The add-on `pyramid_jinja2 `_ provides a scaffold with an example of how to use internationalization with Jinja2 in Pyramid. See the documentation sections `Internalization (i18n) `_ and `Paster Template I18N `_. .. index:: single: localization deployment settings single: default_locale_name .. _localization_deployment_settings: Localization-Related Deployment Settings ---------------------------------------- A :app:`Pyramid` application will have a ``pyramid.default_locale_name`` setting. This value represents the :term:`default locale name` used when the :term:`locale negotiator` returns ``None``. Pass it to the :mod:`~pyramid.config.Configurator` constructor at startup time: .. code-block:: python :linenos: from pyramid.config import Configurator config = Configurator(settings={'pyramid.default_locale_name':'de'}) You may alternately supply a ``pyramid.default_locale_name`` via an application's ``.ini`` file: .. code-block:: ini :linenos: [app:main] use = egg:MyProject pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.default_locale_name = de If this value is not supplied via the Configurator constructor or via a config file, it will default to ``en``. If this setting is supplied within the :app:`Pyramid` application ``.ini`` file, it will be available as a settings key: .. code-block:: python :linenos: from pyramid.threadlocal import get_current_registry settings = get_current_registry().settings default_locale_name = settings['pyramid.default_locale_name'] .. index:: single: detecting languages "Detecting" Available Languages ------------------------------- Other systems provide an API that returns the set of "available languages" as indicated by the union of all languages in all translation directories on disk at the time of the call to the API. It is by design that :app:`Pyramid` doesn't supply such an API. Instead the application itself is responsible for knowing the "available languages". The rationale is this: any particular application deployment must always know which languages it should be translatable to anyway, regardless of which translation files are on disk. Here's why: it's not a given that because translations exist in a particular language within the registered set of translation directories that this particular deployment wants to allow translation to that language. For example, some translations may exist but they may be incomplete or incorrect. Or there may be translations to a language but not for all translation domains. Any nontrivial application deployment will always need to be able to selectively choose to allow only some languages even if that set of languages is smaller than all those detected within registered translation directories. The easiest way to allow for this is to make the application entirely responsible for knowing which languages are allowed to be translated to instead of relying on the framework to divine this information from translation directory file info. You can set up a system to allow a deployer to select available languages based on convention by using the :mod:`pyramid.settings` mechanism. Allow a deployer to modify your application's ``.ini`` file: .. code-block:: ini :linenos: [app:main] use = egg:MyProject # ... available_languages = fr de en ru Then as a part of the code of a custom :term:`locale negotiator`: .. code-block:: python :linenos: from pyramid.settings import aslist def my_locale_negotiator(request): languages = aslist(request.registry.settings['available_languages']) # ... This is only a suggestion. You can create your own "available languages" configuration scheme as necessary. .. index:: pair: translation; activating pair: locale; negotiator single: translation directory .. index:: pair: activating; translation .. _activating_translation: Activating Translation ---------------------- By default, a :app:`Pyramid` application performs no translation. To turn translation on you must: - add at least one :term:`translation directory` to your application. - ensure that your application sets the :term:`locale name` correctly. .. index:: pair: translation directory; adding .. _adding_a_translation_directory: Adding a Translation Directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :term:`gettext` is the underlying machinery behind the :app:`Pyramid` translation machinery. A translation directory is a directory organized to be useful to :term:`gettext`. A translation directory usually includes a listing of language directories, each of which itself includes an ``LC_MESSAGES`` directory. Each ``LC_MESSAGES`` directory should contain one or more ``.mo`` files. Each ``.mo`` file represents a :term:`message catalog`, which is used to provide translations to your application. Adding a :term:`translation directory` registers all of its constituent :term:`message catalog` files within your :app:`Pyramid` application to be available to use for translation services. This includes all of the ``.mo`` files found within all ``LC_MESSAGES`` directories within each locale directory in the translation directory. You can add a translation directory imperatively by using the :meth:`pyramid.config.Configurator.add_translation_dirs` during application startup. For example: .. code-block:: python :linenos: from pyramid.config import Configurator config.add_translation_dirs('my.application:locale/', 'another.application:locale/') A message catalog in a translation directory added via :meth:`~pyramid.config.Configurator.add_translation_dirs` will be merged into translations from a message catalog added earlier if both translation directories contain translations for the same locale and :term:`translation domain`. .. index:: pair: setting; locale Setting the Locale ~~~~~~~~~~~~~~~~~~ When the *default locale negotiator* (see :ref:`default_locale_negotiator`) is in use, you can inform :app:`Pyramid` of the current locale name by doing any of these things before any translations need to be performed: - Set the ``_LOCALE_`` attribute of the request to a valid locale name (usually directly within view code), e.g., ``request._LOCALE_ = 'de'``. - Ensure that a valid locale name value is in the ``request.params`` dictionary under the key named ``_LOCALE_``. This is usually the result of passing a ``_LOCALE_`` value in the query string or in the body of a form post associated with a request. For example, visiting ``http://my.application?_LOCALE_=de``. - Ensure that a valid locale name value is in the ``request.cookies`` dictionary under the key named ``_LOCALE_``. This is usually the result of setting a ``_LOCALE_`` cookie in a prior response, e.g., ``response.set_cookie('_LOCALE_', 'de')``. .. note:: If this locale negotiation scheme is inappropriate for a particular application, you can configure a custom :term:`locale negotiator` function into that application as required. See :ref:`custom_locale_negotiator`. .. index:: single: locale negotiator .. _locale_negotiators: Locale Negotiators ------------------ A :term:`locale negotiator` informs the operation of a :term:`localizer` by telling it what :term:`locale name` is related to a particular request. A locale negotiator is a bit of code which accepts a request and which returns a :term:`locale name`. It is consulted when :meth:`pyramid.i18n.Localizer.translate` or :meth:`pyramid.i18n.Localizer.pluralize` is invoked. It is also consulted when :func:`~pyramid.request.Request.locale_name` is accessed or when :func:`~pyramid.i18n.negotiate_locale_name` is invoked. .. _default_locale_negotiator: The Default Locale Negotiator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most applications can make use of the default locale negotiator, which requires no additional coding or configuration. The default locale negotiator implementation named :class:`~pyramid.i18n.default_locale_negotiator` uses the following set of steps to determine the locale name. - First the negotiator looks for the ``_LOCALE_`` attribute of the request object (possibly set directly by view code or by a listener for an :term:`event`). - Then it looks for the ``request.params['_LOCALE_']`` value. - Then it looks for the ``request.cookies['_LOCALE_']`` value. - If no locale can be found via the request, it falls back to using the :term:`default locale name` (see :ref:`localization_deployment_settings`). - Finally if the default locale name is not explicitly set, it uses the locale name ``en``. .. _custom_locale_negotiator: Using a Custom Locale Negotiator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Locale negotiation is sometimes policy-laden and complex. If the (simple) default locale negotiation scheme described in :ref:`activating_translation` is inappropriate for your application, you may create a special :term:`locale negotiator`. Subsequently you may override the default locale negotiator by adding your newly created locale negotiator to your application's configuration. A locale negotiator is simply a callable which accepts a request and returns a single :term:`locale name` or ``None`` if no locale can be determined. Here's an implementation of a simple locale negotiator: .. code-block:: python :linenos: def my_locale_negotiator(request): locale_name = request.params.get('my_locale') return locale_name If a locale negotiator returns ``None``, it signifies to :app:`Pyramid` that the default application locale name should be used. You may add your newly created locale negotiator to your application's configuration by passing an object which can act as the negotiator (or a :term:`dotted Python name` referring to the object) as the ``locale_negotiator`` argument of the :class:`~pyramid.config.Configurator` instance during application startup. For example: .. code-block:: python :linenos: from pyramid.config import Configurator config = Configurator(locale_negotiator=my_locale_negotiator) Alternatively, use the :meth:`pyramid.config.Configurator.set_locale_negotiator` method. For example: .. code-block:: python :linenos: from pyramid.config import Configurator config = Configurator() config.set_locale_negotiator(my_locale_negotiator)