From 6158c79dbef832bc8530107ab2d34f04a04471da Mon Sep 17 00:00:00 2001
From: Craig Blaszczyk <masterjakul@gmail.com>
Date: Thu, 7 Jun 2012 11:23:25 +0200
Subject: [PATCH] Made (make|compile)messages commands accept multiple locales
 at once.

Thanks Craig Blaszczyk for the initial patch. Refs #17181.
---
 AUTHORS                                       |  1 +
 .../management/commands/compilemessages.py    | 16 +++--
 .../core/management/commands/makemessages.py  |  6 +-
 docs/ref/django-admin.txt                     | 28 +++++++-
 .../i18n/commands/compilation.py              | 31 ++++++++
 .../i18n/commands/extraction.py               | 27 +++++++
 .../commands/locale/hr/LC_MESSAGES/django.po  | 71 +++++++++++++++++++
 tests/regressiontests/i18n/tests.py           |  5 +-
 8 files changed, 171 insertions(+), 14 deletions(-)
 create mode 100644 tests/regressiontests/i18n/commands/locale/hr/LC_MESSAGES/django.po

diff --git a/AUTHORS b/AUTHORS
index 3659d8e0df..16bfa574c9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -98,6 +98,7 @@ answer newbie questions, and generally made Django that much better:
     Mark Biggers <biggers@utsl.com>
     Paul Bissex <http://e-scribe.com/>
     Simon Blanchard
+    Craig Blaszczyk <masterjakul@gmail.com>
     David Blewett <david@dawninglight.net>
     Matt Boersma <matt@sprout.org>
     Artem Gnilov <boobsd@gmail.com>
diff --git a/django/core/management/commands/compilemessages.py b/django/core/management/commands/compilemessages.py
index e1d8a33332..684ef3514c 100644
--- a/django/core/management/commands/compilemessages.py
+++ b/django/core/management/commands/compilemessages.py
@@ -28,10 +28,14 @@ def compile_messages(stderr, locale=None):
 
     for basedir in basedirs:
         if locale:
-            basedir = os.path.join(basedir, locale, 'LC_MESSAGES')
-        for dirpath, dirnames, filenames in os.walk(basedir):
-            for f in filenames:
-                if f.endswith('.po'):
+            dirs = [os.path.join(basedir, l, 'LC_MESSAGES') for l in (locale if isinstance(locale, list) else [locale])]
+        else:
+            dirs = [basedir]
+        for ldir in dirs:
+            for dirpath, dirnames, filenames in os.walk(ldir):
+                for f in filenames:
+                    if not f.endswith('.po'):
+                        continue
                     stderr.write('processing file %s in %s\n' % (f, dirpath))
                     fn = os.path.join(dirpath, f)
                     if has_bom(fn):
@@ -53,8 +57,8 @@ def compile_messages(stderr, locale=None):
 
 class Command(BaseCommand):
     option_list = BaseCommand.option_list + (
-        make_option('--locale', '-l', dest='locale',
-            help='The locale to process. Default is to process all.'),
+        make_option('--locale', '-l', dest='locale', action='append',
+                    help='locale(s) to process (e.g. de_AT). Default is to process all. Can be used multiple times, accepts a comma-separated list of locale names.'),
     )
     help = 'Compiles .po files to .mo files for use with builtin gettext support.'
 
diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
index 2b2755d8d1..31971a9101 100644
--- a/django/core/management/commands/makemessages.py
+++ b/django/core/management/commands/makemessages.py
@@ -304,7 +304,7 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
 
     locales = []
     if locale is not None:
-        locales.append(str(locale))
+        locales += locale.split(',') if not isinstance(locale, list) else locale
     elif all:
         locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir))
         locales = [os.path.basename(l) for l in locale_dirs]
