Repository: projectfluent/python-fluent (language: python)

Issue:
Incorrect handling of tabs in message value
As identified in https://github.com/mozilla/pontoon/issues/2470#issuecomment-1107451952, this happens:

```python
from fluent.syntax import ast, FluentParser, FluentSerializer
parser = FluentParser()
serializer = FluentSerializer()

string = """places-open-in-container-tab =
    .label = Բացել նոր ներդիրում
    .accesskey =	
"""

string
'places-open-in-container-tab =\n    .label = Բացել նոր ներդիրում\n    .accesskey = \t\n'

serializer.serialize(parser.parse(string))
'places-open-in-container-tab =\n    .label = Բացել նոր ներդիրում\n    .accesskey = \n'
```

Tabs should not be lost; they count as valid `inline_text` characters according to the [spec](https://github.com/projectfluent/fluent/blob/master/spec/fluent.ebnf).


Discussion / hints:
Probably also a bug in js syntax parser, and the runtime parser.
Yeah, will need to check all the implementations.

Fix patch:
diff --git a/fluent.syntax/fluent/syntax/parser.py b/fluent.syntax/fluent/syntax/parser.py
index fb296368..7d97fd71 100644
--- a/fluent.syntax/fluent/syntax/parser.py
+++ b/fluent.syntax/fluent/syntax/parser.py
@@ -439,7 +439,7 @@ def dedent(self, elements, common_indent):
         # Trim trailing whitespace from the Pattern.
         last_element = trimmed[-1] if len(trimmed) > 0 else None
         if isinstance(last_element, ast.TextElement):
-            last_element.value = last_element.value.rstrip(' \t\n\r')
+            last_element.value = last_element.value.rstrip(' \n\r')
             if last_element.value == "":
                 trimmed.pop()
 


Test patch:
diff --git a/fluent.syntax/tests/syntax/fixtures_reference/tab.ftl b/fluent.syntax/tests/syntax/fixtures_reference/tab.ftl
index 4b23ad87..4b4e47de 100644
--- a/fluent.syntax/tests/syntax/fixtures_reference/tab.ftl
+++ b/fluent.syntax/tests/syntax/fixtures_reference/tab.ftl
@@ -12,3 +12,10 @@ key03 =
 key04 =
     This line is indented by 4 spaces,
 	whereas this line by 1 tab.
+
+# OK (value is a single tab)
+key05 = 	
+
+# OK (attribute value is two tabs)
+key06 =
+  .attr = 		
diff --git a/fluent.syntax/tests/syntax/fixtures_reference/tab.json b/fluent.syntax/tests/syntax/fixtures_reference/tab.json
index 714eb947..0c0c65a1 100644
--- a/fluent.syntax/tests/syntax/fixtures_reference/tab.json
+++ b/fluent.syntax/tests/syntax/fixtures_reference/tab.json
@@ -64,7 +64,58 @@
         {
             "type": "Junk",
             "annotations": [],
-            "content": "\twhereas this line by 1 tab.\n"
+            "content": "\twhereas this line by 1 tab.\n\n"
+        },
+        {
+            "type": "Message",
+            "id": {
+                "type": "Identifier",
+                "name": "key05"
+            },
+            "value": {
+                "type": "Pattern",
+                "elements": [
+                    {
+                        "type": "TextElement",
+                        "value": "\t"
+                    }
+                ]
+            },
+            "attributes": [],
+            "comment": {
+                "type": "Comment",
+                "content": "OK (value is a single tab)"
+            }
+        },
+        {
+            "type": "Message",
+            "id": {
+                "type": "Identifier",
+                "name": "key06"
+            },
+            "value": null,
+            "attributes": [
+                {
+                    "type": "Attribute",
+                    "id": {
+                        "type": "Identifier",
+                        "name": "attr"
+                    },
+                    "value": {
+                        "type": "Pattern",
+                        "elements": [
+                            {
+                                "type": "TextElement",
+                                "value": "\t\t"
+                            }
+                        ]
+                    }
+                }
+            ],
+            "comment": {
+                "type": "Comment",
+                "content": "OK (attribute value is two tabs)"
+            }
         }
     ]
 }