From ce27fb198dcce5dad47de83fc81119d3bb6567ce Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Fri, 25 Jan 2013 13:58:37 -0300 Subject: [PATCH] Revert "Patch by Claude for #16084." This reverts commit 2babab0bb351ff7a13fd23795f5e926a9bf95d22. --- .../core/management/commands/makemessages.py | 141 ++++++------------ docs/man/django-admin.1 | 3 +- docs/ref/django-admin.txt | 4 +- docs/topics/i18n/translation.txt | 17 ++- .../i18n/commands/extraction.py | 44 ------ .../i18n/commands/project_dir/__init__.py | 3 - .../project_dir/app_no_locale/models.py | 4 - .../project_dir/app_with_locale/models.py | 4 - tests/regressiontests/i18n/tests.py | 2 +- 9 files changed, 69 insertions(+), 153 deletions(-) delete mode 100644 tests/regressiontests/i18n/commands/project_dir/__init__.py delete mode 100644 tests/regressiontests/i18n/commands/project_dir/app_no_locale/models.py delete mode 100644 tests/regressiontests/i18n/commands/project_dir/app_with_locale/models.py diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py index b086e5f2dd..4550605af2 100644 --- a/django/core/management/commands/makemessages.py +++ b/django/core/management/commands/makemessages.py @@ -19,28 +19,25 @@ STATUS_OK = 0 @total_ordering class TranslatableFile(object): - def __init__(self, dirpath, file_name, locale_dir): + def __init__(self, dirpath, file_name): self.file = file_name self.dirpath = dirpath - self.locale_dir = locale_dir def __repr__(self): return "" % os.sep.join([self.dirpath, self.file]) def __eq__(self, other): - return self.path == other.path + return self.dirpath == other.dirpath and self.file == other.file def __lt__(self, other): - return self.path < other.path + if self.dirpath == other.dirpath: + return self.file < other.file + return self.dirpath < other.dirpath - @property - def path(self): - return os.path.join(self.dirpath, self.file) - - def process(self, command, domain): + def process(self, command, potfile, domain, keep_pot=False): """ - Extract translatable literals from self.file for :param domain:, - creating or updating the POT file. + Extract translatable literals from self.file for :param domain: + creating or updating the :param potfile: POT file. Uses the xgettext GNU gettext utility. """ @@ -94,6 +91,8 @@ class TranslatableFile(object): if status != STATUS_OK: if is_templatized: os.unlink(work_file) + if not keep_pot and os.path.exists(potfile): + os.unlink(potfile) raise CommandError( "errors happened while running xgettext on %s\n%s" % (self.file, errors)) @@ -101,14 +100,11 @@ class TranslatableFile(object): # Print warnings command.stdout.write(errors) if msgs: - # Write/append messages to pot file - potfile = os.path.join(self.locale_dir, '%s.pot' % str(domain)) if is_templatized: old = '#: ' + work_file[2:] new = '#: ' + orig_file[2:] msgs = msgs.replace(old, new) write_pot_file(potfile, msgs) - if is_templatized: os.unlink(work_file) @@ -236,21 +232,21 @@ class Command(NoArgsCommand): settings.configure(USE_I18N = True) self.invoked_for_django = False - self.locale_paths = [] - self.default_locale_path = None if os.path.isdir(os.path.join('conf', 'locale')): - self.locale_paths = [os.path.abspath(os.path.join('conf', 'locale'))] - self.default_locale_path = self.locale_paths[0] + localedir = os.path.abspath(os.path.join('conf', 'locale')) self.invoked_for_django = True # Ignoring all contrib apps self.ignore_patterns += ['contrib/*'] + elif os.path.isdir('locale'): + localedir = os.path.abspath('locale') else: - self.locale_paths.extend(list(settings.LOCALE_PATHS)) - # Allow to run makemessages inside an app dir - if os.path.isdir('locale'): - self.locale_paths.append(os.path.abspath('locale')) - if self.locale_paths: - self.default_locale_path = self.locale_paths[0] + raise CommandError("This script should be run from the Django Git " + "tree or your project or app tree. If you did indeed run it " + "from the Git checkout or your project or application, " + "maybe you are just missing the conf/locale (in the django " + "tree) or locale (for project and application) directory? It " + "is not created automatically, you have to create it by hand " + "if you want to enable i18n for your project or application.") # We require gettext version 0.15 or newer. output, errors, status = _popen('xgettext --version') @@ -265,25 +261,24 @@ class Command(NoArgsCommand): "gettext 0.15 or newer. You are using version %s, please " "upgrade your gettext toolset." % match.group()) + potfile = self.build_pot_file(localedir) + + # Build po files for each selected locale + locales = [] + if locale is not None: + locales += locale.split(',') if not isinstance(locale, list) else locale + elif process_all: + locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % localedir)) + locales = [os.path.basename(l) for l in locale_dirs] + try: - potfiles = self.build_potfiles() - - # Build po files for each selected locale - locales = [] - if locale is not None: - locales = locale.split(',') if not isinstance(locale, list) else locale - elif process_all: - locale_dirs = filter(os.path.isdir, glob.glob('%s/*' % self.default_locale_path)) - locales = [os.path.basename(l) for l in locale_dirs] - for locale in locales: if self.verbosity > 0: self.stdout.write("processing locale %s\n" % locale) - for potfile in potfiles: - self.write_po_file(potfile, locale) + self.write_po_file(potfile, locale) finally: - if not self.keep_pot: - self.remove_potfiles() + if not self.keep_pot and os.path.exists(potfile): + os.unlink(potfile) def build_pot_file(self, localedir): file_list = self.find_files(".") @@ -297,41 +292,9 @@ class Command(NoArgsCommand): f.process(self, potfile, self.domain, self.keep_pot) return potfile - def build_potfiles(self): - """Build pot files and apply msguniq to them""" - file_list = self.find_files(".") - self.remove_potfiles() - for f in file_list: - f.process(self, self.domain) - - potfiles = [] - for path in self.locale_paths: - potfile = os.path.join(path, '%s.pot' % str(self.domain)) - if not os.path.exists(potfile): - continue - msgs, errors, status = _popen('msguniq %s %s --to-code=utf-8 "%s"' % - (self.wrap, self.location, potfile)) - if errors: - if status != STATUS_OK: - raise CommandError( - "errors happened while running msguniq\n%s" % errors) - elif self.verbosity > 0: - self.stdout.write(errors) - with open(potfile, 'w') as fp: - fp.write(msgs) - potfiles.append(potfile) - return potfiles - - def remove_potfiles(self): - for path in self.locale_paths: - pot_path = os.path.join(path, '%s.pot' % str(self.domain)) - if os.path.exists(pot_path): - os.unlink(pot_path) - def find_files(self, root): """ - Helper function to get all files in the given root. Also check that there - is a matching locale dir for each file. + Helper method to get all files in the given root. """ def is_ignored(path, ignore_patterns): @@ -352,26 +315,12 @@ class Command(NoArgsCommand): dirnames.remove(dirname) if self.verbosity > 1: self.stdout.write('ignoring directory %s\n' % dirname) - elif dirname == 'locale': - dirnames.remove(dirname) - self.locale_paths.insert(0, os.path.join(os.path.abspath(dirpath), dirname)) for filename in filenames: - file_path = os.path.normpath(os.path.join(dirpath, filename)) - if is_ignored(file_path, self.ignore_patterns): + if is_ignored(os.path.normpath(os.path.join(dirpath, filename)), self.ignore_patterns): if self.verbosity > 1: self.stdout.write('ignoring file %s in %s\n' % (filename, dirpath)) else: - locale_dir = None - for path in self.locale_paths: - if os.path.abspath(dirpath).startswith(os.path.dirname(path)): - locale_dir = path - break - if not locale_dir: - locale_dir = self.default_locale_path - if not locale_dir: - raise CommandError( - "Unable to find a locale path to store translations for file %s" % file_path) - all_files.append(TranslatableFile(dirpath, filename, locale_dir)) + all_files.append(TranslatableFile(dirpath, filename)) return sorted(all_files) def write_po_file(self, potfile, locale): @@ -379,8 +328,16 @@ class Command(NoArgsCommand): Creates or updates the PO file for self.domain and :param locale:. Uses contents of the existing :param potfile:. - Uses msgmerge, and msgattrib GNU gettext utilities. + Uses mguniq, msgmerge, and msgattrib GNU gettext utilities. """ + msgs, errors, status = _popen('msguniq %s %s --to-code=utf-8 "%s"' % + (self.wrap, self.location, potfile)) + if errors: + if status != STATUS_OK: + raise CommandError( + "errors happened while running msguniq\n%s" % errors) + elif self.verbosity > 0: + self.stdout.write(errors) basedir = os.path.join(os.path.dirname(potfile), locale, 'LC_MESSAGES') if not os.path.isdir(basedir): @@ -388,6 +345,8 @@ class Command(NoArgsCommand): pofile = os.path.join(basedir, '%s.po' % str(self.domain)) if os.path.exists(pofile): + with open(potfile, 'w') as fp: + fp.write(msgs) msgs, errors, status = _popen('msgmerge %s %s -q "%s" "%s"' % (self.wrap, self.location, pofile, potfile)) if errors: @@ -396,10 +355,8 @@ class Command(NoArgsCommand): "errors happened while running msgmerge\n%s" % errors) elif self.verbosity > 0: self.stdout.write(errors) - else: - msgs = open(potfile, 'r').read() - if not self.invoked_for_django: - msgs = self.copy_plural_forms(msgs, locale) + elif not self.invoked_for_django: + msgs = self.copy_plural_forms(msgs, locale) msgs = msgs.replace( "#. #-#-#-#-# %s.pot (PACKAGE VERSION) #-#-#-#-#\n" % self.domain, "") with open(pofile, 'w') as fp: diff --git a/docs/man/django-admin.1 b/docs/man/django-admin.1 index c9c8d19869..4d937b488b 100644 --- a/docs/man/django-admin.1 +++ b/docs/man/django-admin.1 @@ -193,8 +193,7 @@ Ignore files or directories matching this glob-style pattern. Use multiple times to ignore more (makemessages command). .TP .I \-\-no\-default\-ignore -Don't ignore the common private glob-style patterns 'CVS', '.*', '*~' and '*.pyc' -(makemessages command). +Don't ignore the common private glob-style patterns 'CVS', '.*' and '*~' (makemessages command). .TP .I \-\-no\-wrap Don't break long message lines into several lines (makemessages command). diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt index f7b91bbdab..8f6664edb7 100644 --- a/docs/ref/django-admin.txt +++ b/docs/ref/django-admin.txt @@ -472,7 +472,7 @@ Example usage:: Use the ``--ignore`` or ``-i`` option to ignore files or directories matching the given :mod:`glob`-style pattern. Use multiple times to ignore more. -These patterns are used by default: ``'CVS'``, ``'.*'``, ``'*~'``, ``'*.pyc'`` +These patterns are used by default: ``'CVS'``, ``'.*'``, ``'*~'`` Example usage:: @@ -499,7 +499,7 @@ for technically skilled translators to understand each message's context. .. versionadded:: 1.6 Use the ``--keep-pot`` option to prevent django from deleting the temporary -.pot files it generates before creating the .po file. This is useful for +.pot file it generates before creating the .po file. This is useful for debugging errors which may prevent the final language files from being created. runfcgi [options] diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 8ef51e4052..01f168bc10 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -1543,9 +1543,24 @@ All message file repositories are structured the same way. They are: * ``$PYTHONPATH/django/conf/locale//LC_MESSAGES/django.(po|mo)`` To create message files, you use the :djadmin:`django-admin.py makemessages ` -tool. And you use :djadmin:`django-admin.py compilemessages ` +tool. You only need to be in the same directory where the ``locale/`` directory +is located. And you use :djadmin:`django-admin.py compilemessages ` to produce the binary ``.mo`` files that are used by ``gettext``. You can also run :djadmin:`django-admin.py compilemessages --settings=path.to.settings ` to make the compiler process all the directories in your :setting:`LOCALE_PATHS` setting. + +Finally, you should give some thought to the structure of your translation +files. If your applications need to be delivered to other users and will be used +in other projects, you might want to use app-specific translations. But using +app-specific translations and project-specific translations could produce weird +problems with :djadmin:`makemessages`: it will traverse all directories below +the current path and so might put message IDs into a unified, common message +file for the current project that are already in application message files. + +The easiest way out is to store applications that are not part of the project +(and so carry their own translations) outside the project tree. That way, +:djadmin:`django-admin.py makemessages `, when ran on a project +level will only extract strings that are connected to your explicit project and +not strings that are distributed independently. diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py index 8b2941c4d4..ef711ec1bb 100644 --- a/tests/regressiontests/i18n/commands/extraction.py +++ b/tests/regressiontests/i18n/commands/extraction.py @@ -5,13 +5,10 @@ import os import re import shutil -from django.conf import settings from django.core import management from django.test import SimpleTestCase -from django.test.utils import override_settings from django.utils.encoding import force_text from django.utils._os import upath -from django.utils import six from django.utils.six import StringIO @@ -355,44 +352,3 @@ class MultipleLocaleExtractionTests(ExtractorTests): 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)) - - -class CustomLayoutExtractionTests(ExtractorTests): - def setUp(self): - self._cwd = os.getcwd() - self.test_dir = os.path.join(os.path.dirname(upath(__file__)), 'project_dir') - - def test_no_locale_raises(self): - os.chdir(self.test_dir) - with six.assertRaisesRegex(self, management.CommandError, - "Unable to find a locale path to store translations for file"): - management.call_command('makemessages', locale=LOCALE, verbosity=0) - - @override_settings( - LOCALE_PATHS=(os.path.join(os.path.dirname(upath(__file__)), 'project_dir/project_locale'),) - ) - def test_project_locale_paths(self): - """ - Test that: - * translations for app containing locale folder are stored in that folder - * translations outside of that app are in LOCALE_PATHS[0] - """ - os.chdir(self.test_dir) - self.addCleanup(shutil.rmtree, os.path.join(settings.LOCALE_PATHS[0], LOCALE)) - self.addCleanup(shutil.rmtree, os.path.join(self.test_dir, 'app_with_locale/locale', LOCALE)) - - management.call_command('makemessages', locale=LOCALE, verbosity=0) - project_de_locale = os.path.join( - self.test_dir, 'project_locale/de/LC_MESSAGES/django.po',) - app_de_locale = os.path.join( - self.test_dir, 'app_with_locale/locale/de/LC_MESSAGES/django.po',) - self.assertTrue(os.path.exists(project_de_locale)) - self.assertTrue(os.path.exists(app_de_locale)) - - with open(project_de_locale, 'r') as fp: - po_contents = force_text(fp.read()) - self.assertMsgId('This app has no locale directory', po_contents) - self.assertMsgId('This is a project-level string', po_contents) - with open(app_de_locale, 'r') as fp: - po_contents = force_text(fp.read()) - self.assertMsgId('This app has a locale directory', po_contents) diff --git a/tests/regressiontests/i18n/commands/project_dir/__init__.py b/tests/regressiontests/i18n/commands/project_dir/__init__.py deleted file mode 100644 index 9c6768e4ab..0000000000 --- a/tests/regressiontests/i18n/commands/project_dir/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.utils.translation import ugettext as _ - -string = _("This is a project-level string") diff --git a/tests/regressiontests/i18n/commands/project_dir/app_no_locale/models.py b/tests/regressiontests/i18n/commands/project_dir/app_no_locale/models.py deleted file mode 100644 index adcb2ef173..0000000000 --- a/tests/regressiontests/i18n/commands/project_dir/app_no_locale/models.py +++ /dev/null @@ -1,4 +0,0 @@ -from django.utils.translation import ugettext as _ - -string = _("This app has no locale directory") - diff --git a/tests/regressiontests/i18n/commands/project_dir/app_with_locale/models.py b/tests/regressiontests/i18n/commands/project_dir/app_with_locale/models.py deleted file mode 100644 index 44037157a0..0000000000 --- a/tests/regressiontests/i18n/commands/project_dir/app_with_locale/models.py +++ /dev/null @@ -1,4 +0,0 @@ -from django.utils.translation import ugettext as _ - -string = _("This app has a locale directory") - diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 9f6e73dcd2..d9843c228a 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -33,7 +33,7 @@ if can_run_extraction_tests: JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests, CopyPluralFormsExtractorTests, NoWrapExtractorTests, NoLocationExtractorTests, KeepPotFileExtractorTests, - MultipleLocaleExtractionTests, CustomLayoutExtractionTests) + MultipleLocaleExtractionTests) if can_run_compilation_tests: from .commands.compilation import (PoFileTests, PoFileContentsTests, PercentRenderingTests, MultipleLocaleCompilationTests)