diff --git a/django/core/management/commands/compilemessages.py b/django/core/management/commands/compilemessages.py index 44e0520b47..308fa8831b 100644 --- a/django/core/management/commands/compilemessages.py +++ b/django/core/management/commands/compilemessages.py @@ -122,10 +122,20 @@ class Command(BaseCommand): with concurrent.futures.ThreadPoolExecutor() as executor: futures = [] for i, (dirpath, f) in enumerate(locations): - if self.verbosity > 0: - self.stdout.write('processing file %s in %s' % (f, dirpath)) po_path = Path(dirpath) / f mo_path = po_path.with_suffix('.mo') + try: + if mo_path.stat().st_mtime >= po_path.stat().st_mtime: + if self.verbosity > 0: + self.stdout.write( + 'File ā€œ%sā€ is already compiled and up to date.' + % po_path + ) + continue + except FileNotFoundError: + pass + if self.verbosity > 0: + self.stdout.write('processing file %s in %s' % (f, dirpath)) if has_bom(po_path): self.stderr.write( diff --git a/tests/i18n/test_compilation.py b/tests/i18n/test_compilation.py index fbacaee221..791c1d4f15 100644 --- a/tests/i18n/test_compilation.py +++ b/tests/i18n/test_compilation.py @@ -49,6 +49,8 @@ class PoFileTests(MessageCompilationTests): # Put file in read-only mode. old_mode = mo_file_en.stat().st_mode mo_file_en.chmod(stat.S_IREAD) + # Ensure .po file is more recent than .mo file. + mo_file_en.with_suffix('.po').touch() try: with self.assertRaisesMessage(CommandError, 'compilemessages generated one or more errors.'): call_command('compilemessages', locale=['en'], stderr=err_buffer, verbosity=0) @@ -56,6 +58,14 @@ class PoFileTests(MessageCompilationTests): finally: mo_file_en.chmod(old_mode) + def test_no_compile_when_unneeded(self): + mo_file_en = Path(self.MO_FILE_EN) + mo_file_en.touch() + stdout = StringIO() + call_command('compilemessages', locale=['en'], stdout=stdout, verbosity=1) + msg = '%sā€ is already compiled and up to date.' % mo_file_en.with_suffix('.po') + self.assertIn(msg, stdout.getvalue()) + class PoFileContentsTests(MessageCompilationTests): # Ticket #11240