diff --git a/.gitignore b/.gitignore index b4c2573..5ba10f3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ dist *.pyc django_babel.egg-info .tox +htmlcov diff --git a/.travis.yml b/.travis.yml index 0512861..3ad88ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,26 +2,17 @@ language: python python: 3.4 sudo: false env: - - TOX_ENV=py27-django15 - - TOX_ENV=py27-django16 - - TOX_ENV=py27-django17 + - TOX_ENV=docs + - TOX_ENV=lint - TOX_ENV=py27-django18 - TOX_ENV=py27-django19 + - TOX_ENV=py27-django110 - TOX_ENV=py27-djangomaster - - TOX_ENV=py34-django15 - - TOX_ENV=py34-django16 - - TOX_ENV=py34-django17 + - TOX_ENV=py33-django18 - TOX_ENV=py34-django18 - TOX_ENV=py34-django19 + - TOX_ENV=py34-django110 - TOX_ENV=py34-djangomaster - - TOX_ENV=py33-django15 - - TOX_ENV=py33-django16 - - TOX_ENV=py33-django17 - - TOX_ENV=py33-django18 - - TOX_ENV=py26-django15 - - TOX_ENV=py26-django16 - - TOX_ENV=lint - - TOX_ENV=docs install: - pip install tox script: diff --git a/django_babel/extract.py b/django_babel/extract.py index a34149a..3169ffb 100644 --- a/django_babel/extract.py +++ b/django_babel/extract.py @@ -1,31 +1,11 @@ # -*- coding: utf-8 -*- -try: - from django.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK -except ImportError: - # Django 1.8 moved most stuff to .base - from django.template.base import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK - -try: - from django.utils.translation import trim_whitespace as trim_django -except ImportError: - trim_django = False - +from django.template.base import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK +from django.utils.translation import trim_whitespace from django.utils.encoding import smart_text from django.utils.translation.trans_real import ( inline_re, block_re, endblock_re, plural_re, constant_re) -def trim_whitespace(string): - """Trim whitespace. - - This is only supported in Django>=1.7. This method help in cases of older - Django versions. - """ - if trim_django: - return trim_django(string) - return string - - def join_tokens(tokens, trim=False): message = ''.join(tokens) if trim: diff --git a/django_babel/management/commands/babel.py b/django_babel/management/commands/babel.py index 240a453..a616bd4 100644 --- a/django_babel/management/commands/babel.py +++ b/django_babel/management/commands/babel.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import os from distutils.dist import Distribution -from optparse import make_option from subprocess import call from django.core.management.base import LabelCommand, CommandError @@ -15,23 +14,23 @@ class Command(LabelCommand): args = '[makemessages] [compilemessages]' - option_list = LabelCommand.option_list + ( - make_option( - '--locale', '-l', default=None, dest='locale', action='append', + def add_arguments(self, parser): + super(Command, self).add_arguments(parser) + parser.add_argument( + '--locale', '-l', default=[], dest='locale', action='append', help=( 'Creates or updates the message files for the given locale(s)' ' (e.g pt_BR). Can be used multiple times.' ), - ), - make_option( + ) + parser.add_argument( '--domain', '-d', default='django', dest='domain', help='The domain of the message files (default: "django").', ), - make_option( + parser.add_argument( '--mapping-file', '-F', default=None, dest='mapping_file', help='Mapping file', ) - ) def handle_label(self, command, **options): if command not in ('makemessages', 'compilemessages'): diff --git a/django_babel/middleware.py b/django_babel/middleware.py index 5c3ac1c..8ad5b38 100644 --- a/django_babel/middleware.py +++ b/django_babel/middleware.py @@ -2,10 +2,14 @@ from babel import Locale, UnknownLocaleError from django.utils.translation import get_language +from threading import local + try: - from threading import local + from django.utils.deprecation import MiddlewareMixin except ImportError: - from django.utils._threading_local import local + # Not required for Django <= 1.9, see: + # https://docs.djangoproject.com/en/1.10/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware + MiddlewareMixin = object __all__ = ['get_current_locale', 'LocaleMiddleware'] @@ -22,7 +26,7 @@ def get_current_locale(): return getattr(_thread_locals, 'locale', None) -class LocaleMiddleware(object): +class LocaleMiddleware(MiddlewareMixin): """Simple Django middleware that makes available a Babel `Locale` object via the `request.locale` attribute. diff --git a/setup.py b/setup.py index c9c1688..b8cbdf1 100755 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def read(*parts): url='https://github.com/python-babel/django-babel/', packages=find_packages(exclude=('tests',)), install_requires=[ - 'django>=1.4,<1.10', + 'django>=1.4,<1.11', 'babel>=1.3', ], classifiers=[ diff --git a/tests/babel.cfg b/tests/babel.cfg new file mode 100644 index 0000000..71fb876 --- /dev/null +++ b/tests/babel.cfg @@ -0,0 +1 @@ +[django: **/templates/**.*] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..49cfd8e --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,7 @@ +from django.conf import settings + +from testproject import settings as testproject_settings + + +def pytest_configure(): + settings.configure(**vars(testproject_settings)) diff --git a/tests/test_command.py b/tests/test_command.py new file mode 100644 index 0000000..d685160 --- /dev/null +++ b/tests/test_command.py @@ -0,0 +1,37 @@ +import os + +import pkg_resources +from django.core.management import call_command + +TEST_LOCALE_DIR = pkg_resources.resource_filename( + 'testproject', 'locale' +) + + +def test_babel_compilemessages(): + call_command( + 'babel', + 'compilemessages', + '-l', 'fi', + ) + # Assert that the .mo file was created by attempting to delete it. + os.unlink( + os.path.join(TEST_LOCALE_DIR, 'fi', 'LC_MESSAGES', 'django.mo') + ) + + +def test_babel_makemessages(): + call_command( + 'babel', + 'makemessages', + '-l', 'en', + '-F', pkg_resources.resource_filename(__name__, 'babel.cfg'), + ) + # See that the expected files get populated with the discovered message + for path in [ + os.path.join(TEST_LOCALE_DIR, 'django.pot'), + os.path.join(TEST_LOCALE_DIR, 'en', 'LC_MESSAGES', 'django.po'), + ]: + with open(path) as infp: + assert '"This could be translated."' in infp.read() + os.unlink(path) # clean up diff --git a/tests/test_render.py b/tests/test_render.py new file mode 100644 index 0000000..61d8ddb --- /dev/null +++ b/tests/test_render.py @@ -0,0 +1,26 @@ +# -- encoding: utf-8 -- +from __future__ import unicode_literals + +import pytest + + +@pytest.mark.parametrize('locale', ('en', 'fi', 'sv', 'pt-BR')) +def test_babel_render(client, locale): + """ + Test the middleware and the rendery bits. + """ + response = client.get('/', HTTP_ACCEPT_LANGUAGE=locale) + # "Parse" the key-value format + lines = response.content.decode('utf-8').strip().splitlines() + content = dict(kv.split('=', 1) for kv in lines) + # See that we're rendering in the locale we expect + assert content['language_code'] == locale.lower() + # check that we could access `babel.Locale.language_name` + assert content['language_name'] == { + 'en': 'English', + 'fi': 'suomi', + 'sv': 'svenska', + 'pt-BR': 'português', + }[locale] + # The rest are not really tested (aside from smoke tests) further; + # the Babel test suite has taken care of that. diff --git a/tests/testproject/__init__.py b/tests/testproject/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/testproject/locale/en/LC_MESSAGES/.gitkeep b/tests/testproject/locale/en/LC_MESSAGES/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/testproject/locale/fi/LC_MESSAGES/django.po b/tests/testproject/locale/fi/LC_MESSAGES/django.po new file mode 100644 index 0000000..94c7244 --- /dev/null +++ b/tests/testproject/locale/fi/LC_MESSAGES/django.po @@ -0,0 +1,8 @@ +# the header message is required to turn off the fuzzy bit for the catalog +msgid "" +msgstr "" + +#: tests/templates/test.txt:2 +msgid "This could be translated." +msgstr "Tämän voisi kääntää." + diff --git a/tests/testproject/settings.py b/tests/testproject/settings.py new file mode 100644 index 0000000..e3b46c8 --- /dev/null +++ b/tests/testproject/settings.py @@ -0,0 +1,28 @@ +import pkg_resources + +SECRET_KEY = 'x' +USE_I18N = True +ROOT_URLCONF = 'testproject.urls' +INSTALLED_APPS = [ + 'django_babel', + 'testproject', +] +MIDDLEWARE_CLASSES = [ + 'django.middleware.locale.LocaleMiddleware', + 'django_babel.middleware.LocaleMiddleware', +] +TEMPLATES = [ + { + 'NAME': 'default', + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.i18n', + ], + }, + }, +] +LOCALE_PATHS = [ + pkg_resources.resource_filename(__name__, 'locale'), +] diff --git a/tests/testproject/templates/test.txt b/tests/testproject/templates/test.txt new file mode 100644 index 0000000..2599688 --- /dev/null +++ b/tests/testproject/templates/test.txt @@ -0,0 +1,12 @@ +{% load i18n babel %} +text={% trans "This could be translated." %} +language_code={{ LANGUAGE_CODE }} +language_name={{ locale.language_name }} +date={{ date|datefmt }} +datetime={{ date|datetimefmt }} +time={{ date|timefmt }} +number={{ number|numberfmt }} +decimal={{ number|decimalfmt }} +currency={{ number|currencyfmt:"EUR" }} +percent={{ number|percentfmt }} +scientificfmt={{ number|scientificfmt }} diff --git a/tests/testproject/urls.py b/tests/testproject/urls.py new file mode 100644 index 0000000..2bf2501 --- /dev/null +++ b/tests/testproject/urls.py @@ -0,0 +1,18 @@ +import time + +from django.conf.urls import url +from django.shortcuts import render +from django.utils.timezone import now + + +def test_view(request): + return render(request, 'test.txt', { + 'date': now(), + 'number': time.time(), + 'locale': request.locale, + }) + + +urlpatterns = [ + url('^$', test_view), +] diff --git a/tox.ini b/tox.ini index fe5926c..8c45bdb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,16 @@ [tox] -envlist = {py27,py34}-django{15,16,17,18,19,master}, py33-django{15,16,17,18}, py26-django{15,16}, lint, docs +envlist = {py27,py34,py35}-django{18,19,110,master}, py33-django18, lint, docs [testenv] deps = coverage pytest pytest-cov + pytest-django python-coveralls - django15: Django>=1.5,<1.6 - django16: Django>=1.6,<1.7 - django17: Django>=1.7,<1.8 django18: Django>=1.8,<1.9 django19: Django>=1.9,<1.10 + django110: Django>=1.10,<1.11 djangomaster: https://github.com/django/django/archive/master.tar.gz#egg=Django commands = py.test {posargs}