class TestNumbers(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource(FluentResource(dedent_ftl(""" one = { 1 } one_point_two = { 1.2 } select = { 1 -> *[0] Zero [1] One } """))) def test_int_number_used_in_placeable(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('one').value, {}) self.assertEqual(val, '1') self.assertEqual(len(errs), 0) def test_float_number_used_in_placeable(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('one_point_two').value, {}) self.assertEqual(val, '1.2') self.assertEqual(len(errs), 0) def test_can_be_used_as_a_selector(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('select').value, {}) self.assertEqual(val, 'One') self.assertEqual(len(errs), 0)
class TestAttributesWithStringValues(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource(FluentResource(dedent_ftl(""" foo = Foo .attr = Foo Attribute bar = { foo } Bar .attr = Bar Attribute ref-foo = { foo.attr } ref-bar = { bar.attr } """))) def test_can_be_referenced_for_entities_with_string_values(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-foo').value, {}) self.assertEqual(val, 'Foo Attribute') self.assertEqual(len(errs), 0) def test_can_be_referenced_for_entities_with_pattern_values(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-bar').value, {}) self.assertEqual(val, 'Bar Attribute') self.assertEqual(len(errs), 0) def test_can_be_formatted_directly_for_entities_with_string_values(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('foo').attributes['attr'], {}) self.assertEqual(val, 'Foo Attribute') self.assertEqual(len(errs), 0) def test_can_be_formatted_directly_for_entities_with_pattern_values(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('bar').attributes['attr'], {}) self.assertEqual(val, 'Bar Attribute') self.assertEqual(len(errs), 0)
class TestNumbersInValues(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource(FluentResource(dedent_ftl(""" foo = Foo { $num } bar = { foo } baz = .attr = Baz Attribute { $num } qux = { "a" -> *[a] Baz Variant A { $num } } """))) def test_can_be_used_in_the_message_value(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('foo').value, {'num': 3}) self.assertEqual(val, 'Foo 3') self.assertEqual(len(errs), 0) def test_can_be_used_in_the_message_value_which_is_referenced(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('bar').value, {'num': 3}) self.assertEqual(val, 'Foo 3') self.assertEqual(len(errs), 0) def test_can_be_used_in_an_attribute(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('baz').attributes['attr'], {'num': 3}) self.assertEqual(val, 'Baz Attribute 3') self.assertEqual(len(errs), 0) def test_can_be_used_in_a_variant(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('qux').value, {'num': 3}) self.assertEqual(val, 'Baz Variant A 3') self.assertEqual(len(errs), 0)
class TestResolving(unittest.TestCase): def setUp(self): self.args_passed = [] def number_processor(number): self.args_passed.append(number) return number self.bundle = FluentBundle( ['en-US'], use_isolating=False, functions={'NUMBER_PROCESSOR': number_processor}) self.bundle.add_resource( FluentResource( dedent_ftl(""" pass-number = { NUMBER_PROCESSOR(1) } pass-arg = { NUMBER_PROCESSOR($arg) } """))) def test_args_passed_as_numbers(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-arg').value, {'arg': 1}) self.assertEqual(val, "1") self.assertEqual(len(errs), 0) self.assertEqual(self.args_passed, [1]) def test_literals_passed_as_numbers(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-number').value, {}) self.assertEqual(val, "1") self.assertEqual(len(errs), 0) self.assertEqual(self.args_passed, [1])
class TestDatetimeBuiltin(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" implicit-call = { $date } explicit-call = { DATETIME($date) } call-with-arg = { DATETIME($date, dateStyle: "long") } """))) def test_implicit_call_date(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('implicit-call').value, {'date': date(2018, 2, 1)}) self.assertEqual(val, "Feb 1, 2018") self.assertEqual(len(errs), 0) def test_implicit_call_datetime(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('implicit-call').value, {'date': datetime(2018, 2, 1, 14, 15, 16)}) self.assertEqual(val, "Feb 1, 2018") self.assertEqual(len(errs), 0) def test_explicit_call_date(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('explicit-call').value, {'date': date(2018, 2, 1)}) self.assertEqual(val, "Feb 1, 2018") self.assertEqual(len(errs), 0) def test_explicit_call_datetime(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('explicit-call').value, {'date': datetime(2018, 2, 1, 14, 15, 16)}) self.assertEqual(val, "Feb 1, 2018") self.assertEqual(len(errs), 0) def test_explicit_call_date_fluent_date(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('explicit-call').value, {'date': fluent_date(date(2018, 2, 1), dateStyle='short')}) self.assertEqual(val, "2/1/18") self.assertEqual(len(errs), 0) def test_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('call-with-arg').value, {'date': date(2018, 2, 1)}) self.assertEqual(val, "February 1, 2018") self.assertEqual(len(errs), 0) def test_arg_overrides_fluent_date(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('call-with-arg').value, {'date': fluent_date(date(2018, 2, 1), dateStyle='short')}) self.assertEqual(val, "February 1, 2018") self.assertEqual(len(errs), 0)
class TestSelectExpressionWithNumbers(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = { 1 -> *[0] A [1] B } bar = { 2 -> *[0] A [1] B } baz = { $num -> *[0] A [1] B } qux = { 1.0 -> *[0] A [1] B } """))) def test_selects_the_right_variant(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, "B") self.assertEqual(len(errs), 0) def test_with_a_non_matching_selector(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('bar').value, {}) self.assertEqual(val, "A") self.assertEqual(len(errs), 0) def test_with_a_missing_selector(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').value, {}) self.assertEqual(val, "A") self.assertEqual(errs, [FluentReferenceError("Unknown external: num")]) def test_with_argument_int(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').value, {'num': 1}) self.assertEqual(val, "B") def test_with_argument_float(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').value, {'num': 1.0}) self.assertEqual(val, "B") def test_with_float(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('qux').value, {}) self.assertEqual(val, "B")
class TestMissing(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource(FluentResource(dedent_ftl(""" foo = Foo bar = Bar .attr = Bar Attribute baz = { foo } Baz qux = { foo } Qux .attr = Qux Attribute ref-foo = { foo.missing } ref-bar = { bar.missing } ref-baz = { baz.missing } ref-qux = { qux.missing } attr-only = .attr = Attr Only Attribute ref-double-missing = { missing.attr } """))) def test_msg_with_string_value_and_no_attributes(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-foo').value, {}) self.assertEqual(val, '{foo.missing}') self.assertEqual(errs, [FluentReferenceError( 'Unknown attribute: foo.missing')]) def test_msg_with_string_value_and_other_attributes(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-bar').value, {}) self.assertEqual(val, '{bar.missing}') self.assertEqual(errs, [FluentReferenceError( 'Unknown attribute: bar.missing')]) def test_msg_with_pattern_value_and_no_attributes(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-baz').value, {}) self.assertEqual(val, '{baz.missing}') self.assertEqual(errs, [FluentReferenceError( 'Unknown attribute: baz.missing')]) def test_msg_with_pattern_value_and_other_attributes(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-qux').value, {}) self.assertEqual(val, '{qux.missing}') self.assertEqual(errs, [FluentReferenceError( 'Unknown attribute: qux.missing')]) def test_attr_only_attribute(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('attr-only').attributes['attr'], {}) self.assertEqual(val, 'Attr Only Attribute') self.assertEqual(len(errs), 0) def test_missing_message_and_attribute(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('ref-double-missing').value, {}) self.assertEqual(val, '{missing.attr}') self.assertEqual(errs, [FluentReferenceError('Unknown attribute: missing.attr')])
class TestStrings(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource(FluentResource(dedent_ftl(""" foo = { $arg } """))) def test_can_be_a_string(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('foo').value, {'arg': 'Argument'}) self.assertEqual(val, 'Argument') self.assertEqual(len(errs), 0)
class TestComplexStringValue(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = Foo bar = { foo }Bar placeable-message = { bar }Baz baz = .attr = { bar }BazAttribute -qux = Qux .attr = { bar }QuxAttribute placeable-attr = { baz.attr } selector-attr = { -qux.attr -> [FooBarQuxAttribute] FooBarQux *[other] Other } """))) def test_can_be_used_as_a_value(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('bar').value, {}) self.assertEqual(val, 'FooBar') self.assertEqual(len(errs), 0) def test_can_be_value_of_a_message_referenced_in_a_placeable(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('placeable-message').value, {}) self.assertEqual(val, 'FooBarBaz') self.assertEqual(len(errs), 0) def test_can_be_used_as_an_attribute_value(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').attributes['attr'], {}) self.assertEqual(val, 'FooBarBazAttribute') self.assertEqual(len(errs), 0) def test_can_be_a_value_of_an_attribute_used_in_a_placeable(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('placeable-attr').value, {}) self.assertEqual(val, 'FooBarBazAttribute') self.assertEqual(len(errs), 0) def test_can_be_a_value_of_an_attribute_used_as_a_selector(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('selector-attr').value, {}) self.assertEqual(val, 'FooBarQux') self.assertEqual(len(errs), 0)
class TestNestedParameterizedTerms(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" -thing = { $article -> *[definite] { $first-letter -> *[lower] the thing [upper] The thing } [indefinite] { $first-letter -> *[lower] a thing [upper] A thing } } both-args = { -thing(first-letter: "upper", article: "indefinite") }. outer-arg = This is { -thing(article: "indefinite") }. inner-arg = { -thing(first-letter: "upper") }. neither-arg = { -thing() }. """))) def test_both_args(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('both-args').value, {}) self.assertEqual(val, 'A thing.') self.assertEqual(errs, []) def test_outer_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('outer-arg').value, {}) self.assertEqual(val, 'This is a thing.') self.assertEqual(errs, []) def test_inner_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('inner-arg').value, {}) self.assertEqual(val, 'The thing.') self.assertEqual(errs, []) def test_inner_arg_with_external_args(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('inner-arg').value, {'article': 'indefinite'}) self.assertEqual(val, 'The thing.') self.assertEqual(errs, []) def test_neither_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('neither-arg').value, {}) self.assertEqual(val, 'the thing.') self.assertEqual(errs, [])
class TestKeywordArgs(unittest.TestCase): def setUp(self): self.args_passed = [] def my_function(arg, kwarg1=None, kwarg2="default"): self.args_passed.append((arg, kwarg1, kwarg2)) return arg self.bundle = FluentBundle(['en-US'], use_isolating=False, functions={'MYFUNC': my_function}) self.bundle.add_resource( FluentResource( dedent_ftl(""" pass-arg = { MYFUNC("a") } pass-kwarg1 = { MYFUNC("a", kwarg1: 1) } pass-kwarg2 = { MYFUNC("a", kwarg2: "other") } pass-kwargs = { MYFUNC("a", kwarg1: 1, kwarg2: "other") } pass-user-arg = { MYFUNC($arg) } """))) def test_defaults(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-arg').value, {}) self.assertEqual(self.args_passed, [("a", None, "default")]) self.assertEqual(len(errs), 0) def test_pass_kwarg1(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-kwarg1').value, {}) self.assertEqual(self.args_passed, [("a", 1, "default")]) self.assertEqual(len(errs), 0) def test_pass_kwarg2(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-kwarg2').value, {}) self.assertEqual(self.args_passed, [("a", None, "other")]) self.assertEqual(len(errs), 0) def test_pass_kwargs(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-kwargs').value, {}) self.assertEqual(self.args_passed, [("a", 1, "other")]) self.assertEqual(len(errs), 0) def test_missing_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-user-arg').value, {}) self.assertEqual(self.args_passed, [(FluentNone('arg'), None, "default")]) self.assertEqual(len(errs), 1)
class TestSelectExpressionWithStrings(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) def test_with_a_matching_selector(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = { "a" -> [a] A *[b] B } """))) val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, "A") self.assertEqual(len(errs), 0) def test_with_a_non_matching_selector(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = { "c" -> [a] A *[b] B } """))) val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, "B") self.assertEqual(len(errs), 0) def test_with_a_missing_selector(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = { $none -> [a] A *[b] B } """))) val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, "B") self.assertEqual(errs, [FluentReferenceError("Unknown external: none")]) def test_with_argument_expression(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = { $arg -> [a] A *[b] B } """))) val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {'arg': 'a'}) self.assertEqual(val, "A")
class TestMissing(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" missing = { MISSING(1) } """))) def test_falls_back_to_name_of_function(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('missing').value, {}) self.assertEqual(val, "MISSING()") self.assertEqual(errs, [FluentReferenceError("Unknown function: MISSING")])
class TestSelectExpressionWithTerms(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" -my-term = term .attr = termattribute ref-term-attr = { -my-term.attr -> [termattribute] Term Attribute *[other] Other } ref-term-attr-other = { -my-term.attr -> [x] Term Attribute *[other] Other } ref-term-attr-missing = { -my-term.missing -> [x] Term Attribute *[other] Other } """))) def test_ref_term_attribute(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('ref-term-attr').value) self.assertEqual(val, "Term Attribute") self.assertEqual(len(errs), 0) def test_ref_term_attribute_fallback(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('ref-term-attr-other').value) self.assertEqual(val, "Other") self.assertEqual(len(errs), 0) def test_ref_term_attribute_missing(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('ref-term-attr-missing').value) self.assertEqual(val, "Other") self.assertEqual(len(errs), 1) self.assertEqual( errs, [FluentReferenceError('Unknown attribute: -my-term.missing')])
class TestMessagesCalledFromTerms(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" msg = Msg is {$arg} -foo = {msg} ref-foo = {-foo(arg: 1)} """))) def test_messages_inherit_term_args(self): # This behaviour may change in future, message calls might be # disallowed from inside terms val, errs = self.bundle.format_pattern( self.bundle.get_message('ref-foo').value, {'arg': 2}) self.assertEqual(val, 'Msg is 1') self.assertEqual(errs, [])
class TestParameterizedTermAttributes(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" -brand = Cool Thing .status = { $version -> [v2] available *[v1] deprecated } attr-with-arg = { -brand } is { -brand.status(version: "v2") -> [available] available, yay! *[deprecated] deprecated, sorry } -other = { $arg -> [a] ABC *[d] DEF } missing-attr-ref = { -other.missing(arg: "a") -> [ABC] ABC option *[DEF] DEF option } """))) def test_with_argument(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('attr-with-arg').value, {}) self.assertEqual(val, 'Cool Thing is available, yay!') self.assertEqual(errs, []) def test_missing_attr(self): # We don't fall back from attributes, get default. val, errs = self.bundle.format_pattern( self.bundle.get_message('missing-attr-ref').value, {}) self.assertEqual(val, 'DEF option') self.assertEqual( errs, [FluentReferenceError('Unknown attribute: -other.missing')])
def get_brand_terms(): """Return a dict of term IDs and english strings ordered with strings longest to shortest""" global BRAND_TERMS if BRAND_TERMS is None: bundle = FluentBundle(['en']) brands_path = settings.FLUENT_LOCAL_PATH / 'en' / 'brands.ftl' with brands_path.open(encoding='utf-8') as brands_file: resource = FluentResource(brands_file.read()) bundle.add_resource(resource) tmp_dict = { k: bundle._lookup(k, term=True).value.value for k, v in bundle._terms.items() } BRAND_TERMS = { k: tmp_dict[k] for k in sorted( tmp_dict, key=lambda k: len(tmp_dict[k]), reverse=True) } return BRAND_TERMS
class TestSkipIsolating(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US']) self.bundle.add_resource( FluentResource( dedent_ftl(""" -brand-short-name = Amaya foo = { -brand-short-name } with-arg = { $arg } """))) def test_skip_isolating_chars_if_just_one_message_ref(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, 'Amaya') self.assertEqual(len(errs), 0) def test_skip_isolating_chars_if_just_one_placeable_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('with-arg').value, {'arg': 'Arg'}) self.assertEqual(val, 'Arg') self.assertEqual(len(errs), 0)
class TestBillionLaughs(unittest.TestCase): def setUp(self): self.ctx = FluentBundle(['en-US'], use_isolating=False) self.ctx.add_resource( FluentResource( dedent_ftl(""" lol0 = 01234567890123456789012345678901234567890123456789 lol1 = {lol0}{lol0}{lol0}{lol0}{lol0}{lol0}{lol0}{lol0}{lol0}{lol0} lol2 = {lol1}{lol1}{lol1}{lol1}{lol1}{lol1}{lol1}{lol1}{lol1}{lol1} lol3 = {lol2}{lol2}{lol2}{lol2}{lol2}{lol2}{lol2}{lol2}{lol2}{lol2} lol4 = {lol3}{lol3}{lol3}{lol3}{lol3}{lol3}{lol3}{lol3}{lol3}{lol3} lolz = {lol4} elol0 = { "" } elol1 = {elol0}{elol0}{elol0}{elol0}{elol0}{elol0}{elol0}{elol0}{elol0}{elol0} elol2 = {elol1}{elol1}{elol1}{elol1}{elol1}{elol1}{elol1}{elol1}{elol1}{elol1} elol3 = {elol2}{elol2}{elol2}{elol2}{elol2}{elol2}{elol2}{elol2}{elol2}{elol2} elol4 = {elol3}{elol3}{elol3}{elol3}{elol3}{elol3}{elol3}{elol3}{elol3}{elol3} elol5 = {elol4}{elol4}{elol4}{elol4}{elol4}{elol4}{elol4}{elol4}{elol4}{elol4} elol6 = {elol5}{elol5}{elol5}{elol5}{elol5}{elol5}{elol5}{elol5}{elol5}{elol5} emptylolz = {elol6} """))) def test_max_length_protection(self): val, errs = self.ctx.format_pattern(self.ctx.get_message('lolz').value) self.assertEqual(val, '{???}') self.assertNotEqual(len(errs), 0) self.assertIn('Too many characters', str(errs[-1])) def test_max_expansions_protection(self): # Without protection, emptylolz will take a really long time to # evaluate, although it generates an empty message. val, errs = self.ctx.format_pattern( self.ctx.get_message('emptylolz').value) self.assertEqual(val, '{???}') self.assertEqual(len(errs), 1) self.assertIn('Too many parts', str(errs[-1]))
class TestUseIsolating(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US']) self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = Foo bar = { foo } Bar baz = { $arg } Baz qux = { bar } { baz } """))) def test_isolates_interpolated_message_references(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('bar').value, {}) self.assertEqual(val, FSI + "Foo" + PDI + " Bar") self.assertEqual(len(errs), 0) def test_isolates_interpolated_string_typed_variable_references(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').value, {'arg': 'Arg'}) self.assertEqual(val, FSI + "Arg" + PDI + " Baz") self.assertEqual(len(errs), 0) def test_isolates_interpolated_number_typed_variable_references(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').value, {'arg': 1}) self.assertEqual(val, FSI + "1" + PDI + " Baz") self.assertEqual(len(errs), 0) def test_isolates_complex_interpolations(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('qux').value, {'arg': 'Arg'}) expected_bar = FSI + FSI + "Foo" + PDI + " Bar" + PDI expected_baz = FSI + FSI + "Arg" + PDI + " Baz" + PDI self.assertEqual(val, expected_bar + " " + expected_baz) self.assertEqual(len(errs), 0)
class TestTermsCalledFromTerms(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" -foo = {$a} {$b} -bar = {-foo(b: 2)} -baz = {-foo} ref-bar = {-bar(a: 1)} ref-baz = {-baz(a: 1)} """))) def test_term_args_isolated_with_call_syntax(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('ref-bar').value, {}) self.assertEqual(val, 'a 2') self.assertEqual(errs, []) def test_term_args_isolated_without_call_syntax(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('ref-baz').value, {}) self.assertEqual(val, 'a b') self.assertEqual(errs, [])
def fluent_bundle(): bundle = FluentBundle(['pl'], use_isolating=False) bundle.add_resource(FluentResource(FTL_CONTENT)) return bundle
class TestSimpleStringValue(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource(FluentResource(dedent_ftl(r""" foo = Foo placeable-literal = { "Foo" } Bar placeable-message = { foo } Bar selector-literal = { "Foo" -> [Foo] Member 1 *[Bar] Member 2 } bar = .attr = Bar Attribute placeable-attr = { bar.attr } -baz = Baz .attr = BazAttribute selector-attr = { -baz.attr -> [BazAttribute] Member 3 *[other] Member 4 } escapes = {" "}stuff{"\u0258}\"\\end"} """))) def test_can_be_used_as_a_value(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('foo').value, {}) self.assertEqual(val, 'Foo') self.assertEqual(len(errs), 0) def test_can_be_used_in_a_placeable(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('placeable-literal').value, {}) self.assertEqual(val, 'Foo Bar') self.assertEqual(len(errs), 0) def test_can_be_a_value_of_a_message_referenced_in_a_placeable(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('placeable-message').value, {}) self.assertEqual(val, 'Foo Bar') self.assertEqual(len(errs), 0) def test_can_be_a_selector(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('selector-literal').value, {}) self.assertEqual(val, 'Member 1') self.assertEqual(len(errs), 0) def test_can_be_used_as_an_attribute_value(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('bar').attributes['attr'], {}) self.assertEqual(val, 'Bar Attribute') self.assertEqual(len(errs), 0) def test_can_be_a_value_of_an_attribute_used_in_a_placeable(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('placeable-attr').value, {}) self.assertEqual(val, 'Bar Attribute') self.assertEqual(len(errs), 0) def test_can_be_a_value_of_an_attribute_used_as_a_selector(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('selector-attr').value, {}) self.assertEqual(val, 'Member 3') self.assertEqual(len(errs), 0) def test_escapes(self): val, errs = self.bundle.format_pattern(self.bundle.get_message('escapes').value, {}) self.assertEqual(val, r' stuffɘ}"\end') self.assertEqual(len(errs), 0)
def compare_locales(from_lang: str, to_lang: str) -> None: """ Compares two whole languages in the Tsundoku translation files. Will point out any missing files or keys that do not exist in `to_lang` from `from_lang`. Parameters ---------- from_lang: str Origin locale. to_lang: str Destination locale. """ from_path = Path(f"l10n/{from_lang}") to_path = Path(f"l10n/{to_lang}") if not from_path.exists(): print(fluent._("compare-missing-lang", {"missing": from_lang})) return elif not to_path.exists(): print(fluent._("compare-missing-lang", {"missing": to_lang})) return conflicts = 0 from_files = {Path(*fp.parts[2:]) for fp in from_path.rglob("*")} to_files = {Path(*fp.parts[2:]) for fp in to_path.rglob("*")} for fp in from_files.difference(to_files): conflicts += 1 print( fluent._("compare-missing-file", { "lang": to_lang, "file": str(fp) })) for fp in from_files.intersection(to_files): from_file = from_path / fp to_file = to_path / fp from_bundle = FluentBundle([from_lang]) to_bundle = FluentBundle([to_lang]) with open(str(from_file), "r", encoding="utf-8") as text: from_bundle.add_resource(FluentResource(text.read())) with open(str(to_file), "r", encoding="utf-8") as text: to_bundle.add_resource(FluentResource(text.read())) from_keys = from_bundle._messages.keys() to_keys = to_bundle._messages.keys() missing_keys = set(from_keys).difference(set(to_keys)) for key in missing_keys: conflicts += 1 print( fluent._("compare-missing-key", { "lang": to_lang, "file": str(fp), "key": key })) if conflicts: print( fluent._("compare-conflict-count", { "count": conflicts, "to": to_lang })) else: print(fluent._("compare-no-conflict"))
class TestSelectExpressionWithPluralCategories(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = { 1 -> [one] A *[other] B } bar = { 1 -> [1] A *[other] B } baz = { "not a number" -> [one] A *[other] B } qux = { $num -> [one] A *[other] B } """))) def test_selects_the_right_category(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, "A") self.assertEqual(len(errs), 0) def test_selects_exact_match(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('bar').value, {}) self.assertEqual(val, "A") self.assertEqual(len(errs), 0) def test_selects_default_with_invalid_selector(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('baz').value, {}) self.assertEqual(val, "B") self.assertEqual(len(errs), 0) def test_with_a_missing_selector(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('qux').value, {}) self.assertEqual(val, "B") self.assertEqual(errs, [FluentReferenceError("Unknown external: num")]) def test_with_argument_integer(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('qux').value, {'num': 1}) self.assertEqual(val, "A") self.assertEqual(len(errs), 0) val, errs = self.bundle.format_pattern( self.bundle.get_message('qux').value, {'num': 2}) self.assertEqual(val, "B") self.assertEqual(len(errs), 0) def test_with_argument_float(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('qux').value, {'num': 1.0}) self.assertEqual(val, "A") self.assertEqual(len(errs), 0)
def interpreting_fluent_bundle(): # We choose 'use_isolating=False' for feature parity with gettext bundle = InterpretingFluentBundle(['pl'], use_isolating=False) bundle.add_resource(FluentResource(FTL_MESSAGES)) return bundle
class TestFunctionCalls(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False, functions={'IDENTITY': lambda x: x}) self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = Foo .attr = Attribute pass-nothing = { IDENTITY() } pass-string = { IDENTITY("a") } pass-number = { IDENTITY(1) } pass-message = { IDENTITY(foo) } pass-attr = { IDENTITY(foo.attr) } pass-external = { IDENTITY($ext) } pass-function-call = { IDENTITY(IDENTITY(1)) } """))) def test_accepts_strings(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-string').value, {}) self.assertEqual(val, "a") self.assertEqual(len(errs), 0) def test_accepts_numbers(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-number').value, {}) self.assertEqual(val, "1") self.assertEqual(len(errs), 0) def test_accepts_entities(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-message').value, {}) self.assertEqual(val, "Foo") self.assertEqual(len(errs), 0) def test_accepts_attributes(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-attr').value, {}) self.assertEqual(val, "Attribute") self.assertEqual(len(errs), 0) def test_accepts_externals(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-external').value, {'ext': 'Ext'}) self.assertEqual(val, "Ext") self.assertEqual(len(errs), 0) def test_accepts_function_calls(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-function-call').value, {}) self.assertEqual(val, "1") self.assertEqual(len(errs), 0) def test_wrong_arity(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('pass-nothing').value, {}) self.assertEqual(val, "IDENTITY()") self.assertEqual(len(errs), 1) self.assertEqual(type(errs[0]), TypeError)
class TestParameterizedTerms(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" -thing = { $article -> *[definite] the thing [indefinite] a thing [none] thing } thing-no-arg = { -thing } thing-no-arg-alt = { -thing() } thing-with-arg = { -thing(article: "indefinite") } thing-positional-arg = { -thing("foo") } thing-fallback = { -thing(article: "somethingelse") } bad-term = { -missing() } """))) def test_argument_omitted(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-no-arg').value, {}) self.assertEqual(val, 'the thing') self.assertEqual(errs, []) def test_argument_omitted_alt(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-no-arg-alt').value, {}) self.assertEqual(val, 'the thing') self.assertEqual(errs, []) def test_with_argument(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-with-arg').value, {}) self.assertEqual(val, 'a thing') self.assertEqual(errs, []) def test_positional_arg(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-positional-arg').value, {}) self.assertEqual(val, 'the thing') self.assertEqual(errs, [ FluentFormatError( "Ignored positional arguments passed to term '-thing'") ]) def test_fallback(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-fallback').value, {}) self.assertEqual(val, 'the thing') self.assertEqual(errs, []) def test_no_implicit_access_to_external_args(self): # The '-thing' term should not get passed article="indefinite" val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-no-arg').value, {'article': 'indefinite'}) self.assertEqual(val, 'the thing') self.assertEqual(errs, []) def test_no_implicit_access_to_external_args_but_term_args_still_passed( self): val, errs = self.bundle.format_pattern( self.bundle.get_message('thing-with-arg').value, {'article': 'none'}) self.assertEqual(val, 'a thing') self.assertEqual(errs, []) def test_bad_term(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('bad-term').value, {}) self.assertEqual(val, '{-missing}') self.assertEqual(errs, [FluentReferenceError('Unknown term: -missing')])
class TestNumberBuiltin(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US'], use_isolating=False) self.bundle.add_resource( FluentResource( dedent_ftl(""" implicit-call = { 123456 } implicit-call2 = { $arg } defaults = { NUMBER(123456) } percent-style = { NUMBER(1.234, style: "percent") } currency-style = { NUMBER(123456, style: "currency", currency: "USD") } from-arg = { NUMBER($arg) } merge-params = { NUMBER($arg, useGrouping: 0) } """))) def test_implicit_call(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('implicit-call').value, {}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_implicit_call2_int(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('implicit-call2').value, {'arg': 123456}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_implicit_call2_float(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('implicit-call2').value, {'arg': 123456.0}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_implicit_call2_decimal(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('implicit-call2').value, {'arg': Decimal('123456.0')}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_defaults(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('defaults').value, {}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_percent_style(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('percent-style').value, {}) self.assertEqual(val, "123%") self.assertEqual(len(errs), 0) def test_currency_style(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('currency-style').value, {}) self.assertEqual(val, "$123,456.00") self.assertEqual(len(errs), 0) def test_from_arg_int(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('from-arg').value, {'arg': 123456}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_from_arg_float(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('from-arg').value, {'arg': 123456.0}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_from_arg_decimal(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('from-arg').value, {'arg': Decimal('123456.0')}) self.assertEqual(val, "123,456") self.assertEqual(len(errs), 0) def test_from_arg_missing(self): val, errs = self.bundle.format_pattern( self.bundle.get_message('from-arg').value, {}) self.assertEqual(val, "arg") self.assertEqual(len(errs), 1) self.assertEqual(errs, [FluentReferenceError('Unknown external: arg')]) def test_partial_application(self): number = fluent_number(123456.78, currency="USD", style="currency") val, errs = self.bundle.format_pattern( self.bundle.get_message('from-arg').value, {'arg': number}) self.assertEqual(val, "$123,456.78") self.assertEqual(len(errs), 0) def test_merge_params(self): number = fluent_number(123456.78, currency="USD", style="currency") val, errs = self.bundle.format_pattern( self.bundle.get_message('merge-params').value, {'arg': number}) self.assertEqual(val, "$123456.78") self.assertEqual(len(errs), 0)
class TestFluentBundle(unittest.TestCase): def setUp(self): self.bundle = FluentBundle(['en-US']) def test_add_resource(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = Foo bar = Bar -baz = Baz """))) self.assertIn('foo', self.bundle._messages) self.assertIn('bar', self.bundle._messages) self.assertIn('baz', self.bundle._terms) def test_has_message(self): self.bundle.add_resource( FluentResource(dedent_ftl(""" foo = Foo """))) self.assertTrue(self.bundle.has_message('foo')) self.assertFalse(self.bundle.has_message('bar')) def test_has_message_for_term(self): self.bundle.add_resource( FluentResource(dedent_ftl(""" -foo = Foo """))) self.assertFalse(self.bundle.has_message('-foo')) def test_has_message_with_attribute(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = Foo .attr = Foo Attribute """))) self.assertTrue(self.bundle.has_message('foo')) self.assertFalse(self.bundle.has_message('foo.attr')) self.assertFalse(self.bundle.has_message('foo.other-attribute')) def test_plural_form_english_ints(self): bundle = FluentBundle(['en-US']) self.assertEqual(bundle._plural_form(0), 'other') self.assertEqual(bundle._plural_form(1), 'one') self.assertEqual(bundle._plural_form(2), 'other') def test_plural_form_english_floats(self): bundle = FluentBundle(['en-US']) self.assertEqual(bundle._plural_form(0.0), 'other') self.assertEqual(bundle._plural_form(1.0), 'one') self.assertEqual(bundle._plural_form(2.0), 'other') self.assertEqual(bundle._plural_form(0.5), 'other') def test_plural_form_french(self): # Just spot check one other, to ensure that we # are not getting the EN locale by accident or bundle = FluentBundle(['fr']) self.assertEqual(bundle._plural_form(0), 'one') self.assertEqual(bundle._plural_form(1), 'one') self.assertEqual(bundle._plural_form(2), 'other') def test_format_args(self): self.bundle.add_resource(FluentResource('foo = Foo')) val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value) self.assertEqual(val, 'Foo') val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, 'Foo') def test_format_missing(self): self.assertRaises(LookupError, self.bundle.get_message, 'a-missing-message') def test_format_term(self): self.bundle.add_resource( FluentResource(dedent_ftl(""" -foo = Foo """))) self.assertRaises(LookupError, self.bundle.get_message, '-foo') self.assertRaises(LookupError, self.bundle.get_message, 'foo') def test_message_and_term_separate(self): self.bundle.add_resource( FluentResource( dedent_ftl(""" foo = Refers to { -foo } -foo = Foo """))) val, errs = self.bundle.format_pattern( self.bundle.get_message('foo').value, {}) self.assertEqual(val, 'Refers to \u2068Foo\u2069') self.assertEqual(errs, [])