prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Pretranslation issues with multiline strings
  Pretranslation struggles to translate Fluent strings where line breaks are used to limit the line width, as it treats each line as a separate sentence.
  
  ```
  rec-pw-1-2 =
    Make this password unique and different from any others you use.
    A good strategy to follow is to combine two or more unrelated
    words to create an entire pass phrase, and include numbers and symbols.
  ```
  
  I don't think we can completely ignore line breaks, because there are some cases where these have an effect on display ([example](https://searchfox.org/mozilla-central/rev/169bf38e150667afac81ab73ef8b5ace8f1dfa8d/browser/locales/en-US/browser/downloads.ftl#244-248)). But maybe we can strip line breaks (i.e. replace them with a whitespace) when sending the translation to the machine translation engine, hoping that translators will catch edge cases when reviewing?
  
  Cc @eemeli because that's a topic that we discussed at some point for the Fluent editor.
  
  --- 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.base.fluent import FlatTransformer, create_locale_plural_variants
  13 from pontoon.machinery.utils import (
  14     get_google_translate_data,
  15     get_translation_memory_data,
  16 )
  17 
  18 
  19 log = logging.getLogger(__name__)
  20 
  21 parser = FluentParser()
  22 serializer = FluentSerializer()
  23 
  24 
  25 class PretranslationTransformer(FlatTransformer):
  26     def __init__(self, locale):
  27         self.services = []
  28         self.locale = locale
  29 
  30     def visit_SelectExpression(self, node):
  31         create_locale_plural_variants(node, self.locale)
  32         return self.generic_visit(node)
  33 
  34     def visit_TextElement(self, node):
  35         pretranslation, service = get_pretranslated_data(node.value, self.locale)
  36 
  37         if pretranslation is None:
  38             raise ValueError(
  39                 f"Pretranslation for `{node.value}` to {self.locale.code} not available."
  40             )
  41 
  42         node.value = pretranslation
  43         self.services.append(service)
  44         return node
  45 
  46 
  47 def get_pretranslations(entity, locale):
  48     """
  49     Get pretranslations for the entity-locale pair using internal translation memory and
  50     Google's machine translation.
  51 
  52     For Fluent strings, uplift SelectExpressions, serialize Placeables as TextElements
  53     and then only pretranslate TextElements. Set the most frequent TextElement
  54     pretranslation author as the author of the entire pretranslation.
  55 
  56     :arg Entity entity: the Entity object
  57     :arg Locale locale: the Locale object
  58 
  59     :returns: a list of tuples, consisting of:
  60         - a pretranslation of the entity
  61         - a plural form
  62         - a user (representing TM or GT service)
  63     """
  64     source = entity.string
  65     services = {
  66         "tm": User.objects.get(email="pontoon-tm@example.com"),
  67         "gt": User.objects.get(email="pontoon-gt@example.com"),
  68     }
  69 
  70     if entity.resource.format == "ftl":
  71         source_ast = parser.parse_entry(source)
  72         pt_transformer = PretranslationTransformer(locale)
  73 
  74         try:
  75             pretranslated_ast = pt_transformer.visit(source_ast)
  76         except ValueError as e:
  77             log.info(f"Fluent pretranslation error: {e}")
  78             return []
  79 
  80         pretranslation = serializer.serialize_entry(pretranslated_ast)
  81 
  82         authors = [services[service] for service in pt_transformer.services]
  83         author = max(set(authors), key=authors.count) if authors else services["tm"]
  84 
  85         return [(pretranslation, None, author)]
  86 
  87     else:
  88         pretranslation, service = get_pretranslated_data(source, locale)
  89 
  90         if pretranslation is None:
  91             return []
  92 
  93         author = services[service]
  94         if entity.string_plural == "":
  95             return [(pretranslation, None, author)]
  96         else:
  97             plural_forms = range(0, locale.nplurals or 1)
  98             return [
  99                 (pretranslation, plural_form, author) for plural_form in plural_forms
  100             ]
  101 
  102 
  103 def get_pretranslated_data(source, locale):
  104     # Empty strings do not need translation
  105     if re.search("^\\s*$", source):
  106         return source, "tm"
  107 
  108     # Try to get matches from Translation Memory
  109     tm_response = get_translation_memory_data(text=source, locale=locale)
  110     tm_perfect = [t for t in tm_response if int(t["quality"]) == 100]
  111     if tm_perfect:
  112         return tm_perfect[0]["target"], "tm"
  113 
  114     # Fetch from Google Translate
  115     elif locale.google_translate_code:
  116         gt_response = get_google_translate_data(text=source, locale=locale)
  117         if gt_response["status"]:
  118             return gt_response["translation"], "gt"
  119 
  120     return None, None
  121 
  122 
  123 def update_changed_instances(tr_filter, tr_dict, translations):
  124     """
  125     Update the latest activity and stats for changed Locales, ProjectLocales
  126     & TranslatedResources
  127     """
  128     tr_filter = tuple(tr_filter)
  129     # Combine all generated filters with an OK operator.
  130     # `operator.ior` is the '|' Python operator, which turns into a logical OR
  131     # when used between django ORM query objects.
  132     tr_query = reduce(operator.ior, tr_filter)
  133 
  134     translatedresources = TranslatedResource.objects.filter(tr_query).annotate(
  135         locale_resource=Concat(
  136             "locale_id", V("-"), "resource_id", output_field=CharField()
  137         )
  138     )
  139 
  140     translatedresources.update_stats()
  141 
  142     for tr in translatedresources:
  143         index = tr_dict[tr.locale_resource]
  144         translation = translations[index]
  145         translation.update_latest_translation()
  146
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/pontoon/pretranslation/pretranslate.py b/pontoon/pretranslation/pretranslate.py
  --- a/pontoon/pretranslation/pretranslate.py
  +++ b/pontoon/pretranslation/pretranslate.py
  @@ -32,11 +32,15 @@
           return self.generic_visit(node)
   
       def visit_TextElement(self, node):
  -        pretranslation, service = get_pretranslated_data(node.value, self.locale)
  +        # Machine translation treats each line as separate sentence,
  +        # hence we replace newline characters with spaces.
  +        source = node.value.replace("\n", " ")
  +
  +        pretranslation, service = get_pretranslated_data(source, self.locale)
   
           if pretranslation is None:
               raise ValueError(
  -                f"Pretranslation for `{node.value}` to {self.locale.code} not available."
  +                f"Pretranslation for `{source}` to {self.locale.code} not available."
               )
   
           node.value = pretranslation
  
