Ticket #17375: issue_17375.diff

File issue_17375.diff, 15.1 KB (added by pigletto, 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 95c1da0..47a1fa1 100644
    a b from django.utils.jslex import prepare_js_for_gettext 
    1313
    1414plural_forms_re = re.compile(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL)
    1515
     16
    1617def handle_extensions(extensions=('html',), ignored=('py',)):
    1718    """
    1819    Organizes multiple extensions that are separated with commas or passed by
    def handle_extensions(extensions=('html',), ignored=('py',)): 
    3738            ext_list[i] = '.%s' % ext_list[i]
    3839    return set([x for x in ext_list if x.strip('.') not in ignored])
    3940
     41
    4042def _popen(cmd):
    4143    """
    4244    Friendly wrapper around Popen for Windows
    def _popen(cmd): 
    4446    p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True)
    4547    return p.communicate()
    4648
     49
    4750def walk(root, topdown=True, onerror=None, followlinks=False):
    4851    """
    4952    A version of os.walk that can follow symlinks for Python < 2.6
    def walk(root, topdown=True, onerror=None, followlinks=False): 
    5760                    for link_dirpath, link_dirnames, link_filenames in walk(p):
    5861                        yield (link_dirpath, link_dirnames, link_filenames)
    5962
     63
    6064def is_ignored(path, ignore_patterns):
    6165    """
    6266    Helper function to check if the given path should be ignored or not.
    def is_ignored(path, ignore_patterns): 
    6670            return True
    6771    return False
    6872
     73
    6974def find_files(root, ignore_patterns, verbosity, symlinks=False):
    7075    """
    7176    Helper function to get all files in the given root.
    def find_files(root, ignore_patterns, verbosity, symlinks=False): 
    8287    all_files.sort()
    8388    return all_files
    8489
     90
    8591def copy_plural_forms(msgs, locale, domain, verbosity):
    8692    """
    8793    Copies plural forms header contents from a Django catalog of locale to
    def copy_plural_forms(msgs, locale, domain, verbosity): 
    112118                break
    113119    return msgs
    114120
    115 def write_pot_file(potfile, msgs, file, work_file, is_templatized):
     121
     122def write_pot_file(potfile, msgs, all_files):
    116123    """
    117124    Write the :param potfile: POT file with the :param msgs: contents,
    118125    previously making sure its format is valid.
    119126    """
    120     if is_templatized:
    121         old = '#: ' + work_file[2:]
    122         new = '#: ' + file[2:]
    123         msgs = msgs.replace(old, new)
     127    for f_data in all_files:
     128        if f_data['is_templatized']:
     129            old = '#: ' + f_data['work'][2:]
     130            new = '#: ' + f_data['orig'][2:]
     131            msgs = msgs.replace(old, new)
    124132    if os.path.exists(potfile):
    125133        # Strip the header
    126134        msgs = '\n'.join(dropwhile(len, msgs.split('\n')))
    def write_pot_file(potfile, msgs, file, work_file, is_templatized): 
    132140    finally:
    133141        f.close()
    134142
    135 def process_file(file, dirpath, potfile, domain, verbosity, extensions, wrap,
    136         location):
    137     """
    138     Extract translatable literals from :param file: for :param domain:
    139     creating or updating the :param potfile: POT file.
    140 
    141     Uses the xgettext GNU gettext utility.
    142     """
    143 
    144     from django.utils.translation import templatize
    145 
    146     if verbosity > 1:
    147         sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
    148     _, file_ext = os.path.splitext(file)
    149     if domain == 'djangojs' and file_ext in extensions:
    150         is_templatized = True
    151         orig_file = os.path.join(dirpath, file)
    152         src_data = open(orig_file).read()
    153         src_data = prepare_js_for_gettext(src_data)
    154         thefile = '%s.c' % file
    155         work_file = os.path.join(dirpath, thefile)
    156         f = open(work_file, "w")
    157         try:
    158             f.write(src_data)
    159         finally:
    160             f.close()
    161         cmd = (
    162             'xgettext -d %s -L C %s %s --keyword=gettext_noop '
    163             '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
    164             '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
    165             '--from-code UTF-8 --add-comments=Translators -o - "%s"' % (
    166                 domain, wrap, location, work_file
    167             )
    168         )
    169     elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):
    170         thefile = file
    171         orig_file = os.path.join(dirpath, file)
    172         is_templatized = file_ext in extensions
    173         if is_templatized:
    174             src_data = open(orig_file, "rU").read()
    175             thefile = '%s.py' % file
    176             content = templatize(src_data, orig_file[2:])
    177             f = open(os.path.join(dirpath, thefile), "w")
    178             try:
    179                 f.write(content)
    180             finally:
    181                 f.close()
    182         work_file = os.path.join(dirpath, thefile)
    183         cmd = (
    184             'xgettext -d %s -L Python %s %s --keyword=gettext_noop '
    185             '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
    186             '--keyword=ugettext_noop --keyword=ugettext_lazy '
    187             '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 '
    188             '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
    189             '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
    190             '--add-comments=Translators -o - "%s"' % (
    191                 domain, wrap, location, work_file)
    192         )
    193     else:
    194         return
    195     msgs, errors = _popen(cmd)
    196     if errors:
    197         if is_templatized:
    198             os.unlink(work_file)
    199         if os.path.exists(potfile):
    200             os.unlink(potfile)
    201         raise CommandError(
    202             "errors happened while running xgettext on %s\n%s" %
    203             (file, errors))
    204     if msgs:
    205         write_pot_file(potfile, msgs, orig_file, work_file, is_templatized)
    206     if is_templatized:
    207         os.unlink(work_file)
    208143
    209144def write_po_file(pofile, potfile, domain, locale, verbosity,
    210145        copy_pforms, wrap, location, no_obsolete):
    def write_po_file(pofile, potfile, domain, locale, verbosity, 
    248183            raise CommandError(
    249184                "errors happened while running msgattrib\n%s" % errors)
    250185
     186
    251187def make_messages(locale=None, domain='django', verbosity=1, all=False,
    252188        extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False,
    253189        no_location=False, no_obsolete=False):
    def make_messages(locale=None, domain='django', verbosity=1, all=False, 
    323259        if os.path.exists(potfile):
    324260            os.unlink(potfile)
    325261
    326         for dirpath, file in find_files(".", ignore_patterns, verbosity,
    327                 symlinks=symlinks):
    328             process_file(file, dirpath, potfile, domain, verbosity, extensions,
    329                     wrap, location)
     262        process_files(potfile, domain, verbosity, extensions,
     263                      wrap, location, ignore_patterns, symlinks)
    330264
    331265        if os.path.exists(potfile):
    332266            write_po_file(pofile, potfile, domain, locale, verbosity,
    333267                    not invoked_for_django, wrap, location, no_obsolete)
    334268
    335269
     270def process_files(potfile, domain, verbosity, extensions, wrap, location,
     271                  ignore_patterns, symlinks):
     272    """
     273    Extract translatable literals from current directory for :param domain:
     274    creating or updating the :param potfile: POT file.
     275
     276    Uses the xgettext GNU gettext utility.
     277    """
     278    from django.utils.translation import templatize
     279
     280    # stores information about all processed files
     281    all_files = []
     282
     283    # prepare command to run xgettext depending on domain type
     284    if domain == 'djangojs':
     285        cmd = (
     286            'xgettext -d %s -L C %s %s --keyword=gettext_noop '
     287            '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
     288            '--keyword=pgettext:1c,2 --keyword=npgettext:1c,2,3 '
     289            '--from-code UTF-8 --add-comments=Translators -o - %s'
     290        )
     291    elif domain == 'django':
     292        cmd = (
     293            'xgettext -d %s -L Python %s %s --keyword=gettext_noop '
     294            '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
     295            '--keyword=ugettext_noop --keyword=ugettext_lazy '
     296            '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 '
     297            '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
     298            '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
     299            '--add-comments=Translators -o - %s'
     300        )
     301
     302    for dirpath, file in find_files(".", ignore_patterns, verbosity,
     303                                    symlinks=symlinks):
     304        if verbosity > 1:
     305            sys.stdout.write('processing file %s in %s\n' % (file, dirpath))
     306        _, file_ext = os.path.splitext(file)
     307
     308        is_templatized = file_ext in extensions
     309
     310        # path to the processed file
     311        orig_file = os.path.join(dirpath, file)
     312
     313        # store information about processed file;
     314        # 'orig' - path to original file
     315        # 'work' - path to either original file or templatized version if one is created
     316        # 'is_templatized' - defines if file was templatized
     317        file_dict = {'orig': orig_file, 'work': orig_file, 'is_templatized': is_templatized}
     318
     319        # not really necessary to initialize these here but added for clarity
     320        thefile = ''
     321        content = ''
     322
     323        if domain == 'djangojs' and is_templatized:
     324            src_data = open(orig_file).read()
     325            content = prepare_js_for_gettext(src_data)
     326            thefile = '%s.c' % file
     327        elif domain == 'django' and (file_ext == '.py' or is_templatized):
     328            if is_templatized:
     329                src_data = open(orig_file, "rU").read()
     330                content = templatize(src_data, orig_file[2:])
     331                thefile = '%s.py' % file
     332        else:
     333            continue
     334
     335        if is_templatized:
     336            # for templatized ones create work file
     337            work_file = os.path.join(dirpath, thefile)
     338            file_dict['work'] = work_file
     339
     340            f = open(work_file, "w")
     341            try:
     342                f.write(content)
     343            finally:
     344                f.close()
     345
     346        all_files.append(file_dict)
     347
     348    if all_files:
     349        # prepare command that runs xgettext with paths to all processed files
     350        input_files = ' '.join(['"%s"' % f_data['work']
     351                                                     for f_data in all_files])
     352        cmd = cmd % (domain, wrap, location, input_files)
     353
     354        msgs, errors = _popen(cmd)
     355        if errors:
     356            # check if file exists because when symlinks are used it might
     357            # happen that it was already removed
     358            [os.unlink(f_data['work']) for f_data in all_files
     359               if f_data['is_templatized'] and os.path.exists(f_data['work'])]
     360
     361            input_files = ', '.join([f_data['work'] for f_data in all_files])
     362            raise CommandError(
     363                "errors happened while running xgettext on %s\n%s" %
     364                (input_files, errors))
     365        if msgs:
     366            write_pot_file(potfile, msgs, all_files)
     367        # check if file exists because when symlinks are used it might
     368        # happen that it was already removed
     369        [os.unlink(f_data['work']) for f_data in all_files
     370               if f_data['is_templatized'] and os.path.exists(f_data['work'])]
     371
     372
    336373class Command(NoArgsCommand):
    337374    option_list = NoArgsCommand.option_list + (
    338375        make_option('--locale', '-l', default=None, dest='locale',
  • tests/regressiontests/i18n/commands/extraction.py

    diff --git a/tests/regressiontests/i18n/commands/extraction.py b/tests/regressiontests/i18n/commands/extraction.py
    index b1fc002..7afded3 100644
    a b class ExtractorTests(TestCase): 
    4141        msgid = re.escape(msgid)
    4242        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})
    4343
     44    def assertMsgIdPlural(self, msgid, s, use_quotes=True):
     45        q = '"'
     46        if use_quotes:
     47            msgid = '"%s"' % msgid
     48            q = "'"
     49        needle = 'msgid_plural %s' % msgid
     50        msgid = re.escape(msgid)
     51        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})
     52
    4453    def assertNotMsgId(self, msgid, s, use_quotes=True):
    4554        if use_quotes:
    4655            msgid = '"%s"' % msgid
    class NoLocationExtractorTests(ExtractorTests): 
    263272        with open(self.PO_FILE, 'r') as fp:
    264273            po_contents = fp.read()
    265274            self.assertTrue('#: templates/test.html:55' in po_contents)
     275
     276
     277class MultipleFilesPluralExtractorTests(ExtractorTests):
     278
     279    def setUp(self):
     280        self._cwd = os.getcwd()
     281        self.test_dir = os.path.abspath(os.path.dirname(__file__))
     282        self.second_test_template = os.path.join(self.test_dir, 'templates', 'plural_test_template.html')
     283
     284        second_test_template = '''{% load i18n %}{% trans 'My string' %}'''
     285
     286        if not os.path.exists(self.second_test_template):
     287            f = open(self.second_test_template, 'w+')
     288            try:
     289                f.write(second_test_template)
     290            finally:
     291                f.close()
     292
     293    def tearDown(self):
     294        super(MultipleFilesPluralExtractorTests, self).tearDown()
     295        os.chdir(self.test_dir)
     296        try:
     297            os.remove(self.second_test_template)
     298        except OSError:
     299            pass
     300        os.chdir(self._cwd)
     301
     302    def test_multiple_files_plural_extraction(self):
     303        os.chdir(self.test_dir)
     304        management.call_command('makemessages', locale=LOCALE, verbosity=0, symlinks=True)
     305        self.assertTrue(os.path.exists(self.PO_FILE))
     306        with open(self.PO_FILE, 'r') as fp:
     307            po_contents = fp.read()
     308            self.assertMsgId('My string', po_contents)
     309            self.assertMsgIdPlural('My strings', po_contents)
  • tests/regressiontests/i18n/commands/templates/test.html

    diff --git a/tests/regressiontests/i18n/commands/templates/test.html b/tests/regressiontests/i18n/commands/templates/test.html
    index 5789346..1e00a80 100644
    a b continued here.{% endcomment %} 
    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

    diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
    index 77bd35b..35f9060 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, MultipleFilesPluralExtractorTests)
    3333if can_run_compilation_tests:
    3434    from .commands.compilation import (PoFileTests, PoFileContentsTests,
    3535        PercentRenderingTests)
Back to Top