Ticket #284: genshi_i18n_custom_format.patch
| File genshi_i18n_custom_format.patch, 36.4 KB (added by palgarvio, 14 years ago) |
|---|
-
genshi/filters/i18n.py
diff --git a/genshi/filters/i18n.py b/genshi/filters/i18n.py
a b 19 19 """ 20 20 21 21 from gettext import NullTranslations 22 import os23 22 import re 24 23 from types import FunctionType 25 24 25 try: 26 from babel.messages.catalog import PYTHON_FORMAT 27 except ImportError: 28 PYTHON_FORMAT = None 29 26 30 from genshi.core import Attrs, Namespace, QName, START, END, TEXT, START_NS, \ 27 31 END_NS, XML_NAMESPACE, _ensure, StreamEventKind 28 32 from genshi.template.eval import _ast … … 68 72 >>> translator = Translator() 69 73 >>> translator.setup(tmpl) 70 74 >>> list(translator.extract(tmpl.stream)) 71 [(2, None, u'Foo', [u'As in Foo Bar'])]75 [(2, None, u'Foo', {'comments': [u'As in Foo Bar']})] 72 76 >>> 73 77 """ 74 78 … … 95 99 96 100 >>> translator = Translator() 97 101 >>> translator.setup(tmpl) 98 >>> list(translator.extract(tmpl.stream)) 99 [(2, None, u'[1:Foo]\n [2:Bar]', []), (6, None, u'Foo [1:bar]!', [])] 102 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE 103 [(2, None, u'[1:Foo]\n [2:Bar]', {'flags': ['genshi-format'], 104 'comments': []}), 105 (6, None, u'Foo [1:bar]!', {'flags': ['genshi-format'], 106 'comments': []})] 100 107 >>> print tmpl.generate().render() 101 108 <html> 102 109 <div><p>Foo</p> … … 113 120 ... </html>''') 114 121 >>> translator.setup(tmpl) 115 122 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE 116 [(2, None, u'[1:First Name: %(fname)s]\n [2:Last Name: %(lname)s]', []), 117 (6, None, u'Foo [1:bar]!', [])] 123 [(2, None, u'[1:First Name: %(fname)s]\n [2:Last Name: %(lname)s]', 124 {'flags': ['genshi-format', 'python-format'], 'comments': []}), 125 (6, None, u'Foo [1:bar]!', {'flags': ['genshi-format'], 'comments': []})] 118 126 >>> 119 127 >>> tmpl = MarkupTemplate('''<html xmlns:i18n="http://genshi.edgewall.org/i18n"> 120 128 ... <div i18n:msg="fname, lname"> … … 193 201 msgbuf.append(*previous) 194 202 195 203 if msgbuf.valid: 196 yield (None, msgbuf.format(), 197 filter(None, [ctxt.get('_i18n.comment')])) 204 message = msgbuf.format() 205 info = { 206 'comments': filter(None, [ctxt.get('_i18n.comment')]) 207 } 208 if len(parse_msg(message)) > 1: 209 info['flags'] = ['genshi-format'] 210 if PYTHON_FORMAT and bool(filter(None, 211 [PYTHON_FORMAT.search(id) for id in 212 [message] if id])): 213 info.setdefault('flags', []).append('python-format') 214 yield None, message, info 198 215 199 216 200 217 class InnerChooseDirective(I18NDirective): … … 264 281 >>> translator.setup(tmpl) 265 282 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE 266 283 [(2, 'ngettext', (u'There is %(num)s coin', 267 u'There are %(num)s coins'), [])] 284 u'There are %(num)s coins'), 285 {'flags': ['python-format'], 'comments': []})] 268 286 >>> 269 287 >>> tmpl = MarkupTemplate('''\ 270 288 <html xmlns:i18n="http://genshi.edgewall.org/i18n"> … … 299 317 >>> translator.setup(tmpl) 300 318 >>> list(translator.extract(tmpl.stream)) #doctest: +NORMALIZE_WHITESPACE 301 319 [(2, 'ngettext', (u'There is %(num)s coin', 302 u'There are %(num)s coins'), [])] 320 u'There are %(num)s coins'), 321 {'flags': ['python-format'], 'comments': []})] 303 322 >>> 304 323 """ 305 324 … … 410 429 singular_msgbuf.append(kind, event, pos) 411 430 plural_msgbuf.append(kind, event, pos) 412 431 if singular_msgbuf.valid and plural_msgbuf.valid: 413 yield 'ngettext', \ 414 (singular_msgbuf.format(), plural_msgbuf.format()), \ 415 filter(None, [ctxt.get('_i18n.comment')]) 432 singular = singular_msgbuf.format() 433 plural = plural_msgbuf.format() 434 info = {'comments': filter(None, [ctxt.get('_i18n.comment')])} 435 if (len(parse_msg(singular)) or len(parse_msg(plural))) > 1: 436 info.setdefault('flags', []).append('genshi-format') 437 if PYTHON_FORMAT and bool(filter(None, 438 [PYTHON_FORMAT.search(id) for id in 439 [singular, plural] if id])): 440 info.setdefault('flags', []).append('python-format') 441 yield 'ngettext', (singular, plural), info 416 442 417 443 418 444 class DomainDirective(I18NDirective): … … 728 754 """Extract localizable strings from the given template stream. 729 755 730 756 For every string found, this function yields a ``(lineno, function, 731 message, comments)`` tuple, where:757 message, info)`` tuple, where: 732 758 733 759 * ``lineno`` is the number of the line on which the string was found, 734 760 * ``function`` is the name of the ``gettext`` function used (if the 735 761 string was extracted from embedded Python code), and 736 * ``message`` is the string itself (a ``unicode`` object, or a tuple 737 of ``unicode`` objects for functions with multiple string 738 arguments). 739 * ``comments`` is a list of comments related to the message, extracted 740 from ``i18n:comment`` attributes found in the markup 762 * ``message`` is the string itself (a ``unicode`` object, or a tuple 763 of ``unicode`` objects for functions with multiple string 764 arguments). 765 * `info`` is a dictionary that contains additional information about the 766 message being extracted like: 767 `comments`: A list of comments embedded in the source code; 768 With genshi you define the comments to be extracted 769 from the ``i18n:comment`` attributes found in the 770 markup; 771 `context`: A string containing the message context; Not yet used 772 in genshi. 773 `flags`: A list of flags used on the message, ie, 774 ``python-format``, etc, or even a custom flag, 775 ``genshi-format`` for genshi nested markup; 741 776 742 777 >>> from genshi.template import MarkupTemplate 743 778 >>> … … 752 787 ... </body> 753 788 ... </html>''', filename='example.html') 754 789 >>> 755 >>> for line, func, msg, comments in Translator().extract(tmpl.stream): 756 ... print "%d, %r, %r" % (line, func, msg) 757 3, None, u'Example' 758 6, None, u'Example' 759 7, '_', u'Hello, %(name)s' 760 8, 'ngettext', (u'You have %d item', u'You have %d items', None) 790 >>> for line, func, msg, info in Translator().extract(tmpl.stream): 791 ... print "%d, %r, %r, %r" % ( 792 ... line, func,msg, info) #doctest: +NORMALIZE_WHITESPACE 793 3, None, u'Example', {'comments': []} 794 6, None, u'Example', {'comments': []} 795 7, '_', u'Hello, %(name)s', {'flags': ['python-format'], 'comments': []} 796 8, 'ngettext', (u'You have %d item', u'You have %d items', None), 797 {'flags': ['python-format'], 'comments': []} 761 798 762 799 :param stream: the event stream to extract strings from; can be a 763 800 regular stream or a template stream … … 807 844 text = value.strip() 808 845 if text: 809 846 # XXX: Do we need to grab i18n:comment from ctxt ??? 810 yield pos[1], None, text, []847 yield pos[1], None, text, {'comments': []} 811 848 else: 812 for lineno, funcname, text, comments in self.extract(813 _ensure(value), gettext_functions,814 search_text=False,815 error_callback=error_callback):816 yield lineno, funcname, text, comments849 for lineno, funcname, text, info in \ 850 self.extract(_ensure(value), gettext_functions, 851 search_text=False, 852 error_callback=error_callback): 853 yield lineno, funcname, text, info 817 854 818 855 if msgbuf: 819 856 msgbuf.append(kind, data, pos) … … 822 859 if not msgbuf: 823 860 text = data.strip() 824 861 if text and filter(None, [ch.isalpha() for ch in text]): 825 yield pos[1], None, text, \ 826 filter(None, [ctxt.get('_i18n.comment')]) 862 yield pos[1], None, text, { 863 'comments': filter(None, 864 [ctxt.get('_i18n.comment')])} 827 865 else: 828 866 msgbuf.append(kind, data, pos) 829 867 830 868 elif not skip and msgbuf and kind is END: 831 869 msgbuf.append(kind, data, pos) 832 870 if not msgbuf.depth and msgbuf.valid: 833 yield msgbuf.lineno, None, msgbuf.format(), \ 834 filter(None, [msgbuf.comment]) 871 message = msgbuf.format() 872 info = {'comments': filter(None, [msgbuf.comment])} 873 if len(message) > 1: 874 singular, plural = message 875 if (len(parse_msg(singular)) or 876 len(parse_msg(plural))) > 1: 877 info.setdefault('flags', []).append('genshi-format') 878 if PYTHON_FORMAT and bool(filter( 879 None, [PYTHON_FORMAT.search(id) 880 for id in [message] if id])): 881 info.setdefault('flags', []).append('python-format') 882 elif len(parse_msg(message)) > 1: 883 info.setdefault('flags', []).append('genshi-format') 884 if PYTHON_FORMAT and bool(filter( 885 None, [PYTHON_FORMAT.search(id) 886 for id in [message] if id])): 887 info.setdefault('flags', []).append('python-format') 888 yield msgbuf.lineno, None, message, info 835 889 msgbuf = None 836 890 837 891 elif kind is EXPR or kind is EXEC: 838 892 if msgbuf: 839 893 msgbuf.append(kind, data, pos) 840 for funcname, strings in extract_from_code(data, 841 gettext_functions): 842 # XXX: Do we need to grab i18n:comment from ctxt ??? 843 yield pos[1], funcname, strings, [] 894 for funcname, strings, info in extract_from_code( 895 data, gettext_functions): 896 yield pos[1], funcname, strings, info 844 897 845 898 elif kind is SUB: 846 899 directives, substream = data … … 860 913 search_text=search_text and not skip, 861 914 msgbuf=msgbuf, ctxt=ctxt, 862 915 error_callback=error_callback) 863 for lineno, funcname, text, commentsin messages:864 yield lineno, funcname, text, comments916 for lineno, funcname, text, info in messages: 917 yield lineno, funcname, text, info 865 918 directives.pop(idx) 866 919 elif not isinstance(directive, I18NDirective): 867 920 # Remove all other non i18n directives from the process … … 874 927 messages = self.extract( 875 928 substream, gettext_functions, 876 929 search_text=search_text and not skip, msgbuf=msgbuf) 877 for lineno, funcname, text, commentsin messages:878 yield lineno, funcname, text, comments930 for lineno, funcname, text, info in messages: 931 yield lineno, funcname, text, info 879 932 880 933 for directive in directives: 881 934 if isinstance(directive, I18NDirectiveExtract): 882 935 messages = directive.extract( 883 936 substream, ctxt, error_callback=error_callback 884 937 ) 885 for funcname, text, commentsin messages:886 yield pos[1], funcname, text, comments938 for funcname, text, info in messages: 939 yield pos[1], funcname, text, info 887 940 else: 888 941 messages = self.extract( 889 942 substream, gettext_functions, 890 943 search_text=search_text and not skip, msgbuf=msgbuf, 891 944 error_callback=error_callback) 892 for lineno, funcname, text, commentsin messages:893 yield lineno, funcname, text, comments945 for lineno, funcname, text, info in messages: 946 yield lineno, funcname, text, info 894 947 if comment: 895 948 ctxt.pop() 896 949 … … 1150 1203 1151 1204 >>> expr = Expression('_("Hello")') 1152 1205 >>> list(extract_from_code(expr, Translator.GETTEXT_FUNCTIONS)) 1153 [('_', u'Hello' )]1206 [('_', u'Hello', {'comments': []})] 1154 1207 1155 1208 >>> expr = Expression('ngettext("You have %(num)s item", ' 1156 1209 ... '"You have %(num)s items", num)') 1157 >>> list(extract_from_code(expr, Translator.GETTEXT_FUNCTIONS)) 1158 [('ngettext', (u'You have %(num)s item', u'You have %(num)s items', None))] 1210 >>> list(extract_from_code( 1211 ... expr, Translator.GETTEXT_FUNCTIONS)) #doctest: +NORMALIZE_WHITESPACE 1212 [('ngettext', (u'You have %(num)s item', u'You have %(num)s items', None), 1213 {'flags': ['python-format'], 'comments': []})] 1159 1214 1160 1215 :param code: the `Code` object 1161 1216 :type code: `genshi.template.eval.Code` … … 1174 1229 [_add(arg) for arg in node.args] 1175 1230 _add(node.starargs) 1176 1231 _add(node.kwargs) 1232 info = {'comments': []} 1177 1233 if len(strings) == 1: 1178 1234 strings = strings[0] 1235 if PYTHON_FORMAT and bool(filter(None, [PYTHON_FORMAT.search(id) 1236 for id in [strings] 1237 if id])): 1238 info.setdefault('flags', []).append('python-format') 1179 1239 else: 1180 1240 strings = tuple(strings) 1181 yield node.func.id, strings 1241 if PYTHON_FORMAT and bool(filter(None, [PYTHON_FORMAT.search(id) 1242 for id in strings 1243 if id])): 1244 info.setdefault('flags', []).append('python-format') 1245 yield node.func.id, strings, info 1182 1246 elif node._fields: 1183 1247 children = [] 1184 1248 for field in node._fields: … … 1189 1253 elif isinstance(child, _ast.AST): 1190 1254 children.append(child) 1191 1255 for child in children: 1192 for funcname, strings in _walk(child):1193 yield funcname, strings 1256 for funcname, strings, info in _walk(child): 1257 yield funcname, strings, info 1194 1258 return _walk(code.ast) 1195 1259 1196 1260 1197 def extract(fileobj, keywords, comment_tags, options ):1261 def extract(fileobj, keywords, comment_tags, options, error_callback): 1198 1262 """Babel extraction method for Genshi templates. 1199 1263 1264 This function generates tuples of the form: 1265 1266 ``(lineno, funcname, message, info)`` 1267 1268 * `info`` is a dictionary that contains additional information about the 1269 message being extracted like: 1270 `comments`: A list of comments embedded in the source code; 1271 With genshi you define the comments to be extracted 1272 from the ``i18n:comment`` attributes found in the 1273 markup; 1274 `context`: A string containing the message context; Not yet used 1275 in genshi. 1276 `flags`: A list of flags used on the message, ie, 1277 ``python-format``, etc, or even a custom flag, 1278 ``genshi-format`` for genshi nested markup; 1279 1200 1280 :param fileobj: the file-like object the messages should be extracted from 1201 1281 :param keywords: a list of keywords (i.e. function names) that should be 1202 1282 recognized as translation functions 1203 1283 :param comment_tags: a list of translator tags to search for and include 1204 1284 in the results 1205 1285 :param options: a dictionary of additional options (optional) 1206 :return: an iterator over ``(lineno, funcname, message, comments)`` tuples 1286 :param error_callback: an error function called if defined, and, in case 1287 of errors, to send them to the user. 1288 Accepts three arguments; 1289 ``filename``, ``line_number``, ``error_message`` 1290 :return: an iterator over ``(lineno, funcname, message, info)`` tuples 1207 1291 :rtype: ``iterator`` 1208 1292 """ 1209 1293 template_class = options.get('template_class', MarkupTemplate) … … 1229 1313 tmpl = template_class(fileobj, filename=getattr(fileobj, 'name', None), 1230 1314 encoding=encoding) 1231 1315 1232 error_callback = options.get('error_callback')1233 1234 1316 translator = Translator(None, ignore_tags, include_attrs, extract_text) 1235 1317 if hasattr(tmpl, 'add_directives'): 1236 1318 tmpl.add_directives(Translator.NAMESPACE, translator) 1237 for message in translator.extract(tmpl.stream, gettext_functions=keywords,1238 error_callback=error_callback):1239 yield message1319 for lineno, funcname, messages, info in translator.extract( 1320 tmpl.stream, gettext_functions=keywords, error_callback=error_callback): 1321 yield lineno, funcname, messages, info 1240 1322 1241 1323 1242 1324 def setup_i18n(tmpl, translator): -
genshi/filters/tests/i18n.py
diff --git a/genshi/filters/tests/i18n.py b/genshi/filters/tests/i18n.py
a b 88 88 translator = Translator(extract_text=False) 89 89 messages = list(translator.extract(tmpl.stream)) 90 90 self.assertEqual(1, len(messages)) 91 self.assertEqual((3, 'ngettext', (u'Singular', u'Plural', None), []), 91 self.assertEqual((3, 'ngettext', (u'Singular', u'Plural', None), 92 {'comments': []}), 92 93 messages[0]) 93 94 94 95 def test_extract_plural_form(self): … … 98 99 translator = Translator() 99 100 messages = list(translator.extract(tmpl.stream)) 100 101 self.assertEqual(1, len(messages)) 101 self.assertEqual((2, 'ngettext', (u'Singular', u'Plural', None), []), 102 self.assertEqual((2, 'ngettext', (u'Singular', u'Plural', None), 103 {'comments': []}), 102 104 messages[0]) 103 105 104 106 def test_extract_funky_plural_form(self): … … 108 110 translator = Translator() 109 111 messages = list(translator.extract(tmpl.stream)) 110 112 self.assertEqual(1, len(messages)) 111 self.assertEqual((2, 'ngettext', (None, None), []), messages[0])113 self.assertEqual((2, 'ngettext', (None, None), {'comments': []}), messages[0]) 112 114 113 115 def test_extract_gettext_with_unicode_string(self): 114 116 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> … … 117 119 translator = Translator() 118 120 messages = list(translator.extract(tmpl.stream)) 119 121 self.assertEqual(1, len(messages)) 120 self.assertEqual((2, 'gettext', u'Gr\xfc\xdfe', []), messages[0])122 self.assertEqual((2, 'gettext', u'Gr\xfc\xdfe', {'comments': []}), messages[0]) 121 123 122 124 def test_extract_included_attribute_text(self): 123 125 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> … … 126 128 translator = Translator() 127 129 messages = list(translator.extract(tmpl.stream)) 128 130 self.assertEqual(1, len(messages)) 129 self.assertEqual((2, None, u'Foo', []), messages[0])131 self.assertEqual((2, None, u'Foo', {'comments': []}), messages[0]) 130 132 131 133 def test_extract_attribute_expr(self): 132 134 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> … … 135 137 translator = Translator() 136 138 messages = list(translator.extract(tmpl.stream)) 137 139 self.assertEqual(1, len(messages)) 138 self.assertEqual((2, '_', u'Save', []), messages[0])140 self.assertEqual((2, '_', u'Save', {'comments': []}), messages[0]) 139 141 140 142 def test_extract_non_included_attribute_interpolated(self): 141 143 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> … … 144 146 translator = Translator() 145 147 messages = list(translator.extract(tmpl.stream)) 146 148 self.assertEqual(1, len(messages)) 147 self.assertEqual((2, None, u'Foo', []), messages[0])149 self.assertEqual((2, None, u'Foo', {'comments': []}), messages[0]) 148 150 149 151 def test_extract_text_from_sub(self): 150 152 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> … … 153 155 translator = Translator() 154 156 messages = list(translator.extract(tmpl.stream)) 155 157 self.assertEqual(1, len(messages)) 156 self.assertEqual((2, None, u'Foo', []), messages[0])158 self.assertEqual((2, None, u'Foo', {'comments': []}), messages[0]) 157 159 158 160 def test_ignore_tag_with_fixed_xml_lang(self): 159 161 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/"> … … 170 172 translator = Translator() 171 173 messages = list(translator.extract(tmpl.stream)) 172 174 self.assertEqual(1, len(messages)) 173 self.assertEqual((2, None, u'(c) 2007 Edgewall Software', []),175 self.assertEqual((2, None, u'(c) 2007 Edgewall Software', {'comments': []}), 174 176 messages[0]) 175 177 176 178 def test_ignore_attribute_with_expression(self): … … 468 470 tmpl.add_directives(Translator.NAMESPACE, translator) 469 471 messages = list(translator.extract(tmpl.stream)) 470 472 self.assertEqual(1, len(messages)) 471 self.assertEqual((3, None, u'Foo', ['As in foo bar']), messages[0]) 473 self.assertEqual((3, None, u'Foo', {'comments': [u'As in foo bar']}), 474 messages[0]) 472 475 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" 473 476 xmlns:i18n="http://genshi.edgewall.org/i18n"> 474 477 <p i18n:msg="" i18n:comment="As in foo bar">Foo</p> … … 477 480 tmpl.add_directives(Translator.NAMESPACE, translator) 478 481 messages = list(translator.extract(tmpl.stream)) 479 482 self.assertEqual(1, len(messages)) 480 self.assertEqual((3, None, u'Foo', ['As in foo bar']), messages[0]) 483 self.assertEqual((3, None, u'Foo', {'comments': [u'As in foo bar']}), 484 messages[0]) 481 485 482 486 def test_translate_i18n_msg_with_comment(self): 483 487 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 499 503 translator = Translator() 500 504 messages = list(translator.extract(tmpl.stream)) 501 505 self.assertEqual(2, len(messages)) 502 self.assertEqual((3, None, u'Foo bar', []), messages[0])503 self.assertEqual((3, None, u'Foo', []), messages[1])506 self.assertEqual((3, None, u'Foo bar', {'comments': []}), messages[0]) 507 self.assertEqual((3, None, u'Foo', {'comments': []}), messages[1]) 504 508 505 509 def test_translate_i18n_msg_with_attr(self): 506 510 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 554 558 messages = list(translator.extract(tmpl.stream)) 555 559 self.assertEqual(1, len(messages)) 556 560 self.assertEqual( 557 (3, None, u'Changed %(date)s ago by %(author)s', []), messages[0] 561 (3, None, u'Changed %(date)s ago by %(author)s', 562 {'flags': ['python-format'], 'comments': []}), messages[0] 558 563 ) 559 564 560 565 def test_i18n_msg_ticket_300_translate(self): … … 584 589 messages = list(translator.extract(tmpl.stream)) 585 590 self.assertEqual(1, len(messages)) 586 591 self.assertEqual( 587 (3, None, u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]', []), messages[0] 592 (3, None, u'[1:[2:Translation\\[\xa00\xa0\\]]: [3:One coin]]', 593 {'flags': ['genshi-format'], 'comments': []}), messages[0] 588 594 ) 589 595 590 596 def test_i18n_msg_ticket_251_translate(self): … … 1003 1009 tmpl.add_directives(Translator.NAMESPACE, translator) 1004 1010 messages = list(translator.extract(tmpl.stream)) 1005 1011 self.assertEqual(2, len(messages)) 1006 self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), []), messages[0])1007 self.assertEqual((7, 'ngettext', (u'FooBar', u'FooBars'), []), messages[1])1012 self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), {'comments': []}), messages[0]) 1013 self.assertEqual((7, 'ngettext', (u'FooBar', u'FooBars'), {'comments': []}), messages[1]) 1008 1014 1009 1015 def test_extract_i18n_choose_as_directive(self): 1010 1016 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 1022 1028 tmpl.add_directives(Translator.NAMESPACE, translator) 1023 1029 messages = list(translator.extract(tmpl.stream)) 1024 1030 self.assertEqual(2, len(messages)) 1025 self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), []), messages[0])1026 self.assertEqual((7, 'ngettext', (u'FooBar', u'FooBars'), []), messages[1])1031 self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), {'comments': []}), messages[0]) 1032 self.assertEqual((7, 'ngettext', (u'FooBar', u'FooBars'), {'comments': []}), messages[1]) 1027 1033 1028 1034 def test_extract_i18n_choose_as_attribute_with_params(self): 1029 1035 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 1038 1044 messages = list(translator.extract(tmpl.stream)) 1039 1045 self.assertEqual(1, len(messages)) 1040 1046 self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s', 1041 u'Foos %(fname)s %(lname)s'), []), 1047 u'Foos %(fname)s %(lname)s'), 1048 {'flags': ['python-format'], 'comments': []}), 1042 1049 messages[0]) 1043 1050 1044 1051 def test_extract_i18n_choose_as_attribute_with_params_and_domain_as_param(self): … … 1055 1062 messages = list(translator.extract(tmpl.stream)) 1056 1063 self.assertEqual(1, len(messages)) 1057 1064 self.assertEqual((4, 'ngettext', (u'Foo %(fname)s %(lname)s', 1058 u'Foos %(fname)s %(lname)s'), []), 1065 u'Foos %(fname)s %(lname)s'), 1066 {'flags': ['python-format'], 'comments': []}), 1059 1067 messages[0]) 1060 1068 1061 1069 def test_extract_i18n_choose_as_directive_with_params(self): … … 1075 1083 messages = list(translator.extract(tmpl.stream)) 1076 1084 self.assertEqual(2, len(messages)) 1077 1085 self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s', 1078 u'Foos %(fname)s %(lname)s'), []), 1086 u'Foos %(fname)s %(lname)s'), 1087 {'flags': ['python-format'], 'comments': []}), 1079 1088 messages[0]) 1080 1089 self.assertEqual((7, 'ngettext', (u'Foo %(fname)s %(lname)s', 1081 u'Foos %(fname)s %(lname)s'), []), 1090 u'Foos %(fname)s %(lname)s'), 1091 {'flags': ['python-format'], 'comments': []}), 1082 1092 messages[1]) 1083 1093 1084 1094 def test_extract_i18n_choose_as_directive_with_params_and_domain_as_directive(self): … … 1100 1110 messages = list(translator.extract(tmpl.stream)) 1101 1111 self.assertEqual(2, len(messages)) 1102 1112 self.assertEqual((4, 'ngettext', (u'Foo %(fname)s %(lname)s', 1103 u'Foos %(fname)s %(lname)s'), []), 1113 u'Foos %(fname)s %(lname)s'), 1114 {'flags': ['python-format'], 'comments': []}), 1104 1115 messages[0]) 1105 1116 self.assertEqual((9, 'ngettext', (u'Foo %(fname)s %(lname)s', 1106 u'Foos %(fname)s %(lname)s'), []), 1117 u'Foos %(fname)s %(lname)s'), 1118 {'flags': ['python-format'], 'comments': []}), 1107 1119 messages[1]) 1108 1120 1109 1121 def test_extract_i18n_choose_as_attribute_with_params_and_comment(self): … … 1120 1132 self.assertEqual(1, len(messages)) 1121 1133 self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s', 1122 1134 u'Foos %(fname)s %(lname)s'), 1123 [u'As in Foo Bar']), 1135 {'flags': ['python-format'], 1136 'comments': [u'As in Foo Bar']}), 1124 1137 messages[0]) 1125 1138 1126 1139 def test_extract_i18n_choose_as_directive_with_params_and_comment(self): … … 1137 1150 self.assertEqual(1, len(messages)) 1138 1151 self.assertEqual((3, 'ngettext', (u'Foo %(fname)s %(lname)s', 1139 1152 u'Foos %(fname)s %(lname)s'), 1140 [u'As in Foo Bar']), 1153 {'flags': ['python-format'], 1154 'comments': [u'As in Foo Bar']}), 1141 1155 messages[0]) 1142 1156 1143 1157 def test_translate_i18n_domain_with_nested_inlcudes(self): … … 1369 1383 tmpl.add_directives(Translator.NAMESPACE, translator) 1370 1384 messages = list(translator.extract(tmpl.stream)) 1371 1385 self.assertEqual(1, len(messages)) 1372 self.assertEqual((3, None, u'Please see [1:Help] for details.', []), 1386 self.assertEqual((3, None, u'Please see [1:Help] for details.', 1387 {'flags': ['genshi-format'], 'comments': []}), 1373 1388 messages[0]) 1374 1389 1375 1390 def test_extract_i18n_msg_with_py_strip_and_comment(self): … … 1384 1399 messages = list(translator.extract(tmpl.stream)) 1385 1400 self.assertEqual(1, len(messages)) 1386 1401 self.assertEqual((3, None, u'Please see [1:Help] for details.', 1387 ['Foo']), messages[0]) 1402 {'flags': ['genshi-format'], 'comments': [u'Foo']}), 1403 messages[0]) 1388 1404 1389 1405 def test_extract_i18n_choose_as_attribute_and_py_strip(self): 1390 1406 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 1398 1414 tmpl.add_directives(Translator.NAMESPACE, translator) 1399 1415 messages = list(translator.extract(tmpl.stream)) 1400 1416 self.assertEqual(1, len(messages)) 1401 self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), []), messages[0])1417 self.assertEqual((3, 'ngettext', (u'FooBar', u'FooBars'), {'comments': []}), messages[0]) 1402 1418 1403 1419 def test_translate_i18n_domain_with_inline_directive_on_START_NS(self): 1404 1420 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 1472 1488 <p>${ngettext("You have %d item", "You have %d items", num)}</p> 1473 1489 </body> 1474 1490 </html>""") 1475 results = list(extract(buf, ['_', 'ngettext'], [], {} ))1491 results = list(extract(buf, ['_', 'ngettext'], [], {}, None)) 1476 1492 self.assertEqual([ 1477 (3, None, u'Example', []),1478 (6, None, u'Example', []),1479 (7, '_', u'Hello, %(name)s', []),1493 (3, None, u'Example', {'comments': []}), 1494 (6, None, u'Example', {'comments': []}), 1495 (7, '_', u'Hello, %(name)s', {'flags': ['python-format'], 'comments': []}), 1480 1496 (8, 'ngettext', (u'You have %d item', u'You have %d items', None), 1481 []),1497 {'flags': ['python-format'], 'comments': []}), 1482 1498 ], results) 1483 1499 1484 1500 def test_extraction_without_text(self): … … 1488 1504 </html>""") 1489 1505 results = list(extract(buf, ['_', 'ngettext'], [], { 1490 1506 'extract_text': 'no' 1491 } ))1507 }, None)) 1492 1508 self.assertEqual([ 1493 (3, 'ngettext', (u'Singular', u'Plural', None), []),1509 (3, 'ngettext', (u'Singular', u'Plural', None), {'comments': []}), 1494 1510 ], results) 1495 1511 1496 1512 def test_text_template_extraction(self): … … 1505 1521 Foobar""") 1506 1522 results = list(extract(buf, ['_', 'ngettext'], [], { 1507 1523 'template_class': 'genshi.template:TextTemplate' 1508 } ))1524 }, None)) 1509 1525 self.assertEqual([ 1510 (1, '_', u'Dear %(name)s', []), 1511 (3, 'ngettext', (u'Your item:', u'Your items', None), []), 1512 (7, None, u'All the best,\n Foobar', []) 1526 (1, '_', u'Dear %(name)s', {'flags': ['python-format'], 'comments': []}), 1527 (3, 'ngettext', (u'Your item:', u'Your items', None), 1528 {'comments': []}), 1529 (7, None, u'All the best,\n Foobar', {'comments': []}) 1513 1530 ], results) 1514 1531 1515 1532 def test_extraction_with_keyword_arg(self): 1516 1533 buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/"> 1517 1534 ${gettext('Foobar', foo='bar')} 1518 1535 </html>""") 1519 results = list(extract(buf, ['gettext'], [], {} ))1536 results = list(extract(buf, ['gettext'], [], {}, None)) 1520 1537 self.assertEqual([ 1521 (2, 'gettext', (u'Foobar'), []),1538 (2, 'gettext', (u'Foobar'), {'comments': []}), 1522 1539 ], results) 1523 1540 1524 1541 def test_extraction_with_nonstring_arg(self): 1525 1542 buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/"> 1526 1543 ${dgettext(curdomain, 'Foobar')} 1527 1544 </html>""") 1528 results = list(extract(buf, ['dgettext'], [], {} ))1545 results = list(extract(buf, ['dgettext'], [], {}, None)) 1529 1546 self.assertEqual([ 1530 (2, 'dgettext', (None, u'Foobar'), []),1547 (2, 'dgettext', (None, u'Foobar'), {'comments': []}), 1531 1548 ], results) 1532 1549 1533 1550 def test_extraction_inside_ignored_tags(self): … … 1539 1556 }); 1540 1557 </script> 1541 1558 </html>""") 1542 results = list(extract(buf, ['_'], [], {})) 1543 self.assertEqual([ 1544 (5, '_', u'Please wait...', []), 1545 ], results) 1559 results = list(extract(buf, ['_'], [], {}, None)) 1560 self.assertEqual([(5, '_', u'Please wait...', {'comments': []})], 1561 results) 1546 1562 1547 1563 def test_extraction_inside_ignored_tags_with_directives(self): 1548 1564 buf = StringIO("""<html xmlns:py="http://genshi.edgewall.org/"> … … 1552 1568 </py:if> 1553 1569 </script> 1554 1570 </html>""") 1555 self.assertEqual([], list(extract(buf, ['_'], [], {} )))1571 self.assertEqual([], list(extract(buf, ['_'], [], {}, None))) 1556 1572 1557 1573 def test_extract_py_def_directive_with_py_strip(self): 1558 1574 # Failed extraction from Trac … … 1597 1613 messages = list(translator.extract(tmpl.stream)) 1598 1614 self.assertEqual(10, len(messages)) 1599 1615 self.assertEqual([ 1600 (3, None, u'View differences', []), 1601 (6, None, u'inline', []), 1602 (8, None, u'side by side', []), 1603 (10, None, u'Show', []), 1604 (13, None, u'lines around each change', []), 1605 (16, None, u'Ignore:', []), 1606 (20, None, u'Blank lines', []), 1607 (25, None, u'Case changes',[]), 1608 (30, None, u'White space changes', []), 1609 (34, '_', u'Update', [])], messages) 1616 (3, None, u'View differences', {'comments': []}), 1617 (6, None, u'inline', {'comments': []}), 1618 (8, None, u'side by side', {'comments': []}), 1619 (10, None, u'Show', {'comments': []}), 1620 (13, None, u'lines around each change', {'comments': []}), 1621 (16, None, u'Ignore:', {'comments': []}), 1622 (20, None, u'Blank lines', {'comments': []}), 1623 (25, None, u'Case changes', {'comments': []}), 1624 (30, None, u'White space changes', {'comments': []}), 1625 (34, '_', u'Update', {'comments': []}) 1626 ], messages) 1610 1627 1611 1628 1612 1629 def suite():
