@@ -23,7 +23,8 @@ def getParser(path):
23
23
from .cldr import get_plural_categories
24
24
from .transforms import Source
25
25
from .merge import merge_resource
26
- from .errors import NotSupportedError , UnreadableReferenceError
26
+ from .errors import (
27
+ EmptyLocalizationError , NotSupportedError , UnreadableReferenceError )
27
28
28
29
29
30
class MergeContext (object ):
@@ -114,6 +115,47 @@ def read_legacy_resource(self, path):
114
115
# Transform the parsed result which is an iterator into a dict.
115
116
return {entity .key : entity .val for entity in parser }
116
117
118
+ def read_reference_ftl (self , path ):
119
+ """Read and parse a reference FTL file.
120
+
121
+ A missing resource file is a fatal error and will raise an
122
+ UnreadableReferenceError.
123
+ """
124
+ fullpath = os .path .join (self .reference_dir , path )
125
+ try :
126
+ return self .read_ftl_resource (fullpath )
127
+ except IOError as err :
128
+ error_message = 'Missing reference file: {}' .format (fullpath )
129
+ logging .getLogger ('migrate' ).error (error_message )
130
+ raise UnreadableReferenceError (error_message )
131
+ except UnicodeDecodeError as err :
132
+ error_message = 'Error reading file {}: {}' .format (fullpath , err )
133
+ logging .getLogger ('migrate' ).error (error_message )
134
+ raise UnreadableReferenceError (error_message )
135
+
136
+ def read_localization_ftl (self , path ):
137
+ """Read and parse an existing localization FTL file.
138
+
139
+ Create a new FTL.Resource if the file doesn't exist or can't be
140
+ decoded.
141
+ """
142
+ fullpath = os .path .join (self .localization_dir , path )
143
+ try :
144
+ return self .read_ftl_resource (fullpath )
145
+ except IOError :
146
+ logger = logging .getLogger ('migrate' )
147
+ logger .info (
148
+ 'Localization file {} does not exist and '
149
+ 'it will be created' .format (path ))
150
+ return FTL .Resource ()
151
+ except UnicodeDecodeError :
152
+ logger = logging .getLogger ('migrate' )
153
+ logger .warn (
154
+ 'Localization file {} has broken encoding. '
155
+ 'It will be re-created and some translations '
156
+ 'may be lost' .format (path ))
157
+ return FTL .Resource ()
158
+
117
159
def maybe_add_localization (self , path ):
118
160
"""Add a localization resource to migrate translations from.
119
161
@@ -159,21 +201,8 @@ def get_sources(acc, cur):
159
201
acc .add ((cur .path , cur .key ))
160
202
return acc
161
203
162
- refpath = os .path .join (self .reference_dir , reference )
163
- try :
164
- ast = self .read_ftl_resource (refpath )
165
- except IOError as err :
166
- error_message = 'Missing reference file: {}' .format (refpath )
167
- logging .getLogger ('migrate' ).error (error_message )
168
- raise UnreadableReferenceError (error_message )
169
- except UnicodeDecodeError as err :
170
- error_message = 'Error reading file {}: {}' .format (refpath , err )
171
- logging .getLogger ('migrate' ).error (error_message )
172
- raise UnreadableReferenceError (error_message )
173
- else :
174
- # The reference file will be used by the merge function as
175
- # a template for serializing the merge results.
176
- self .reference_resources [target ] = ast
204
+ reference_ast = self .read_reference_ftl (reference )
205
+ self .reference_resources [target ] = reference_ast
177
206
178
207
for node in transforms :
179
208
# Scan `node` for `Source` nodes and collect the information they
@@ -182,29 +211,33 @@ def get_sources(acc, cur):
182
211
# Set these sources as dependencies for the current transform.
183
212
self .dependencies [(target , node .id .name )] = dependencies
184
213
185
- # Read all legacy translation files defined in Source transforms.
214
+ # Keep track of localization resource paths which were defined as
215
+ # sources in the transforms.
216
+ expected_paths = set ()
217
+
218
+ # Read all legacy translation files defined in Source transforms. This
219
+ # may fail but a single missing legacy resource doesn't mean that the
220
+ # migration can't succeed.
221
+ for dependencies in self .dependencies .values ():
186
222
for path in set (path for path , _ in dependencies ):
223
+ expected_paths .add (path )
187
224
self .maybe_add_localization (path )
188
225
226
+ # However, if all legacy resources are missing, bail out early. There
227
+ # are no translations to migrate. We'd also get errors in hg annotate.
228
+ if len (expected_paths ) > 0 and len (self .localization_resources ) == 0 :
229
+ error_message = 'No localization files were found'
230
+ logging .getLogger ('migrate' ).error (error_message )
231
+ raise EmptyLocalizationError (error_message )
232
+
233
+ # Add the current transforms to any other transforms added earlier for
234
+ # this path.
189
235
path_transforms = self .transforms .setdefault (target , [])
190
236
path_transforms += transforms
191
237
192
238
if target not in self .localization_resources :
193
- fullpath = os .path .join (self .localization_dir , target )
194
- try :
195
- ast = self .read_ftl_resource (fullpath )
196
- except IOError :
197
- logger = logging .getLogger ('migrate' )
198
- logger .info (
199
- 'Localization file {} does not exist and '
200
- 'it will be created' .format (target ))
201
- except UnicodeDecodeError :
202
- logger = logging .getLogger ('migrate' )
203
- logger .warn (
204
- 'Localization file {} will be re-created and some '
205
- 'translations might be lost' .format (target ))
206
- else :
207
- self .localization_resources [target ] = ast
239
+ target_ast = self .read_localization_ftl (target )
240
+ self .localization_resources [target ] = target_ast
208
241
209
242
def get_source (self , path , key ):
210
243
"""Get an entity value from a localized legacy source.
@@ -259,7 +292,7 @@ def merge_changeset(self, changeset=None):
259
292
}
260
293
261
294
for path , reference in self .reference_resources .iteritems ():
262
- current = self .localization_resources . get ( path , FTL . Resource ())
295
+ current = self .localization_resources [ path ]
263
296
transforms = self .transforms .get (path , [])
264
297
265
298
def in_changeset (ident ):
0 commit comments