Ticket #17008: 17008.makemessages-keep-pot.diff

File 17008.makemessages-keep-pot.diff, 19.8 KB (added by julien, 3 years ago)
  • django/core/management/commands/makemessages.py

    diff --git a/django/core/management/commands/makemessages.py b/django/core/management/commands/makemessages.py
    index a903c3c..796262b 100644
    a b from django.core.management.base import CommandError, NoArgsCommand 
    1111from django.utils.text import get_text_list
    1212from django.utils.jslex import prepare_js_for_gettext
    1313
    14 plural_forms_re = re.compile(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL)
     14plural_forms_re = re.compile(
     15    r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL)
    1516
    1617def handle_extensions(extensions=('html',)):
    1718    """
    def handle_extensions(extensions=('html',)): 
    3738            ext_list[i] = '.%s' % ext_list[i]
    3839    return set([x for x in ext_list if x != '.py'])
    3940
     41
    4042def _popen(cmd):
    4143    """
    4244    Friendly wrapper around Popen for Windows
    4345    """
    44     p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True)
     46    p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE,
     47              close_fds=os.name != 'nt', universal_newlines=True)
    4548    return p.communicate()
    4649
     50
    4751def walk(root, topdown=True, onerror=None, followlinks=False):
    4852    """
    4953    A version of os.walk that can follow symlinks for Python < 2.6
    def walk(root, topdown=True, onerror=None, followlinks=False): 
    5761                    for link_dirpath, link_dirnames, link_filenames in walk(p):
    5862                        yield (link_dirpath, link_dirnames, link_filenames)
    5963
     64
    6065def is_ignored(path, ignore_patterns):
    6166    """
    6267    Helper function to check if the given path should be ignored or not.
    def is_ignored(path, ignore_patterns): 
    6671            return True
    6772    return False
    6873
     74
    6975def find_files(root, ignore_patterns, verbosity, symlinks=False):
    7076    """
    7177    Helper function to get all files in the given root.
    def find_files(root, ignore_patterns, verbosity, symlinks=False): 
    8288    all_files.sort()
    8389    return all_files
    8490
     91
    8592def copy_plural_forms(msgs, locale, domain, verbosity):
    8693    """
    8794    Copies plural forms header contents from a Django catalog of locale to
    def copy_plural_forms(msgs, locale, domain, verbosity): 
    8996    contents of a newly created .po file.
    9097    """
    9198    import django
    92     django_dir = os.path.normpath(os.path.join(os.path.dirname(django.__file__)))
     99    django_dir = os.path.normpath(
     100                     os.path.join(os.path.dirname(django.__file__)))
    93101    if domain == 'djangojs':
    94102        domains = ('djangojs', 'django')
    95103    else:
    96104        domains = ('django',)
    97105    for domain in domains:
    98         django_po = os.path.join(django_dir, 'conf', 'locale', locale, 'LC_MESSAGES', '%s.po' % domain)
     106        django_po = os.path.join(django_dir, 'conf', 'locale', locale,
     107                                 'LC_MESSAGES', '%s.po' % domain)
    99108        if os.path.exists(django_po):
    100109            m = plural_forms_re.search(open(django_po, 'rU').read())
    101110            if m:
    102111                if verbosity > 1:
    103                     sys.stderr.write("copying plural forms: %s\n" % m.group('value'))
     112                    sys.stderr.write(
     113                        "copying plural forms: %s\n" % m.group('value'))
    104114                lines = []
    105115                seen = False
    106116                for line in msgs.split('\n'):
    def copy_plural_forms(msgs, locale, domain, verbosity): 
    115125
    116126def make_messages(locale=None, domain='django', verbosity='1', all=False,
    117127        extensions=None, symlinks=False, ignore_patterns=[], no_wrap=False,
    118         no_location=False,
    119         no_obsolete=False):
     128        no_location=False, no_obsolete=False, keep_pot=False):
    120129    """
    121130    Uses the locale directory from the Django SVN tree or an application/
    122131    project to process all
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    139148    elif os.path.isdir('locale'):
    140149        localedir = os.path.abspath('locale')
    141150    else:
    142         raise CommandError("This script should be run from the Django SVN tree or your project or app tree. If you did indeed run it from the SVN 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.")
     151        raise CommandError(
     152            "This script should be run from the Django SVN tree or your "
     153            "project or app tree. If you did indeed run it from the SVN "
     154            "checkout or your project or application, maybe you are just "
     155            "missing the conf/locale (in the django tree) or locale (for "
     156            "project and application) directory? It is not created "
     157            "automatically, you have to create it by hand if you want to "
     158            "enable i18n for your project or application.")
    143159
    144160    if domain not in ('django', 'djangojs'):
    145         raise CommandError("currently makemessages only supports domains 'django' and 'djangojs'")
     161        raise CommandError("currently makemessages only supports domains "
     162                           "'django' and 'djangojs'")
    146163
    147164    if (locale is None and not all) or domain is None:
    148         message = "Type '%s help %s' for usage information." % (os.path.basename(sys.argv[0]), sys.argv[1])
     165        message = "Type '%s help %s' for usage information." % (
     166                  os.path.basename(sys.argv[0]), sys.argv[1])
    149167        raise CommandError(message)
    150168
    151169    # We require gettext version 0.15 or newer.
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    154172    if match:
    155173        xversion = (int(match.group('major')), int(match.group('minor')))
    156174        if xversion < (0, 15):
    157             raise CommandError("Django internationalization requires GNU gettext 0.15 or newer. You are using version %s, please upgrade your gettext toolset." % match.group())
     175            raise CommandError(
     176                "Django internationalization requires GNU gettext 0.15 or "
     177                "newer. You are using version %s, please upgrade your gettext "
     178                "toolset." % match.group())
    158179
    159180    languages = []
    160181    if locale is not None:
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    179200        if os.path.exists(potfile):
    180201            os.unlink(potfile)
    181202
    182         for dirpath, file in find_files(".", ignore_patterns, verbosity, symlinks=symlinks):
     203        for dirpath, file in find_files(".", ignore_patterns, verbosity,
     204                                        symlinks=symlinks):
    183205            file_base, file_ext = os.path.splitext(file)
    184206            if domain == 'djangojs' and file_ext in extensions:
    185207                if verbosity > 1:
    186                     sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
     208                    sys.stdout.write('processing file %s in %s\n'
     209                                     % (file, dirpath))
    187210                src = open(os.path.join(dirpath, file), "rU").read()
    188211                src = prepare_js_for_gettext(src)
    189212                thefile = '%s.c' % file
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    196219                    'xgettext -d %s -L C %s %s --keyword=gettext_noop '
    197220                    '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
    198221                    '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
    199                     '--from-code UTF-8 --add-comments=Translators -o - "%s"' % (
    200                         domain, wrap, location, os.path.join(dirpath, thefile)
    201                     )
     222                    '--from-code UTF-8 --add-comments=Translators -o - "%s"'
     223                    % (domain, wrap, location, os.path.join(dirpath, thefile))
    202224                )
    203225                msgs, errors = _popen(cmd)
    204226                if errors:
    205227                    os.unlink(os.path.join(dirpath, thefile))
    206                     if os.path.exists(potfile):
     228                    if os.path.exists(potfile) and not keep_pot:
    207229                        os.unlink(potfile)
    208230                    raise CommandError(
    209231                        "errors happened while running xgettext on %s\n%s" %
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    223245                    finally:
    224246                        f.close()
    225247                os.unlink(os.path.join(dirpath, thefile))
    226             elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
     248            elif (domain == 'django'
     249                  and (file_ext == '.py' or file_ext in extensions)):
    227250                thefile = file
    228251                orig_file = os.path.join(dirpath, file)
    229252                if file_ext in extensions:
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    235258                    finally:
    236259                        f.close()
    237260                if verbosity > 1:
    238                     sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
     261                    sys.stdout.write('processing file %s in %s\n'
     262                                     % (file, dirpath))
    239263                cmd = (
    240264                    'xgettext -d %s -L Python %s %s --keyword=gettext_noop '
    241265                    '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    250274                if errors:
    251275                    if thefile != file:
    252276                        os.unlink(os.path.join(dirpath, thefile))
    253                     if os.path.exists(potfile):
     277                    if os.path.exists(potfile) and not keep_pot:
    254278                        os.unlink(potfile)
    255279                    raise CommandError(
    256280                        "errors happened while running xgettext on %s\n%s" %
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    277301            msgs, errors = _popen('msguniq %s %s --to-code=utf-8 "%s"' %
    278302                                  (wrap, location, potfile))
    279303            if errors:
    280                 os.unlink(potfile)
     304                if not keep_pot:
     305                    os.unlink(potfile)
    281306                raise CommandError(
    282307                    "errors happened while running msguniq\n%s" % errors)
    283308            if os.path.exists(pofile):
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    289314                msgs, errors = _popen('msgmerge %s %s -q "%s" "%s"' %
    290315                                      (wrap, location, pofile, potfile))
    291316                if errors:
    292                     os.unlink(potfile)
     317                    if not keep_pot:
     318                        os.unlink(potfile)
    293319                    raise CommandError(
    294320                        "errors happened while running msgmerge\n%s" % errors)
    295321            elif not invoked_for_django:
    296322                msgs = copy_plural_forms(msgs, locale, domain, verbosity)
    297323            msgs = msgs.replace(
    298                 "#. #-#-#-#-#  %s.pot (PACKAGE VERSION)  #-#-#-#-#\n" % domain, "")
     324                "#. #-#-#-#-#  %s.pot (PACKAGE VERSION)  #-#-#-#-#\n"
     325                % domain, "")
    299326            f = open(pofile, 'wb')
    300327            try:
    301328                f.write(msgs)
    302329            finally:
    303330                f.close()
    304             os.unlink(potfile)
     331            if not keep_pot:
     332                os.unlink(potfile)
    305333            if no_obsolete:
    306                 msgs, errors = _popen('msgattrib %s %s -o "%s" --no-obsolete "%s"' %
    307                                       (wrap, location, pofile, pofile))
     334                msgs, errors = _popen(
     335                    'msgattrib %s %s -o "%s" --no-obsolete "%s"'
     336                    %(wrap, location, pofile, pofile))
    308337                if errors:
    309338                    raise CommandError(
    310339                        "errors happened while running msgattrib\n%s" % errors)
    def make_messages(locale=None, domain='django', verbosity='1', all=False, 
    313342class Command(NoArgsCommand):
    314343    option_list = NoArgsCommand.option_list + (
    315344        make_option('--locale', '-l', default=None, dest='locale',
    316             help='Creates or updates the message files for the given locale (e.g. pt_BR).'),
     345            help='Creates or updates the message files for the given locale '
     346                 '(e.g. pt_BR).'),
    317347        make_option('--domain', '-d', default='django', dest='domain',
    318348            help='The domain of the message files (default: "django").'),
    319349        make_option('--all', '-a', action='store_true', dest='all',
    320             default=False, help='Updates the message files for all existing locales.'),
    321         make_option('--extension', '-e', dest='extensions',
    322             help='The file extension(s) to examine (default: "html,txt", or "js" if the domain is "djangojs"). Separate multiple extensions with commas, or use -e multiple times.',
    323             action='append'),
     350            default=False, help='Updates the message files for all existing '
     351                                'locales.'),
     352        make_option('--extension', '-e', dest='extensions', action='append',
     353            help='The file extension(s) to examine (default: "html,txt", or '
     354                 '"js" if the domain is "djangojs"). Separate multiple '
     355                 'extensions with commas, or use -e multiple times.'),
    324356        make_option('--symlinks', '-s', action='store_true', dest='symlinks',
    325             default=False, help='Follows symlinks to directories when examining source code and templates for translation strings.'),
     357            default=False, help='Follows symlinks to directories when '
     358                                'examining source code and templates for '
     359                                'translation strings.'),
    326360        make_option('--ignore', '-i', action='append', dest='ignore_patterns',
    327             default=[], metavar='PATTERN', help='Ignore files or directories matching this glob-style pattern. Use multiple times to ignore more.'),
    328         make_option('--no-default-ignore', action='store_false', dest='use_default_ignore_patterns',
    329             default=True, help="Don't ignore the common glob-style patterns 'CVS', '.*' and '*~'."),
     361            default=[], metavar='PATTERN',
     362            help='Ignore files or directories matching this glob-style '
     363                 'pattern. Use multiple times to ignore more.'),
     364        make_option('--no-default-ignore', action='store_false',
     365            default=True, dest='use_default_ignore_patterns',
     366            help="Don't ignore the common glob-style patterns 'CVS', '.*' and "
     367                 "'*~'."),
    330368        make_option('--no-wrap', action='store_true', dest='no_wrap',
    331             default=False, help="Don't break long message lines into several lines"),
     369            default=False, help="Don't break long message lines into several "
     370                                "lines"),
    332371        make_option('--no-location', action='store_true', dest='no_location',
    333372            default=False, help="Don't write '#: filename:line' lines"),
    334373        make_option('--no-obsolete', action='store_true', dest='no_obsolete',
    335374            default=False, help="Remove obsolete message strings"),
     375        make_option('--keep-pot', action='store_true', dest='keep_pot',
     376            default=False, help="Keep .pot file after making messages. Useful "
     377                                "when debugging."),
    336378    )
    337379    help = ( "Runs over the entire source tree of the current directory and "
    338 "pulls out all strings marked for translation. It creates (or updates) a message "
    339 "file in the conf/locale (in the django tree) or locale (for projects and "
    340 "applications) directory.\n\nYou must run this command with one of either the "
    341 "--locale or --all options.")
     380"pulls out all strings marked for translation. It creates (or updates) a "
     381"message file in the conf/locale (in the django tree) or locale (for projects "
     382"and applications) directory.\n\nYou must run this command with one of either "
     383"the --locale or --all options.")
    342384
    343385    requires_model_validation = False
    344386    can_import_settings = False
    class Command(NoArgsCommand): 
    357399        no_wrap = options.get('no_wrap')
    358400        no_location = options.get('no_location')
    359401        no_obsolete = options.get('no_obsolete')
     402        keep_pot = options.get('keep_pot')
    360403        if domain == 'djangojs':
    361404            extensions = handle_extensions(extensions or ['js'])
    362405        else:
    class Command(NoArgsCommand): 
    366409            sys.stdout.write('examining files with the extensions: %s\n'
    367410                             % get_text_list(list(extensions), 'and'))
    368411
    369         make_messages(locale, domain, verbosity, process_all, extensions, symlinks, ignore_patterns, no_wrap, no_location, no_obsolete)
     412        make_messages(locale, domain, verbosity, process_all, extensions,
     413            symlinks, ignore_patterns, no_wrap, no_location, no_obsolete,
     414            keep_pot)
  • docs/ref/django-admin.txt

    diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
    index 8b83a43..5d71901 100644
    a b Use the ``--no-location`` option to not write '``#: filename:line``' 
    483483comment lines in language files. Note that using this option makes it harder
    484484for technically skilled translators to understand each message's context.
    485485
     486.. django-admin-option:: --keep-pot
     487
     488.. versionadded:: 1.4
     489
     490Use the ``--keep-pot`` option to prevent django from deleting the temporary
     491.pot file it generates before creating the .po file. This is useful for
     492debugging errors which may prevent the final language files from being created.
     493
    486494reset <appname appname ...>
    487495---------------------------
    488496
  • tests/regressiontests/i18n/commands/extraction.py

    diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
    index 6566ef4..7a2683c 100644
    a b class NoLocationExtractorTests(ExtractorTests): 
    265265        with open(self.PO_FILE, 'r') as fp:
    266266            po_contents = fp.read()
    267267            self.assertTrue('#: templates/test.html:55' in po_contents)
     268
     269
     270class KeepPotFileExtractorTests(ExtractorTests):
     271
     272    def setUp(self):
     273        self.POT_FILE = self.PO_FILE + 't'
     274        super(KeepPotFileExtractorTests, self).setUp()
     275
     276    def tearDown(self):
     277        super(KeepPotFileExtractorTests, self).tearDown()
     278        os.chdir(self.test_dir)
     279        try:
     280            os.unlink(self.POT_FILE)
     281        except OSError:
     282            pass
     283        os.chdir(self._cwd)
     284
     285    def test_keep_pot_disabled_by_default(self):
     286        os.chdir(self.test_dir)
     287        management.call_command('makemessages', locale=LOCALE, verbosity=0)
     288        self.assertFalse(os.path.exists(self.POT_FILE))
     289
     290    def test_keep_pot_explicitly_disabled(self):
     291        os.chdir(self.test_dir)
     292        management.call_command('makemessages', locale=LOCALE, verbosity=0,
     293            keep_pot=False)
     294        self.assertFalse(os.path.exists(self.POT_FILE))
     295
     296    def test_keep_pot_enabled(self):
     297        os.chdir(self.test_dir)
     298        management.call_command('makemessages', locale=LOCALE, verbosity=0,
     299            keep_pot=True)
     300        self.assertTrue(os.path.exists(self.POT_FILE))
  • tests/regressiontests/i18n/tests.py

    diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
    index cc17d74..a87de2e 100644
    a b if can_run_extraction_tests: 
    2929    from .commands.extraction import (ExtractorTests, BasicExtractorTests,
    3030        JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests,
    3131        CopyPluralFormsExtractorTests, NoWrapExtractorTests,
    32         NoLocationExtractorTests)
     32        NoLocationExtractorTests, KeepPotFileExtractorTests)
    3333if can_run_compilation_tests:
    3434    from .commands.compilation import (PoFileTests, PoFileContentsTests,
    3535        PercentRenderingTests)
Back to Top