How do I change the local path of a language in Django? - django

I have a web set up with 3 languages (['es', 'en', 'it']) ​​in Angular that works with the Django server.
Django by default has the English local path defined as /en, I want the url of the English configuration to be '/us' (by default it is '/ en'), I just want to change it for English, for 'es' or 'it' as it comes by default is fine.
I want the URL to look like this myurl.com/us in English, how would you recommend me to make this change?
The structure is the following:
-apps
--webapp
---templates
----webapp
-----en (inside index.html)
-----es (inside index.html)
-----it (inside index.html)
-conf
--settings.py
-middleware
--locale.py
conf.settings.py have this language configuration
LANGUAGES = (
('es', _('Spanish')),
('it', _('Italian')),
('en', _('English')),
)
LANGUAGE_CODE = 'es'
LANGUAGE_CODES = [language[0] for language in LANGUAGES]
Additionally I have configured a middleware to recognize the user's location and place the corresponding language code in the URL
from django.conf import settings
from .utils.geolocation import get_language_by_ip
cookie_name = settings.LANGUAGE_COOKIE_NAME
class LocalizationMiddleware(object):
def get_language_cookie(self, request):
return request.COOKIES.get(cookie_name)
def set_language_cookie(self, request, value):
request.COOKIES[cookie_name] = value
def get_i18n_url_language(self, request):
url = request.path.split('/')
if len(url) > 1 and len(url[1].split('-')[0]) == 2:
return url[1]
return None
def process_request(self, request):
language = self.get_i18n_url_language(request)
if language is not None and language not in settings.LANGUAGE_CODES:
return
if language is None:
language = self.get_language_cookie(request)
if language is None:
language = get_language_by_ip(request)
if language is None:
language = settings.LANGUAGE_CODE
self.set_language_cookie(request, language)
return None
def process_response(self, request, response):
response.set_cookie(cookie_name, request.COOKIES.get(cookie_name, settings.LANGUAGE_CODE))
return response
This all works fine, however I want the url for the English language to change from .../en to .../us

Related

Django: prefix/postfix language slug in i18n_urls

