Django:prefix/postfix i18n_urls 中的语言塞
Django: prefix/postfix language slug in i18n_urls
我有一个 django-cms 站点,它在 urls.py
中使用 i18n_patterns
。这行得通,url 的构建方式类似于 /lang/here-starts-the-normal/etc/
。
现在,我想要 url 是这样的:/prefix-lang/here-starts...
。由于将有几个特定于国家/地区的域,这将类似于瑞士/.ch 域的 /ch-de/here-...
,各州的 /us-en/here-starts....
,等等。因此,当 url 为 /ch-de/...
时,LANGUAGE
仍为 de
。希望这是清楚的?
由于内容充满了现有 LANGUAGES=(('de', 'DE'), ('en', 'EN'), ...)
,我无法为每个域更改 LANGUAGES
- 在 cms 和模型翻译中找不到任何内容,仅提及这两个。
如何在 i18n_patterns
中为语言别名添加前缀?有可能吗?
我认为一种不过度攻击 Django 的方法是使用 URL 您 运行 的网络服务器提供的重写工具,例如,对于 mod_wsgi
您可以使用 mod_rewrite, similar facility 也存在于 uWSGI
.
您可能还需要 post 处理 Django 的输出,以确保所有链接也被正确重写以遵循新模式。不是最干净的方法,但似乎可行。
工作示例,虽然 country/language 顺序颠倒了(en-ch
而不是 ch-en
),但在尝试查找语言(即设置将语言设置为“en-ch”,它将找到“en”(如果可用)。
此解决方案涉及修改后的 LocaleMiddleware
、i18n_patterns
、LocaleRegexResolver
。它不支持任何国家或 2 个字符的国家代码,设置为 settings.SITE_COUNTRY
。它通过将 url 更改为 lang-country
模式来工作,但在中间件中找到的语言代码仍将仅是语言,2 个字符,并且可以与包含 2 个字符语言代码的现有 LANGUAGES
完美配合。
custom_i18n_patterns.py - 这只是使用我们的新解析器,见下文
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 - 只更改了很少几行,但必须替换完整的 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
我有一个 django-cms 站点,它在 urls.py
中使用 i18n_patterns
。这行得通,url 的构建方式类似于 /lang/here-starts-the-normal/etc/
。
现在,我想要 url 是这样的:/prefix-lang/here-starts...
。由于将有几个特定于国家/地区的域,这将类似于瑞士/.ch 域的 /ch-de/here-...
,各州的 /us-en/here-starts....
,等等。因此,当 url 为 /ch-de/...
时,LANGUAGE
仍为 de
。希望这是清楚的?
由于内容充满了现有 LANGUAGES=(('de', 'DE'), ('en', 'EN'), ...)
,我无法为每个域更改 LANGUAGES
- 在 cms 和模型翻译中找不到任何内容,仅提及这两个。
如何在 i18n_patterns
中为语言别名添加前缀?有可能吗?
我认为一种不过度攻击 Django 的方法是使用 URL 您 运行 的网络服务器提供的重写工具,例如,对于 mod_wsgi
您可以使用 mod_rewrite, similar facility 也存在于 uWSGI
.
您可能还需要 post 处理 Django 的输出,以确保所有链接也被正确重写以遵循新模式。不是最干净的方法,但似乎可行。
工作示例,虽然 country/language 顺序颠倒了(en-ch
而不是 ch-en
),但在尝试查找语言(即设置将语言设置为“en-ch”,它将找到“en”(如果可用)。
此解决方案涉及修改后的 LocaleMiddleware
、i18n_patterns
、LocaleRegexResolver
。它不支持任何国家或 2 个字符的国家代码,设置为 settings.SITE_COUNTRY
。它通过将 url 更改为 lang-country
模式来工作,但在中间件中找到的语言代码仍将仅是语言,2 个字符,并且可以与包含 2 个字符语言代码的现有 LANGUAGES
完美配合。
custom_i18n_patterns.py - 这只是使用我们的新解析器,见下文
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 - 只更改了很少几行,但必须替换完整的 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