We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fluent pretranslations not serialized in canonical form
Fluent pretranslations are not stored in a canonical form. For example, `key = {example}` should actually be stored as `key = { example }` (mind the whitespace).

In the exported files in the repository, the strings appear as they should, which results in sync trying to import pretranslations, because they aren't the same as in Pontoon DB. That also triggers #2284. 

In the UI (editor, string list) the strings are also rendered properly, which only makes the problem more difficult to spot.

We should make sure that pretranslations are always stored them in the canonical form.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pontoon/pretranslation/pretranslate.py`
Content:
```
1 import logging
2 import operator
3 import re
4 
5 from django.db.models import CharField, Value as V
6 from django.db.models.functions import Concat
7 
8 from fluent.syntax import FluentParser, FluentSerializer
9 from functools import reduce
10 
11 from pontoon.base.models import User, TranslatedResource
12 from pontoon.machinery.utils import (
13     get_google_translate_data,
14     get_translation_memory_data,
15 )
16 from pontoon.pretranslation import AUTHORS
17 from .transformer import ApplyPretranslation
18 
19 
20 log = logging.getLogger(__name__)
21 
22 parser = FluentParser()
23 serializer = FluentSerializer()
24 
25 
26 def get_pretranslations(entity, locale, preserve_placeables=False):
27     """
28     Get pretranslations for the entity-locale pair using internal translation memory and
29     Google's machine translation.
30 
31     For Fluent strings, uplift SelectExpressions, serialize Placeables as TextElements
32     and then only pretranslate TextElements. Set the most frequent TextElement
33     pretranslation author as the author of the entire pretranslation.
34 
35     :arg Entity entity: the Entity object
36     :arg Locale locale: the Locale object
37     :arg boolean preserve_placeables
38 
39     :returns: a list of tuples, consisting of:
40         - a pretranslation of the entity
41         - a plural form
42         - a user (representing TM or GT service)
43     """
44     source = entity.string
45     services = {k: User.objects.get(email=email) for k, email in AUTHORS.items()}
46 
47     if entity.resource.format == "ftl":
48         entry = parser.parse_entry(source)
49         pretranslate = ApplyPretranslation(
50             locale, entry, get_pretranslated_data, preserve_placeables
51         )
52 
53         try:
54             pretranslate.visit(entry)
55         except ValueError as e:
56             log.info(f"Fluent pretranslation error: {e}")
57             return []
58 
59         pretranslation = serializer.serialize_entry(entry)
60 
61         authors = [services[service] for service in pretranslate.services]
62         author = max(set(authors), key=authors.count) if authors else services["tm"]
63 
64         return [(pretranslation, None, author)]
65 
66     else:
67         pretranslation, service = get_pretranslated_data(
68             source, locale, preserve_placeables
69         )
70 
71         if pretranslation is None:
72             return []
73 
74         author = services[service]
75         if entity.string_plural == "":
76             return [(pretranslation, None, author)]
77         else:
78             plural_forms = range(0, locale.nplurals or 1)
79             return [
80                 (pretranslation, plural_form, author) for plural_form in plural_forms
81             ]
82 
83 
84 def get_pretranslated_data(source, locale, preserve_placeables):
85     # Empty strings do not need translation
86     if re.search("^\\s*$", source):
87         return source, "tm"
88 
89     # Try to get matches from Translation Memory
90     tm_response = get_translation_memory_data(text=source, locale=locale)
91     tm_perfect = [t for t in tm_response if int(t["quality"]) == 100]
92     if tm_perfect:
93         return tm_perfect[0]["target"], "tm"
94 
95     # Fetch from Google Translate
96     elif locale.google_translate_code:
97         gt_response = get_google_translate_data(
98             text=source, locale=locale, preserve_placeables=preserve_placeables
99         )
100         if gt_response["status"]:
101             return gt_response["translation"], "gt"
102 
103     return None, None
104 
105 
106 def update_changed_instances(tr_filter, tr_dict, translations):
107     """
108     Update the latest activity and stats for changed Locales, ProjectLocales
109     & TranslatedResources
110     """
111     tr_filter = tuple(tr_filter)
112     # Combine all generated filters with an OK operator.
113     # `operator.ior` is the '|' Python operator, which turns into a logical OR
114     # when used between django ORM query objects.
115     tr_query = reduce(operator.ior, tr_filter)
116 
117     translatedresources = TranslatedResource.objects.filter(tr_query).annotate(
118         locale_resource=Concat(
119             "locale_id", V("-"), "resource_id", output_field=CharField()
120         )
121     )
122 
123     translatedresources.update_stats()
124 
125     for tr in translatedresources:
126         index = tr_dict[tr.locale_resource]
127         translation = translations[index]
128         translation.update_latest_translation()
129
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pontoon/pretranslation/pretranslate.py b/pontoon/pretranslation/pretranslate.py
--- a/pontoon/pretranslation/pretranslate.py
+++ b/pontoon/pretranslation/pretranslate.py
@@ -58,6 +58,10 @@
 
         pretranslation = serializer.serialize_entry(entry)
 