I have a django-cms site, that uses i18n_patterns in urls.py. This works, urls are built like /lang/here-starts-the-normal/etc/.
Now, I would like to have urls like this: /prefix-lang/here-starts.... As there will be a couple of country specific domains, this wille be like /ch-de/here-... for Switzerland/.ch domain, /us-en/here-starts.... for the states, and some more. So, when the url would be /ch-de/..., the LANGUAGE would still be de. Hope this is clear?
As the content is filled with existing LANGUAGES=(('de', 'DE'), ('en', 'EN'), ...), I cannot change LANGUAGES for every domain - no content would be found in the cms, modeltranslation, only to mention those two.
How can I prefix the language slug in i18n_patterns? Is it possible at all?
I think a way without hacking Django too much would be to use URL rewrite facility provided by the webserver you run, for example, for mod_wsgi you can use mod_rewrite, similar facility exists also for uWSGI.
You may need to also post-process the output from Django to make sure that any links are also correctly re-written to follow the new schema. Not the cleanest approach but seems doable.
Working example, though the country/language order is reversed (en-ch instead of ch-en), to have it as django expects it when trying to find a language (ie, setting language to "en-ch", it will find "en", if available).
This solution involves a modified LocaleMiddleware, i18n_patterns, LocaleRegexResolver. It supports no country, or a 2 char country code, setup with settings.SITE_COUNTRY. It works by changing urls to a lang-country mode, but the found language code in middleware will still be language only, 2 chars, and work perfectly with existing LANGUAGES, that contain 2 chars language codes.
custom_i18n_patterns.py - this just uses our new resolver, see below
from django.conf import settings
from ceco.resolvers import CountryLocaleRegexURLResolver
def country_i18n_patterns(*urls, **kwargs):
"""
Adds the language code prefix to every URL pattern within this
function. This may only be used in the root URLconf, not in an included
URLconf.
"""
if not settings.USE_I18N:
return list(urls)
prefix_default_language = kwargs.pop('prefix_default_language', True)
assert not kwargs, 'Unexpected kwargs for i18n_patterns(): %s' % kwargs
return [CountryLocaleRegexURLResolver(list(urls), prefix_default_language=prefix_default_language)]
resolvers.py
import re
from django.conf import settings
from django.urls import LocaleRegexURLResolver
from modeltranslation.utils import get_language
class CountryLocaleRegexURLResolver(LocaleRegexURLResolver):
"""
A URL resolver that always matches the active language code as URL prefix.
extended, to support custom country postfixes as well.
"""
#property
def regex(self):
language_code = get_language() or settings.LANGUAGE_CODE
if language_code not in self._regex_dict:
if language_code == settings.LANGUAGE_CODE and not self.prefix_default_language:
regex_string = ''
else:
# start country changes
country_postfix = ''
if getattr(settings, 'SITE_COUNTRY', None):
country_postfix = '-{}'.format(settings.SITE_COUNTRY)
regex_string = '^%s%s/' % (language_code, country_postfix)
# end country changes
self._regex_dict[language_code] = re.compile(regex_string, re.UNICODE)
return self._regex_dict[language_code]
middleware.py - only very few lines changed, but had to replace the complete process_response.
from django.middleware.locale import LocaleMiddleware
from django.conf import settings
from django.conf.urls.i18n import is_language_prefix_patterns_used
from django.http import HttpResponseRedirect
from django.urls import get_script_prefix, is_valid_path
from django.utils import translation
from django.utils.cache import patch_vary_headers
class CountryLocaleMiddleware(LocaleMiddleware):
"""
This is a very simple middleware that parses a request
and decides what translation object to install in the current
thread context. This allows pages to be dynamically
translated to the language the user desires (if the language
is available, of course).
"""
response_redirect_class = HttpResponseRedirect
def process_response(self, request, response):
language = translation.get_language()
language_from_path = translation.get_language_from_path(request.path_info)
urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF)
i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf)
if (response.status_code == 404 and not language_from_path and
i18n_patterns_used and prefixed_default_language):
# Maybe the language code is missing in the URL? Try adding the
# language prefix and redirecting to that URL.
# start country changes
language_country = language
if getattr(settings, 'SITE_COUNTRY', None):
language_country = '{}-{}'.format(language, settings.SITE_COUNTRY)
language_path = '/%s%s' % (language_country, request.path_info)
# end country changes!
path_valid = is_valid_path(language_path, urlconf)
path_needs_slash = (
not path_valid and (
settings.APPEND_SLASH and not language_path.endswith('/') and
is_valid_path('%s/' % language_path, urlconf)
)
)
if path_valid or path_needs_slash:
script_prefix = get_script_prefix()
# Insert language after the script prefix and before the
# rest of the URL
language_url = request.get_full_path(force_append_slash=path_needs_slash).replace(
script_prefix,
'%s%s/' % (script_prefix, language_country),
1
)
return self.response_redirect_class(language_url)
if not (i18n_patterns_used and language_from_path):
patch_vary_headers(response, ('Accept-Language',))
if 'Content-Language' not in response:
response['Content-Language'] = language
return response

How to replace LocaleRegexURLResolver from django 1.11

I am currently upgrading Django from 1.11 to 2.0 (to 2.2 in the long run). But recently I bumped into a problem which a can't solve for quiet some time. LocaleRegexURLResolver has been removed in 2.0 without any warning and the whole code has been refactored. Sadly we were using LocaleRegexURLResolver and I am not sure how to replicate it's functionality in the current version of Django. Any ideas?
class SiteLocaleRegexURLResolver(LocaleRegexURLResolver):
"""
Overrides LocaleRegexURLResolver to use specified default language
by site instead of global default language
"""
def __init__(
self, urlconf_name, site, default_kwargs=None, app_name=None, namespace=None,
prefix_default_language=True
):
super(LocaleRegexURLResolver, self).__init__(
None, urlconf_name, default_kwargs, app_name, namespace,
)
self.prefix_default_language = prefix_default_language
self.default_language = site.language_code
#property
def regex(self):
language_code = get_language() or self.default_language
if language_code not in self._regex_dict:
if language_code == self.default_language and not self.prefix_default_language:
regex_string = ''
else:
regex_string = '^%s/' % language_code
self._regex_dict[language_code] = re.compile(regex_string, re.UNICODE)
return self._regex_dict[language_code]
Basically it changes the default language. In UK, site /docs/ would be in english and /fr/docs/ in french. In FR on the other hand, /docs/ would be in french and /uk/docs/ in english

