16
16
:license: BSD, see LICENSE for more details.
17
17
"""
18
18
import ast
19
+ import io
19
20
import os
20
- from os .path import relpath
21
21
import sys
22
+ from os .path import relpath
22
23
from tokenize import generate_tokens , COMMENT , NAME , OP , STRING
23
24
24
25
from babel .util import parse_encoding , parse_future_flags , pathmatch
@@ -532,7 +533,7 @@ def _parse_python_string(value, encoding, future_flags):
532
533
return None
533
534
534
535
535
- def extract_javascript (fileobj , keywords , comment_tags , options ):
536
+ def extract_javascript (fileobj , keywords , comment_tags , options , lineno = 1 ):
536
537
"""Extract messages from JavaScript source code.
537
538
538
539
:param fileobj: the seekable, file-like object the messages should be
@@ -544,7 +545,11 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
544
545
:param options: a dictionary of additional options (optional)
545
546
Supported options are:
546
547
* `jsx` -- set to false to disable JSX/E4X support.
547
- * `template_string` -- set to false to disable ES6 template string support.
548
+ * `template_string` -- if `True`, supports gettext(`key`)
549
+ * `parse_template_string` -- if `True` will parse the
550
+ contents of javascript
551
+ template strings.
552
+ :param lineno: line number offset (for parsing embedded fragments)
548
553
"""
549
554
from babel .messages .jslexer import Token , tokenize , unquote_string
550
555
funcname = message_lineno = None
@@ -556,12 +561,12 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
556
561
last_token = None
557
562
call_stack = - 1
558
563
dotted = any ('.' in kw for kw in keywords )
559
-
560
564
for token in tokenize (
561
565
fileobj .read ().decode (encoding ),
562
566
jsx = options .get ("jsx" , True ),
563
567
template_string = options .get ("template_string" , True ),
564
- dotted = dotted
568
+ dotted = dotted ,
569
+ lineno = lineno
565
570
):
566
571
if ( # Turn keyword`foo` expressions into keyword("foo") calls:
567
572
funcname and # have a keyword...
@@ -573,7 +578,11 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
573
578
call_stack = 0
574
579
token = Token ('operator' , ')' , token .lineno )
575
580
576
- if token .type == 'operator' and token .value == '(' :
581
+ if options .get ('parse_template_string' ) and not funcname and token .type == 'template_string' :
582
+ for item in parse_template_string (token .value , keywords , comment_tags , options , token .lineno ):
583
+ yield item
584
+
585
+ elif token .type == 'operator' and token .value == '(' :
577
586
if funcname :
578
587
message_lineno = token .lineno
579
588
call_stack += 1
@@ -665,3 +674,41 @@ def extract_javascript(fileobj, keywords, comment_tags, options):
665
674
funcname = token .value
666
675
667
676
last_token = token
677
+
678
+
679
+ def parse_template_string (template_string , keywords , comment_tags , options , lineno = 1 ):
680
+ """Parse JavaScript template string.
681
+
682
+ :param template_string: the template string to be parsed
683
+ :param keywords: a list of keywords (i.e. function names) that should be
684
+ recognized as translation functions
685
+ :param comment_tags: a list of translator tags to search for and include
686
+ in the results
687
+ :param options: a dictionary of additional options (optional)
688
+ :param lineno: starting line number (optional)
689
+ """
690
+ from babel .messages .jslexer import line_re
691
+ prev_character = None
692
+ level = 0
693
+ inside_str = False
694
+ expression_contents = ''
695
+ for character in template_string [1 :- 1 ]:
696
+ if not inside_str and character in ('"' , "'" , '`' ):
697
+ inside_str = character
698
+ elif inside_str == character and prev_character != r'\\' :
699
+ inside_str = False
700
+ if level :
701
+ expression_contents += character
702
+ if not inside_str :
703
+ if character == '{' and prev_character == '$' :
704
+ level += 1
705
+ elif level and character == '}' :
706
+ level -= 1
707
+ if level == 0 and expression_contents :
708
+ expression_contents = expression_contents [0 :- 1 ]
709
+ fake_file_obj = io .BytesIO (expression_contents .encode ())
710
+ for item in extract_javascript (fake_file_obj , keywords , comment_tags , options , lineno ):
711
+ yield item
712
+ lineno += len (line_re .findall (expression_contents ))
713
+ expression_contents = ''
714
+ prev_character = character
0 commit comments