13

I want to make internationalization for my project. I followed how it is described in official documentation, but localization still doesn't work. Here is how I try get user locale:

def get_locale_name(request):
    """ Return the :term:`locale name` associated with the current
    request (possibly cached)."""
    locale_name = getattr(request, 'locale_name', None)
    if locale_name is None:
       locale_name = negotiate_locale_name(request)
       request.locale_name = locale_name
   return locale_name

But request doesn't have attr "local_name", but it has "Accept-Language" and so when function get_local_name doesn't find "local_name" in the request, it calls another function:

def negotiate_locale_name(request):
    """ Negotiate and return the :term:`locale name` associated with
    the current request (never cached)."""
    try:
        registry = request.registry
    except AttributeError:
        registry = get_current_registry()
    negotiator = registry.queryUtility(ILocaleNegotiator,
                                       default=default_locale_negotiator)
    locale_name = negotiator(request)

   if locale_name is None:
        settings = registry.settings or {}
        locale_name = settings.get('default_locale_name', 'en')

   return locale_name

How can I see negotiator try to get local from global environment but if it cant to do that its set value from config. And I cant understand why Pyramid doesn't get locale directly from request's field "Accept-Language"?

And, how can I make a correct determination of the locale?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Denis
  • 7,127
  • 8
  • 37
  • 58

3 Answers3

15

Pyramid doesn't dictate how a locale should be negotiated. Basing your site language on the "Accept-Language" header can cause problems as most users do not know how to set their preferred browser languages. Make sure your users can switch languages easily and use a cookie to store that preference for future visits.

You either need to set a _LOCALE_ key on the request (via an event handler, for example), or provide your own custom locale negotiator.

Here's an example using the NewRequest event and the accept_language header, which is an instance of the webob Accept class:

from pyramid.events import NewRequest
from pyramid.events import subscriber

@subscriber(NewRequest)
def setAcceptedLanguagesLocale(event):
    if not event.request.accept_language:
        return
    accepted = event.request.accept_language
    event.request._LOCALE_ = accepted.best_match(('en', 'fr', 'de'), 'en')
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • But there is no need create object accepted from accept_language caus accept_language is allready instance of Accept() and have method best_match() by itself. It is try for pyramid version 1.2 many thanks – Denis Jun 30 '12 at 18:43
  • 1
    @Denis: You are indeed correct; `accept_language` is already an instance of `Accept`. Corrected. – Martijn Pieters Jun 30 '12 at 21:40
  • user prefered language isn't as bad as it is. As for me, it apparently use the locale of my computer by default. So if my computer has for interface, russian language, it will place ru first in the list and then I guess it's pretty random since I never defined anything else – Loïc Faure-Lacroix Jul 01 '12 at 12:24
  • @LoïcFaure-Lacroix: It's a good starting point, but I've found that registering multiple domains (.no, .de, etc.) is often a better starting point. After that, let the user switch languages and store that as a cookie preference. But we digress from the point of the answer, and I might just remove my opinion on Accept-Language – Martijn Pieters Jul 01 '12 at 12:30
  • lots of ways to do that too. like using subdomains instead of TLD. or even virtual roots like /fr/sdffd/sdfsfd etc. still think that the subdomain is the one that is the easiest to handle. – Loïc Faure-Lacroix Jul 01 '12 at 12:32
  • @LoïcFaure-Lacroix: Yup; we generaly redirect a TLD to a language-specific root. – Martijn Pieters Jul 01 '12 at 12:35
  • on a side note, one has to make sure that language selection is done before any translation if you aren't using "lazy" translators. One might end up with text being not translated. NewRequest is quite good but make sure it is used first. – Loïc Faure-Lacroix Jul 01 '12 at 12:37
  • @LoïcFaure-Lacroix: Agreed, but that is all well outside the scope of this post here. – Martijn Pieters Jul 01 '12 at 12:38
2

Do note that the request._LOCALE_ works only because by default the locale negotiator is a default_locale_negotiator. If you have very complex checks, for example you have to fetch the user from DB if a cookie does not exist, the NewRequest handler does have overhead for requests that do not need any translations. For them you can also use a custom locale negotiator, like:

def my_locale_negotiator(request):
    if not hasattr(request, '_LOCALE_'):
        request._LOCALE_ = request.accept_language.best_match(
            ('en', 'fr', 'de'), 'en')

    return request._LOCALE_


from pyramid.config import Configurator
config = Configurator()
config.set_locale_negotiator(my_locale_negotiator)
0

As of pyramid 1.5, you can use these request properties to access current locale:

request.localizer an instance of Localizer

request.locale same as request.localizer.locale_name

Taha Jahangir
  • 4,774
  • 2
  • 42
  • 49