Take language code from session in Django

I use i18n_patterns
What should I do so that http://localhost:8000 didn't redirect to url prefixed with language code?
In addition, I want to supply language, taken from session, not request.LANGUAGE_CODE
I found following code:
class NoPrefixLocaleRegexURLResolver(LocaleRegexURLResolver):
#property
def regex(self):
language_code = get_language()
if language_code not in self._regex_dict:
regex_compiled = (re.compile('' % language_code, re.UNICODE)
if language_code == settings.LANGUAGE_CODE
else re.compile('^%s/' % language_code, re.UNICODE))
self._regex_dict[language_code] = regex_compiled
return self._regex_dict[language_code]
However, there is problem with that code in checking if language_code == settings.LANGUAGE_CODE. If I enter http://localchost:8000, it will not redirect, but supply a page with translation from settings.LANGUAGE_CODE instead of request.session.get('django_language'). As I understood, I can't access request, so what should be done?

Issues with multiple languages

I want my app will be available in multiple languages (let say two,one is default english and one more).
And these both options available in my home page and there must be a link shown which makes user able to select his choice of language.
I am reading the Django official documentation for this
so any one can let me know the general idea how I can do that.
and one more thing......in settings.py there is default LANGUAGE_CODE = 'en-us' given,BUT as I want my app in more then one language so How i can specify that country code here.
like this works LANGUAGE_CODE = 'en-us','es-MX (Spanish)' or I have to do it in some way.
And what is the purpose of this .po extension in this.
settings.py
LANGUAGE_CODE='en_us'
gettext = lambda s: s
LANGUAGES = (
('en', gettext('English')),
('de', gettext('German')),
)
MIDDLEWARE_CLASSES = (
...
'lang.SessionBasedLocaleMiddleware',
)
lang.py
from django.conf import settings
from django.utils.cache import patch_vary_headers
from django.utils import translation
class SessionBasedLocaleMiddleware(object):
"""
This Middleware saves the desired content language in the user session.
The SessionMiddleware has to be activated.
"""
def process_request(self, request):
if request.method == 'GET' and 'lang' in request.GET:
language = request.GET['lang']
request.session['language'] = language
elif 'language' in request.session:
language = request.session['language']
else:
language = translation.get_language_from_request(request)
for lang in settings.LANGUAGES:
if lang[0] == language:
translation.activate(language)
request.LANGUAGE_CODE = translation.get_language()
def process_response(self, request, response):
patch_vary_headers(response, ('Accept-Language',))
if 'Content-Language' not in response:
response['Content-Language'] = translation.get_language()
translation.deactivate()
return response
Access different languages http://example.com/?lang=de
And finaly let django create your .po files. Heres the documentation for that.
You want internationalization (or localization) of your software. With C it is often done thru gettext (which is related to .po files). Probably django uses these things.

Detect the language & django locale-url

