This application provides a collection of URL-related utilities for multi-lingual projects.
- Localizable URLs - the same page can have a different url in a different language. (eg /products/ can be /produits/ in French)
- Language-in-Path - a replacement for Django's language cookie that makes URLs language-specific by storing the language code in the URL path.
- Language-in-Domain - a replacement for Django's language cookie that makes URLs language-specific by mapping each domain for the site onto a language.
Add
transurlvania
toINSTALLED_APPS
in your settings fileAdd the following middlewares to
MIDDLEWARE_CLASSES
in your settings file:transurlvania.middleware.URLCacheResetMiddleware
(must be before theSessionMiddleware
)This middleware is responsible for clearing the URLconf cache that Django populates. If this cache is not cleared, URL reversing will return all URLs for the language that was active when the cache was populated independent of the language that is currently active.
transurlvania.middleware.URLTransMiddleware
(must be before theCommonMiddleware
in order for APPEND_SLASH to work)
Add
transurlvania.context_processors.translate
toTEMPLATE_CONTEXT_PROCESSORS
.
Replace the usual:
from django.conf.urls.defaults import *
with:
from transurlvania.defaults import *
transurlvania.defaults
provides a custom url
function that works with both
translated and regular URLs. Also, a lang_prefixed_patterns
function is
provided. The rest of the functions imported from transurlvania.defaults are
identical to their counterparts in django.conf.urls.defaults
.
You will need the ugettext_noop function if you want to mark any URL patterns for localization:
from django.utils.translation import ugettext_noop as _
To make an URL pattern localizable, first ensure that it is in the
url(...)
form, then wrap the URL pattern itself in a gettext function
call:
url(_(r'^about-us/$'), 'about_us', name='about_us'),
Now, when you next run the makemessages
management command, these URL
patterns will be collected in the .po file along with all the other
localizable strings.
transurlvania will deal with the actual translating of these strings for URL resolving and reversing.
transurlvania will attempt to translate the entire pattern so the following will not work:
url(r'^' + _('articles') + '/' + _('archive') + '/$', ...)
Notes:
- because the strings in the po file are not raw strings, some regex characters will be escaped, so the URL patterns are sometimes less readable
- When providing a translation for a URL pattern that includes regex elements, ensure that the translation contains the same regex elements, otherwise the pattern matching behaviour may vary from language to language.
Any language-aware models that define get_absolute_url
should decorate it
with permalink_in_lang
, from transurlvania.decorators
so that the
returned URLs will be properly translated to the language of the object.
permalink_in_lang
accepts the same tuple values as permalink
except
that the language code to be used for the URL should be inserted between the
name of the view or URL and the view_args
parameter:
@permalink_in_lang def get_absolute_url(self): ('name_of_view_or_url', self.language, (), {})
Alternatively, reverse_for_language
from transurlvania.urlresolvers
can
be used as a replacement for django.core.urlresolvers.reverse
.
If the model is not tied to any specific language and has a URL for any
installed language, Django's built-in permalink
decorator or the reverse
function will return the correct, translated URL for the currently active
language. A call such as reverse(url_name, ...)
will have the same effect
as reverse_for_language(url_name, translation.get_language(), ...)
.
transurlvania provides two ways for making URLs language-specific, meaning that the URL itself will indicate what language to use when generating the response.
Add
transurlvania.middleware.LangInPathMiddleware
toMIDDLEWARE_CLASSES
afterLocaleMiddleware
.Make these changes to your root URL conf module:
- If you haven't already done so, replace
from django.conf.urls.defaults import *
withfrom transurlvania.defaults import *
. - Replace the call to
patterns
that populates theurlpatterns
variable with a call tolang_prefixed_patterns
. - To handle requests to the root URL itself ("/") or any URLs you wish to
keep outside of the language prefixing, declare the URL patterns as
normal inside a call to
patterns
and append the result to theurlpatterns
variable.
Here's an example of what a root URLconf might look like before adding language prefixing:
from django.contrib import admin from django.utils.translation import ugettext_noop as _ from transurlvania.defaults import * admin.autodiscover() urlpatterns = patterns('example.views', url(r'^$', 'home'), url(r'^admin/', include(admin.site.urls)), url(_(r'^about-us/$'), 'about_us', name='about_us'), )
And here's what it would look like after it's been converted:
from django.contrib import admin from django.utils.translation import ugettext_noop as _ from transurlvania.defaults import * admin.autodiscover() urlpatterns = lang_prefixed_patterns('example.views', url(r'^$', 'home'), url(r'^admin/', include(admin.site.urls)), url(_(r'^about-us/$'), 'about_us', name='about_us'), ) urlpatterns += patterns('example.views', url(r'^$', 'language_selection_splash'), )
- If you haven't already done so, replace
Add
transurlvania.middleware.LangInDomainMiddleware
toMIDDLEWARE_CLASSES
afterLocaleMiddleware
.Add
MULTILANG_LANGUAGE_DOMAINS
to the project's settings module.This settings should be a dictionary mapping language codes to two-element tuples, where the first element is the domain for that language, and the second element is the name of the site this represents.
Example:
MULTILANG_LANGUAGE_DOMAINS = { 'en': ('www.example-en.com', 'English Site'), 'fr': ('www.example-fr.com', 'French Site') }
Django's language switching view is incompatible with transurlvania's techniques for setting site language using the URL. transurlvania provides its own language switching tools that make it possible to link directly to the loaded page's alternate-language equivalent.
The main requirement for this functionality is that
transurlvania.middleware.URLTransMiddleware
is in MIDDLEWARE_CLASSES
, and
transurlvania.context_processors.translate
is in
TEMPLATE_CONTEXT_PROCESSORS
. With these installed you can then use the
this_page_in_lang
template tag to get the URL for the page currently being
viewed in the language requested.
So, {% this_page_in_lang "fr" %}
would return the URL to the French
version of the page being displayed.
The language switching code has two schemes for determining the URL to use:
1. If there's a variable named object
in the context, and that variable
implements a method named get_translation
, the switcher will call the
method with the requsted language, call get_absolute_url
on what's
returned and then use that URL for the translation.
2. If the first method fails, the switcher will call transurlvania's reverse_for_language function using the view name and the parameters that were resolved from the current request.
There are cases where neither of these schemes will work such as when the
object isn't named object
, or when the same view is used by multiple URLs.
In those cases, you can use the decorators provided by the translators
module to decorate the view and change which URL look-up scheme is used. You
can also define your own look-up schemes.
If your model is able to display itself in multiple languages as opposed to
retrieving and delegating to other, translated DB instances, the
get_translation
method should return a language specific wrapper object
whose get_absolute_url
method returns the URL of your model object in that
language:
class LangWrapper(object): def __init__(self, obj, lang): self.obj, self.lang = obj, lang def get_absolute_url(self): return self.obj.get_absolute_url(lang=self.lang) class MultiLangBlogPost(models.Model): slug_en = models.SlugField() slug_fr = models.SlugField() def get_translation(self, lang): return LangWrapper(self, lang) def get_absolute_url(self, lang=None): slug = self.get_translated('slug', lang) return reverse_for_language('blogpost_detail', lang, args=(slug, ))
transurlvania provides a mixin to do this for you, so only the extended
get_absolute_url
method needs to be implemented for language switching to
work and the rest will be automatic:
from transurlvania.utils import MultiLangModel class MultiLangBlogPost(MultiLangModel): slug_en = models.SlugField() slug_fr = models.SlugField() def get_absolute_url(self, lang=None): slug = self.get_translated('slug', lang) return reverse_for_language('blogpost_detail', lang, args=(slug, ))
The BlockLocaleMiddleware
will block non-admins from accessing the site in any language
listed in the BLOCKED_LANGUAGES
setting in the settings file.