We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Download translation & Weblate-specific data
Hello,
When downloading for offline translation, you get the store data but you never get Weblate-specific data such as Suggestions, Weblate Comments, or Weblate source string tags like check-length etc.
Do you think it would be appropriate to get this data at download\* so that offline translator has a level of information closer to online translation?
Thanks!

*) I guess all formats have a comment zone where all of this could be fed. 
For .po, we could be a bit more specific, as Weblate comments and suggestions could qualify as translator comments (#) (rather than extracted comments #.), and for Weblate flags, maybe flags? (#,)
(Or maybe you'd envision that as a separate downloaded file??)
## 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `weblate/formats/exporters.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright © 2012 - 2019 Michal Čihař <michal@cihar.com>
4 #
5 # This file is part of Weblate <https://weblate.org/>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
19 #
20 """Exporter using translate-toolkit"""
21 from __future__ import unicode_literals
22 
23 from django.http import HttpResponse
24 from django.utils.functional import cached_property
25 from django.utils.translation import ugettext_lazy as _
26 from translate.misc.multistring import multistring
27 from translate.storage.csvl10n import csvfile
28 from translate.storage.mo import mofile
29 from translate.storage.po import pofile
30 from translate.storage.poxliff import PoXliffFile
31 from translate.storage.tbx import tbxfile
32 from translate.storage.tmx import tmxfile
33 from translate.storage.xliff import xlifffile
34 
35 import weblate
36 from weblate.formats.external import XlsxFormat
37 from weblate.formats.ttkit import TTKitFormat
38 from weblate.utils.site import get_site_url
39 
40 # Map to remove control chars except newlines and tabs
41 _CHARMAP = dict.fromkeys(
42     x for x in range(32) if x not in (9, 10, 13)
43 )
44 
45 EXPORTERS = {}
46 
47 
48 def register_exporter(exporter):
49     """Register an exporter."""
50     EXPORTERS[exporter.name] = exporter
51     return exporter
52 
53 
54 def get_exporter(name):
55     """Return registered exporter"""
56     return EXPORTERS[name]
57 
58 
59 def list_exporters(translation):
60     return [
61         {'name': x.name, 'verbose': x.verbose}
62         for x in sorted(EXPORTERS.values(), key=lambda x: x.name)
63         if x.supports(translation)
64     ]
65 
66 
67 class BaseExporter(object):
68     content_type = 'text/plain'
69     extension = 'txt'
70     name = ''
71     verbose = ''
72     set_id = False
73 
74     def __init__(self, project=None, language=None, url=None,
75                  translation=None, fieldnames=None):
76         if translation is not None:
77             self.plural = translation.plural
78             self.project = translation.component.project
79             self.language = translation.language
80             self.url = get_site_url(translation.get_absolute_url())
81         else:
82             self.project = project
83             self.language = language
84             self.plural = language.plural
85             self.url = url
86         self.fieldnames = fieldnames
87 
88     @staticmethod
89     def supports(translation):
90         return True
91 
92     @cached_property
93     def storage(self):
94         storage = self.get_storage()
95         storage.setsourcelanguage(
96             self.project.source_language.code
97         )
98         storage.settargetlanguage(
99             self.language.code
100         )
101         return storage
102 
103     def string_filter(self, text):
104         return text
105 
106     def handle_plurals(self, plurals):
107         if len(plurals) == 1:
108             return self.string_filter(plurals[0])
109         return multistring(
110             [self.string_filter(plural) for plural in plurals]
111         )
112 
113     def get_storage(self):
114         raise NotImplementedError()
115 
116     def add(self, unit, word):
117         unit.target = word
118 
119     def add_dictionary(self, word):
120         """Add dictionary word"""
121         unit = self.storage.UnitClass(self.string_filter(word.source))
122         self.add(unit, self.string_filter(word.target))
123         self.storage.addunit(unit)
124 
125     def add_units(self, units):
126         for unit in units.iterator():
127             self.add_unit(unit)
128 
129     def add_unit(self, unit):
130         output = self.storage.UnitClass(
131             self.handle_plurals(unit.get_source_plurals())
132         )
133         self.add(output, self.handle_plurals(unit.get_target_plurals()))
134         # Location needs to be set prior to ID to avoid overwrite
135         # on some formats (eg. xliff)
136         for location in unit.location.split():
137             if location:
138                 output.addlocation(location)
139 
140         # Store context as context and ID
141         context = self.string_filter(unit.context)
142         if context:
143             output.setcontext(context)
144             if self.set_id:
145                 output.setid(context)
146         elif self.set_id:
147             # Use checksum based ID on formats requiring it
148             output.setid(unit.checksum)
149 
150         # Store note
151         note = self.string_filter(unit.comment)
152         if note:
153             output.addnote(note, origin='developer')
154 
155         # Set type comment (for Gettext)
156         if hasattr(output, 'settypecomment'):
157             for flag in unit.flags.split(','):
158                 if flag:
159                     output.settypecomment(flag)
160 
161         # Store fuzzy flag
162         if unit.fuzzy:
163             output.markfuzzy(True)
164 
165         self.storage.addunit(output)
166 
167     def get_response(self, filetemplate='{project}-{language}.{extension}'):
168         filename = filetemplate.format(
169             project=self.project.slug,
170             language=self.language.code,
171             extension=self.extension
172         )
173 
174         response = HttpResponse(
175             content_type='{0}; charset=utf-8'.format(self.content_type)
176         )
177         response['Content-Disposition'] = 'attachment; filename={0}'.format(
178             filename
179         )
180 
181         # Save to response
182         response.write(self.serialize())
183 
184         return response
185 
186     def serialize(self):
187         """Return storage content"""
188         return TTKitFormat.serialize(self.storage)
189 
190 
191 @register_exporter
192 class PoExporter(BaseExporter):
193     name = 'po'
194     content_type = 'text/x-po'
195     extension = 'po'
196     verbose = _('gettext PO')
197     _storage = pofile
198 
199     def get_storage(self):
200         store = self._storage()
201         plural = self.plural
202 
203         # Set po file header
204         store.updateheader(
205             add=True,
206             language=self.language.code,
207             x_generator='Weblate {0}'.format(weblate.VERSION),
208             project_id_version='{0} ({1})'.format(
209                 self.language.name, self.project.name
210             ),
211             plural_forms=plural.plural_form,
212             language_team='{0} <{1}>'.format(
213                 self.language.name,
214                 self.url
215             )
216         )
217         return store
218 
219 
220 class XMLExporter(BaseExporter):
221     """Wrapper for XML based exporters to strip control chars"""
222 
223     def string_filter(self, text):
224         return text.translate(_CHARMAP)
225 
226     def get_storage(self):
227         raise NotImplementedError()
228 
229     def add(self, unit, word):
230         unit.settarget(word, self.language.code)
231 
232 
233 @register_exporter
234 class PoXliffExporter(XMLExporter):
235     name = 'xliff'
236     content_type = 'application/x-xliff+xml'
237     extension = 'xlf'
238     set_id = True
239     verbose = _('XLIFF with gettext extensions')
240 
241     def get_storage(self):
242         return PoXliffFile()
243 
244 
245 @register_exporter
246 class XliffExporter(XMLExporter):
247     name = 'xliff11'
248     content_type = 'application/x-xliff+xml'
249     extension = 'xlf'
250     set_id = True
251     verbose = _('XLIFF 1.1')
252 
253     def get_storage(self):
254         return xlifffile()
255 
256 
257 @register_exporter
258 class TBXExporter(XMLExporter):
259     name = 'tbx'
260     content_type = 'application/x-tbx'
261     extension = 'tbx'
262     verbose = _('TBX')
263 
264     def get_storage(self):
265         return tbxfile()
266 
267 
268 @register_exporter
269 class TMXExporter(XMLExporter):
270     name = 'tmx'
271     content_type = 'application/x-tmx'
272     extension = 'tmx'
273     verbose = _('TMX')
274 
275     def get_storage(self):
276         return tmxfile()
277 
278 
279 @register_exporter
280 class MoExporter(PoExporter):
281     name = 'mo'
282     content_type = 'application/x-gettext-catalog'
283     extension = 'mo'
284     verbose = _('gettext MO')
285     _storage = mofile
286 
287     def __init__(self, project=None, language=None, url=None,
288                  translation=None, fieldnames=None):
289         super(MoExporter, self).__init__(
290             project, language, url, translation, fieldnames
291         )
292         # Detect storage properties
293         self.monolingual = False
294         self.use_context = False
295         if translation:
296             self.monolingual = translation.component.has_template()
297             if self.monolingual:
298                 unit = next(translation.store.translatable_units, None)
299                 self.use_context = unit is not None and not unit.template.source
300 
301     def add_unit(self, unit):
302         # We do not store not translated units
303         if not unit.translated:
304             return
305         # Parse properties from unit
306         if self.monolingual:
307             if self.use_context:
308                 source = ''
309                 context = unit.context
310             else:
311                 source = unit.context
312                 context = ''
313         else:
314             source = self.handle_plurals(unit.get_source_plurals())
315             context = unit.context
316         # Actually create the unit and set attributes
317         output = self.storage.UnitClass(source)
318         output.target = self.handle_plurals(unit.get_target_plurals())
319         if context:
320             # The setcontext doesn't work on mounit
321             output.msgctxt = [context]
322         # Add unit to the storage
323         self.storage.addunit(output)
324 
325     @staticmethod
326     def supports(translation):
327         return translation.component.file_format == 'po'
328 
329 
330 @register_exporter
331 class CSVExporter(BaseExporter):
332     name = 'csv'
333     content_type = 'text/csv'
334     extension = 'csv'
335     verbose = _('CSV')
336 
337     def get_storage(self):
338         return csvfile(fieldnames=self.fieldnames)
339 
340     def string_filter(self, text):
341         """Avoid Excel interpreting text as formula.
342 
343         This is really bad idea, implemented in Excel, as this change leads
344         to displaying additional ' in all other tools, but this seems to be
345         what most people have gotten used to. Hopefully these chars are not
346         widely used at first position of translatable strings, so that harm is
347         reduced.
348         """
349         if text and text[0] in ('=', '+', '-', '@', '|', '%'):
350             return "'{0}'".format(text.replace('|', '\\|'))
351         return text
352 
353 
354 @register_exporter
355 class XlsxExporter(BaseExporter):
356     name = 'xlsx'
357     content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
358     extension = 'xlsx'
359     verbose = _('Excel Open XML')
360 
361     def get_storage(self):
362         return csvfile(fieldnames=self.fieldnames)
363 
364     def serialize(self):
365         """Return storage content"""
366         return XlsxFormat.serialize(self.storage)
367
```
--- 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/weblate/formats/exporters.py b/weblate/formats/exporters.py
--- a/weblate/formats/exporters.py
+++ b/weblate/formats/exporters.py
@@ -151,12 +151,23 @@
         note = self.string_filter(unit.comment)
         if note:
             output.addnote(note, origin='developer')
+        # In Weblate context
+        note = self.string_filter(unit.source_info.context)
+        if context:
+            output.addnote(note, origin='developer')
+        # Comments
+        for comment in unit.get_comments():
+            output.addnote(comment.comment, origin='translator')
+        # Suggestions
+        for suggestion in unit.suggestions:
+            output.addnote(
+                'Suggested in Weblate: {}'.format(suggestion.target),
+                origin='translator'
+            )
 
-        # Set type comment (for Gettext)
-        if hasattr(output, 'settypecomment'):
-            for flag in unit.flags.split(','):
-                if flag:
-                    output.settypecomment(flag)
+        # Store flags
+        if unit.all_flags:
+            self.store_flags(output, unit.all_flags)
 
         # Store fuzzy flag
         if unit.fuzzy:
@@ -187,6 +198,9 @@
         """Return storage content"""
         return TTKitFormat.serialize(self.storage)
 
+    def store_flags(self, output, flags):
+        return
+
 
 @register_exporter
 class PoExporter(BaseExporter):
@@ -196,6 +210,10 @@
     verbose = _('gettext PO')
     _storage = pofile
 
+    def store_flags(self, output, flags):
+        for flag in flags.items():
+            output.settypecomment(flag)
+
     def get_storage(self):
         store = self._storage()
         plural = self.plural
@@ -241,9 +259,15 @@
     def get_storage(self):
         return PoXliffFile()
 
+    def store_flags(self, output, flags):
+        if flags.has_value('max-length'):
+            output.xmlelement.set("maxwidth", str(flags.get_value('max-length')))
+
+        output.xmlelement.set("weblate-flags", flags.format())
+
 
 @register_exporter
-class XliffExporter(XMLExporter):
+class XliffExporter(PoXliffExporter):
     name = 'xliff11'
     content_type = 'application/x-xliff+xml'
     extension = 'xlf'
@@ -298,6 +322,9 @@
                 unit = next(translation.store.translatable_units, None)
                 self.use_context = unit is not None and not unit.template.source
 
+    def store_flags(self, output, flags):
+        return
+
     def add_unit(self, unit):
         # We do not store not translated units
         if not unit.translated:

{"golden_diff": "diff --git a/weblate/formats/exporters.py b/weblate/formats/exporters.py\n--- a/weblate/formats/exporters.py\n+++ b/weblate/formats/exporters.py\n@@ -151,12 +151,23 @@\n         note = self.string_filter(unit.comment)\n         if note:\n             output.addnote(note, origin='developer')\n+        # In Weblate context\n+        note = self.string_filter(unit.source_info.context)\n+        if context:\n+            output.addnote(note, origin='developer')\n+        # Comments\n+        for comment in unit.get_comments():\n+            output.addnote(comment.comment, origin='translator')\n+        # Suggestions\n+        for suggestion in unit.suggestions:\n+            output.addnote(\n+                'Suggested in Weblate: {}'.format(suggestion.target),\n+                origin='translator'\n+            )\n \n-        # Set type comment (for Gettext)\n-        if hasattr(output, 'settypecomment'):\n-            for flag in unit.flags.split(','):\n-                if flag:\n-                    output.settypecomment(flag)\n+        # Store flags\n+        if unit.all_flags:\n+            self.store_flags(output, unit.all_flags)\n \n         # Store fuzzy flag\n         if unit.fuzzy:\n@@ -187,6 +198,9 @@\n         \"\"\"Return storage content\"\"\"\n         return TTKitFormat.serialize(self.storage)\n \n+    def store_flags(self, output, flags):\n+        return\n+\n \n @register_exporter\n class PoExporter(BaseExporter):\n@@ -196,6 +210,10 @@\n     verbose = _('gettext PO')\n     _storage = pofile\n \n+    def store_flags(self, output, flags):\n+        for flag in flags.items():\n+            output.settypecomment(flag)\n+\n     def get_storage(self):\n         store = self._storage()\n         plural = self.plural\n@@ -241,9 +259,15 @@\n     def get_storage(self):\n         return PoXliffFile()\n \n+    def store_flags(self, output, flags):\n+        if flags.has_value('max-length'):\n+            output.xmlelement.set(\"maxwidth\", str(flags.get_value('max-length')))\n+\n+        output.xmlelement.set(\"weblate-flags\", flags.format())\n+\n \n @register_exporter\n-class XliffExporter(XMLExporter):\n+class XliffExporter(PoXliffExporter):\n     name = 'xliff11'\n     content_type = 'application/x-xliff+xml'\n     extension = 'xlf'\n@@ -298,6 +322,9 @@\n                 unit = next(translation.store.translatable_units, None)\n                 self.use_context = unit is not None and not unit.template.source\n \n+    def store_flags(self, output, flags):\n+        return\n+\n     def add_unit(self, unit):\n         # We do not store not translated units\n         if not unit.translated:\n", "issue": "Download translation & Weblate-specific data\nHello,\nWhen downloading for offline translation, you get the store data but you never get Weblate-specific data such as Suggestions, Weblate Comments, or Weblate source string tags like check-length etc.\nDo you think it would be appropriate to get this data at download\\* so that offline translator has a level of information closer to online translation?\nThanks!\n\n*) I guess all formats have a comment zone where all of this could be fed. \nFor .po, we could be a bit more specific, as Weblate comments and suggestions could qualify as translator comments (#) (rather than extracted comments #.), and for Weblate flags, maybe flags? (#,)\n(Or maybe you'd envision that as a separate downloaded file??)\n## \n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright \u00a9 2012 - 2019 Michal \u010ciha\u0159 <michal@cihar.com>\n#\n# This file is part of Weblate <https://weblate.org/>\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <https://www.gnu.org/licenses/>.\n#\n\"\"\"Exporter using translate-toolkit\"\"\"\nfrom __future__ import unicode_literals\n\nfrom django.http import HttpResponse\nfrom django.utils.functional import cached_property\nfrom django.utils.translation import ugettext_lazy as _\nfrom translate.misc.multistring import multistring\nfrom translate.storage.csvl10n import csvfile\nfrom translate.storage.mo import mofile\nfrom translate.storage.po import pofile\nfrom translate.storage.poxliff import PoXliffFile\nfrom translate.storage.tbx import tbxfile\nfrom translate.storage.tmx import tmxfile\nfrom translate.storage.xliff import xlifffile\n\nimport weblate\nfrom weblate.formats.external import XlsxFormat\nfrom weblate.formats.ttkit import TTKitFormat\nfrom weblate.utils.site import get_site_url\n\n# Map to remove control chars except newlines and tabs\n_CHARMAP = dict.fromkeys(\n    x for x in range(32) if x not in (9, 10, 13)\n)\n\nEXPORTERS = {}\n\n\ndef register_exporter(exporter):\n    \"\"\"Register an exporter.\"\"\"\n    EXPORTERS[exporter.name] = exporter\n    return exporter\n\n\ndef get_exporter(name):\n    \"\"\"Return registered exporter\"\"\"\n    return EXPORTERS[name]\n\n\ndef list_exporters(translation):\n    return [\n        {'name': x.name, 'verbose': x.verbose}\n        for x in sorted(EXPORTERS.values(), key=lambda x: x.name)\n        if x.supports(translation)\n    ]\n\n\nclass BaseExporter(object):\n    content_type = 'text/plain'\n    extension = 'txt'\n    name = ''\n    verbose = ''\n    set_id = False\n\n    def __init__(self, project=None, language=None, url=None,\n                 translation=None, fieldnames=None):\n        if translation is not None:\n            self.plural = translation.plural\n            self.project = translation.component.project\n            self.language = translation.language\n            self.url = get_site_url(translation.get_absolute_url())\n        else:\n            self.project = project\n            self.language = language\n            self.plural = language.plural\n            self.url = url\n        self.fieldnames = fieldnames\n\n    @staticmethod\n    def supports(translation):\n        return True\n\n    @cached_property\n    def storage(self):\n        storage = self.get_storage()\n        storage.setsourcelanguage(\n            self.project.source_language.code\n        )\n        storage.settargetlanguage(\n            self.language.code\n        )\n        return storage\n\n    def string_filter(self, text):\n        return text\n\n    def handle_plurals(self, plurals):\n        if len(plurals) == 1:\n            return self.string_filter(plurals[0])\n        return multistring(\n            [self.string_filter(plural) for plural in plurals]\n        )\n\n    def get_storage(self):\n        raise NotImplementedError()\n\n    def add(self, unit, word):\n        unit.target = word\n\n    def add_dictionary(self, word):\n        \"\"\"Add dictionary word\"\"\"\n        unit = self.storage.UnitClass(self.string_filter(word.source))\n        self.add(unit, self.string_filter(word.target))\n        self.storage.addunit(unit)\n\n    def add_units(self, units):\n        for unit in units.iterator():\n            self.add_unit(unit)\n\n    def add_unit(self, unit):\n        output = self.storage.UnitClass(\n            self.handle_plurals(unit.get_source_plurals())\n        )\n        self.add(output, self.handle_plurals(unit.get_target_plurals()))\n        # Location needs to be set prior to ID to avoid overwrite\n        # on some formats (eg. xliff)\n        for location in unit.location.split():\n            if location:\n                output.addlocation(location)\n\n        # Store context as context and ID\n        context = self.string_filter(unit.context)\n        if context:\n            output.setcontext(context)\n            if self.set_id:\n                output.setid(context)\n        elif self.set_id:\n            # Use checksum based ID on formats requiring it\n            output.setid(unit.checksum)\n\n        # Store note\n        note = self.string_filter(unit.comment)\n        if note:\n            output.addnote(note, origin='developer')\n\n        # Set type comment (for Gettext)\n        if hasattr(output, 'settypecomment'):\n            for flag in unit.flags.split(','):\n                if flag:\n                    output.settypecomment(flag)\n\n        # Store fuzzy flag\n        if unit.fuzzy:\n            output.markfuzzy(True)\n\n        self.storage.addunit(output)\n\n    def get_response(self, filetemplate='{project}-{language}.{extension}'):\n        filename = filetemplate.format(\n            project=self.project.slug,\n            language=self.language.code,\n            extension=self.extension\n        )\n\n        response = HttpResponse(\n            content_type='{0}; charset=utf-8'.format(self.content_type)\n        )\n        response['Content-Disposition'] = 'attachment; filename={0}'.format(\n            filename\n        )\n\n        # Save to response\n        response.write(self.serialize())\n\n        return response\n\n    def serialize(self):\n        \"\"\"Return storage content\"\"\"\n        return TTKitFormat.serialize(self.storage)\n\n\n@register_exporter\nclass PoExporter(BaseExporter):\n    name = 'po'\n    content_type = 'text/x-po'\n    extension = 'po'\n    verbose = _('gettext PO')\n    _storage = pofile\n\n    def get_storage(self):\n        store = self._storage()\n        plural = self.plural\n\n        # Set po file header\n        store.updateheader(\n            add=True,\n            language=self.language.code,\n            x_generator='Weblate {0}'.format(weblate.VERSION),\n            project_id_version='{0} ({1})'.format(\n                self.language.name, self.project.name\n            ),\n            plural_forms=plural.plural_form,\n            language_team='{0} <{1}>'.format(\n                self.language.name,\n                self.url\n            )\n        )\n        return store\n\n\nclass XMLExporter(BaseExporter):\n    \"\"\"Wrapper for XML based exporters to strip control chars\"\"\"\n\n    def string_filter(self, text):\n        return text.translate(_CHARMAP)\n\n    def get_storage(self):\n        raise NotImplementedError()\n\n    def add(self, unit, word):\n        unit.settarget(word, self.language.code)\n\n\n@register_exporter\nclass PoXliffExporter(XMLExporter):\n    name = 'xliff'\n    content_type = 'application/x-xliff+xml'\n    extension = 'xlf'\n    set_id = True\n    verbose = _('XLIFF with gettext extensions')\n\n    def get_storage(self):\n        return PoXliffFile()\n\n\n@register_exporter\nclass XliffExporter(XMLExporter):\n    name = 'xliff11'\n    content_type = 'application/x-xliff+xml'\n    extension = 'xlf'\n    set_id = True\n    verbose = _('XLIFF 1.1')\n\n    def get_storage(self):\n        return xlifffile()\n\n\n@register_exporter\nclass TBXExporter(XMLExporter):\n    name = 'tbx'\n    content_type = 'application/x-tbx'\n    extension = 'tbx'\n    verbose = _('TBX')\n\n    def get_storage(self):\n        return tbxfile()\n\n\n@register_exporter\nclass TMXExporter(XMLExporter):\n    name = 'tmx'\n    content_type = 'application/x-tmx'\n    extension = 'tmx'\n    verbose = _('TMX')\n\n    def get_storage(self):\n        return tmxfile()\n\n\n@register_exporter\nclass MoExporter(PoExporter):\n    name = 'mo'\n    content_type = 'application/x-gettext-catalog'\n    extension = 'mo'\n    verbose = _('gettext MO')\n    _storage = mofile\n\n    def __init__(self, project=None, language=None, url=None,\n                 translation=None, fieldnames=None):\n        super(MoExporter, self).__init__(\n            project, language, url, translation, fieldnames\n        )\n        # Detect storage properties\n        self.monolingual = False\n        self.use_context = False\n        if translation:\n            self.monolingual = translation.component.has_template()\n            if self.monolingual:\n                unit = next(translation.store.translatable_units, None)\n                self.use_context = unit is not None and not unit.template.source\n\n    def add_unit(self, unit):\n        # We do not store not translated units\n        if not unit.translated:\n            return\n        # Parse properties from unit\n        if self.monolingual:\n            if self.use_context:\n                source = ''\n                context = unit.context\n            else:\n                source = unit.context\n                context = ''\n        else:\n            source = self.handle_plurals(unit.get_source_plurals())\n            context = unit.context\n        # Actually create the unit and set attributes\n        output = self.storage.UnitClass(source)\n        output.target = self.handle_plurals(unit.get_target_plurals())\n        if context:\n            # The setcontext doesn't work on mounit\n            output.msgctxt = [context]\n        # Add unit to the storage\n        self.storage.addunit(output)\n\n    @staticmethod\n    def supports(translation):\n        return translation.component.file_format == 'po'\n\n\n@register_exporter\nclass CSVExporter(BaseExporter):\n    name = 'csv'\n    content_type = 'text/csv'\n    extension = 'csv'\n    verbose = _('CSV')\n\n    def get_storage(self):\n        return csvfile(fieldnames=self.fieldnames)\n\n    def string_filter(self, text):\n        \"\"\"Avoid Excel interpreting text as formula.\n\n        This is really bad idea, implemented in Excel, as this change leads\n        to displaying additional ' in all other tools, but this seems to be\n        what most people have gotten used to. Hopefully these chars are not\n        widely used at first position of translatable strings, so that harm is\n        reduced.\n        \"\"\"\n        if text and text[0] in ('=', '+', '-', '@', '|', '%'):\n            return \"'{0}'\".format(text.replace('|', '\\\\|'))\n        return text\n\n\n@register_exporter\nclass XlsxExporter(BaseExporter):\n    name = 'xlsx'\n    content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n    extension = 'xlsx'\n    verbose = _('Excel Open XML')\n\n    def get_storage(self):\n        return csvfile(fieldnames=self.fieldnames)\n\n    def serialize(self):\n        \"\"\"Return storage content\"\"\"\n        return XlsxFormat.serialize(self.storage)\n", "path": "weblate/formats/exporters.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright \u00a9 2012 - 2019 Michal \u010ciha\u0159 <michal@cihar.com>\n#\n# This file is part of Weblate <https://weblate.org/>\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <https://www.gnu.org/licenses/>.\n#\n\"\"\"Exporter using translate-toolkit\"\"\"\nfrom __future__ import unicode_literals\n\nfrom django.http import HttpResponse\nfrom django.utils.functional import cached_property\nfrom django.utils.translation import ugettext_lazy as _\nfrom translate.misc.multistring import multistring\nfrom translate.storage.csvl10n import csvfile\nfrom translate.storage.mo import mofile\nfrom translate.storage.po import pofile\nfrom translate.storage.poxliff import PoXliffFile\nfrom translate.storage.tbx import tbxfile\nfrom translate.storage.tmx import tmxfile\nfrom translate.storage.xliff import xlifffile\n\nimport weblate\nfrom weblate.formats.external import XlsxFormat\nfrom weblate.formats.ttkit import TTKitFormat\nfrom weblate.utils.site import get_site_url\n\n# Map to remove control chars except newlines and tabs\n_CHARMAP = dict.fromkeys(\n    x for x in range(32) if x not in (9, 10, 13)\n)\n\nEXPORTERS = {}\n\n\ndef register_exporter(exporter):\n    \"\"\"Register an exporter.\"\"\"\n    EXPORTERS[exporter.name] = exporter\n    return exporter\n\n\ndef get_exporter(name):\n    \"\"\"Return registered exporter\"\"\"\n    return EXPORTERS[name]\n\n\ndef list_exporters(translation):\n    return [\n        {'name': x.name, 'verbose': x.verbose}\n        for x in sorted(EXPORTERS.values(), key=lambda x: x.name)\n        if x.supports(translation)\n    ]\n\n\nclass BaseExporter(object):\n    content_type = 'text/plain'\n    extension = 'txt'\n    name = ''\n    verbose = ''\n    set_id = False\n\n    def __init__(self, project=None, language=None, url=None,\n                 translation=None, fieldnames=None):\n        if translation is not None:\n            self.plural = translation.plural\n            self.project = translation.component.project\n            self.language = translation.language\n            self.url = get_site_url(translation.get_absolute_url())\n        else:\n            self.project = project\n            self.language = language\n            self.plural = language.plural\n            self.url = url\n        self.fieldnames = fieldnames\n\n    @staticmethod\n    def supports(translation):\n        return True\n\n    @cached_property\n    def storage(self):\n        storage = self.get_storage()\n        storage.setsourcelanguage(\n            self.project.source_language.code\n        )\n        storage.settargetlanguage(\n            self.language.code\n        )\n        return storage\n\n    def string_filter(self, text):\n        return text\n\n    def handle_plurals(self, plurals):\n        if len(plurals) == 1:\n            return self.string_filter(plurals[0])\n        return multistring(\n            [self.string_filter(plural) for plural in plurals]\n        )\n\n    def get_storage(self):\n        raise NotImplementedError()\n\n    def add(self, unit, word):\n        unit.target = word\n\n    def add_dictionary(self, word):\n        \"\"\"Add dictionary word\"\"\"\n        unit = self.storage.UnitClass(self.string_filter(word.source))\n        self.add(unit, self.string_filter(word.target))\n        self.storage.addunit(unit)\n\n    def add_units(self, units):\n        for unit in units.iterator():\n            self.add_unit(unit)\n\n    def add_unit(self, unit):\n        output = self.storage.UnitClass(\n            self.handle_plurals(unit.get_source_plurals())\n        )\n        self.add(output, self.handle_plurals(unit.get_target_plurals()))\n        # Location needs to be set prior to ID to avoid overwrite\n        # on some formats (eg. xliff)\n        for location in unit.location.split():\n            if location:\n                output.addlocation(location)\n\n        # Store context as context and ID\n        context = self.string_filter(unit.context)\n        if context:\n            output.setcontext(context)\n            if self.set_id:\n                output.setid(context)\n        elif self.set_id:\n            # Use checksum based ID on formats requiring it\n            output.setid(unit.checksum)\n\n        # Store note\n        note = self.string_filter(unit.comment)\n        if note:\n            output.addnote(note, origin='developer')\n        # In Weblate context\n        note = self.string_filter(unit.source_info.context)\n        if context:\n            output.addnote(note, origin='developer')\n        # Comments\n        for comment in unit.get_comments():\n            output.addnote(comment.comment, origin='translator')\n        # Suggestions\n        for suggestion in unit.suggestions:\n            output.addnote(\n                'Suggested in Weblate: {}'.format(suggestion.target),\n                origin='translator'\n            )\n\n        # Store flags\n        if unit.all_flags:\n            self.store_flags(output, unit.all_flags)\n\n        # Store fuzzy flag\n        if unit.fuzzy:\n            output.markfuzzy(True)\n\n        self.storage.addunit(output)\n\n    def get_response(self, filetemplate='{project}-{language}.{extension}'):\n        filename = filetemplate.format(\n            project=self.project.slug,\n            language=self.language.code,\n            extension=self.extension\n        )\n\n        response = HttpResponse(\n            content_type='{0}; charset=utf-8'.format(self.content_type)\n        )\n        response['Content-Disposition'] = 'attachment; filename={0}'.format(\n            filename\n        )\n\n        # Save to response\n        response.write(self.serialize())\n\n        return response\n\n    def serialize(self):\n        \"\"\"Return storage content\"\"\"\n        return TTKitFormat.serialize(self.storage)\n\n    def store_flags(self, output, flags):\n        return\n\n\n@register_exporter\nclass PoExporter(BaseExporter):\n    name = 'po'\n    content_type = 'text/x-po'\n    extension = 'po'\n    verbose = _('gettext PO')\n    _storage = pofile\n\n    def store_flags(self, output, flags):\n        for flag in flags.items():\n            output.settypecomment(flag)\n\n    def get_storage(self):\n        store = self._storage()\n        plural = self.plural\n\n        # Set po file header\n        store.updateheader(\n            add=True,\n            language=self.language.code,\n            x_generator='Weblate {0}'.format(weblate.VERSION),\n            project_id_version='{0} ({1})'.format(\n                self.language.name, self.project.name\n            ),\n            plural_forms=plural.plural_form,\n            language_team='{0} <{1}>'.format(\n                self.language.name,\n                self.url\n            )\n        )\n        return store\n\n\nclass XMLExporter(BaseExporter):\n    \"\"\"Wrapper for XML based exporters to strip control chars\"\"\"\n\n    def string_filter(self, text):\n        return text.translate(_CHARMAP)\n\n    def get_storage(self):\n        raise NotImplementedError()\n\n    def add(self, unit, word):\n        unit.settarget(word, self.language.code)\n\n\n@register_exporter\nclass PoXliffExporter(XMLExporter):\n    name = 'xliff'\n    content_type = 'application/x-xliff+xml'\n    extension = 'xlf'\n    set_id = True\n    verbose = _('XLIFF with gettext extensions')\n\n    def get_storage(self):\n        return PoXliffFile()\n\n    def store_flags(self, output, flags):\n        if flags.has_value('max-length'):\n            output.xmlelement.set(\"maxwidth\", str(flags.get_value('max-length')))\n\n        output.xmlelement.set(\"weblate-flags\", flags.format())\n\n\n@register_exporter\nclass XliffExporter(PoXliffExporter):\n    name = 'xliff11'\n    content_type = 'application/x-xliff+xml'\n    extension = 'xlf'\n    set_id = True\n    verbose = _('XLIFF 1.1')\n\n    def get_storage(self):\n        return xlifffile()\n\n\n@register_exporter\nclass TBXExporter(XMLExporter):\n    name = 'tbx'\n    content_type = 'application/x-tbx'\n    extension = 'tbx'\n    verbose = _('TBX')\n\n    def get_storage(self):\n        return tbxfile()\n\n\n@register_exporter\nclass TMXExporter(XMLExporter):\n    name = 'tmx'\n    content_type = 'application/x-tmx'\n    extension = 'tmx'\n    verbose = _('TMX')\n\n    def get_storage(self):\n        return tmxfile()\n\n\n@register_exporter\nclass MoExporter(PoExporter):\n    name = 'mo'\n    content_type = 'application/x-gettext-catalog'\n    extension = 'mo'\n    verbose = _('gettext MO')\n    _storage = mofile\n\n    def __init__(self, project=None, language=None, url=None,\n                 translation=None, fieldnames=None):\n        super(MoExporter, self).__init__(\n            project, language, url, translation, fieldnames\n        )\n        # Detect storage properties\n        self.monolingual = False\n        self.use_context = False\n        if translation:\n            self.monolingual = translation.component.has_template()\n            if self.monolingual:\n                unit = next(translation.store.translatable_units, None)\n                self.use_context = unit is not None and not unit.template.source\n\n    def store_flags(self, output, flags):\n        return\n\n    def add_unit(self, unit):\n        # We do not store not translated units\n        if not unit.translated:\n            return\n        # Parse properties from unit\n        if self.monolingual:\n            if self.use_context:\n                source = ''\n                context = unit.context\n            else:\n                source = unit.context\n                context = ''\n        else:\n            source = self.handle_plurals(unit.get_source_plurals())\n            context = unit.context\n        # Actually create the unit and set attributes\n        output = self.storage.UnitClass(source)\n        output.target = self.handle_plurals(unit.get_target_plurals())\n        if context:\n            # The setcontext doesn't work on mounit\n            output.msgctxt = [context]\n        # Add unit to the storage\n        self.storage.addunit(output)\n\n    @staticmethod\n    def supports(translation):\n        return translation.component.file_format == 'po'\n\n\n@register_exporter\nclass CSVExporter(BaseExporter):\n    name = 'csv'\n    content_type = 'text/csv'\n    extension = 'csv'\n    verbose = _('CSV')\n\n    def get_storage(self):\n        return csvfile(fieldnames=self.fieldnames)\n\n    def string_filter(self, text):\n        \"\"\"Avoid Excel interpreting text as formula.\n\n        This is really bad idea, implemented in Excel, as this change leads\n        to displaying additional ' in all other tools, but this seems to be\n        what most people have gotten used to. Hopefully these chars are not\n        widely used at first position of translatable strings, so that harm is\n        reduced.\n        \"\"\"\n        if text and text[0] in ('=', '+', '-', '@', '|', '%'):\n            return \"'{0}'\".format(text.replace('|', '\\\\|'))\n        return text\n\n\n@register_exporter\nclass XlsxExporter(BaseExporter):\n    name = 'xlsx'\n    content_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n    extension = 'xlsx'\n    verbose = _('Excel Open XML')\n\n    def get_storage(self):\n        return csvfile(fieldnames=self.fieldnames)\n\n    def serialize(self):\n        \"\"\"Return storage content\"\"\"\n        return XlsxFormat.serialize(self.storage)\n", "path": "weblate/formats/exporters.py"}]}