Repository: WeblateOrg/weblate

Issue:
Empty `<target />` tags in XLIFF files.
### Describe the issue

Weblate adds `<target />` empty tags to the translation files I create with it. Which makes other tools thinks the string got translated to an empty string.
This appears to have been fixed a long time ago: https://github.com/WeblateOrg/weblate/issues/4946

Though, my XLIFF files still contains empty `<target />` tags.

Thanks for the tool, pleased to provide any further information :)

### 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

1. Go to '...'
2. Scroll down to '...'
3. Click on '...'
4. See error


### Expected behavior

No target tag if the string is not translated.

### Screenshots

![image](https://user-images.githubusercontent.com/13108166/145242044-0656ea23-ca4f-4875-aadc-e89f8bf68604.png)
![image](https://user-images.githubusercontent.com/13108166/145242141-49f1f060-f5f4-4fac-814f-548e9317d5ba.png)


### Exception traceback

_No response_

### How do you run Weblate?

Docker container

### Weblate versions

* Weblate: 4.9.1
 * Django: 3.2.9
 * siphashc: 2.1
 * translate-toolkit: 3.5.1
 * lxml: 4.6.4
 * Pillow: 8.4.0
 * bleach: 4.1.0
 * python-dateutil: 2.8.2
 * social-auth-core: 4.1.0
 * social-auth-app-django: 5.0.0
 * django-crispy-forms: 1.13.0
 * oauthlib: 3.1.1
 * django-compressor: 2.4.1
 * djangorestframework: 3.12.4
 * django-filter: 21.1
 * django-appconf: 1.0.5
 * user-agents: 2.2.0
 * filelock: 3.4.0
 * setuptools: 58.5.3
 * jellyfish: 0.8.9
 * openpyxl: 3.0.9
 * celery: 5.2.0
 * kombu: 5.2.2
 * translation-finder: 2.10
 * weblate-language-data: 2021.6
 * html2text: 2020.1.16
 * pycairo: 1.16.2
 * pygobject: 3.38.0
 * diff-match-patch: 20200713
 * requests: 2.26.0
 * django-redis: 5.0.0
 * hiredis: 2.0.0
 * sentry_sdk: 1.5.0
 * Cython: 0.29.24
 * misaka: 2.1.1
 * GitPython: 3.1.24
 * borgbackup: 1.1.17
 * pyparsing: 3.0.6
 * pyahocorasick: 1.4.2
 * python-redis-lock: 3.7.0
 * Python: 3.9.2
 * Git: 2.30.2
 * psycopg2: 2.9.2
 * psycopg2-binary: 2.9.2
 * phply: 1.2.5
 * chardet: 4.0.0
 * ruamel.yaml: 0.17.17
 * tesserocr: 2.5.2
 * akismet: 1.1
 * boto3: 1.20.10
 * zeep: 4.1.0
 * aeidon: 1.10.1
 * iniparse: 0.5
 * mysqlclient: 2.1.0
 * Mercurial: 5.9.3
 * git-svn: 2.30.2
 * git-review: 2.1.0
 * Redis server: 6.2.6
 * PostgreSQL server: 13.5
 * Database backends: django.db.backends.postgresql
 * Cache backends: default:RedisCache, avatar:FileBasedCache
 * Email setup: django.core.mail.backends.smtp.EmailBackend: smtp.example.com
 * OS encoding: filesystem=utf-8, default=utf-8
 * Celery: redis://cache:6379/1, redis://cache:6379/1, regular
 * Platform: Linux 5.10.47-linuxkit (aarch64)

### Weblate deploy checks

```shell
It's run locally as if it were a standalone translation tool, so I'm not sure this is relevant.
```


### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/weblate/formats/ttkit.py b/weblate/formats/ttkit.py
--- a/weblate/formats/ttkit.py
+++ b/weblate/formats/ttkit.py
@@ -400,6 +400,17 @@ def create_unit(
 
         return unit
 
+    def untranslate_unit(self, unit, plural, fuzzy: bool):
+        if hasattr(unit, "markapproved"):
+            # Xliff only
+            unit.markapproved(False)
+        else:
+            unit.markfuzzy(fuzzy)
+        if unit.hasplural():
+            unit.target = [""] * plural.number
+        else:
+            unit.target = ""
+
     def untranslate_store(self, language, fuzzy: bool = False):
         """Remove translations from Translate Toolkit store."""
         self.store.settargetlanguage(self.get_language_code(language.code))
@@ -407,15 +418,7 @@ def untranslate_store(self, language, fuzzy: bool = False):
 
         for unit in self.store.units:
             if unit.istranslatable():
-                if hasattr(unit, "markapproved"):
-                    # Xliff only
-                    unit.markapproved(False)
-                else:
-                    unit.markfuzzy(fuzzy)
-                if unit.hasplural():
-                    unit.target = [""] * plural.number
-                else:
-                    unit.target = ""
+                self.untranslate_unit(unit, plural, fuzzy)
 
     @classmethod
     def get_new_file_content(cls):
@@ -1111,6 +1114,16 @@ class XliffFormat(TTKitFormat):
     language_format = "bcp"
     use_settarget = True
 
+    def untranslate_unit(self, unit, plural, fuzzy: bool):
+        super().untranslate_unit(unit, plural, fuzzy)
+        # Delete empty <target/> tag
+        try:
+            xmlnode = self.unit.getlanguageNode(lang=None, index=1)
+            if xmlnode is not None:
+                xmlnode.getparent().remove(xmlnode)
+        except AttributeError:
+            pass
+
     def construct_unit(self, source: str):
         unit = super().construct_unit(source)
         # Make sure new unit is using same namespace as the original


Test patch: