6464"""
6565
6666from __future__ import unicode_literals
67+ import itertools
6768
6869import fluent .syntax .ast as FTL
6970from .errors import NotSupportedError
@@ -128,11 +129,11 @@ def __call__(self, ctx):
128129
129130
130131class REPLACE_IN_TEXT (Transform ):
131- """Replace various placeables in the translation with FTL placeables .
132+ """Replace various placeables in the translation with FTL.
132133
133134 The original placeables are defined as keys on the `replacements` dict.
134- For each key the value is defined as a list of FTL Expressions to be
135- interpolated.
135+ For each key the value is defined as a FTL Pattern, Placeable,
136+ TextElement or Expressions to be interpolated.
136137 """
137138
138139 def __init__ (self , value , replacements ):
@@ -141,7 +142,7 @@ def __init__(self, value, replacements):
141142
142143 def __call__ (self , ctx ):
143144
144- # Only replace placeable which are present in the translation.
145+ # Only replace placeables which are present in the translation.
145146 replacements = {
146147 key : evaluate (ctx , repl )
147148 for key , repl in self .replacements .iteritems ()
@@ -154,40 +155,43 @@ def __call__(self, ctx):
154155 lambda x , y : self .value .find (x ) - self .value .find (y )
155156 )
156157
157- # Used to reduce the `keys_in_order` list.
158- def replace (acc , cur ):
159- """Convert original placeables and text into FTL Nodes.
160-
161- For each original placeable the translation will be partitioned
162- around it and the text before it will be converted into an
163- `FTL.TextElement` and the placeable will be replaced with its
164- replacement. The text following the placebale will be fed again to
165- the `replace` function.
166- """
167-
168- parts , rest = acc
169- before , key , after = rest .value .partition (cur )
170-
171- placeable = FTL .Placeable (replacements [key ])
172-
173- # Return the elements found and converted so far, and the remaining
174- # text which hasn't been scanned for placeables yet.
175- return (
176- parts + [FTL .TextElement (before ), placeable ],
177- FTL .TextElement (after )
178- )
179-
180- def is_non_empty (elem ):
181- """Used for filtering empty `FTL.TextElement` nodes out."""
182- return not isinstance (elem , FTL .TextElement ) or len (elem .value )
183-
184- # Start with an empty list of elements and the original translation.
185- init = ([], FTL .TextElement (self .value ))
186- parts , tail = reduce (replace , keys_in_order , init )
187-
188- # Explicitly concat the trailing part to get the full list of elements
189- # and filter out the empty ones.
190- elements = filter (is_non_empty , parts + [tail ])
158+ # A list of PatternElements built from the legacy translation and the
159+ # FTL replacements. It may contain empty or adjacent TextElements.
160+ parts = []
161+ tail = self .value
162+
163+ # Convert original placeables and text into FTL Nodes. For each
164+ # original placeable the translation will be partitioned around it and
165+ # the text before it will be converted into an `FTL.TextElement` and
166+ # the placeable will be replaced with its replacement.
167+ for key in keys_in_order :
168+ before , key , tail = tail .partition (key )
169+
170+ # The replacement value can be of different types.
171+ replacement = replacements [key ]
172+ if isinstance (replacement , FTL .Pattern ):
173+ repl_elements = replacement .elements
174+ elif isinstance (replacement , FTL .PatternElement ):
175+ repl_elements = [replacement ]
176+ elif isinstance (replacement , FTL .Expression ):
177+ repl_elements = [FTL .Placeable (replacement )]
178+
179+ parts .append (FTL .TextElement (before ))
180+ parts .extend (repl_elements )
181+
182+ # Dont' forget about the tail after the loop ends.
183+ parts .append (FTL .TextElement (tail ))
184+
185+ # Join adjacent TextElements.
186+ elements = []
187+ for elem_type , elems in itertools .groupby (parts , key = type ):
188+ if elem_type is FTL .TextElement :
189+ text = FTL .TextElement ('' .join (elem .value for elem in elems ))
190+ # And remove empty ones.
191+ if len (text .value ) > 0 :
192+ elements .append (text )
193+ else :
194+ elements .extend (elems )
191195
192196 return FTL .Pattern (elements )
193197
0 commit comments