Skip to content

Commit f951d5c

Browse files
committed
Never migrate partial translations
Partial translations may break the AST because the produce TextElements with None values. For now, explicitly skip any migrations which depend on at least one missing legacy string. In the future we'd like to be able to allow partial migrations in some situations. See https://bugzilla.mozilla.org/show_bug.cgi?id=1321271.
1 parent b402643 commit f951d5c

File tree

4 files changed

+317
-18
lines changed

4 files changed

+317
-18
lines changed

fluent/migrate/context.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,11 @@ def in_changeset(ident):
324324
if len(message_deps) == 0:
325325
return True
326326

327-
# If the intersection of the dependencies and the current
328-
# changeset is non-empty, merge this message.
329-
return message_deps & changeset
327+
# Make sure all the dependencies are present in the current
328+
# changeset. Partial migrations are not currently supported.
329+
# See https://bugzilla.mozilla.org/show_bug.cgi?id=1321271
330+
available_deps = message_deps & changeset
331+
return message_deps == available_deps
330332

331333
# Merge legacy translations with the existing ones using the
332334
# reference as a template.

tests/migrate/test_context.py

Lines changed: 291 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def test_missing_reference_file(self):
251251

252252

253253
@unittest.skipUnless(compare_locales, 'compare-locales requried')
254-
class TestEmptyLocalization(unittest.TestCase):
254+
class TestMissingLocalizationFiles(unittest.TestCase):
255255
def setUp(self):
256256
# Silence all logging.
257257
logging.disable(logging.CRITICAL)
@@ -266,7 +266,40 @@ def tearDown(self):
266266
# Resume logging.
267267
logging.disable(logging.NOTSET)
268268

269-
def test_all_localization_missing(self):
269+
def test_missing_file(self):
270+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
271+
FTL.Message(
272+
id=FTL.Identifier('title'),
273+
value=COPY(
274+
'aboutDownloads.dtd',
275+
'aboutDownloads.title'
276+
)
277+
),
278+
FTL.Message(
279+
id=FTL.Identifier('header'),
280+
value=COPY(
281+
'missing.dtd',
282+
'missing'
283+
)
284+
),
285+
])
286+
287+
expected = {
288+
'aboutDownloads.ftl': ftl_resource_to_json('''
289+
# This Source Code Form is subject to the terms of the Mozilla Public
290+
# License, v. 2.0. If a copy of the MPL was not distributed with this
291+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
292+
293+
title = Pobrane pliki
294+
''')
295+
}
296+
297+
self.assertDictEqual(
298+
to_json(self.ctx.merge_changeset()),
299+
expected
300+
)
301+
302+
def test_all_files_missing(self):
270303
pattern = ('No localization files were found')
271304
with self.assertRaisesRegexp(EmptyLocalizationError, pattern):
272305
self.ctx.add_transforms('existing.ftl', 'existing.ftl', [
@@ -281,7 +314,9 @@ def test_all_localization_missing(self):
281314

282315

283316
@unittest.skipUnless(compare_locales, 'compare-locales requried')
284-
class TestIncompleteLocalization(unittest.TestCase):
317+
class TestMissingLocalizationStrings(unittest.TestCase):
318+
maxDiff = None
319+
285320
def setUp(self):
286321
# Silence all logging.
287322
logging.disable(logging.CRITICAL)
@@ -296,20 +331,131 @@ def tearDown(self):
296331
# Resume logging.
297332
logging.disable(logging.NOTSET)
298333

299-
def test_missing_localization_file(self):
300-
self.ctx.add_transforms('existing.ftl', 'existing.ftl', [
334+
def test_missing_string_in_simple_value(self):
335+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
301336
FTL.Message(
302-
id=FTL.Identifier('foo'),
337+
id=FTL.Identifier('title'),
303338
value=COPY(
304-
'existing.dtd',
305-
'foo'
339+
'aboutDownloads.dtd',
340+
'missing'
306341
)
307342
),
343+
])
344+
345+
self.assertDictEqual(
346+
to_json(self.ctx.merge_changeset()),
347+
{}
348+
)
349+
350+
def test_missing_string_in_only_variant(self):
351+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
308352
FTL.Message(
309-
id=FTL.Identifier('bar'),
310-
value=COPY(
311-
'missing.dtd',
312-
'bar'
353+
id=FTL.Identifier('title'),
354+
value=FTL.Pattern(
355+
elements=[
356+
FTL.Placeable(
357+
expression=FTL.SelectExpression(
358+
expression=FTL.CallExpression(
359+
callee=FTL.Identifier('PLATFORM'),
360+
args=[]
361+
),
362+
variants=[
363+
FTL.Variant(
364+
key=FTL.VariantName('other'),
365+
default=True,
366+
value=COPY(
367+
'aboutDownloads.dtd',
368+
'missing'
369+
)
370+
),
371+
]
372+
)
373+
),
374+
]
375+
)
376+
),
377+
])
378+
379+
self.assertDictEqual(
380+
to_json(self.ctx.merge_changeset()),
381+
{}
382+
)
383+
384+
def test_missing_string_in_all_variants(self):
385+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
386+
FTL.Message(
387+
id=FTL.Identifier('title'),
388+
value=FTL.Pattern(
389+
elements=[
390+
FTL.Placeable(
391+
expression=FTL.SelectExpression(
392+
expression=FTL.CallExpression(
393+
callee=FTL.Identifier('PLATFORM'),
394+
args=[]
395+
),
396+
variants=[
397+
FTL.Variant(
398+
key=FTL.VariantName('windows'),
399+
default=False,
400+
value=COPY(
401+
'aboutDownloads.dtd',
402+
'missing.windows'
403+
)
404+
),
405+
FTL.Variant(
406+
key=FTL.VariantName('other'),
407+
default=True,
408+
value=COPY(
409+
'aboutDownloads.dtd',
410+
'missing.other'
411+
)
412+
),
413+
]
414+
)
415+
),
416+
]
417+
)
418+
),
419+
])
420+
421+
self.assertDictEqual(
422+
to_json(self.ctx.merge_changeset()),
423+
{}
424+
)
425+
426+
def test_missing_string_in_one_of_variants(self):
427+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
428+
FTL.Message(
429+
id=FTL.Identifier('title'),
430+
value=FTL.Pattern(
431+
elements=[
432+
FTL.Placeable(
433+
expression=FTL.SelectExpression(
434+
expression=FTL.CallExpression(
435+
callee=FTL.Identifier('PLATFORM'),
436+
args=[]
437+
),
438+
variants=[
439+
FTL.Variant(
440+
key=FTL.VariantName('windows'),
441+
default=False,
442+
value=COPY(
443+
'aboutDownloads.dtd',
444+
'aboutDownloads.title'
445+
)
446+
),
447+
FTL.Variant(
448+
key=FTL.VariantName('other'),
449+
default=True,
450+
value=COPY(
451+
'aboutDownloads.dtd',
452+
'missing.other'
453+
)
454+
),
455+
]
456+
)
457+
),
458+
]
313459
)
314460
),
315461
])
@@ -319,6 +465,139 @@ def test_missing_localization_file(self):
319465
{}
320466
)
321467

