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( translated_page.test_streamfield.stream_data, [{ "id": str(block_id), "type": "test_urlblock", "value": "http://test-content.fr/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 translated_page = page.copy_for_translation(self.locale) 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_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( list(translated_page.test_streamfield.stream_data), [{ "id": str(block_id), "type": "test_blockquoteblock", "value": "Tester le contenu", }], )
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_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_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_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_structblock(self): block_id = uuid.uuid4() page = make_test_page_with_streamfield_block( str(block_id), "test_structblock", {"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}.field_a", "Tester le contenu" ), StringSegmentValue( f"test_streamfield.{block_id}.field_b", "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_structblock", "value": { "field_a": "Tester le contenu", "field_b": "Encore du contenu de test", }, } ], )
def test_replace_html_attrs(self): segment = StringSegmentValue.from_html( "foo.bar", 'This is some text. <foo> <b>Bold text</b> <a id="a1">A link and some more <b>Bold text</b></a>', ) segment.attrs = {"a1": {"href": "http://changed-example.com"}} self.assertEqual( segment.render_html(), 'This is some text. <foo> <b>Bold text</b> <a href="http://changed-example.com">A link and some more <b>Bold text</b></a>', )
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_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 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_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 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_segment_value(self): segment = StringSegmentValue.from_html( "foo.bar", 'This is some text. <foo> <b>Bold text</b> <a href="http://example.com">A link and some more <b>Bold text</b></a>', ) self.assertEqual(segment.path, "foo.bar") self.assertEqual(segment.order, 0) self.assertEqual( segment.attrs, {"a1": {"href": "http://example.com"}} ) self.assertEqual( segment.render_text(), "This is some text. <foo> Bold text A link and some more Bold text", ) self.assertEqual( segment.render_html(), 'This is some text. <foo> <b>Bold text</b> <a href="http://example.com">A link and some more <b>Bold text</b></a>', ) # .with_order() orderred = segment.with_order(123) self.assertEqual(segment.order, 0) self.assertEqual(orderred.order, 123) self.assertEqual(orderred.path, "foo.bar") self.assertEqual(orderred.string, segment.string) self.assertEqual(orderred.attrs, segment.attrs) # .wrap() wrapped = segment.wrap("baz") self.assertEqual(segment.path, "foo.bar") self.assertEqual(wrapped.path, "baz.foo.bar") self.assertEqual(wrapped.order, segment.order) self.assertEqual(wrapped.string, segment.string) self.assertEqual(wrapped.attrs, segment.attrs) # .unwrap() path_component, unwrapped = segment.unwrap() self.assertEqual(segment.path, "foo.bar") self.assertEqual(path_component, "foo") self.assertEqual(unwrapped.path, "bar") self.assertEqual(unwrapped.order, segment.order) self.assertEqual(unwrapped.string, segment.string) self.assertEqual(unwrapped.attrs, segment.attrs)
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): """ 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() ]
def test_slugfield(self): page = make_test_page(test_slugfield="test-content") segments = extract_segments(page) self.assertEqual( segments, [StringSegmentValue("test_slugfield", "test-content")])
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_FRENCH_SEGMENTS = [ TemplateSegmentValue( "", "html", '<h1><text position="0"></text></h1><p><text position="1"></text></p><ul><li><text position="2"></text></li></ul>', 3, order=9, ), StringSegmentValue("", "Ceci est une rubrique", order=10), StringSegmentValue.from_source_html( "", 'Ceci est un paragraphe. <foo> <b>Texte en gras</b>', order=11, ), StringSegmentValue( "", StringValue('<a id="a1">Ceci est un lien</a>'), attrs={ "a1": {"href": "http://example.com"} }, order=12, ), ]
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 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 get_translatable_segments(self, value): if not value: # Don't disrupt other tests return [] return [StringSegmentValue("foo", "{} and some extra".format(value))]
def machine_translate(request, translation_id): translation = get_object_or_404(Translation, id=translation_id) instance = translation.get_target_instance() if not user_can_edit_instance(request.user, instance): raise PermissionDenied translator = get_machine_translator() if translator is None: raise Http404 if not translator.can_translate(translation.source.locale, translation.target_locale): raise Http404 # Get segments segments = defaultdict(list) for string_segment in translation.source.stringsegment_set.all( ).select_related("context", "string"): segment = StringSegmentValue( string_segment.context.path, string_segment.string.as_value()).with_order(string_segment.order) if string_segment.attrs: segment.attrs = json.loads(string_segment.attrs) # Don't translate if there already is a translation if StringTranslation.objects.filter( translation_of_id=string_segment.string_id, locale=translation.target_locale, context_id=string_segment.context_id, ).exists(): continue segments[segment.string].append( (string_segment.string_id, string_segment.context_id)) if segments: translations = translator.translate(translation.source.locale, translation.target_locale, segments.keys()) with transaction.atomic(): for string, contexts in segments.items(): for string_id, context_id in contexts: StringTranslation.objects.get_or_create( translation_of_id=string_id, locale=translation.target_locale, context_id=context_id, defaults={ 'data': translations[string].data, 'translation_type': StringTranslation.TRANSLATION_TYPE_MACHINE, 'tool_name': translator.display_name, 'last_translated_by': request.user, 'has_error': False, 'field_error': "", }) messages.success( request, _("Successfully translated with {}.").format( translator.display_name)) else: messages.warning(request, _("There isn't anything left to translate.")) # Work out where to redirect to next_url = get_valid_next_url_from_request(request) if not next_url: # Note: You should always provide a next URL when using this view! next_url = reverse('wagtailadmin_home') return redirect(next_url)