Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions weblate/formats/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@
# actual main unit
raise MissingTemplateError
else:
self.mainunit = unit

Check failure on line 172 in weblate/formats/base.py

View workflow job for this annotation

GitHub Actions / mypy

Incompatible types in assignment (expression has type "TranslationUnit | BaseItem | None", variable has type "TranslationUnit | BaseItem")

def _invalidate_target(self) -> None:
"""Invalidate target cache."""
Expand Down Expand Up @@ -292,6 +292,10 @@
"""Check whether unit is read-only."""
return False

def is_automatically_translated(self) -> bool:
"""Check whether unit is automatically translated."""
return False

def set_target(self, target: str | list[str]) -> None:
"""Set translation unit target."""
raise NotImplementedError
Expand Down
10 changes: 10 additions & 0 deletions weblate/formats/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,16 @@ def test_set_state(self) -> None:
Path(testfile).read_text(encoding="utf-8"),
)

def test_state_qualifier_autotranslated(self) -> None:
storage = self.parse_file(get_test_file("cs-auto.xliff"))
units = storage.all_units

self.assertTrue(
units[0].is_automatically_translated(),
)
self.assertTrue(units[1].is_automatically_translated())
self.assertFalse(units[2].is_automatically_translated())


class RichXliffFormatTest(XliffFormatTest):
format_class = RichXliffFormat
Expand Down
16 changes: 16 additions & 0 deletions weblate/formats/ttkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
return get_string(self.template.source)
if self.unit is None:
raise MissingTemplateError
return get_string(self.unit.name)

Check failure on line 215 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationUnit" has no attribute "name"

@cached_property
def target(self):
Expand Down Expand Up @@ -248,7 +248,7 @@
super().set_target(target)
# Propagate to value so that searializing of empty values works correctly
if not target:
self.unit.value = self.unit.target

Check failure on line 251 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationUnit" has no attribute "value"


class TTKitFormat(TranslationFormat):
Expand Down Expand Up @@ -321,7 +321,7 @@

def get_store_instance(self, **kwargs):
kwargs.update(self.get_format_class_kwargs())
store = self.get_class()(**kwargs)

Check failure on line 324 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationStore" not callable

# Apply possible fixups
self.fixup(store)
Expand Down Expand Up @@ -384,16 +384,16 @@
if self.use_settarget and self.source_language:
# Setting source on LISAunit will make it use default language
unit = self.store.UnitClass(None, **self.get_unit_class_kwargs())
unit.setsource(source, self.source_language)

Check failure on line 387 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationUnit" has no attribute "setsource"
elif hasattr(self.store, "wrapper"):
# gettext PO
unit = self.store.UnitClass(

Check failure on line 390 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Unexpected keyword argument "wrapper" for "TranslationUnit"
source, wrapper=self.store.wrapper, **self.get_unit_class_kwargs()
)
else:
unit = self.store.UnitClass(source, **self.get_unit_class_kwargs())
# Needed by some formats (Android) to set target
unit._store = self.store # noqa: SLF001

Check failure on line 396 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

Incompatible types in assignment (expression has type "TranslationStore", variable has type "None")
return unit

