From 5db8d617c0a5b16fabe16d1d52b2f9db519d8bb6 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 3 Sep 2018 18:43:55 +1000 Subject: [PATCH] Fixed #29713 -- Added check that LANGUAGE_CODE uses standard language id format. --- django/core/checks/__init__.py | 1 + django/core/checks/registry.py | 1 + django/core/checks/translation.py | 25 +++++++++++++++ docs/ref/checks.txt | 13 ++++++++ tests/check_framework/test_translation.py | 39 +++++++++++++++++++++++ tests/i18n/tests.py | 1 + 6 files changed, 80 insertions(+) create mode 100644 django/core/checks/translation.py create mode 100644 tests/check_framework/test_translation.py diff --git a/django/core/checks/__init__.py b/django/core/checks/__init__.py index 8ef3f5db90..ededae38ba 100644 --- a/django/core/checks/__init__.py +++ b/django/core/checks/__init__.py @@ -12,6 +12,7 @@ import django.core.checks.security.base # NOQA isort:skip import django.core.checks.security.csrf # NOQA isort:skip import django.core.checks.security.sessions # NOQA isort:skip import django.core.checks.templates # NOQA isort:skip +import django.core.checks.translation # NOQA isort:skip import django.core.checks.urls # NOQA isort:skip diff --git a/django/core/checks/registry.py b/django/core/checks/registry.py index 9660272d9b..51999bdf95 100644 --- a/django/core/checks/registry.py +++ b/django/core/checks/registry.py @@ -15,6 +15,7 @@ class Tags: security = 'security' signals = 'signals' templates = 'templates' + translation = 'translation' urls = 'urls' diff --git a/django/core/checks/translation.py b/django/core/checks/translation.py new file mode 100644 index 0000000000..5c183aad2d --- /dev/null +++ b/django/core/checks/translation.py @@ -0,0 +1,25 @@ +import re + +from django.conf import settings +from django.utils.translation.trans_real import language_code_re + +from . import Error, Tags, register + + +@register(Tags.translation) +def check_setting_language_code(app_configs, **kwargs): + """ + Errors if language code is in the wrong format. Language codes specification outlined by + https://en.wikipedia.org/wiki/IETF_language_tag#Syntax_of_language_tags + """ + match_result = re.match(language_code_re, settings.LANGUAGE_CODE) + errors = [] + if not match_result: + errors.append(Error( + "LANGUAGE_CODE in settings.py is {}. It should be in the form ll or ll-cc where ll is the language and cc " + "is the country. Examples include: it, de-at, es, pt-br. The full set of language codes specifications is " + "outlined by https://en.wikipedia.org/wiki/IETF_language_tag#Syntax_of_language_tags".format( + settings.LANGUAGE_CODE), + id="translation.E001", + )) + return errors diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index 42296f65c9..734381e2b8 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -86,6 +86,7 @@ Django's system checks are organized using the following tags: * ``staticfiles``: Checks :mod:`django.contrib.staticfiles` configuration. * ``templates``: Checks template related configuration. * ``urls``: Checks URL configuration. +* ``translation``: Checks language formats used for translation. Some checks may be registered with multiple tags. @@ -449,6 +450,18 @@ The following checks are performed on your URL configuration: * **urls.E006**: The :setting:`MEDIA_URL`/ :setting:`STATIC_URL` setting must end with a slash. +Translation +----------- + +The following checks are performed on your translation configuration: + +* **translation.E001**: LANGUAGE_CODE in settings.py is ````. + It should be in the form ll or ll-cc where ll is the language and cc is the + country. Examples include: ``it``, ``de-at``, ``es``, ``pt-br``. The full + set of language codes specifications is outlined by + https://en.wikipedia.org/wiki/IETF_language_tag#Syntax_of_language_tags + + ``contrib`` app checks ====================== diff --git a/tests/check_framework/test_translation.py b/tests/check_framework/test_translation.py new file mode 100644 index 0000000000..9ff698c29f --- /dev/null +++ b/tests/check_framework/test_translation.py @@ -0,0 +1,39 @@ +from django.core.checks.translation import check_setting_language_code +from django.test import SimpleTestCase, override_settings + + +class TranslationCheckTests(SimpleTestCase): + + @override_settings(LANGUAGE_CODE="eu") + def test_valid_language_code_format_ll_only(self): + result = check_setting_language_code(None) + self.assertEqual(len(result), 0) + + @override_settings(LANGUAGE_CODE="eü") + def test_invalid_language_code_format_ll_only(self): + result = check_setting_language_code(None) + self.assertEqual(len(result), 1) + error = result[0] + self.assertEqual(error.id, 'translation.E001') + self.assertEqual(error.msg, ( + "LANGUAGE_CODE in settings.py is eü. It should be in the form ll or ll-cc where ll is the language and cc " + "is the country. Examples include: it, de-at, es, pt-br. The full set of language codes specifications is " + "outlined by https://en.wikipedia.org/wiki/IETF_language_tag#Syntax_of_language_tags" + )) + + @override_settings(LANGUAGE_CODE="en-US") + def test_valid_language_code_format_ll_cc(self): + result = check_setting_language_code(None) + self.assertEqual(len(result), 0) + + @override_settings(LANGUAGE_CODE="en_US") + def test_invalid_language_code_format_ll_cc(self): + result = check_setting_language_code(None) + self.assertEqual(len(result), 1) + error = result[0] + self.assertEqual(error.id, 'translation.E001') + self.assertEqual(error.msg, ( + "LANGUAGE_CODE in settings.py is en_US. It should be in the form ll or ll-cc where ll is the language and " + "cc is the country. Examples include: it, de-at, es, pt-br. The full set of language codes specifications " + "is outlined by https://en.wikipedia.org/wiki/IETF_language_tag#Syntax_of_language_tags" + )) diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 5d76e26653..c9d4ecde28 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -1649,6 +1649,7 @@ class CountrySpecificLanguageTests(SimpleTestCase): self.assertTrue(check_for_language('en')) self.assertTrue(check_for_language('en-us')) self.assertTrue(check_for_language('en-US')) + self.assertFalse(check_for_language('en_US')) self.assertTrue(check_for_language('be')) self.assertTrue(check_for_language('be@latin')) self.assertTrue(check_for_language('sr-RS@latin'))