diff --git a/analytics/urls.py b/analytics/urls.py index 7eff6fe999..684970fcbd 100644 --- a/analytics/urls.py +++ b/analytics/urls.py @@ -1,8 +1,9 @@ from django.conf.urls import patterns, url -urlpatterns = patterns('analytics.views', - url(r'^activity$', 'get_activity'), - url(r'^realm_activity/(?P[\S]+)/$', 'get_realm_activity'), - url(r'^user_activity/(?P[\S]+)/$', 'get_user_activity'), -) +i18n_urlpatterns = [ + url(r'^activity$', 'analytics.views.get_activity'), + url(r'^realm_activity/(?P[\S]+)/$', 'analytics.views.get_realm_activity'), + url(r'^user_activity/(?P[\S]+)/$', 'analytics.views.get_user_activity'), +] +urlpatterns = patterns('', *i18n_urlpatterns) diff --git a/corporate/urls.py b/corporate/urls.py index a74e3aa494..534fe279e3 100644 --- a/corporate/urls.py +++ b/corporate/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import patterns, url from django.views.generic import TemplateView, RedirectView -urlpatterns = patterns('', +i18n_urlpatterns = [ # Zephyr/MIT url(r'^zephyr/$', TemplateView.as_view(template_name='corporate/zephyr.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-enterprise/$', TemplateView.as_view(template_name='corporate/terms-enterprise.html')), url(r'^privacy/$', TemplateView.as_view(template_name='corporate/privacy.html')), +] -) +urlpatterns = patterns('', *i18n_urlpatterns) diff --git a/docs/translating.md b/docs/translating.md index e1cd4873bc..6517b63a23 100644 --- a/docs/translating.md +++ b/docs/translating.md @@ -63,3 +63,34 @@ You can instead use: [Handlebars]: http://handlebarsjs.com/ [trans]: http://jinja.pocoo.org/docs/dev/templates/#i18n [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) +``` diff --git a/zerver/tests/test_i18n.py b/zerver/tests/test_i18n.py new file mode 100644 index 0000000000..8c20c7a364 --- /dev/null +++ b/zerver/tests/test_i18n.py @@ -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) diff --git a/zilencer/urls.py b/zilencer/urls.py index 179bf364d9..b3bcb73964 100644 --- a/zilencer/urls.py +++ b/zilencer/urls.py @@ -1,13 +1,14 @@ from django.conf.urls import patterns, url, include -urlpatterns = patterns('zilencer.views', +i18n_urlpatterns = [ # SSO dispatch page for desktop app with SSO # Allows the user to enter their email address only, # and then redirects the user to the proper deployment # 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'}), -) +] # Zilencer views following the REST API style 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'), ) -urlpatterns += patterns('', +urlpatterns = patterns('', url(r'^api/v1/', include(v1_api_and_json_patterns)), url(r'^json/', include(v1_api_and_json_patterns)), + *i18n_urlpatterns ) diff --git a/zproject/urls.py b/zproject/urls.py index 928bcd830d..e23f1aea00 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -1,6 +1,8 @@ from django.conf import settings 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.utils.module_loading import import_string import os.path import zerver.forms @@ -13,7 +15,7 @@ import zerver.forms # # - Likewise for the local dev server in tools/run-dev.py. -urlpatterns = patterns('', +i18n_urls = [ 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 # want to require a new desktop app build for everyone in that case @@ -86,7 +88,10 @@ urlpatterns = patterns('', name='landing-page'), url(r'^new-user/$', RedirectView.as_view(url='/hello')), 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 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')): urlpatterns += patterns('', url(r'^', include('%s.urls' % (app_name,))), ) + i18n_urls += import_string("{}.urls.i18n_urlpatterns".format(app_name)) urlpatterns += patterns('zerver.tornadoviews', # Tornado views @@ -294,3 +300,8 @@ if settings.DEVELOPMENT: urlpatterns += patterns('', url(r'^static/(?P.*)$', 'django.views.static.serve', {'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