diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index 93039ac0960..72390e4a756 100644 --- a/django/core/management/commands/makemessages.py +++ b/django/core/management/commands/makemessages.py @@ -11,6 +11,7 @@ from itertools import dropwhile import django from django.conf import settings +from django.core.exceptions import ImproperlyConfigured from django.core.files.temp import NamedTemporaryFile from django.core.management.base import BaseCommand, CommandError from django.core.management.utils import ( @@ -103,13 +104,14 @@ class BuildFile(object): if not self.is_templatized: return - with io.open(self.path, 'r', encoding=settings.FILE_CHARSET) as fp: + encoding = settings.FILE_CHARSET if self.command.settings_available else 'utf-8' + with io.open(self.path, 'r', encoding=encoding) as fp: src_data = fp.read() if self.domain == 'djangojs': content = prepare_js_for_gettext(src_data) elif self.domain == 'django': - content = templatize(src_data, self.path[2:]) + content = templatize(src_data, origin=self.path[2:], charset=encoding) with io.open(self.work_path, 'w', encoding='utf-8') as fp: fp.write(content) @@ -325,7 +327,8 @@ class Command(BaseCommand): self.default_locale_path = self.locale_paths[0] self.invoked_for_django = True else: - self.locale_paths.extend(settings.LOCALE_PATHS) + if self.settings_available: + self.locale_paths.extend(settings.LOCALE_PATHS) # Allow to run makemessages inside an app dir if os.path.isdir('locale'): self.locale_paths.append(os.path.abspath('locale')) @@ -377,6 +380,16 @@ class Command(BaseCommand): else: raise CommandError("Unable to get gettext version. Is it installed?") + @cached_property + def settings_available(self): + try: + settings.LOCALE_PATHS + except ImproperlyConfigured: + if self.verbosity > 1: + self.stderr.write("Running without configured settings.") + return False + return True + def build_potfiles(self): """ Build pot files and apply msguniq to them. @@ -438,7 +451,9 @@ class Command(BaseCommand): norm_patterns.append(p) all_files = [] - ignored_roots = [os.path.normpath(p) for p in (settings.MEDIA_ROOT, settings.STATIC_ROOT) if p] + ignored_roots = [] + if self.settings_available: + ignored_roots = [os.path.normpath(p) for p in (settings.MEDIA_ROOT, settings.STATIC_ROOT) if p] for dirpath, dirnames, filenames in os.walk(root, topdown=True, followlinks=self.symlinks): for dirname in dirnames[:]: if (is_ignored(os.path.normpath(os.path.join(dirpath, dirname)), norm_patterns) or diff --git a/django/utils/translation/__init__.py b/django/utils/translation/__init__.py index a5975b005ce..39d0f4162b7 100644 --- a/django/utils/translation/__init__.py +++ b/django/utils/translation/__init__.py @@ -212,9 +212,9 @@ def get_language_from_path(path): return _trans.get_language_from_path(path) -def templatize(src, origin=None): +def templatize(src, **kwargs): from .template import templatize - return templatize(src, origin) + return templatize(src, **kwargs) def deactivate_all(): diff --git a/django/utils/translation/template.py b/django/utils/translation/template.py index fd7c0652566..b8618c4dd2e 100644 --- a/django/utils/translation/template.py +++ b/django/utils/translation/template.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import re import warnings -from django.conf import settings from django.template.base import ( TOKEN_BLOCK, TOKEN_COMMENT, TOKEN_TEXT, TOKEN_VAR, TRANSLATOR_COMMENT_MARK, Lexer, @@ -40,13 +39,13 @@ plural_re = re.compile(r"""^\s*plural$""") constant_re = re.compile(r"""_\(((?:".*?")|(?:'.*?'))\)""") -def templatize(src, origin=None): +def templatize(src, origin=None, charset='utf-8'): """ Turn a Django template into something that is understood by xgettext. It does so by translating the Django translation tags into standard gettext function invocations. """ - src = force_text(src, settings.FILE_CHARSET) + src = force_text(src, charset) out = StringIO('') message_context = None intrans = False diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 74f105be742..86b5e724fa9 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -564,6 +564,11 @@ directory. After making changes to the messages files you need to compile them with :djadmin:`compilemessages` for use with the builtin gettext support. See the :ref:`i18n documentation ` for details. +This command doesn't require configured settings. However, when settings aren't +configured, the command can't ignore the :setting:`MEDIA_ROOT` and +:setting:`STATIC_ROOT` directories or include :setting:`LOCALE_PATHS`. It will +also write files in UTF-8 rather than in :setting:`FILE_CHARSET`. + .. django-admin-option:: --all, -a Updates the message files for all available languages. diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index a9de4b095bb..bc780a50310 100644 --- a/docs/releases/1.11.txt +++ b/docs/releases/1.11.txt @@ -515,9 +515,6 @@ Miscellaneous called a second time before calling :func:`~django.test.utils.teardown_test_environment`. -* The :djadmin:`makemessages` command now requires configured settings, like - most other commands. - * The undocumented ``DateTimeAwareJSONEncoder`` alias for :class:`~django.core.serializers.json.DjangoJSONEncoder` (renamed in Django 1.0) is removed. diff --git a/tests/i18n/test_extraction.py b/tests/i18n/test_extraction.py index 75b1d80b791..40ccc822d7d 100644 --- a/tests/i18n/test_extraction.py +++ b/tests/i18n/test_extraction.py @@ -9,6 +9,8 @@ import time import warnings from unittest import skipUnless +from admin_scripts.tests import AdminScriptTestCase + from django.core import management from django.core.management import execute_from_command_line from django.core.management.base import CommandError @@ -713,3 +715,10 @@ class CustomLayoutExtractionTests(ExtractorTests): with open(app_de_locale, 'r') as fp: po_contents = force_text(fp.read()) self.assertMsgId('This app has a locale directory', po_contents) + + +class NoSettingsExtractionTests(AdminScriptTestCase): + def test_makemessages_no_settings(self): + out, err = self.run_django_admin(['makemessages', '-l', 'en', '-v', '0']) + self.assertNoOutput(err) + self.assertNoOutput(out)