mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +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