Repository: WeblateOrg/weblate

Issue:
iOS string download is encoded in UTF16 instead of UTF8
### Describe the issue

When downloading iOS translation strings (either through the website or the API) the file is encoded in utf16le.
However, the recommended (and broadly used) encoding is UTF-8:
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/Strings/Strings.html

> Note: It is recommended that you save strings files using the UTF-8 encoding, which is the default encoding for standard strings files

UTF16 also cause issues when working with git. They are treated as "binary", making it difficult to see `diff`s: https://git-scm.com/docs/gitattributes#_working_tree_encoding

### 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. Download "iOS strings" from base_url/projects/my_proj/my_component/de/#download (or alternatively the API)
2. Open in Visual Studio Code
3. ![image](https://github.com/WeblateOrg/weblate/assets/2863198/84da5239-3003-4740-a009-0e1578a6a03a)


### Expected behavior

![image](https://github.com/WeblateOrg/weblate/assets/2863198/56d93e50-f203-43c5-aae7-949dd520cd30)


### How do you run Weblate?

Self-hosted

### Weblate versions

 * Weblate: 5.1
 * Django: 4.2.6
 * siphashc: 2.3
 * translate-toolkit: 3.10.1
 * lxml: 4.9.3
 * Pillow: 10.1.0
 * nh3: 0.2.14
 * python-dateutil: 2.8.2
 * social-auth-core: 4.4.2
 * social-auth-app-django: 5.4.0
 * django-crispy-forms: 2.1
 * oauthlib: 3.2.2
 * django-compressor: 4.4
 * djangorestframework: 3.14.0
 * django-filter: 23.3
 * django-appconf: 1.0.5
 * user-agents: 2.2.0
 * filelock: 3.12.4
 * rapidfuzz: 3.4.0
 * openpyxl: 3.1.2
 * celery: 5.3.4
 * django-celery-beat: 2.5.0
 * kombu: 5.3.2
 * translation-finder: 2.15
 * weblate-language-data: 2023.5
 * html2text: 2020.1.16
 * pycairo: 1.25.1
 * PyGObject: 3.44.1
 * diff-match-patch: 20230430
 * requests: 2.31.0
 * django-redis: 5.4.0
 * hiredis: 2.2.3
 * sentry-sdk: 1.31.0
 * Cython: 3.0.4
 * misaka: 2.1.1
 * GitPython: 3.1.40
 * borgbackup: 1.2.6
 * pyparsing: 3.1.1
 * ahocorasick_rs: 0.20.0
 * python-redis-lock: 4.0.0
 * charset-normalizer: 3.3.0
 * Python: 3.11.6
 * Git: 2.36.6
 * psycopg2-binary: 2.9.9
 * phply: 1.2.6
 * ruamel.yaml: 0.17.40
 * tesserocr: 2.6.2
 * aeidon: 1.13
 * iniparse: 0.5
 * Mercurial: 6.5.1
 * git-svn: 2.36.6
 * PostgreSQL server: 15.4
 * Database backends: django.db.backends.postgresql
 * Cache backends: default:RedisCache, avatar:FileBasedCache
 * Email setup: django.core.mail.backends.smtp.EmailBackend: mrkos.uberspace.de
 * OS encoding: filesystem=utf-8, default=utf-8
 * Celery: redis+socket:///home/user/.redis/sock, redis+socket:///home/user/.redis/sock, regular
 * Platform: Linux 3.10.0-1160.99.1.el7.x86_64 (x86_64)



Discussion:

Resolving patch:
diff --git a/weblate/formats/exporters.py b/weblate/formats/exporters.py
--- a/weblate/formats/exporters.py
+++ b/weblate/formats/exporters.py
@@ -18,7 +18,7 @@
 from translate.storage.mo import mofile
 from translate.storage.po import pofile
 from translate.storage.poxliff import PoXliffFile
-from translate.storage.properties import stringsfile
+from translate.storage.properties import stringsutf8file
 from translate.storage.tbx import tbxfile
 from translate.storage.tmx import tmxfile
 from translate.storage.xliff import xlifffile
@@ -458,7 +458,7 @@ def add_note(self, output, note: str, origin: str):
 
 
 class StringsExporter(MonolingualExporter):
-    storage_class = stringsfile
+    storage_class = stringsutf8file
     name = "strings"
     content_type = "text/plain"
     extension = "strings"


Test patch:
diff --git a/weblate/formats/tests/test_exporters.py b/weblate/formats/tests/test_exporters.py
--- a/weblate/formats/tests/test_exporters.py
+++ b/weblate/formats/tests/test_exporters.py
@@ -298,10 +298,6 @@ class StringsExporterTest(PoExporterTest):
     _class = StringsExporter
     _has_comments = False
 
-    def _encode(self, string):
-        # Skip BOM
-        return string.encode("utf-16")[2:]
-
     def check_plurals(self, result):
         # Doesn't support plurals
         pass