@@ -341,8 +341,8 @@ def make_messages(locale=None, domain='django', verbosity=1, all=False,
 
 class Command(NoArgsCommand):
     option_list = NoArgsCommand.option_list + (
-        make_option('--locale', '-l', default=None, dest='locale',
-            help='Creates or updates the message files for the given locale (e.g. pt_BR).'),
+        make_option('--locale', '-l', default=None, 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, accepts a comma-separated list of locale names.'),
         make_option('--domain', '-d', default='django', dest='domain',
             help='The domain of the message files (default: "django").'),
         make_option('--all', '-a', action='store_true', dest='all',
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index 3c73e268e2..06ec8e2031 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -107,12 +107,21 @@ compilemessages
 Compiles .po files created with ``makemessages`` to .mo files for use with
 the builtin gettext support. See :doc:`/topics/i18n/index`.
 
-Use the :djadminopt:`--locale` option to specify the locale to process.
-If not provided, all locales are processed.
+Use the :djadminopt:`--locale` option (or its shorter version ``-l``) to
+specify the locale(s) to process. If not provided, all locales are processed.
 
 Example usage::
 
     django-admin.py compilemessages --locale=pt_BR
+    django-admin.py compilemessages --locale=pt_BR --locale=fr
+    django-admin.py compilemessages -l pt_BR
+    django-admin.py compilemessages -l pt_BR -l fr
+    django-admin.py compilemessages --locale=pt_BR,fr
+    django-admin.py compilemessages -l pt_BR,fr
+
+.. versionchanged:: 1.6
+
+Added the ability to specify multiple locales.
 
 createcachetable
 ----------------
@@ -422,11 +431,24 @@ Separate multiple extensions with commas or use -e or --extension multiple times
 
     django-admin.py makemessages --locale=de --extension=html,txt --extension xml
 
-Use the :djadminopt:`--locale` option to specify the locale to process.
+Use the :djadminopt:`--locale` option (or its shorter version ``-l``) to
+specify the locale(s) to process.
 
 Example usage::
 
     django-admin.py makemessages --locale=pt_BR
+    django-admin.py makemessages --locale=pt_BR --locale=fr
+    django-admin.py makemessages -l pt_BR
+    django-admin.py makemessages -l pt_BR -l fr
+
+You can also use commas to separate multiple locales::
+
+    django-admin.py makemessages --locale=de,fr,pt_BR
+    django-admin.py makemessages -l de,fr,pt_BR
+
+.. versionchanged:: 1.6
+
+Added the ability to specify multiple locales.
 
 .. django-admin-option:: --domain
 
diff --git a/tests/regressiontests/i18n/commands/compilation.py b/tests/regressiontests/i18n/commands/compilation.py
index 492b0d22a3..c15b95eb0e 100644
--- a/tests/regressiontests/i18n/commands/compilation.py
+++ b/tests/regressiontests/i18n/commands/compilation.py
@@ -68,3 +68,34 @@ class PercentRenderingTests(MessageCompilationTests):
             t = Template('{% load i18n %}{% trans "Completed 50%% of all the tasks" %}')
             rendered = t.render(Context({}))
             self.assertEqual(rendered, 'IT translation of Completed 50%% of all the tasks')
+
+
+@override_settings(LOCALE_PATHS=(os.path.join(test_dir, 'locale'),))
+class MultipleLocaleCompilationTests(MessageCompilationTests):
+    MO_FILE_HR = None
+    MO_FILE_FR = None
+
+    def setUp(self):
+        super(MultipleLocaleCompilationTests, self).setUp()
+        self.localedir = os.path.join(test_dir, 'locale')
+        self.MO_FILE_HR = os.path.join(self.localedir, 'hr/LC_MESSAGES/django.mo')
+        self.MO_FILE_FR = os.path.join(self.localedir, 'fr/LC_MESSAGES/django.mo')
+        self.addCleanup(self._rmfile, os.path.join(self.localedir, self.MO_FILE_HR))
+        self.addCleanup(self._rmfile, os.path.join(self.localedir, self.MO_FILE_FR))
+
+    def _rmfile(self, filepath):
+        if os.path.exists(filepath):
+            os.remove(filepath)
+
+    def test_one_locale(self):
+        os.chdir(test_dir)
+        call_command('compilemessages', locale='hr', stderr=StringIO())
+
+        self.assertTrue(os.path.exists(self.MO_FILE_HR))
+
+    def test_multiple_locales(self):
+        os.chdir(test_dir)
+        call_command('compilemessages', locale=['hr', 'fr'], stderr=StringIO())
+
+        self.assertTrue(os.path.exists(self.MO_FILE_HR))
+        self.assertTrue(os.path.exists(self.MO_FILE_FR))
diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
index 575f23cfee..1d6a72d725 100644
--- a/tests/regressiontests/i18n/commands/extraction.py
+++ b/tests/regressiontests/i18n/commands/extraction.py
@@ -327,3 +327,30 @@ class KeepPotFileExtractorTests(ExtractorTests):
         management.call_command('makemessages', locale=LOCALE, verbosity=0,
                                 keep_pot=True)
         self.assertTrue(os.path.exists(self.POT_FILE))
+
+
+class MultipleLocaleExtractionTests(ExtractorTests):
+    PO_FILE_PT = 'locale/pt/LC_MESSAGES/django.po'
+    PO_FILE_DE = 'locale/de/LC_MESSAGES/django.po'
+    LOCALES = ['pt', 'de', 'ch']
+
+    def tearDown(self):
+        os.chdir(self.test_dir)
+        for locale in self.LOCALES:
+            try:
+                self._rmrf('locale/%s' % locale)
+            except OSError:
+                pass
+        os.chdir(self._cwd)
+
+    def test_multiple_locales(self):
+        os.chdir(self.test_dir)
+        management.call_command('makemessages', locale=['pt','de'], verbosity=0)
+        self.assertTrue(os.path.exists(self.PO_FILE_PT))
+        self.assertTrue(os.path.exists(self.PO_FILE_DE))
+
+    def test_comma_separated_locales(self):
+        os.chdir(self.test_dir)
+        management.call_command('makemessages', locale='pt,de,ch', verbosity=0)
+        self.assertTrue(os.path.exists(self.PO_FILE_PT))
+        self.assertTrue(os.path.exists(self.PO_FILE_DE))
diff --git a/tests/regressiontests/i18n/commands/locale/hr/LC_MESSAGES/django.po b/tests/regressiontests/i18n/commands/locale/hr/LC_MESSAGES/django.po
new file mode 100644
index 0000000000..663ca0000f
--- /dev/null
+++ b/tests/regressiontests/i18n/commands/locale/hr/LC_MESSAGES/django.po
@@ -0,0 +1,71 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-12-04 04:59-0600\n"
+"PO-Revision-Date: 2013-01-16 22:53-0300\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#. Translators: Django template comment for translators
+#: templates/test.html:9
+#, python-format
+msgid "I think that 100%% is more that 50%% of anything."
+msgstr ""
+
+#: templates/test.html:10
+#, python-format
+msgid "I think that 100%% is more that 50%% of %(obj)s."
+msgstr ""
+
+#: templates/test.html:70
+#, python-format
+msgid "Literal with a percent symbol at the end %%"
+msgstr ""
+
+#: templates/test.html:71
+#, python-format
+msgid "Literal with a percent %% symbol in the middle"
+msgstr ""
+
+#: templates/test.html:72
+#, python-format
+msgid "Completed 50%% of all the tasks"
+msgstr ""
+
+#: templates/test.html:73
+#, python-format
+msgctxt "ctx0"
+msgid "Completed 99%% of all the tasks"
+msgstr ""
+
+#: templates/test.html:74
+#, python-format
+msgid "Shouldn't double escape this sequence: %% (two percent signs)"
+msgstr ""
+
+#: templates/test.html:75
+#, python-format
+msgctxt "ctx1"
+msgid "Shouldn't double escape this sequence %% either"
+msgstr ""
+
+#: templates/test.html:76
+#, python-format
+msgid "Looks like a str fmt spec %%s but shouldn't be interpreted as such"
+msgstr "Translation of the above string"
+
+#: templates/test.html:77
+#, python-format
+msgid "Looks like a str fmt spec %% o but shouldn't be interpreted as such"
+msgstr "Translation contains %% for the above string"
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index 5d789b4acb..d9843c228a 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -32,10 +32,11 @@ if can_run_extraction_tests:
     from .commands.extraction import (ExtractorTests, BasicExtractorTests,
         JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests,
         CopyPluralFormsExtractorTests, NoWrapExtractorTests,
-        NoLocationExtractorTests, KeepPotFileExtractorTests)
+        NoLocationExtractorTests, KeepPotFileExtractorTests,
+        MultipleLocaleExtractionTests)
 if can_run_compilation_tests:
     from .commands.compilation import (PoFileTests, PoFileContentsTests,
-        PercentRenderingTests)
+        PercentRenderingTests, MultipleLocaleCompilationTests)
 from .contenttypes.tests import ContentTypeTests
 from .forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
 from .models import Company, TestModel