Ticket #17375: patch_17375_updated.diff

File patch_17375_updated.diff, 13.9 KB (added by pigletto, 3 years ago)

Patch updated to current trunk

  • tests/regressiontests/i18n/commands/extraction.py

     
    4242        msgid = re.escape(msgid)
    4343        return self.assertTrue(re.search('^msgid %s' % msgid, s, re.MULTILINE), 'Could not find %(q)s%(n)s%(q)s in generated PO file' % {'n':needle, 'q':q})
    4444
     45    def assertMsgIdPlural(self, msgid, s, use_quotes=True):
     46        q = '"'
     47        if use_quotes:
     48            msgid = '"%s"' % msgid
     49            q = "'"
     50        needle = 'msgid_plural %s' % msgid
     51        msgid = re.escape(msgid)
     52        return self.assertTrue(re.search('^msgid_plural %s' % msgid, s, re.MULTILINE), 'Could not find %(q)s%(n)s%(q)s in generated PO file' % {'n':needle, 'q':q})
     53
    4554    def assertNotMsgId(self, msgid, s, use_quotes=True):
    4655        if use_quotes:
    4756            msgid = '"%s"' % msgid
     
    268277        with open(self.PO_FILE, 'r') as fp:
    269278            po_contents = fp.read()
    270279            self.assertTrue('#: templates/test.html:55' in po_contents)
     280
     281
     282class MultipleFilesPluralExtractorTests(ExtractorTests):
     283
     284    def setUp(self):
     285        self._cwd = os.getcwd()
     286        self.test_dir = os.path.abspath(os.path.dirname(__file__))
     287        self.second_test_template = os.path.join(self.test_dir, 'templates', 'plural_test_template.html')
     288
     289        second_test_template = '''{% load i18n %}{% trans 'My string' %}'''
     290
     291        if not os.path.exists(self.second_test_template):
     292            f = open(self.second_test_template, 'w+')
     293            try:
     294                f.write(second_test_template)
     295            finally:
     296                f.close()
     297
     298    def tearDown(self):
     299        super(MultipleFilesPluralExtractorTests, self).tearDown()
     300        os.chdir(self.test_dir)
     301        try:
     302            os.remove(self.second_test_template)
     303        except OSError:
     304            pass
     305        os.chdir(self._cwd)
     306
     307    def test_multiple_files_plural_extraction(self):
     308        os.chdir(self.test_dir)
     309        management.call_command('makemessages', locale=LOCALE, verbosity=0, symlinks=True)
     310        self.assertTrue(os.path.exists(self.PO_FILE))
     311        with open(self.PO_FILE, 'r') as fp:
     312            po_contents = fp.read()
     313            self.assertMsgId('My string', po_contents)
     314            self.assertMsgIdPlural('My strings', po_contents)
     315 No newline at end of file
  • tests/regressiontests/i18n/commands/templates/test.html

     
    7777{% trans "Shouldn't double escape this sequence %% either" context "ctx1" %}
    7878{% trans "Looks like a str fmt spec %s but shouldn't be interpreted as such" %}
    7979{% trans "Looks like a str fmt spec % o but shouldn't be interpreted as such" %}
     80{% blocktrans count counter=mylist|length %}My string{% plural %}My strings{% endblocktrans %}
  • tests/regressiontests/i18n/tests.py

     
    2929    from .commands.extraction import (ExtractorTests, BasicExtractorTests,
    3030        JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests,
    3131        CopyPluralFormsExtractorTests, NoWrapExtractorTests,
    32         NoLocationExtractorTests)
     32        NoLocationExtractorTests, MultipleFilesPluralExtractorTests)
    3333if can_run_compilation_tests:
    3434    from .commands.compilation import (PoFileTests, PoFileContentsTests,
    3535        PercentRenderingTests)
  • django/core/management/commands/makemessages.py

     
    33import os
    44import re
    55import sys
     6import tempfile
    67from itertools import dropwhile
    78from optparse import make_option
    89from subprocess import PIPE, Popen
     
    127128                break
    128129    return msgs
    129130
    130 def write_pot_file(potfile, msgs, file, work_file, is_templatized):
     131def write_pot_file(potfile, msgs, all_files):
    131132    """
    132133    Write the :param potfile: POT file with the :param msgs: contents,
    133134    previously making sure its format is valid.
    134135    """
    135     if is_templatized:
    136         old = '#: ' + work_file[2:]
    137         new = '#: ' + file[2:]
    138         msgs = msgs.replace(old, new)
     136    for f_data in all_files:
     137        if f_data['is_templatized']:
     138            old = '#: ' + f_data['work'][2:]
     139            new = '#: ' + f_data['orig'][2:]
     140            msgs = msgs.replace(old, new)
    139141    if os.path.exists(potfile):
    140142        # Strip the header
    141143        msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
     
    147149    finally:
    148150        f.close()
    149151
    150 def process_file(file, dirpath, potfile, domain, verbosity,
    151                  extensions, wrap, location, stdout=sys.stdout):
     152def process_files(potfile, domain, verbosity, extensions, wrap, location,
     153                  stdout, ignore_patterns, symlinks):
    152154    """
    153     Extract translatable literals from :param file: for :param domain:
     155    Extract translatable literals from current directory for :param domain:
    154156    creating or updating the :param potfile: POT file.
    155157
    156158    Uses the xgettext GNU gettext utility.
     159
     160    Procedure used is as follows:
     161    1. Prepare template of xgettext command that will be executed
     162       (depending on domain) - cmd
     163
     164    2. Parse all files and prepare list of dictionaries containing
     165       information about each file that will be processed:
     166          {'orig': ''  # path to original file
     167           'work': ''  # path to either original file or templatized
     168                       # version if one will be created
     169           'is_templatized': False  # defines if file will be templatized
     170
     171    3. Create temporary file containing all filenames that are going to
     172       be processed by xgettext
     173
     174    4. Execute xgettext command (cmd prepared in step 1) using path to
     175       file created in step 4
     176
     177    5. If everything is ok: write POT file
     178       If error occured while running xgettext: raise exception
     179
     180    6. For both success and error remove all templatized files that were
     181       created
    157182    """
    158 
    159183    from django.utils.translation import templatize
    160184
    161     if verbosity > 1:
    162         stdout.write('processing file %s in %s\n' % (file, dirpath))
    163     _, file_ext = os.path.splitext(file)
    164     if domain == 'djangojs' and file_ext in extensions:
    165         is_templatized = True
    166         orig_file = os.path.join(dirpath, file)
    167         src_data = open(orig_file).read()
    168         src_data = prepare_js_for_gettext(src_data)
    169         thefile = '%s.c' % file
    170         work_file = os.path.join(dirpath, thefile)
    171         f = open(work_file, "w")
    172         try:
    173             f.write(src_data)
    174         finally:
    175             f.close()
     185    # stores information about all processed files
     186    all_files = []
     187
     188    # prepare command to run xgettext depending on domain type
     189    if domain == 'djangojs':
    176190        cmd = (
    177191            'xgettext -d %s -L C %s %s --keyword=gettext_noop '
    178192            '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
    179193            '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
    180             '--from-code UTF-8 --add-comments=Translators -o - "%s"' %
    181             (domain, wrap, location, work_file))
    182     elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
    183         thefile = file
    184         orig_file = os.path.join(dirpath, file)
    185         is_templatized = file_ext in extensions
    186         if is_templatized:
    187             src_data = open(orig_file, "rU").read()
    188             thefile = '%s.py' % file
    189             content = templatize(src_data, orig_file[2:])
    190             f = open(os.path.join(dirpath, thefile), "w")
    191             try:
    192                 f.write(content)
    193             finally:
    194                 f.close()
    195         work_file = os.path.join(dirpath, thefile)
     194            '--from-code UTF-8 --add-comments=Translators -o - -f %s'
     195        )
     196    elif domain == 'django':
    196197        cmd = (
    197198            'xgettext -d %s -L Python %s %s --keyword=gettext_noop '
    198199            '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
     
    200201            '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 '
    201202            '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
    202203            '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
    203             '--add-comments=Translators -o - "%s"' %
    204             (domain, wrap, location, work_file))
     204            '--add-comments=Translators -o - -f %s'
     205        )
     206
     207    # check all files and prepare list of dictionaries containing information
     208    # about each file that is going to be processed by xgettext
     209    for dirpath, file in find_files(".", ignore_patterns, verbosity,
     210                                    stdout, symlinks=symlinks):
     211        file_dict = process_file(file, dirpath, domain, verbosity,
     212                                 extensions, stdout)
     213        if file_dict:
     214            all_files.append(file_dict)
     215
     216    if all_files:
     217        # Prepare command that runs xgettext.
     218        # Part of this command is a path to config file that
     219        # contains paths of all files to be processed
     220        input_files = '\n'.join([f_data['work'] for f_data in all_files])
     221
     222        # create temporary config file for xgettext
     223        f_handle, input_files_path = tempfile.mkstemp()
     224        f = open(input_files_path, 'w')
     225        try:
     226            f.write(input_files)
     227        finally:
     228            f.close()
     229
     230        # run xgettext against temporary config file
     231        cmd = cmd % (domain, wrap, location, input_files_path)
     232        msgs, errors = _popen(cmd)
     233
     234        # remove temporary config file
     235        os.unlink(input_files_path)
     236
     237        if errors:
     238            # if error(s) occured we have to remove all templatized files
     239            # We have to check if file exists because when symlinks are used it might
     240            # happen that file was already removed
     241            [os.unlink(f_data['work']) for f_data in all_files
     242               if f_data['is_templatized'] and os.path.exists(f_data['work'])]
     243
     244            input_files = ', '.join([f_data['work'] for f_data in all_files])
     245            raise CommandError(
     246                "errors happened while running xgettext on %s\n%s" %
     247                (input_files, errors))
     248
     249        if msgs:
     250            write_pot_file(potfile, msgs, all_files)
     251        # check if file exists because when symlinks are used it might
     252        # happen that file was already removed
     253        [os.unlink(f_data['work']) for f_data in all_files
     254               if f_data['is_templatized'] and os.path.exists(f_data['work'])]
     255
     256def process_file(file, dirpath, domain, verbosity,
     257                 extensions, stdout=sys.stdout):
     258    """
     259    Extract translatable literals from :param file: for :param domain:
     260    and return dictionary containing informations about original file path,
     261    and path to templatized file if one was created.
     262    """
     263
     264    from django.utils.translation import templatize
     265
     266    if verbosity > 1:
     267        stdout.write('processing file %s in %s\n' % (file, dirpath))
     268    _, file_ext = os.path.splitext(file)
     269
     270    is_templatized = file_ext in extensions
     271
     272    # path to the processed file
     273    orig_file_path = os.path.join(dirpath, file)
     274
     275    # prepare information about processed file;
     276    # 'orig' - path to original file
     277    # 'work' - path to either original file or templatized version if one will be created
     278    # 'is_templatized' - defines if file will be templatized
     279    file_dict = {'orig': orig_file_path,
     280                 'work': orig_file_path,
     281                 'is_templatized': is_templatized}
     282
     283    # if file is going to be templatized then we have to create its special
     284    # version prepared for gettext and save it with new filename
     285    if domain == 'djangojs' and is_templatized:
     286        src_data = open(orig_file_path).read()
     287        content = prepare_js_for_gettext(src_data)
     288        thefile = '%s.c' % file
     289    elif domain == 'django' and (file_ext == '.py' or is_templatized):
     290        if is_templatized:
     291            src_data = open(orig_file_path, "rU").read()
     292            content = templatize(src_data, orig_file_path[2:])
     293            thefile = '%s.py' % file
    205294    else:
    206295        return
    207     msgs, errors = _popen(cmd)
    208     if errors:
    209         if is_templatized:
    210             os.unlink(work_file)
    211         if os.path.exists(potfile):
    212             os.unlink(potfile)
    213         raise CommandError(
    214             "errors happened while running xgettext on %s\n%s" %
    215             (file, errors))
    216     if msgs:
    217         write_pot_file(potfile, msgs, orig_file, work_file, is_templatized)
     296
    218297    if is_templatized:
    219         os.unlink(work_file)
     298        # for templatized files create work file
     299        work_file = os.path.join(dirpath, thefile)
     300        file_dict['work'] = work_file
    220301
     302        f = open(work_file, "w")
     303        try:
     304            f.write(content)
     305        finally:
     306            f.close()
     307    return file_dict
     308
    221309def write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
    222310                  copy_pforms, wrap, location, no_obsolete):
    223311    """
     
    335423        if os.path.exists(potfile):
    336424            os.unlink(potfile)
    337425
    338         for dirpath, file in find_files(".", ignore_patterns, verbosity,
    339                 stdout, symlinks=symlinks):
    340             process_file(file, dirpath, potfile, domain, verbosity, extensions,
    341                     wrap, location, stdout)
     426        process_files(potfile, domain, verbosity, extensions,
     427                      wrap, location, stdout, ignore_patterns, symlinks)
    342428
    343429        if os.path.exists(potfile):
    344430            write_po_file(pofile, potfile, domain, locale, verbosity, stdout,
Back to Top