Ticket #17375: issue_17375.diff
File issue_17375.diff, 15.1 KB (added by , 13 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 13 13 14 14 plural_forms_re = re.compile(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL) 15 15 16 16 17 def handle_extensions(extensions=('html',), ignored=('py',)): 17 18 """ 18 19 Organizes multiple extensions that are separated with commas or passed by … … def handle_extensions(extensions=('html',), ignored=('py',)): 37 38 ext_list[i] = '.%s' % ext_list[i] 38 39 return set([x for x in ext_list if x.strip('.') not in ignored]) 39 40 41 40 42 def _popen(cmd): 41 43 """ 42 44 Friendly wrapper around Popen for Windows … … def _popen(cmd): 44 46 p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE, close_fds=os.name != 'nt', universal_newlines=True) 45 47 return p.communicate() 46 48 49 47 50 def walk(root, topdown=True, onerror=None, followlinks=False): 48 51 """ 49 52 A version of os.walk that can follow symlinks for Python < 2.6 … … def walk(root, topdown=True, onerror=None, followlinks=False): 57 60 for link_dirpath, link_dirnames, link_filenames in walk(p): 58 61 yield (link_dirpath, link_dirnames, link_filenames) 59 62 63 60 64 def is_ignored(path, ignore_patterns): 61 65 """ 62 66 Helper function to check if the given path should be ignored or not. … … def is_ignored(path, ignore_patterns): 66 70 return True 67 71 return False 68 72 73 69 74 def find_files(root, ignore_patterns, verbosity, symlinks=False): 70 75 """ 71 76 Helper function to get all files in the given root. … … def find_files(root, ignore_patterns, verbosity, symlinks=False): 82 87 all_files.sort() 83 88 return all_files 84 89 90 85 91 def copy_plural_forms(msgs, locale, domain, verbosity): 86 92 """ 87 93 Copies plural forms header contents from a Django catalog of locale to … … def copy_plural_forms(msgs, locale, domain, verbosity): 112 118 break 113 119 return msgs 114 120 115 def write_pot_file(potfile, msgs, file, work_file, is_templatized): 121 122 def write_pot_file(potfile, msgs, all_files): 116 123 """ 117 124 Write the :param potfile: POT file with the :param msgs: contents, 118 125 previously making sure its format is valid. 119 126 """ 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) 124 132 if os.path.exists(potfile): 125 133 # Strip the header 126 134 msgs = '\n'.join(dropwhile(len, msgs.split('\n'))) … … def write_pot_file(potfile, msgs, file, work_file, is_templatized): 132 140 finally: 133 141 f.close() 134 142 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 templatize145 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 = True151 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' % file155 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_file167 )168 )169 elif domain == 'django' and (file_ext == '.py' or file_ext in extensions):170 thefile = file171 orig_file = os.path.join(dirpath, file)172 is_templatized = file_ext in extensions173 if is_templatized:174 src_data = open(orig_file, "rU").read()175 thefile = '%s.py' % file176 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 return195 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)208 143 209 144 def write_po_file(pofile, potfile, domain, locale, verbosity, 210 145 copy_pforms, wrap, location, no_obsolete): … … def write_po_file(pofile, potfile, domain, locale, verbosity, 248 183 raise CommandError( 249 184 "errors happened while running msgattrib\n%s" % errors) 250 185 186 251 187 def make_messages(locale=None, domain='django', verbosity=1, all=False, 252 188 extensions=None, symlinks=False, ignore_patterns=None, no_wrap=False, 253 189 no_location=False, no_obsolete=False): … … def make_messages(locale=None, domain='django', verbosity=1, all=False, 323 259 if os.path.exists(potfile): 324 260 os.unlink(potfile) 325 261 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) 330 264 331 265 if os.path.exists(potfile): 332 266 write_po_file(pofile, potfile, domain, locale, verbosity, 333 267 not invoked_for_django, wrap, location, no_obsolete) 334 268 335 269 270 def 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 336 373 class Command(NoArgsCommand): 337 374 option_list = NoArgsCommand.option_list + ( 338 375 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): 41 41 msgid = re.escape(msgid) 42 42 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}) 43 43 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 44 53 def assertNotMsgId(self, msgid, s, use_quotes=True): 45 54 if use_quotes: 46 55 msgid = '"%s"' % msgid … … class NoLocationExtractorTests(ExtractorTests): 263 272 with open(self.PO_FILE, 'r') as fp: 264 273 po_contents = fp.read() 265 274 self.assertTrue('#: templates/test.html:55' in po_contents) 275 276 277 class 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 %} 77 77 {% trans "Shouldn't double escape this sequence %% either" context "ctx1" %} 78 78 {% trans "Looks like a str fmt spec %s but shouldn't be interpreted as such" %} 79 79 {% 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: 29 29 from .commands.extraction import (ExtractorTests, BasicExtractorTests, 30 30 JavascriptExtractorTests, IgnoredExtractorTests, SymlinkExtractorTests, 31 31 CopyPluralFormsExtractorTests, NoWrapExtractorTests, 32 NoLocationExtractorTests )32 NoLocationExtractorTests, MultipleFilesPluralExtractorTests) 33 33 if can_run_compilation_tests: 34 34 from .commands.compilation import (PoFileTests, PoFileContentsTests, 35 35 PercentRenderingTests)