+        # Parse and serialize pretranslation again in order to assure cannonical style
+        parsed_pretranslation = parser.parse_entry(pretranslation)
+        pretranslation = serializer.serialize_entry(parsed_pretranslation)
+
         authors = [services[service] for service in pretranslate.services]
         author = max(set(authors), key=authors.count) if authors else services["tm"]
 
@@ -82,7 +86,7 @@
 
 
 def get_pretranslated_data(source, locale, preserve_placeables):
-    # Empty strings do not need translation
+    # Empty strings and strings containing whitespace only do not need translation
     if re.search("^\\s*$", source):
         return source, "tm"

{"golden_diff": "diff --git a/pontoon/pretranslation/pretranslate.py b/pontoon/pretranslation/pretranslate.py\n--- a/pontoon/pretranslation/pretranslate.py\n+++ b/pontoon/pretranslation/pretranslate.py\n@@ -58,6 +58,10 @@\n \n         pretranslation = serializer.serialize_entry(entry)\n \n+        # Parse and serialize pretranslation again in order to assure cannonical style\n+        parsed_pretranslation = parser.parse_entry(pretranslation)\n+        pretranslation = serializer.serialize_entry(parsed_pretranslation)\n+\n         authors = [services[service] for service in pretranslate.services]\n         author = max(set(authors), key=authors.count) if authors else services[\"tm\"]\n \n@@ -82,7 +86,7 @@\n \n \n def get_pretranslated_data(source, locale, preserve_placeables):\n-    # Empty strings do not need translation\n+    # Empty strings and strings containing whitespace only do not need translation\n     if re.search(\"^\\\\s*$\", source):\n         return source, \"tm\"\n", "issue": "Fluent pretranslations not serialized in canonical form\nFluent pretranslations are not stored in a canonical form. For example, `key = {example}` should actually be stored as `key = { example }` (mind the whitespace).\r\n\r\nIn the exported files in the repository, the strings appear as they should, which results in sync trying to import pretranslations, because they aren't the same as in Pontoon DB. That also triggers #2284. \r\n\r\nIn the UI (editor, string list) the strings are also rendered properly, which only makes the problem more difficult to spot.\r\n\r\nWe should make sure that pretranslations are always stored them in the canonical form.\n", "before_files": [{"content": "import logging\nimport operator\nimport re\n\nfrom django.db.models import CharField, Value as V\nfrom django.db.models.functions import Concat\n\nfrom fluent.syntax import FluentParser, FluentSerializer\nfrom functools import reduce\n\nfrom pontoon.base.models import User, TranslatedResource\nfrom pontoon.machinery.utils import (\n    get_google_translate_data,\n    get_translation_memory_data,\n)\nfrom pontoon.pretranslation import AUTHORS\nfrom .transformer import ApplyPretranslation\n\n\nlog = logging.getLogger(__name__)\n\nparser = FluentParser()\nserializer = FluentSerializer()\n\n\ndef get_pretranslations(entity, locale, preserve_placeables=False):\n    \"\"\"\n    Get pretranslations for the entity-locale pair using internal translation memory and\n    Google's machine translation.\n\n    For Fluent strings, uplift SelectExpressions, serialize Placeables as TextElements\n    and then only pretranslate TextElements. Set the most frequent TextElement\n    pretranslation author as the author of the entire pretranslation.\n\n    :arg Entity entity: the Entity object\n    :arg Locale locale: the Locale object\n    :arg boolean preserve_placeables\n\n    :returns: a list of tuples, consisting of:\n        - a pretranslation of the entity\n        - a plural form\n        - a user (representing TM or GT service)\n    \"\"\"\n    source = entity.string\n    services = {k: User.objects.get(email=email) for k, email in AUTHORS.items()}\n\n    if entity.resource.format == \"ftl\":\n        entry = parser.parse_entry(source)\n        pretranslate = ApplyPretranslation(\n            locale, entry, get_pretranslated_data, preserve_placeables\n        )\n\n        try:\n            pretranslate.visit(entry)\n        except ValueError as e:\n            log.info(f\"Fluent pretranslation error: {e}\")\n            return []\n\n        pretranslation = serializer.serialize_entry(entry)\n\n        authors = [services[service] for service in pretranslate.services]\n        author = max(set(authors), key=authors.count) if authors else services[\"tm\"]\n\n        return [(pretranslation, None, author)]\n\n    else:\n        pretranslation, service = get_pretranslated_data(\n            source, locale, preserve_placeables\n        )\n\n        if pretranslation is None:\n            return []\n\n        author = services[service]\n        if entity.string_plural == \"\":\n            return [(pretranslation, None, author)]\n        else:\n            plural_forms = range(0, locale.nplurals or 1)\n            return [\n                (pretranslation, plural_form, author) for plural_form in plural_forms\n            ]\n\n\ndef get_pretranslated_data(source, locale, preserve_placeables):\n    # Empty strings do not need translation\n    if re.search(\"^\\\\s*$\", source):\n        return source, \"tm\"\n\n    # Try to get matches from Translation Memory\n    tm_response = get_translation_memory_data(text=source, locale=locale)\n    tm_perfect = [t for t in tm_response if int(t[\"quality\"]) == 100]\n    if tm_perfect:\n        return tm_perfect[0][\"target\"], \"tm\"\n\n    # Fetch from Google Translate\n    elif locale.google_translate_code:\n        gt_response = get_google_translate_data(\n            text=source, locale=locale, preserve_placeables=preserve_placeables\n        )\n        if gt_response[\"status\"]:\n            return gt_response[\"translation\"], \"gt\"\n\n    return None, None\n\n\ndef update_changed_instances(tr_filter, tr_dict, translations):\n    \"\"\"\n    Update the latest activity and stats for changed Locales, ProjectLocales\n    & TranslatedResources\n    \"\"\"\n    tr_filter = tuple(tr_filter)\n    # Combine all generated filters with an OK operator.\n    # `operator.ior` is the '|' Python operator, which turns into a logical OR\n    # when used between django ORM query objects.\n    tr_query = reduce(operator.ior, tr_filter)\n\n    translatedresources = TranslatedResource.objects.filter(tr_query).annotate(\n        locale_resource=Concat(\n            \"locale_id\", V(\"-\"), \"resource_id\", output_field=CharField()\n        )\n    )\n\n    translatedresources.update_stats()\n\n    for tr in translatedresources:\n        index = tr_dict[tr.locale_resource]\n        translation = translations[index]\n        translation.update_latest_translation()\n", "path": "pontoon/pretranslation/pretranslate.py"}], "after_files": [{"content": "import logging\nimport operator\nimport re\n\nfrom django.db.models import CharField, Value as V\nfrom django.db.models.functions import Concat\n\nfrom fluent.syntax import FluentParser, FluentSerializer\nfrom functools import reduce\n\nfrom pontoon.base.models import User, TranslatedResource\nfrom pontoon.machinery.utils import (\n    get_google_translate_data,\n    get_translation_memory_data,\n)\nfrom pontoon.pretranslation import AUTHORS\nfrom .transformer import ApplyPretranslation\n\n\nlog = logging.getLogger(__name__)\n\nparser = FluentParser()\nserializer = FluentSerializer()\n\n\ndef get_pretranslations(entity, locale, preserve_placeables=False):\n    \"\"\"\n    Get pretranslations for the entity-locale pair using internal translation memory and\n    Google's machine translation.\n\n    For Fluent strings, uplift SelectExpressions, serialize Placeables as TextElements\n    and then only pretranslate TextElements. Set the most frequent TextElement\n    pretranslation author as the author of the entire pretranslation.\n\n    :arg Entity entity: the Entity object\n    :arg Locale locale: the Locale object\n    :arg boolean preserve_placeables\n\n    :returns: a list of tuples, consisting of:\n        - a pretranslation of the entity\n        - a plural form\n        - a user (representing TM or GT service)\n    \"\"\"\n    source = entity.string\n    services = {k: User.objects.get(email=email) for k, email in AUTHORS.items()}\n\n    if entity.resource.format == \"ftl\":\n        entry = parser.parse_entry(source)\n        pretranslate = ApplyPretranslation(\n            locale, entry, get_pretranslated_data, preserve_placeables\n        )\n\n        try:\n            pretranslate.visit(entry)\n        except ValueError as e:\n            log.info(f\"Fluent pretranslation error: {e}\")\n            return []\n\n        pretranslation = serializer.serialize_entry(entry)\n\n        # Parse and serialize pretranslation again in order to assure cannonical style\n        parsed_pretranslation = parser.parse_entry(pretranslation)\n        pretranslation = serializer.serialize_entry(parsed_pretranslation)\n\n        authors = [services[service] for service in pretranslate.services]\n        author = max(set(authors), key=authors.count) if authors else services[\"tm\"]\n\n        return [(pretranslation, None, author)]\n\n    else:\n        pretranslation, service = get_pretranslated_data(\n            source, locale, preserve_placeables\n        )\n\n        if pretranslation is None:\n            return []\n\n        author = services[service]\n        if entity.string_plural == \"\":\n            return [(pretranslation, None, author)]\n        else:\n            plural_forms = range(0, locale.nplurals or 1)\n            return [\n                (pretranslation, plural_form, author) for plural_form in plural_forms\n            ]\n\n\ndef get_pretranslated_data(source, locale, preserve_placeables):\n    # Empty strings and strings containing whitespace only do not need translation\n    if re.search(\"^\\\\s*$\", source):\n        return source, \"tm\"\n\n    # Try to get matches from Translation Memory\n    tm_response = get_translation_memory_data(text=source, locale=locale)\n    tm_perfect = [t for t in tm_response if int(t[\"quality\"]) == 100]\n    if tm_perfect:\n        return tm_perfect[0][\"target\"], \"tm\"\n\n    # Fetch from Google Translate\n    elif locale.google_translate_code:\n        gt_response = get_google_translate_data(\n            text=source, locale=locale, preserve_placeables=preserve_placeables\n        )\n        if gt_response[\"status\"]:\n            return gt_response[\"translation\"], \"gt\"\n\n    return None, None\n\n\ndef update_changed_instances(tr_filter, tr_dict, translations):\n    \"\"\"\n    Update the latest activity and stats for changed Locales, ProjectLocales\n    & TranslatedResources\n    \"\"\"\n    tr_filter = tuple(tr_filter)\n    # Combine all generated filters with an OK operator.\n    # `operator.ior` is the '|' Python operator, which turns into a logical OR\n    # when used between django ORM query objects.\n    tr_query = reduce(operator.ior, tr_filter)\n\n    translatedresources = TranslatedResource.objects.filter(tr_query).annotate(\n        locale_resource=Concat(\n            \"locale_id\", V(\"-\"), \"resource_id\", output_field=CharField()\n        )\n    )\n\n    translatedresources.update_stats()\n\n    for tr in translatedresources:\n        index = tr_dict[tr.locale_resource]\n        translation = translations[index]\n        translation.update_latest_translation()\n", "path": "pontoon/pretranslation/pretranslate.py"}]}