Skip to content

Commit ad81512

Browse files
authored
fix(unit): preserve fuzzy state for formats without fuzzy support (#18421)
Consider the following: There is an **untranslated** unit in a file format that doesn't support writing fuzzy state to the file. The project has been configured with approved only commit policy. The unit is translated to a fuzzy state. As it is the first uncommitted change to the unit, `unit.details["disk_state"]["state"]"` will be updated and set to `STATE_EMPTY`. `unit.state` is `STATE_FUZZY`. There is a `PendingUnitChange` in the database to commit the translation to disk. When a file sync is triggered, it attempts a commit. The pending change above is not committed because it is not approved and nothing changes so far. During file sync, `Unit.update_from_unit` is called on this unit which in turn calls `Unit.get_unit_state` to get current unit state. `unit.state` is `STATE_FUZZY` so `unit.is_fuzzy(fallback=True)` is called. https://github.com/WeblateOrg/weblate/blob/370adc360759d3453598eb0b3b4074539093456a/weblate/trans/models/unit.py#L857-L858 JSON format is not `SUPPORTS_FUZZY` so the fallback is returned directly. https://github.com/WeblateOrg/weblate/blob/370adc360759d3453598eb0b3b4074539093456a/weblate/formats/ttkit.py#L158-L166 As a result, `Unit.get_unit_state` returns `STATE_FUZZY`. `comparison_state["state"]` is STATE_EMPTY. `same_state` evaluates to `False` and the pending change is deleted from the database. https://github.com/WeblateOrg/weblate/blob/370adc360759d3453598eb0b3b4074539093456a/weblate/trans/models/unit.py#L1102 * check whether unit is empty in `is_fuzzy`
1 parent d216403 commit ad81512

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

weblate/formats/ttkit.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,12 @@ def is_fuzzy(self, fallback=False):
159159
"""Check whether unit needs editing."""
160160
if not self.has_unit():
161161
return fallback
162+
if not self.has_translation():
163+
return False
162164
# Most of the formats do not support this, but they
163165
# happily return False
164166
if isinstance(self.unit, SUPPORTS_FUZZY):
165-
return self.has_translation() and self.unit.isfuzzy()
167+
return self.unit.isfuzzy()
166168
return fallback
167169

168170
def has_content(self) -> bool:

weblate/trans/tests/test_remote.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
from weblate.trans.tests.test_views import ViewTestCase
1717
from weblate.trans.tests.utils import REPOWEB_URL
1818
from weblate.utils.files import remove_tree
19-
from weblate.utils.state import STATE_APPROVED, STATE_EMPTY, STATE_TRANSLATED
19+
from weblate.utils.state import (
20+
STATE_APPROVED,
21+
STATE_EMPTY,
22+
STATE_FUZZY,
23+
STATE_TRANSLATED,
24+
)
2025
from weblate.vcs.models import VCS_REGISTRY
2126

2227
EXTRA_PO = """
@@ -809,6 +814,41 @@ def test_tbx_explanation_sync(self) -> None:
809814

810815
self.assertEqual(PendingUnitChange.objects.filter(unit=unit).count(), 0)
811816

817+
def _test_fuzzy_state_commit_policy(self, component) -> None:
818+
self.project.commit_policy = CommitPolicyChoices.APPROVED_ONLY
819+
self.project.translation_review = True
820+
self.project.save()
821+
822+
translation = component.translation_set.get(language_code="cs")
823+
unit = translation.unit_set.get(source="Hello, world!\n")
824+
825+
unit.translate(self.user, "Ahoj světe!\n", STATE_FUZZY)
826+
self.assertEqual(unit.state, STATE_FUZZY)
827+
self.assertEqual(unit.details["disk_state"]["state"], STATE_EMPTY)
828+
829+
count = PendingUnitChange.objects.filter(unit=unit).count()
830+
self.assertEqual(count, 1)
831+
832+
# trigger commit and check_sync to simulate repository rescan
833+
translation.commit_pending("test", None)
834+
translation.check_sync(force=True)
835+
836+
unit = translation.unit_set.get(source="Hello, world!\n")
837+
self.assertEqual(unit.state, STATE_FUZZY)
838+
839+
# pending changes for unit are not discarded
840+
count = PendingUnitChange.objects.filter(unit=unit).count()
841+
self.assertEqual(count, 1)
842+
self.assertEqual(unit.details["disk_state"]["state"], STATE_EMPTY)
843+
844+
def test_fuzzy_state_commit_policy_json(self) -> None:
845+
component = self.create_json(name="JSON component", project=self.project)
846+
self._test_fuzzy_state_commit_policy(component)
847+
848+
def test_fuzzy_state_commit_policy_po(self) -> None:
849+
component = self.create_po(name="Po component", project=self.project)
850+
self._test_fuzzy_state_commit_policy(component)
851+
812852

813853
class GitBranchMultiRepoTest(MultiRepoTest):
814854
_vcs = "git"

weblate/trans/tests/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,8 @@ def create_android(self, suffix="", **kwargs) -> Component:
336336
**kwargs,
337337
)
338338

339-
def create_json(self) -> Component:
340-
return self._create_component("json", "json/*.json")
339+
def create_json(self, **kwargs) -> Component:
340+
return self._create_component("json", "json/*.json", **kwargs)
341341

342342
def create_json_mono(self, suffix="mono", **kwargs) -> Component:
343343
return self._create_component(

0 commit comments

Comments
 (0)