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 Paul Bissex Simon Blanchard + Craig Blaszczyk David Blewett Matt Boersma Artem Gnilov 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 , 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 \n" +"Language-Team: LANGUAGE \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