def create_unit_key(
Expand Down Expand Up @@ -546,7 +546,7 @@
if self.template is not None:
return get_string(self.template.source)
# Need to decode property encoded string
return get_string(quote.propertiesdecode(self.unit.name))

Check failure on line 549 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationUnit" has no attribute "name"

@cached_property
def target(self):
Expand All @@ -563,8 +563,8 @@
"""Set fuzzy /approved flag on translated unit."""
super().set_state(state)
if state not in FUZZY_STATES:
self.unit.prev_msgid = []

Check failure on line 566 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationUnit" has no attribute "prev_msgid"
self.unit.prev_msgid_plural = []

Check failure on line 567 in weblate/formats/ttkit.py

View workflow job for this annotation

GitHub Actions / mypy

"TranslationUnit" has no attribute "prev_msgid_plural"
self.unit.prev_msgctxt = []

@cached_property
Expand Down Expand Up @@ -669,6 +669,22 @@
result.add(state)
return result

def get_xliff_state_qualifiers(self) -> set[str]:
"""Return set of state-qualifier values from target nodes."""
result = set()
for node in self.get_xliff_nodes():
if node is None:
continue
state_qualifier = node.get("state-qualifier", None)
if state_qualifier is not None:
result.add(state_qualifier)
return result

def is_automatically_translated(self) -> bool:
"""Check if unit is automatically translated based on state-qualifier."""
state_qualifiers = self.get_xliff_state_qualifiers()
return bool({"leveraged-mt", "mt-suggestion"}.intersection(state_qualifiers))

@cached_property
def context(self):
"""
Expand Down
4 changes: 4 additions & 0 deletions weblate/trans/models/unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ class UnitAttributesDict(TypedDict):
created: bool
pos: int
id_hash: int
automatically_translated: bool


class Unit(models.Model, LoggerMixin):
Expand Down Expand Up @@ -946,6 +947,7 @@ def store_unit_attributes(
"created": created,
"pos": pos,
"id_hash": id_hash,
"automatically_translated": unit.is_automatically_translated(),
}
return self.unit_attributes

Expand Down Expand Up @@ -981,6 +983,7 @@ def update_from_unit( # noqa: C901,PLR0914
unit = unit_attributes["unit"]
created = unit_attributes["created"]
pos = unit_attributes["pos"]
automatically_translated = unit_attributes["automatically_translated"]

# Should not be needed again
self.unit_attributes = None
Expand Down Expand Up @@ -1094,6 +1097,7 @@ def update_from_unit( # noqa: C901,PLR0914
self.context = context
self.note = note
self.previous_source = previous_source
self.automatically_translated = automatically_translated
self.update_priority(save=False)

# Metadata update only, these do not trigger any actions in Weblate and
Expand Down
19 changes: 19 additions & 0 deletions weblate/trans/tests/data/cs-auto.xliff
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
<file source-language="en" target-language="cs" datatype="plaintext">
<body>
<trans-unit id="1">
<source>Hello</source>
<target state-qualifier="leveraged-mt">Ahoj</target>
</trans-unit>
<trans-unit id="2">
<source>World</source>
<target state-qualifier="mt-suggestion">svět</target>
</trans-unit>
<trans-unit id="3">
<source>Car</source>
<target>Auto</target>
</trans-unit>
</body>
</file>
</xliff>
23 changes: 23 additions & 0 deletions weblate/trans/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import os
from datetime import timedelta
from pathlib import Path

from django.contrib.staticfiles.testing import StaticLiveServerTestCase
from django.core.management.color import no_style
Expand Down Expand Up @@ -38,6 +39,7 @@
RepoTestMixin,
create_another_user,
create_test_user,
get_test_file,
)
from weblate.utils.django_hacks import immediate_on_commit, immediate_on_commit_leave
from weblate.utils.files import remove_tree
Expand Down Expand Up @@ -1030,3 +1032,24 @@ def test_find_committable_components_with_retry_filter(self) -> None:
set(components.values_list("pk", flat=True)),
{self.component.pk, self.component3.pk},
)


class AutomaticallyTranslatedFromFileTest(RepoTestCase):
def test_xliff_state_qualifier_loaded_to_database(self) -> None:
"""Test that XLIFF state-qualifier is loaded as automatically_translated flag."""
component = self.create_xliff()
translation = component.translation_set.get(language_code="cs")

content = Path(get_test_file("cs-auto.xliff")).read_text(encoding="utf-8")
Path(translation.get_filename()).write_text(content, encoding="utf-8")
translation.check_sync(force=True)

self.assertTrue(
translation.unit_set.get(source="Hello").automatically_translated
)
self.assertTrue(
translation.unit_set.get(source="World").automatically_translated
)
self.assertFalse(
translation.unit_set.get(source="Car").automatically_translated
)
Loading