verification_info: {"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@@ -32,11 +32,15 @@\n         return self.generic_visit(node)\n \n     def visit_TextElement(self, node):\n-        pretranslation, service = get_pretranslated_data(node.value, self.locale)\n+        # Machine translation treats each line as separate sentence,\n+        # hence we replace newline characters with spaces.\n+        source = node.value.replace(\"\\n\", \" \")\n+\n+        pretranslation, service = get_pretranslated_data(source, self.locale)\n \n         if pretranslation is None:\n             raise ValueError(\n-                f\"Pretranslation for `{node.value}` to {self.locale.code} not available.\"\n+                f\"Pretranslation for `{source}` to {self.locale.code} not available.\"\n             )\n \n         node.value = pretranslation\n", "issue": "Pretranslation issues with multiline strings\nPretranslation struggles to translate Fluent strings where line breaks are used to limit the line width, as it treats each line as a separate sentence.\r\n\r\n```\r\nrec-pw-1-2 =\r\n  Make this password unique and different from any others you use.\r\n  A good strategy to follow is to combine two or more unrelated\r\n  words to create an entire pass phrase, and include numbers and symbols.\r\n```\r\n\r\nI don't think we can completely ignore line breaks, because there are some cases where these have an effect on display ([example](https://searchfox.org/mozilla-central/rev/169bf38e150667afac81ab73ef8b5ace8f1dfa8d/browser/locales/en-US/browser/downloads.ftl#244-248)). But maybe we can strip line breaks (i.e. replace them with a whitespace) when sending the translation to the machine translation engine, hoping that translators will catch edge cases when reviewing?\r\n\r\nCc @eemeli because that's a topic that we discussed at some point for the Fluent editor.\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.base.fluent import FlatTransformer, create_locale_plural_variants\nfrom pontoon.machinery.utils import (\n    get_google_translate_data,\n    get_translation_memory_data,\n)\n\n\nlog = logging.getLogger(__name__)\n\nparser = FluentParser()\nserializer = FluentSerializer()\n\n\nclass PretranslationTransformer(FlatTransformer):\n    def __init__(self, locale):\n        self.services = []\n        self.locale = locale\n\n    def visit_SelectExpression(self, node):\n        create_locale_plural_variants(node, self.locale)\n        return self.generic_visit(node)\n\n    def visit_TextElement(self, node):\n        pretranslation, service = get_pretranslated_data(node.value, self.locale)\n\n        if pretranslation is None:\n            raise ValueError(\n                f\"Pretranslation for `{node.value}` to {self.locale.code} not available.\"\n            )\n\n        node.value = pretranslation\n        self.services.append(service)\n        return node\n\n\ndef get_pretranslations(entity, locale):\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\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 = {\n        \"tm\": User.objects.get(email=\"pontoon-tm@example.com\"),\n        \"gt\": User.objects.get(email=\"pontoon-gt@example.com\"),\n    }\n\n    if entity.resource.format == \"ftl\":\n        source_ast = parser.parse_entry(source)\n        pt_transformer = PretranslationTransformer(locale)\n\n        try:\n            pretranslated_ast = pt_transformer.visit(source_ast)\n        except ValueError as e:\n            log.info(f\"Fluent pretranslation error: {e}\")\n            return []\n\n        pretranslation = serializer.serialize_entry(pretranslated_ast)\n\n        authors = [services[service] for service in pt_transformer.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(source, locale)\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):\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(text=source, locale=locale)\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.base.fluent import FlatTransformer, create_locale_plural_variants\nfrom pontoon.machinery.utils import (\n    get_google_translate_data,\n    get_translation_memory_data,\n)\n\n\nlog = logging.getLogger(__name__)\n\nparser = FluentParser()\nserializer = FluentSerializer()\n\n\nclass PretranslationTransformer(FlatTransformer):\n    def __init__(self, locale):\n        self.services = []\n        self.locale = locale\n\n    def visit_SelectExpression(self, node):\n        create_locale_plural_variants(node, self.locale)\n        return self.generic_visit(node)\n\n    def visit_TextElement(self, node):\n        # Machine translation treats each line as separate sentence,\n        # hence we replace newline characters with spaces.\n        source = node.value.replace(\"\\n\", \" \")\n\n        pretranslation, service = get_pretranslated_data(source, self.locale)\n\n        if pretranslation is None:\n            raise ValueError(\n                f\"Pretranslation for `{source}` to {self.locale.code} not available.\"\n            )\n\n        node.value = pretranslation\n        self.services.append(service)\n        return node\n\n\ndef get_pretranslations(entity, locale):\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\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 = {\n        \"tm\": User.objects.get(email=\"pontoon-tm@example.com\"),\n        \"gt\": User.objects.get(email=\"pontoon-gt@example.com\"),\n    }\n\n    if entity.resource.format == \"ftl\":\n        source_ast = parser.parse_entry(source)\n        pt_transformer = PretranslationTransformer(locale)\n\n        try:\n            pretranslated_ast = pt_transformer.visit(source_ast)\n        except ValueError as e:\n            log.info(f\"Fluent pretranslation error: {e}\")\n            return []\n\n        pretranslation = serializer.serialize_entry(pretranslated_ast)\n\n        authors = [services[service] for service in pt_transformer.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(source, locale)\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):\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(text=source, locale=locale)\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"}]}