def handle_block(self, block_type, block_value, raw_value=None): # Need to check if the app is installed before importing EmbedBlock # See: https://github.com/wagtail/wagtail-localize/issues/309 if apps.is_installed("wagtail.embeds"): from wagtail.embeds.blocks import EmbedBlock if isinstance(block_type, EmbedBlock): if self.include_overridables: return [OverridableSegmentValue("", block_value.url)] else: return [] if hasattr(block_type, "get_translatable_segments"): return block_type.get_translatable_segments(block_value) elif isinstance(block_type, (blocks.URLBlock, blocks.EmailBlock)): if self.include_overridables: return [OverridableSegmentValue("", block_value)] else: return [] elif isinstance( block_type, (blocks.CharBlock, blocks.TextBlock, blocks.BlockQuoteBlock)): return [StringSegmentValue("", block_value)] elif isinstance(block_type, blocks.RichTextBlock): template, strings = extract_strings(block_value.source) # Find all unique href values hrefs = set() for _string, attrs in strings: for tag_attrs in attrs.values(): if "href" in tag_attrs: hrefs.add(tag_attrs["href"]) ret = ( [TemplateSegmentValue("", "html", template, len(strings))] + [ StringSegmentValue("", string, attrs=attrs) for string, attrs in strings ] + [ OverridableSegmentValue(quote_path_component(href), href) for href in sorted(hrefs) ]) return ret elif isinstance(block_type, blocks.ChooserBlock): return self.handle_related_object_block(block_value) elif isinstance(block_type, blocks.StructBlock): return self.handle_struct_block(block_value) elif isinstance(block_type, blocks.ListBlock): return self.handle_list_block(block_value, raw_value) elif isinstance(block_type, blocks.StreamBlock): return self.handle_stream_block(block_value) # Ignore everything else return []
def test_listblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_listblock", ["Test content", "Some more test content"]) translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [ StringSegmentValue(f"test_streamfield.{block_id}", "Tester le contenu", order=0), StringSegmentValue(f"test_streamfield.{block_id}", "Encore du contenu de test", order=1), ], ) translated_page.save() translated_page.refresh_from_db() self.assertEqual( translated_page.test_streamfield.stream_data, [{ "id": str(block_id), "type": "test_listblock", "value": ["Tester le contenu", "Encore du contenu de test"], }], )
def handle_block(self, block_type, block_value): if hasattr(block_type, "get_translatable_segments"): return block_type.get_translatable_segments(block_value) elif isinstance(block_type, (blocks.CharBlock, blocks.TextBlock)): return [StringSegmentValue("", block_value)] elif isinstance(block_type, blocks.RichTextBlock): template, strings = extract_strings(block_value.source) return [TemplateSegmentValue("", "html", template, len(strings)) ] + [ StringSegmentValue("", string, attrs=attrs) for string, attrs in strings ] elif isinstance(block_type, blocks.ChooserBlock): return self.handle_related_object_block(block_value) elif isinstance(block_type, blocks.StructBlock): return self.handle_struct_block(block_value) elif isinstance(block_type, blocks.ListBlock): return self.handle_list_block(block_value) elif isinstance(block_type, blocks.StreamBlock): return self.handle_stream_block(block_value) elif isinstance(block_type, (blocks.ChoiceBlock, EmbedBlock)): return [] else: raise Exception( "Unrecognised StreamField block type '{}'. Have you implemented get_translatable_segments on this class?" .format(block_type.__class__.__name__))
def test_urlblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_urlblock", "http://test-content.com/foo") translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [ StringSegmentValue(f"test_streamfield.{block_id}", "http://test-content.fr/foo") ], ) translated_page.save() translated_page.refresh_from_db() self.assertEqual( list(translated_page.test_streamfield.stream_data), [{ "id": str(block_id), "type": "test_urlblock", "value": "http://test-content.fr/foo", }], )
def test_emailfield(self): page = make_test_page(test_emailfield="*****@*****.**") segments = extract_segments(page) self.assertEqual( segments, [StringSegmentValue("test_emailfield", "*****@*****.**")])
def test_blockquoteblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_blockquoteblock", "Test content" ) translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [StringSegmentValue(f"test_streamfield.{block_id}", "Tester le contenu")], ) translated_page.save() translated_page.refresh_from_db() self.assertEqual( translated_page.test_streamfield.stream_data, [ { "id": str(block_id), "type": "test_blockquoteblock", "value": "Tester le contenu", } ], )
def test_childobjects(self): page = make_test_page() page.test_childobjects.add(TestChildObject(field="Test content")) page.save() child_translation_key = TestChildObject.objects.get().translation_key translated_page = page.copy_for_translation(self.locale) copy_synchronised_fields(page, translated_page) ingest_segments( page, translated_page, self.src_locale, self.locale, [ StringSegmentValue( f"test_childobjects.{child_translation_key}.field", "Tester le contenu", ) ], ) old_child_object = page.test_childobjects.get() new_child_object = translated_page.test_childobjects.get() self.assertNotEqual(old_child_object.id, new_child_object.id) self.assertEqual(old_child_object.locale, self.src_locale) self.assertEqual(new_child_object.locale, self.locale) self.assertEqual( old_child_object.translation_key, new_child_object.translation_key ) self.assertEqual(new_child_object.field, "Tester le contenu")
def test_listblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_listblock", [ { "type": "item", "value": "Test content", "id": "11111111-1111-1111-1111-111111111111", }, { "type": "item", "value": "Some more test content", "id": "22222222-2222-2222-2222-222222222222", }, ], ) expected_segments = [ StringSegmentValue(f"test_streamfield.{block_id}.{item.id}", item.value) for item in page.test_streamfield[0].value.bound_blocks ] segments = extract_segments(page) self.assertEqual(segments, expected_segments)
def test_urlfield(self): page = make_test_page(test_urlfield="http://test-content.com/foo") segments = extract_segments(page) self.assertEqual(segments, [ StringSegmentValue("test_urlfield", "http://test-content.com/foo") ])
def test_listblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_listblock", ["Test content", "Some more test content"]) segments = extract_segments(page) self.assertEqual( segments, [ StringSegmentValue(f"test_streamfield.{block_id}", "Test content"), StringSegmentValue(f"test_streamfield.{block_id}", "Some more test content"), ], )
def test_raises_typeerror_if_string_is_none(self): with self.assertRaises(TypeError) as e: StringSegmentValue("foo.bar", None) self.assertEqual( str(e.exception), "`string` must be either a `StringValue` or a `str`. Got `NoneType`", )
def test_blockquoteblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block(str(block_id), "test_blockquoteblock", "Test content") segments = extract_segments(page) self.assertEqual(segments, [ StringSegmentValue(f"test_streamfield.{block_id}", "Test content") ])
def test_customfield(self): page = make_test_page(test_customfield="Test content") segments = extract_segments(page) self.assertEqual( segments, [ StringSegmentValue("test_customfield.foo", "Test content and some extra") ], )
def test_emailblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block(str(block_id), "test_emailblock", "*****@*****.**") segments = extract_segments(page) self.assertEqual(segments, [ StringSegmentValue(f"test_streamfield.{block_id}", "*****@*****.**") ])
def test_urlfield(self): page = make_test_page(test_urlfield="http://test-content.com/foo") translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [StringSegmentValue("test_urlfield", "http://test-content.fr/foo")], ) self.assertEqual(translated_page.test_urlfield, "http://test-content.fr/foo")
def test_textfield(self): page = make_test_page(test_textfield="Test content") translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [StringSegmentValue("test_textfield", "Tester le contenu")], ) self.assertEqual(translated_page.test_textfield, "Tester le contenu")
def test_emailfield(self): page = make_test_page(test_emailfield="*****@*****.**") translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [StringSegmentValue("test_emailfield", "*****@*****.**")], ) self.assertEqual(translated_page.test_emailfield, "*****@*****.**")
def handle_block(self, block_type, block_value): if hasattr(block_type, "get_translatable_segments"): return block_type.get_translatable_segments(block_value) elif isinstance(block_type, (blocks.URLBlock, blocks.EmailBlock)): if self.include_overridables: return [OverridableSegmentValue("", block_value)] else: return [] elif isinstance( block_type, (blocks.CharBlock, blocks.TextBlock, blocks.BlockQuoteBlock)): return [StringSegmentValue("", block_value)] elif isinstance(block_type, blocks.RichTextBlock): template, strings = extract_strings(block_value.source) return [TemplateSegmentValue("", "html", template, len(strings)) ] + [ StringSegmentValue("", string, attrs=attrs) for string, attrs in strings ] elif isinstance(block_type, blocks.ChooserBlock): return self.handle_related_object_block(block_value) elif isinstance(block_type, blocks.StructBlock): return self.handle_struct_block(block_value) elif isinstance(block_type, blocks.ListBlock): return self.handle_list_block(block_value) elif isinstance(block_type, blocks.StreamBlock): return self.handle_stream_block(block_value) # Ignore everything else return []
def test_urlblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_urlblock", "http://test-content.com/foo") segments = extract_segments(page) self.assertEqual( segments, [ StringSegmentValue(f"test_streamfield.{block_id}", "http://test-content.com/foo") ], )
def test_childobjects(self): page = make_test_page() page.test_childobjects.add(TestChildObject(field="Test content")) page.save() child_translation_key = TestChildObject.objects.get().translation_key segments = extract_segments(page) self.assertEqual( segments, [ StringSegmentValue( f"test_childobjects.{child_translation_key}.field", "Test content") ], )
def test_snippet(self): test_snippet = TestSnippet.objects.create(field="Test content") translated_snippet = test_snippet.copy_for_translation(self.locale) translated_snippet.save() # Ingest segments into the snippet ingest_segments( test_snippet, translated_snippet, self.src_locale, self.locale, [StringSegmentValue("field", "Tester le contenu")], ) translated_snippet.save() self.assertEqual(translated_snippet.field, "Tester le contenu") # Now ingest a RelatedObjectSegmentValue into the page page = make_test_page(test_snippet=test_snippet) translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [ RelatedObjectSegmentValue.from_instance( "test_snippet", test_snippet) ], ) # Check the translated snippet was linked to the translated page self.assertNotEqual(page.test_snippet_id, translated_page.test_snippet_id) self.assertEqual(page.test_snippet.locale, self.src_locale) self.assertEqual(translated_page.test_snippet.locale, self.locale) self.assertEqual( page.test_snippet.translation_key, translated_page.test_snippet.translation_key, ) self.assertEqual(translated_page.test_snippet.field, "Tester le contenu")
def test_customstructblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_customstructblock", { "field_a": "Test content", "field_b": "Some more test content" }, ) translated_page = page.copy_for_translation(self.locale) ingest_segments( page, translated_page, self.src_locale, self.locale, [ StringSegmentValue( f"test_streamfield.{block_id}.foo", "Tester le contenu / Encore du contenu de test", ) ], ) translated_page.save() translated_page.refresh_from_db() self.assertEqual( translated_page.test_streamfield.stream_data, [{ "id": str(block_id), "type": "test_customstructblock", "value": { "field_a": "Tester le contenu", "field_b": "Encore du contenu de test", }, }], )
def test_customstructblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_customstructblock", { "field_a": "Test content", "field_b": "Some more test content" }, ) segments = extract_segments(page) self.assertEqual( segments, [ StringSegmentValue( f"test_streamfield.{block_id}.foo", "Test content / Some more test content", ) ], )
def test_nestedstreamblock(self): block_id = uuid.uuid4() nested_block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_nestedstreamblock", [{ "id": str(nested_block_id), "type": "block_a", "value": "Test content" }], ) segments = extract_segments(page) self.assertEqual( segments, [ StringSegmentValue( f"test_streamfield.{block_id}.{nested_block_id}", "Test content") ], )
def extract_segments(instance): segments = [] for translatable_field in get_translatable_fields(instance.__class__): if not translatable_field.is_translated(instance): continue field = translatable_field.get_field(instance.__class__) if hasattr(field, "get_translatable_segments"): segments.extend( segment.wrap(field.name) for segment in field.get_translatable_segments( field.value_from_object(instance))) elif isinstance(field, StreamField): segments.extend( segment.wrap(field.name) for segment in StreamFieldSegmentExtractor(field). handle_stream_block(field.value_from_object(instance))) elif isinstance(field, RichTextField): template, strings = extract_strings( field.value_from_object(instance)) field_segments = [ TemplateSegmentValue("", "html", template, len(strings)) ] + [ StringSegmentValue("", string, attrs=attrs) for string, attrs in strings ] segments.extend( segment.wrap(field.name) for segment in field_segments) elif isinstance(field, (models.TextField, models.CharField)): if not field.choices: segments.append( StringSegmentValue(field.name, field.value_from_object(instance))) elif isinstance(field, (models.ForeignKey)) and issubclass( field.related_model, TranslatableMixin): related_instance = getattr(instance, field.name) if related_instance: segments.append( RelatedObjectSegmentValue.from_instance( field.name, related_instance)) elif (isinstance(field, (models.ManyToOneRel)) and isinstance(field.remote_field, ParentalKey) and issubclass(field.related_model, TranslatableMixin)): manager = getattr(instance, field.name) for child_instance in manager.all(): segments.extend( segment.wrap(str(child_instance.translation_key)).wrap( field.name) for segment in extract_segments(child_instance)) class Counter: def __init__(self): self.value = 0 def next(self): self.value += 1 return self.value counter = Counter() return [ segment.with_order(counter.next()) for segment in segments if not segment.is_empty() ]
def get_translatable_segments(self, value): return [ StringSegmentValue( "foo", "{} / {}".format(value["field_a"], value["field_b"])) ]
def get_translatable_segments(self, value): if not value: # Don't disrupt other tests return [] return [StringSegmentValue("foo", "{} and some extra".format(value))]
def make_test_page(**kwargs): root_page = Page.objects.get(id=1) kwargs.setdefault("title", "Test page") return root_page.add_child(instance=TestPage(**kwargs)) RICH_TEXT_TEST_INPUT = '<h1>This is a heading</h1><p>This is a paragraph. <foo> <b>Bold text</b></p><ul><li><a href="http://example.com">This is a link</a>.</li></ul>' RICH_TEXT_TEST_OUTPUT = [ TemplateSegmentValue( "", "html", '<h1><text position="0"></text></h1><p><text position="1"></text></p><ul><li><text position="2"></text></li></ul>', 3, ), StringSegmentValue("", "This is a heading"), StringSegmentValue.from_source_html( "", "This is a paragraph. <foo> <b>Bold text</b>", ), StringSegmentValue( "", StringValue('<a id="a1">This is a link</a>.'), attrs={"a1": { "href": "http://example.com" }}, ), OverridableSegmentValue("'http://example.com'", "http://example.com"), ]
def test_slugfield(self): page = make_test_page(test_slugfield="test-content") segments = extract_segments(page) self.assertEqual( segments, [StringSegmentValue("test_slugfield", "test-content")])
def extract_segments(instance): """ Extracts segments from the given model instance. Args: instance (Model): The model instance to extract segments from. Returns: list[StringSegmentValue, TemplateSegmentValue, RelatedObjectSegmentValue, or OverridableSegmentValue]: The segment values that have been extracted. """ segments = [] for translatable_field in get_translatable_fields(instance.__class__): field = translatable_field.get_field(instance.__class__) is_translatable = translatable_field.is_translated(instance) is_synchronized = translatable_field.is_synchronized(instance) is_overridable = translatable_field.is_overridable(instance) extract_overridables = is_synchronized and is_overridable if hasattr(field, "get_translatable_segments"): if is_translatable: segments.extend( segment.wrap(field.name) for segment in field.get_translatable_segments( field.value_from_object(instance))) elif isinstance(field, StreamField): if is_translatable: segments.extend( segment.wrap(field.name) for segment in StreamFieldSegmentExtractor( field, include_overridables=extract_overridables). handle_stream_block(field.value_from_object(instance))) elif isinstance(field, RichTextField): if is_translatable: template, strings = extract_strings( field.value_from_object(instance)) # Find all unique href values hrefs = set() for _string, attrs in strings: for tag_attrs in attrs.values(): if "href" in tag_attrs: hrefs.add(tag_attrs["href"]) field_segments = ([ TemplateSegmentValue("", "html", template, len(strings)) ] + [ StringSegmentValue("", string, attrs=attrs) for string, attrs in strings ] + [ OverridableSegmentValue(quote_path_component(href), href) for href in sorted(hrefs) ]) segments.extend( segment.wrap(field.name) for segment in field_segments) if extract_overridables: pass # TODO: Extract images and links elif isinstance(field, (models.TextField, models.CharField)): if not field.choices: value = field.value_from_object(instance) if value is None: continue if is_translatable: segments.append(StringSegmentValue(field.name, value)) elif extract_overridables: segments.append(OverridableSegmentValue(field.name, value)) elif isinstance(field, (models.ForeignKey)): if is_translatable: if not issubclass(field.related_model, TranslatableMixin): raise ImproperlyConfigured( "The foreign key `{}.{}.{}` was registered as a translatable " "field but the model it points to `{}.{}` is not translatable" .format( field.model._meta.app_label, field.model.__name__, field.name, field.related_model._meta.app_label, field.related_model.__name__, )) related_instance = getattr(instance, field.name) if related_instance: segments.append( RelatedObjectSegmentValue.from_instance( field.name, related_instance)) elif extract_overridables: related_instance = getattr(instance, field.name) if related_instance: segments.append( OverridableSegmentValue(field.name, related_instance.pk)) elif (isinstance(field, (models.ManyToOneRel)) and isinstance(field.remote_field, ParentalKey) and issubclass(field.related_model, TranslatableMixin)): manager = getattr(instance, field.name) if is_translatable: for child_instance in manager.all(): segments.extend( segment.wrap(str(child_instance.translation_key)).wrap( field.name) for segment in extract_segments(child_instance)) elif extract_overridables: pass # TODO class Counter: def __init__(self): self.value = 0 def next(self): self.value += 1 return self.value counter = Counter() return [ segment.with_order(counter.next()) for segment in segments if not segment.is_empty() ]