Repository: WeblateOrg/weblate

Issue:
Source changes are not synced as fuzzy in the monolingual po
### Describe the issue

Our project recently had some changes in the source strings. As it uses monolingual po, Weblate recognized the changes and flagged them as Needs Editing as described [here](https://docs.weblate.org/no/latest/faq.html#can-weblate-highlight-changes-in-a-fuzzy-string).

After that I tried to make the po files reflect the changes which Weblate had automatically made, by using the Force synchronization command. However after the push it seems the command does nothing to those entries, even if they're marked as `Needs Editing`.


### I already tried

- [X] I've read and searched [the documentation](https://docs.weblate.org/).
- [X] I've searched for similar issues in this repository.

### Steps to reproduce the behavior

The repo is located in https://github.com/ftl-mv-translation/ftl-mv-translation.

1. Fork the repo, make a branch at commitid 39fd34cbb7b1f5d4d6039e26763d8ef428b8c2b7.
2. Create a Weblate component for `locale/data/events_lightspeed.xml/*.po` on the aforementioned branch.
3. Fast-forward the branch to commitid dcf5b36c19059ab87574158480ed996806ee4e84.
4. After the Update command Weblate grabs the source string changes.
5. Issue Force synchronization and Push.

### Expected behavior

From https://github.com/WeblateOrg/weblate/blob/main/docs/workflows.rst:

> ### Needs editing
>    Translation needs editing, this is usually the result of a source string change, fuzzy matching or translator action.
>    The translation is stored in the file, depending on the file format it might
>    be marked as needing edit (for example as it gets a ``fuzzy`` flag in the Gettext file).

After Force synchronization such entries should have been marked as `#, fuzzy`, as they're marked as `Needs Editing` in Weblate.


### Screenshots

![image](https://user-images.githubusercontent.com/11943013/185889878-e1f7dff0-d008-44d7-8b4b-c6e7c5f85d63.png)

After `Force synchronization`, the entry is dumped as:

```po
#: src-en/data/events_lightspeed.xml:637
msgid ""
"data/events_lightspeed.xml$//event[@name=\"ATLAS_MENU_NOEQUIPMENT\"]/choice[@req=\"SEC"
" SECTOR_DUSKBRINGER_UNIQUE\"]/text"
msgstr "더스크브링어 주도로 나아간다."
```

Notice that there's no `#, fuzzy` though `Needs Editing` is checked by Weblate.

### Exception traceback

_No response_

### How do you run Weblate?

Docker container

### Weblate versions

_No response_

### Weblate deploy checks

_No response_

### Additional context

Checks are not available as I have no access on them. The site says the version is 4.12.1.


Discussion:
For me, the fuzzy flag is written to the file on the force synchronization:

```po
#: src-en/data/events_lightspeed.xml:384
#, fuzzy
msgid ""
"data/events_lightspeed.xml$//event[@name=\"ATLAS_MENU\"]/choice[@req=\"SEC "
"SECTOR_DUSKBRINGER_UNIQUE\"]/text"
msgstr "Zum Duskbringer Capitol weiterreisen."

```

What is intentional is that this state is not written to the file directly - most of the monolingual files do not support his, and thus Weblate doesn't attempt to write the states caused by source string change.
Weird, [here's](https://weblate.hyperq.be/projects/ftl-multiverse/dataevents_lightspeedxml/ko/) our environment. Neither the Force sync nor the Files -> Download translation yield that entry marked fuzzy:

```
#: src-en/data/events_lightspeed.xml:384
msgid ""
"data/events_lightspeed.xml$//event[@name=\"ATLAS_MENU\"]/choice[@req=\"SEC "
"SECTOR_DUSKBRINGER_UNIQUE\"]/text"
msgstr "Zum Duskbringer Capitol weiterreisen."
```

I'm curious what made the difference here.

> What is intentional is that this state is not written to the file directly - most of the monolingual files do not support his, and thus Weblate doesn't attempt to write the states caused by source string change.

Could you elaborate more? If that was the case then we would have never witnessed them being marked as fuzzy in the first place. But I've got it in my small-scale test (not that repo), and you've got it on this test run as well. Is there a specific condition whether it's enabled or not? Or is there a documentation on this behavior that I can refer to fix it accordingly?
There is no condition - the needs editing state triggered by source string change is never marked for writing out to the file. The state is kept in Weblate database only. This is because most monolingual formats do not support storing string states.
Well that's a bit confusing, contradicting both the documentation and the test results.

Anyway is there a way that we may query the state programmatically?
The states changed by Weblate are indeed not covered by the docs at all. Maybe the current behavior is wrong as well, I'm just describing how it currently behaves.

Note that I'm testing on 4.14, so it might behave slightly differently than you observe.

You can get the state using API: https://docs.weblate.org/en/latest/api.html#get--api-translations-(string-project)-(string-component)-(string-language)-units-
Got it, thanks. I'll try that API as a workaround for now.
The issue you have reported is now resolved. If you don’t feel it’s right, please follow its labels to get a clue for further steps.

* In case you see a similar problem, please open a separate issue.
* If you are happy with the outcome, don’t hesitate to support Weblate by making a [donation](https://weblate.org/donate/).

Resolving patch:
diff --git a/weblate/trans/models/change.py b/weblate/trans/models/change.py
--- a/weblate/trans/models/change.py
+++ b/weblate/trans/models/change.py
@@ -405,6 +405,7 @@ class Change(models.Model, UserDisplayMixin):
         ACTION_BULK_EDIT,
         ACTION_APPROVE,
         ACTION_MARKED_EDIT,
+        ACTION_SOURCE_CHANGE,
     }
 
     # Actions shown on the repository management page
diff --git a/weblate/trans/models/unit.py b/weblate/trans/models/unit.py
--- a/weblate/trans/models/unit.py
+++ b/weblate/trans/models/unit.py
@@ -661,7 +661,7 @@ def update_from_unit(self, unit, pos, created):  # noqa: C901
             context = unit.context
             self.check_valid([context])
             note = unit.notes
-            previous_source = unit.previous_source
+            source_change = previous_source = unit.previous_source
         except DjangoDatabaseError:
             raise
         except Exception as error:
@@ -695,15 +695,17 @@ def update_from_unit(self, unit, pos, created):  # noqa: C901
             and same_target
         ):
             if not same_source and state in (STATE_TRANSLATED, STATE_APPROVED):
-                if self.previous_source == self.source and self.fuzzy:
+                if self.previous_source == source and self.fuzzy:
                     # Source change was reverted
+                    source_change = self.source
                     previous_source = ""
                     state = STATE_TRANSLATED
                 else:
                     # Store previous source and fuzzy flag for monolingual
                     if previous_source == "":
-                        previous_source = self.source
+                        source_change = previous_source = self.source
                     state = STATE_FUZZY
+                self.pending = True
             elif (
                 self.state == STATE_FUZZY
                 and state == STATE_FUZZY
@@ -777,12 +779,12 @@ def update_from_unit(self, unit, pos, created):  # noqa: C901
         if translation.is_template:
             component.updated_sources[self.id] = self
         # Indicate source string change
-        if not same_source and previous_source:
+        if not same_source and source_change:
             translation.update_changes.append(
                 Change(
                     unit=self,
                     action=Change.ACTION_SOURCE_CHANGE,
-                    old=previous_source,
+                    old=source_change,
                     target=self.source,
                 )
             )


Test patch: