diff --git a/django/core/management/commands/compilemessages.py b/django/core/management/commands/compilemessages.py index ba800d269e..00ab2db6fc 100644 --- a/django/core/management/commands/compilemessages.py +++ b/django/core/management/commands/compilemessages.py @@ -4,7 +4,9 @@ import glob import os from django.core.management.base import BaseCommand, CommandError -from django.core.management.utils import find_command, popen_wrapper +from django.core.management.utils import ( + find_command, is_ignored_path, popen_wrapper, +) def has_bom(fn): @@ -46,10 +48,17 @@ class Command(BaseCommand): '--use-fuzzy', '-f', dest='fuzzy', action='store_true', help='Use fuzzy translations.', ) + parser.add_argument( + '--ignore', '-i', action='append', dest='ignore_patterns', + default=[], metavar='PATTERN', + help='Ignore directories matching this glob-style pattern. ' + 'Use multiple times to ignore more.', + ) def handle(self, **options): locale = options['locale'] exclude = options['exclude'] + ignore_patterns = set(options['ignore_patterns']) self.verbosity = options['verbosity'] if options['fuzzy']: self.program_options = self.program_options + ['-f'] @@ -66,7 +75,9 @@ class Command(BaseCommand): # Walk entire tree, looking for locale directories for dirpath, dirnames, filenames in os.walk('.', topdown=True): for dirname in dirnames: - if dirname == 'locale': + if is_ignored_path(os.path.normpath(os.path.join(dirpath, dirname)), ignore_patterns): + dirnames.remove(dirname) + elif dirname == 'locale': basedirs.append(os.path.join(dirpath, dirname)) # Gather existing directories. diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index 5a4ce33c56..ba8c5cadba 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -173,6 +173,17 @@ Example usage:: django-admin compilemessages -x pt_BR django-admin compilemessages -x pt_BR -x fr +.. django-admin-option:: --ignore PATTERN, -i PATTERN + +.. versionadded:: 3.0 + +Ignores directories matching the given :mod:`glob`-style pattern. Use +multiple times to ignore more. + +Example usage:: + + django-admin compilemessages --ignore=cache --ignore=outdated/*/locale + ``createcachetable`` -------------------- diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index 3c75c14d88..bf5733ed52 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -150,7 +150,8 @@ Internationalization Management Commands ~~~~~~~~~~~~~~~~~~~ -* ... +* The new :option:`compilemessages --ignore` option allows ignoring specific + directories when searching for ``.po`` files to compile. Migrations ~~~~~~~~~~ diff --git a/tests/i18n/test_compilation.py b/tests/i18n/test_compilation.py index 11266b7a58..aa457f21f6 100644 --- a/tests/i18n/test_compilation.py +++ b/tests/i18n/test_compilation.py @@ -3,6 +3,7 @@ import os import stat import unittest from io import StringIO +from pathlib import Path from subprocess import Popen from unittest import mock @@ -134,6 +135,44 @@ class ExcludedLocaleCompilationTests(MessageCompilationTests): self.assertFalse(os.path.exists(self.MO_FILE % 'it')) +class IgnoreDirectoryCompilationTests(MessageCompilationTests): + # Reuse the exclude directory since it contains some locale fixtures. + work_subdir = 'exclude' + MO_FILE = '%s/%s/LC_MESSAGES/django.mo' + CACHE_DIR = Path('cache') / 'locale' + NESTED_DIR = Path('outdated') / 'v1' / 'locale' + + def setUp(self): + super().setUp() + copytree('canned_locale', 'locale') + copytree('canned_locale', self.CACHE_DIR) + copytree('canned_locale', self.NESTED_DIR) + + def assertAllExist(self, dir, langs): + self.assertTrue(all(Path(self.MO_FILE % (dir, lang)).exists() for lang in langs)) + + def assertNoneExist(self, dir, langs): + self.assertTrue(all(Path(self.MO_FILE % (dir, lang)).exists() is False for lang in langs)) + + def test_one_locale_dir_ignored(self): + call_command('compilemessages', ignore=['cache'], verbosity=0) + self.assertAllExist('locale', ['en', 'fr', 'it']) + self.assertNoneExist(self.CACHE_DIR, ['en', 'fr', 'it']) + self.assertAllExist(self.NESTED_DIR, ['en', 'fr', 'it']) + + def test_multiple_locale_dirs_ignored(self): + call_command('compilemessages', ignore=['cache/locale', 'outdated'], verbosity=0) + self.assertAllExist('locale', ['en', 'fr', 'it']) + self.assertNoneExist(self.CACHE_DIR, ['en', 'fr', 'it']) + self.assertNoneExist(self.NESTED_DIR, ['en', 'fr', 'it']) + + def test_ignores_based_on_pattern(self): + call_command('compilemessages', ignore=['*/locale'], verbosity=0) + self.assertAllExist('locale', ['en', 'fr', 'it']) + self.assertNoneExist(self.CACHE_DIR, ['en', 'fr', 'it']) + self.assertNoneExist(self.NESTED_DIR, ['en', 'fr', 'it']) + + class CompilationErrorHandling(MessageCompilationTests): def test_error_reported_by_msgfmt(self): # po file contains wrong po formatting.