I want to deploy a website in english & spanish and detect the user browser language & redirect to the correct locale site.
My site is www.elmalabarista.com
I install django-localeurl, but I discover that the language is not correctly detected.
This are my middlewares:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'multilingual.middleware.DefaultLanguageMiddleware',
'middleware.feedburner.FeedburnerMiddleware',
'lib.threadlocals.ThreadLocalsMiddleware',
'middleware.url.UrlMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'maintenancemode.middleware.MaintenanceModeMiddleware',
'middleware.redirect.RedirectMiddleware',
'openidconsumer.middleware.OpenIDMiddleware',
'django.middleware.doc.XViewMiddleware',
'middleware.ajax_errors.AjaxMiddleware',
'pingback.middleware.PingbackMiddleware',
'localeurl.middleware.LocaleURLMiddleware',
'multilingual.flatpages.middleware.FlatpageFallbackMiddleware',
'django.middleware.common.CommonMiddleware',
)
But ALWAYS the site get to US despite the fact my OS & Browser setup is spanish.
LANGUAGES = (
('en', ugettext('English')),
('es', ugettext('Spanish')),
)
DEFAULT_LANGUAGE = 1
Then, I hack the middleware of locale-url and do this:
def process_request(self, request):
locale, path = self.split_locale_from_request(request)
if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
locale = utils.supported_language(request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0])
locale_path = utils.locale_path(path, locale)
if locale_path != request.path_info:
if request.META.get("QUERY_STRING", ""):
locale_path = "%s?%s" % (locale_path,
request.META['QUERY_STRING'])
return HttpResponseRedirect(locale_path)
request.path_info = path
if not locale:
locale = settings.LANGUAGE_CODE
translation.activate(locale)
request.LANGUAGE_CODE = translation.get_language()
However, this detect fine the language but redirect the "en" urls to "es". So is impossible navigate in english.
UPDATE: This is the final code (after the input from Carl Meyer) with a fix for the case of "/":
def process_request(self, request):
locale, path = self.split_locale_from_request(request)
if (not locale) or (locale==''):
if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
locale = utils.supported_language(request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0])
else:
locale = settings.LANGUAGE_CODE
locale_path = utils.locale_path(path, locale)
if locale_path != request.path_info:
if request.META.get("QUERY_STRING", ""):
locale_path = "%s?%s" % (locale_path, request.META['QUERY_STRING'])
return HttpResponseRedirect(locale_path)
request.path_info = path
translation.activate(locale)
request.LANGUAGE_CODE = translation.get_language()
(Update: django-localeurl's LocaleURLMiddleware now directly supports HTTP Accept-Language as fallback, if LOCALEURL_USE_ACCEPT_LANGUAGE setting is True. So the OP's desired behavior is now available without writing a custom middleware).
It does not make sense to have both Django's built-in LocaleMiddleware and LocaleURLMiddleware enabled. They are intended as mutually exclusive alternatives, and have different logic for choosing a language. Locale-url does what it says on the tin: the locale is defined by a URL component (thus not by the Accept-Language header). Django's LocaleMiddleware will choose the language based on a session value or cookie or Accept-Language header. Enabling both just means that whichever one comes last wins, which is why you're seeing the LocaleURLMiddleware behavior.
It sounds like maybe you want some kind of mix of the two, where the initial language (when visiting the root URL of the site?) is chosen based on Accept-Language, and thereafter defined by the URL? It's not entirely clear what behavior you want, so clarifying that is the first step. Then you'll probably need to write your own LocaleMiddleware that implements that behavior. Your first attempt at hacking LocaleURLMiddleware always uses Accept-Language in place of what's defined in the URL. Instead, you want to check the Accept-Language header further down, in the "if not locale:" section where it defaults to settings.LANGUAGE_CODE. Something more like this (untested code):
def process_request(self, request):
locale, path = self.split_locale_from_request(request)
locale_path = utils.locale_path(path, locale)
if locale_path != request.path_info:
if request.META.get("QUERY_STRING", ""):
locale_path = "%s?%s" % (locale_path, request.META['QUERY_STRING'])
return HttpResponseRedirect(locale_path)
request.path_info = path
if not locale:
if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
locale = utils.supported_language(request.META['HTTP_ACCEPT_LANGUAGE'].split(',')[0])
else:
locale = settings.LANGUAGE_CODE
translation.activate(locale)
request.LANGUAGE_CODE = translation.get_language()
i would need this behaviour too.
are you using only your custom middleware now to get the language or are you still using the LocaleURLMiddleware and the LocaleMiddleware in combination with the middleware in the code above?
really it should be like this:
There can be multiple languages accepted by order of preference
def process_request(self, request):
locale, path = utils.strip_path(request.path_info)
if (not locale) or (locale==''):
if request.META.has_key('HTTP_ACCEPT_LANGUAGE'):
l = [x.strip()[:2] for x in request.META['HTTP_ACCEPT_LANGUAGE'].split(',')]
for lang_code in l:
locale = utils.supported_language(lang_code)
if locale:
break
else:
locale = settings.LANGUAGE_CODE
locale_path = utils.locale_path(path, locale)
if locale_path != request.path_info:
if request.META.get("QUERY_STRING", ""):
locale_path = "%s?%s" % (locale_path,
request.META['QUERY_STRING'])
return HttpResponseRedirect(locale_path)
request.path_info = path
if not locale:
try:
locale = request.LANGUAGE_CODE
except AttributeError:
locale = settings.LANGUAGE_CODE
translation.activate(locale)
request.LANGUAGE_CODE = translation.get_language()