468+
def test_missing_string_in_all_attributes(self):
469+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
470+
FTL.Message(
471+
id=FTL.Identifier('title'),
472+
attributes=[
473+
FTL.Attribute(
474+
FTL.Identifier('one'),
475+
COPY(
476+
'aboutDownloads.dtd',
477+
'missing.one'
478+
)
479+
),
480+
FTL.Attribute(
481+
FTL.Identifier('two'),
482+
COPY(
483+
'aboutDownloads.dtd',
484+
'missing.two'
485+
)
486+
),
487+
]
488+
),
489+
])
490+
491+
self.assertDictEqual(
492+
to_json(self.ctx.merge_changeset()),
493+
{}
494+
)
495+
496+
def test_missing_string_in_one_of_attributes(self):
497+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
498+
FTL.Message(
499+
id=FTL.Identifier('title'),
500+
attributes=[
501+
FTL.Attribute(
502+
FTL.Identifier('title'),
503+
COPY(
504+
'aboutDownloads.dtd',
505+
'aboutDownloads.title'
506+
)
507+
),
508+
FTL.Attribute(
509+
FTL.Identifier('missing'),
510+
COPY(
511+
'aboutDownloads.dtd',
512+
'missing'
513+
)
514+
),
515+
]
516+
),
517+
])
518+
519+
self.assertDictEqual(
520+
to_json(self.ctx.merge_changeset()),
521+
{}
522+
)
523+
524+
def test_missing_string_in_only_attribute(self):
525+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
526+
FTL.Message(
527+
id=FTL.Identifier('title'),
528+
attributes=[
529+
FTL.Attribute(
530+
FTL.Identifier('one'),
531+
COPY(
532+
'aboutDownloads.dtd',
533+
'missing'
534+
)
535+
),
536+
]
537+
),
538+
])
539+
540+
self.assertDictEqual(
541+
to_json(self.ctx.merge_changeset()),
542+
{}
543+
)
544+
545+
def test_missing_string_in_all_attributes(self):
546+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
547+
FTL.Message(
548+
id=FTL.Identifier('title'),
549+
attributes=[
550+
FTL.Attribute(
551+
FTL.Identifier('one'),
552+
COPY(
553+
'aboutDownloads.dtd',
554+
'missing.one'
555+
)
556+
),
557+
FTL.Attribute(
558+
FTL.Identifier('two'),
559+
COPY(
560+
'aboutDownloads.dtd',
561+
'missing.two'
562+
)
563+
),
564+
]
565+
),
566+
])
567+
568+
self.assertDictEqual(
569+
to_json(self.ctx.merge_changeset()),
570+
{}
571+
)
572+
573+
def test_missing_string_in_one_of_attributes(self):
574+
self.ctx.add_transforms('aboutDownloads.ftl', 'aboutDownloads.ftl', [
575+
FTL.Message(
576+
id=FTL.Identifier('title'),
577+
attributes=[
578+
FTL.Attribute(
579+
FTL.Identifier('title'),
580+
COPY(
581+
'aboutDownloads.dtd',
582+
'aboutDownloads.title'
583+
)
584+
),
585+
FTL.Attribute(
586+
FTL.Identifier('missing'),
587+
COPY(
588+
'aboutDownloads.dtd',
589+
'missing'
590+
)
591+
),
592+
]
593+
),
594+
])
595+
596+
self.assertDictEqual(
597+
to_json(self.ctx.merge_changeset()),
598+
{}
599+
)
600+
322601

323602
@unittest.skipUnless(compare_locales, 'compare-locales requried')
324603
class TestExistingTarget(unittest.TestCase):
@@ -360,7 +639,6 @@ def test_existing_target_ftl_missing_string(self):
360639
''')
361640
}
362641

363-
self.maxDiff = None
364642
self.assertDictEqual(
365643
to_json(self.ctx.merge_changeset()),
366644
expected
@@ -399,7 +677,6 @@ def test_existing_target_ftl_existing_string(self):
399677
''')
400678
}
401679

402-
self.maxDiff = None
403680
self.assertDictEqual(
404681
to_json(self.ctx.merge_changeset()),
405682
expected

0 commit comments

Comments
 (0)