mirror of
https://github.com/zulip/zulip.git
synced 2025-11-04 22:13:26 +00:00
Enable i18n support in URL configuration.
This supports i18n using all of the following: - I18N urls - Session - Cookie - HTTP header
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
|
|
||||||
urlpatterns = patterns('analytics.views',
|
i18n_urlpatterns = [
|
||||||
url(r'^activity$', 'get_activity'),
|
url(r'^activity$', 'analytics.views.get_activity'),
|
||||||
url(r'^realm_activity/(?P<realm>[\S]+)/$', 'get_realm_activity'),
|
url(r'^realm_activity/(?P<realm>[\S]+)/$', 'analytics.views.get_realm_activity'),
|
||||||
url(r'^user_activity/(?P<email>[\S]+)/$', 'get_user_activity'),
|
url(r'^user_activity/(?P<email>[\S]+)/$', 'analytics.views.get_user_activity'),
|
||||||
)
|
]
|
||||||
|
|
||||||
|
urlpatterns = patterns('', *i18n_urlpatterns)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.conf.urls import patterns, url
|
from django.conf.urls import patterns, url
|
||||||
from django.views.generic import TemplateView, RedirectView
|
from django.views.generic import TemplateView, RedirectView
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
i18n_urlpatterns = [
|
||||||
# Zephyr/MIT
|
# Zephyr/MIT
|
||||||
url(r'^zephyr/$', TemplateView.as_view(template_name='corporate/zephyr.html')),
|
url(r'^zephyr/$', TemplateView.as_view(template_name='corporate/zephyr.html')),
|
||||||
url(r'^mit/$', TemplateView.as_view(template_name='corporate/mit.html')),
|
url(r'^mit/$', TemplateView.as_view(template_name='corporate/mit.html')),
|
||||||
@@ -11,5 +11,6 @@ urlpatterns = patterns('',
|
|||||||
url(r'^terms/$', TemplateView.as_view(template_name='corporate/terms.html')),
|
url(r'^terms/$', TemplateView.as_view(template_name='corporate/terms.html')),
|
||||||
url(r'^terms-enterprise/$', TemplateView.as_view(template_name='corporate/terms-enterprise.html')),
|
url(r'^terms-enterprise/$', TemplateView.as_view(template_name='corporate/terms-enterprise.html')),
|
||||||
url(r'^privacy/$', TemplateView.as_view(template_name='corporate/privacy.html')),
|
url(r'^privacy/$', TemplateView.as_view(template_name='corporate/privacy.html')),
|
||||||
|
]
|
||||||
|
|
||||||
)
|
urlpatterns = patterns('', *i18n_urlpatterns)
|
||||||
|
|||||||
@@ -63,3 +63,34 @@ You can instead use:
|
|||||||
[Handlebars]: http://handlebarsjs.com/
|
[Handlebars]: http://handlebarsjs.com/
|
||||||
[trans]: http://jinja.pocoo.org/docs/dev/templates/#i18n
|
[trans]: http://jinja.pocoo.org/docs/dev/templates/#i18n
|
||||||
[blocktrans]: https://docs.djangoproject.com/en/1.8/topics/i18n/translation/#std:templatetag-blocktrans
|
[blocktrans]: https://docs.djangoproject.com/en/1.8/topics/i18n/translation/#std:templatetag-blocktrans
|
||||||
|
|
||||||
|
## Testing Translations
|
||||||
|
|
||||||
|
First of all make sure that you have compiled the translation strings
|
||||||
|
using `python manage.py compilemessages`.
|
||||||
|
|
||||||
|
Django figures out the effective language by going through the
|
||||||
|
following steps:
|
||||||
|
|
||||||
|
1. It looks for the language code in the url.
|
||||||
|
2. It loooks for the LANGUGE_SESSION_KEY key in the current user's
|
||||||
|
session.
|
||||||
|
3. It looks for the cookie named 'django_language'. You can set a
|
||||||
|
different name through LANGUAGE_COOKIE_NAME setting.
|
||||||
|
4. It looks for the `Accept-Language` HTTP header in the HTTP request.
|
||||||
|
Normally your browser will take care of this.
|
||||||
|
|
||||||
|
The easiest way to test translations is through the i18n urls e.g. if
|
||||||
|
you have German translations available you can access the German
|
||||||
|
version of a page by going to `/de/path_to_page`.
|
||||||
|
|
||||||
|
To test translations using other methods you will need an HTTP client
|
||||||
|
library like `requests`, `cURL` or `urllib`. Here is a sample code to
|
||||||
|
test `Accept-Language` header using requests:
|
||||||
|
|
||||||
|
```
|
||||||
|
import requests
|
||||||
|
headers = {"Accept-Language": "de"}
|
||||||
|
response = requests.get("http://localhost:9991/login/", headers=headers)
|
||||||
|
print(response.content)
|
||||||
|
```
|
||||||
|
|||||||
57
zerver/tests/test_i18n.py
Normal file
57
zerver/tests/test_i18n.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.conf import settings
|
||||||
|
from http.cookies import SimpleCookie
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
Tranlations strings should change with locale. URLs should be locale
|
||||||
|
aware.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def fetch(self, method, url, expected_status, **kwargs):
|
||||||
|
# e.g. self.client.post(url) if method is "post"
|
||||||
|
response = getattr(self.client, method)(url, **kwargs)
|
||||||
|
self.assertEqual(response.status_code, expected_status,
|
||||||
|
msg="Expected %d, received %d for %s to %s" % (
|
||||||
|
expected_status, response.status_code, method, url))
|
||||||
|
return response
|
||||||
|
|
||||||
|
def test_accept_language_header(self):
|
||||||
|
languages = [('en', 'Register'),
|
||||||
|
('de', 'Registrieren'),
|
||||||
|
('sr', 'Региструј се'),
|
||||||
|
('zh-cn', '注册'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for lang, word in languages:
|
||||||
|
response = self.fetch('get', '/integrations/', 200,
|
||||||
|
HTTP_ACCEPT_LANGUAGE=lang)
|
||||||
|
self.assertTrue(word in response.content)
|
||||||
|
|
||||||
|
def test_cookie(self):
|
||||||
|
languages = [('en', 'Register'),
|
||||||
|
('de', 'Registrieren'),
|
||||||
|
('sr', 'Региструј се'),
|
||||||
|
('zh-cn', '注册'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for lang, word in languages:
|
||||||
|
self.client.cookies = SimpleCookie({settings.LANGUAGE_COOKIE_NAME: lang})
|
||||||
|
|
||||||
|
response = self.fetch('get', '/integrations/', 200)
|
||||||
|
self.assertTrue(word in response.content)
|
||||||
|
|
||||||
|
def test_i18n_urls(self):
|
||||||
|
languages = [('en', 'Register'),
|
||||||
|
('de', 'Registrieren'),
|
||||||
|
('sr', 'Региструј се'),
|
||||||
|
('zh-cn', '注册'),
|
||||||
|
]
|
||||||
|
|
||||||
|
for lang, word in languages:
|
||||||
|
response = self.fetch('get', '/{}/integrations/'.format(lang), 200)
|
||||||
|
self.assertTrue(word in response.content)
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
from django.conf.urls import patterns, url, include
|
from django.conf.urls import patterns, url, include
|
||||||
|
|
||||||
urlpatterns = patterns('zilencer.views',
|
i18n_urlpatterns = [
|
||||||
# SSO dispatch page for desktop app with SSO
|
# SSO dispatch page for desktop app with SSO
|
||||||
# Allows the user to enter their email address only,
|
# Allows the user to enter their email address only,
|
||||||
# and then redirects the user to the proper deployment
|
# and then redirects the user to the proper deployment
|
||||||
# SSO-login page
|
# SSO-login page
|
||||||
url(r'^accounts/deployment_dispatch$', 'account_deployment_dispatch',
|
url(r'^accounts/deployment_dispatch$',
|
||||||
|
'zilencer.views.account_deployment_dispatch',
|
||||||
{'template_name': 'zerver/login.html'}),
|
{'template_name': 'zerver/login.html'}),
|
||||||
)
|
]
|
||||||
|
|
||||||
# Zilencer views following the REST API style
|
# Zilencer views following the REST API style
|
||||||
v1_api_and_json_patterns = patterns('zilencer.views',
|
v1_api_and_json_patterns = patterns('zilencer.views',
|
||||||
@@ -18,7 +19,8 @@ v1_api_and_json_patterns = patterns('zilencer.views',
|
|||||||
url('^endpoints$', 'lookup_endpoints_for_user'),
|
url('^endpoints$', 'lookup_endpoints_for_user'),
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns += patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^api/v1/', include(v1_api_and_json_patterns)),
|
url(r'^api/v1/', include(v1_api_and_json_patterns)),
|
||||||
url(r'^json/', include(v1_api_and_json_patterns)),
|
url(r'^json/', include(v1_api_and_json_patterns)),
|
||||||
|
*i18n_urlpatterns
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls import patterns, url, include
|
from django.conf.urls import patterns, url, include
|
||||||
|
from django.conf.urls.i18n import i18n_patterns
|
||||||
from django.views.generic import TemplateView, RedirectView
|
from django.views.generic import TemplateView, RedirectView
|
||||||
|
from django.utils.module_loading import import_string
|
||||||
import os.path
|
import os.path
|
||||||
import zerver.forms
|
import zerver.forms
|
||||||
|
|
||||||
@@ -13,7 +15,7 @@ import zerver.forms
|
|||||||
#
|
#
|
||||||
# - Likewise for the local dev server in tools/run-dev.py.
|
# - Likewise for the local dev server in tools/run-dev.py.
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
i18n_urls = [
|
||||||
url(r'^$', 'zerver.views.home'),
|
url(r'^$', 'zerver.views.home'),
|
||||||
# We have a desktop-specific landing page in case we change our / to not log in in the future. We don't
|
# We have a desktop-specific landing page in case we change our / to not log in in the future. We don't
|
||||||
# want to require a new desktop app build for everyone in that case
|
# want to require a new desktop app build for everyone in that case
|
||||||
@@ -86,7 +88,10 @@ urlpatterns = patterns('',
|
|||||||
name='landing-page'),
|
name='landing-page'),
|
||||||
url(r'^new-user/$', RedirectView.as_view(url='/hello')),
|
url(r'^new-user/$', RedirectView.as_view(url='/hello')),
|
||||||
url(r'^features/$', TemplateView.as_view(template_name='zerver/features.html')),
|
url(r'^features/$', TemplateView.as_view(template_name='zerver/features.html')),
|
||||||
)
|
]
|
||||||
|
|
||||||
|
urlpatterns = []
|
||||||
|
urlpatterns += patterns('', *i18n_urls)
|
||||||
|
|
||||||
# These are used for voyager development. On a real voyager instance,
|
# These are used for voyager development. On a real voyager instance,
|
||||||
# these files would be served by nginx.
|
# these files would be served by nginx.
|
||||||
@@ -271,6 +276,7 @@ for app_name in settings.EXTRA_INSTALLED_APPS:
|
|||||||
if os.path.exists(os.path.join(app_dir, 'urls.py')):
|
if os.path.exists(os.path.join(app_dir, 'urls.py')):
|
||||||
urlpatterns += patterns('', url(r'^', include('%s.urls' % (app_name,))),
|
urlpatterns += patterns('', url(r'^', include('%s.urls' % (app_name,))),
|
||||||
)
|
)
|
||||||
|
i18n_urls += import_string("{}.urls.i18n_urlpatterns".format(app_name))
|
||||||
|
|
||||||
urlpatterns += patterns('zerver.tornadoviews',
|
urlpatterns += patterns('zerver.tornadoviews',
|
||||||
# Tornado views
|
# Tornado views
|
||||||
@@ -294,3 +300,8 @@ if settings.DEVELOPMENT:
|
|||||||
urlpatterns += patterns('',
|
urlpatterns += patterns('',
|
||||||
url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
|
url(r'^static/(?P<path>.*)$', 'django.views.static.serve',
|
||||||
{'document_root': static_root}))
|
{'document_root': static_root}))
|
||||||
|
|
||||||
|
# The sequence is important; if i18n urls don't come first then
|
||||||
|
# reverse url mapping points to i18n urls which causes the frontend
|
||||||
|
# tests to fail
|
||||||
|
urlpatterns = i18n_patterns(*i18n_urls) + urlpatterns
|
||||||
|
|||||||
Reference in New Issue
Block a user