We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix properties roundtrip
After #3607 some changes need to be introduced in order to fix properties roundtrip. More details in https://github.com/translate/translate/pull/3607#issuecomment-291440437 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `translate/convert/po2prop.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright 2002-2006 Zuza Software Foundation
4 #
5 # This file is part of translate.
6 #
7 # translate 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 2 of the License, or
10 # (at your option) any later version.
11 #
12 # translate 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 <http://www.gnu.org/licenses/>.
19 
20 """Convert Gettext PO localization files to Java/Mozilla .properties files.
21 
22 See: http://docs.translatehouse.org/projects/translate-toolkit/en/latest/commands/prop2po.html
23 for examples and usage instructions.
24 """
25 
26 import warnings
27 
28 from translate.convert import accesskey, convert
29 from translate.misc import quote
30 from translate.storage import po, properties
31 
32 
33 eol = u"\n"
34 
35 
36 def applytranslation(key, propunit, inunit, mixedkeys):
37     """applies the translation for key in the po unit to the prop unit"""
38     # this converts the po-style string to a prop-style string
39     value = inunit.target
40     # handle mixed keys
41     for labelsuffix in properties.labelsuffixes:
42         if key.endswith(labelsuffix):
43             if key in mixedkeys:
44                 value, akey = accesskey.extract(value)
45                 break
46     else:
47         for akeysuffix in properties.accesskeysuffixes:
48             if key.endswith(akeysuffix):
49                 if key in mixedkeys:
50                     label, value = accesskey.extract(value)
51                     if not value:
52                         warnings.warn("Could not find accesskey for %s" % key)
53                         # Use the source language accesskey
54                         label, value = accesskey.extract(inunit.source)
55                     else:
56                         original = propunit.source
57                         # For the sake of diffs we keep the case of the
58                         # accesskey the same if we know the translation didn't
59                         # change. Casing matters in XUL.
60                         if value == propunit.source and original.lower() == value.lower():
61                             if original.isupper():
62                                 value = value.upper()
63                             elif original.islower():
64                                 value = value.lower()
65     return value
66 
67 
68 class reprop:
69 
70     def __init__(self, templatefile, inputstore, personality, encoding=None,
71                  remove_untranslated=False):
72         self.templatefile = templatefile
73         self.inputstore = inputstore
74         self.personality = properties.get_dialect(personality)
75         self.encoding = encoding
76         if self.encoding is None:
77             self.encoding = self.personality.default_encoding
78         self.remove_untranslated = remove_untranslated
79         self.mixer = accesskey.UnitMixer(properties.labelsuffixes,
80                                          properties.accesskeysuffixes)
81 
82     def convertstore(self, includefuzzy=False):
83         self.includefuzzy = includefuzzy
84         self.inmultilinemsgid = False
85         self.inecho = False
86         self.inputstore.makeindex()
87         if self.personality.name == "gaia":
88             self._explode_gaia_plurals()
89         outputlines = []
90         # Readlines doesn't work for UTF-16, we read() and splitlines(keepends) instead
91         content = self.templatefile.read().decode(self.encoding)
92         for line in content.splitlines(True):
93             outputstr = self.convertline(line)
94             outputlines.append(outputstr)
95         return u"".join(outputlines).encode(self.encoding)
96 
97     def _handle_accesskeys(self, inunit, currkey):
98         value = inunit.target
99         if self.personality.name == "mozilla":
100             keys = inunit.getlocations()
101             mixedkeys = self.mixer.match_entities(keys)
102             for key in keys:
103                 if key == currkey and key in self.inputstore.locationindex:
104                     propunit = self.inputstore.locationindex[key]  # find the prop
105                     value = applytranslation(key, propunit, inunit, mixedkeys)
106                     break
107 
108         return value
109 
110     def _explode_gaia_plurals(self):
111         """Explode the gaia plurals."""
112         from translate.lang import data
113         for unit in self.inputstore.units:
114             if not unit.hasplural():
115                 continue
116             if unit.isfuzzy() and not self.includefuzzy or not unit.istranslated():
117                 continue
118 
119             names = data.cldr_plural_categories
120             location = unit.getlocations()[0]
121             for category, text in zip(names, unit.target.strings):
122                 # TODO: for now we assume all forms are present. We need to
123                 # fill in the rest after mapping things to the proper CLDR names.
124                 if category == 'zero':
125                     # [zero] cases are translated as separate units
126                     continue
127                 new_unit = self.inputstore.addsourceunit(u"fish")  # not used
128                 new_location = '%s[%s]' % (location, category)
129                 new_unit.addlocation(new_location)
130                 new_unit.target = text
131                 self.inputstore.locationindex[new_location] = new_unit
132 
133             # We don't want the plural marker to be translated:
134             del self.inputstore.locationindex[location]
135 
136     def convertline(self, line):
137         returnline = u""
138         # handle multiline msgid if we're in one
139         if self.inmultilinemsgid:
140             msgid = quote.rstripeol(line).strip()
141             # see if there's more
142             self.inmultilinemsgid = (msgid[-1:] == '\\')
143             # if we're echoing...
144             if self.inecho:
145                 returnline = line
146         # otherwise, this could be a comment
147         elif line.strip()[:1] == '#':
148             returnline = quote.rstripeol(line) + eol
149         else:
150             line = quote.rstripeol(line)
151             delimiter_char, delimiter_pos = self.personality.find_delimiter(line)
152             if quote.rstripeol(line)[-1:] == '\\':
153                 self.inmultilinemsgid = True
154             if delimiter_pos == -1:
155                 key = self.personality.key_strip(line)
156                 delimiter = " %s " % self.personality.delimiters[0]
157             else:
158                 key = self.personality.key_strip(line[:delimiter_pos])
159                 # Calculate space around the equal sign
160                 prespace = line[line.find(' ', len(key)):delimiter_pos]
161                 postspacestart = len(line[delimiter_pos+1:])
162                 postspaceend = len(line[delimiter_pos+1:].lstrip())
163                 postspace = line[delimiter_pos+1:delimiter_pos+(postspacestart-postspaceend)+1]
164                 delimiter = prespace + delimiter_char + postspace
165             if key in self.inputstore.locationindex:
166                 unit = self.inputstore.locationindex[key]
167                 if unit is None or not unit.istranslated() and bool(unit.source) and self.remove_untranslated:
168                     returnline = u""
169                     self.inecho = False
170                 else:
171                     if unit.isfuzzy() and not self.includefuzzy or len(unit.target) == 0:
172                         value = unit.source
173                     else:
174                         value = self._handle_accesskeys(unit, key)
175                     self.inecho = False
176                     assert isinstance(value, str)
177                     returnline = "%(key)s%(del)s%(value)s%(term)s%(eol)s" % {
178                         "key": "%s%s%s" % (self.personality.key_wrap_char,
179                                            key,
180                                            self.personality.key_wrap_char),
181                         "del": delimiter,
182                         "value": "%s%s%s" % (self.personality.value_wrap_char,
183                                              self.personality.encode(value),
184                                              self.personality.value_wrap_char),
185                         "term": self.personality.pair_terminator,
186                         "eol": eol,
187                     }
188             else:
189                 self.inecho = True
190                 returnline = line + eol
191         assert isinstance(returnline, str)
192         return returnline
193 
194 
195 def convertstrings(inputfile, outputfile, templatefile, personality="strings",
196                    includefuzzy=False, encoding=None, outputthreshold=None,
197                    remove_untranslated=False):
198     """.strings specific convertor function"""
199     return convertprop(inputfile, outputfile, templatefile,
200                        personality="strings", includefuzzy=includefuzzy,
201                        encoding=encoding, outputthreshold=outputthreshold,
202                        remove_untranslated=remove_untranslated)
203 
204 
205 def convertmozillaprop(inputfile, outputfile, templatefile,
206                        includefuzzy=False, remove_untranslated=False,
207                        outputthreshold=None):
208     """Mozilla specific convertor function"""
209     return convertprop(inputfile, outputfile, templatefile,
210                        personality="mozilla", includefuzzy=includefuzzy,
211                        remove_untranslated=remove_untranslated,
212                        outputthreshold=outputthreshold)
213 
214 
215 def convertprop(inputfile, outputfile, templatefile, personality="java",
216                 includefuzzy=False, encoding=None, remove_untranslated=False,
217                 outputthreshold=None):
218     inputstore = po.pofile(inputfile)
219 
220     if not convert.should_output_store(inputstore, outputthreshold):
221         return False
222 
223     if templatefile is None:
224         raise ValueError("must have template file for properties files")
225         # convertor = po2prop()
226     else:
227         convertor = reprop(templatefile, inputstore, personality, encoding,
228                            remove_untranslated)
229     outputprop = convertor.convertstore(includefuzzy)
230     outputfile.write(outputprop)
231     return True
232 
233 
234 formats = {
235     ("po", "properties"): ("properties", convertprop),
236     ("po", "lang"): ("lang", convertprop),
237     ("po", "strings"): ("strings", convertstrings),
238 }
239 
240 
241 def main(argv=None):
242     # handle command line options
243     parser = convert.ConvertOptionParser(formats, usetemplates=True,
244                                          description=__doc__)
245     parser.add_option(
246         "", "--personality", dest="personality",
247         default=properties.default_dialect, type="choice",
248         choices=list(properties.dialects),
249         help="override the input file format: %s (for .properties files, default: %s)" % (
250             ", ".join(properties.dialects), properties.default_dialect),
251         metavar="TYPE")
252     parser.add_option(
253         "", "--encoding", dest="encoding", default=None,
254         help="override the encoding set by the personality",
255         metavar="ENCODING")
256     parser.add_threshold_option()
257     parser.add_fuzzy_option()
258     parser.add_remove_untranslated_option()
259     parser.passthrough.append("personality")
260     parser.passthrough.append("encoding")
261     parser.run(argv)
262 
263 
264 if __name__ == '__main__':
265     main()
266
```
--- 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/translate/convert/po2prop.py b/translate/convert/po2prop.py
--- a/translate/convert/po2prop.py
+++ b/translate/convert/po2prop.py
@@ -178,7 +178,7 @@
                         "key": "%s%s%s" % (self.personality.key_wrap_char,
                                            key,
                                            self.personality.key_wrap_char),
-                        "del": delimiter,
+                        "del": delimiter if delimiter_pos != -1 or value else "",
                         "value": "%s%s%s" % (self.personality.value_wrap_char,
                                              self.personality.encode(value),
                                              self.personality.value_wrap_char),

{"golden_diff": "diff --git a/translate/convert/po2prop.py b/translate/convert/po2prop.py\n--- a/translate/convert/po2prop.py\n+++ b/translate/convert/po2prop.py\n@@ -178,7 +178,7 @@\n                         \"key\": \"%s%s%s\" % (self.personality.key_wrap_char,\n                                            key,\n                                            self.personality.key_wrap_char),\n-                        \"del\": delimiter,\n+                        \"del\": delimiter if delimiter_pos != -1 or value else \"\",\n                         \"value\": \"%s%s%s\" % (self.personality.value_wrap_char,\n                                              self.personality.encode(value),\n                                              self.personality.value_wrap_char),\n", "issue": "Fix properties roundtrip\nAfter #3607 some changes need to be introduced in order to fix properties roundtrip. More details in https://github.com/translate/translate/pull/3607#issuecomment-291440437 \n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright 2002-2006 Zuza Software Foundation\n#\n# This file is part of translate.\n#\n# translate 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 2 of the License, or\n# (at your option) any later version.\n#\n# translate 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 <http://www.gnu.org/licenses/>.\n\n\"\"\"Convert Gettext PO localization files to Java/Mozilla .properties files.\n\nSee: http://docs.translatehouse.org/projects/translate-toolkit/en/latest/commands/prop2po.html\nfor examples and usage instructions.\n\"\"\"\n\nimport warnings\n\nfrom translate.convert import accesskey, convert\nfrom translate.misc import quote\nfrom translate.storage import po, properties\n\n\neol = u\"\\n\"\n\n\ndef applytranslation(key, propunit, inunit, mixedkeys):\n    \"\"\"applies the translation for key in the po unit to the prop unit\"\"\"\n    # this converts the po-style string to a prop-style string\n    value = inunit.target\n    # handle mixed keys\n    for labelsuffix in properties.labelsuffixes:\n        if key.endswith(labelsuffix):\n            if key in mixedkeys:\n                value, akey = accesskey.extract(value)\n                break\n    else:\n        for akeysuffix in properties.accesskeysuffixes:\n            if key.endswith(akeysuffix):\n                if key in mixedkeys:\n                    label, value = accesskey.extract(value)\n                    if not value:\n                        warnings.warn(\"Could not find accesskey for %s\" % key)\n                        # Use the source language accesskey\n                        label, value = accesskey.extract(inunit.source)\n                    else:\n                        original = propunit.source\n                        # For the sake of diffs we keep the case of the\n                        # accesskey the same if we know the translation didn't\n                        # change. Casing matters in XUL.\n                        if value == propunit.source and original.lower() == value.lower():\n                            if original.isupper():\n                                value = value.upper()\n                            elif original.islower():\n                                value = value.lower()\n    return value\n\n\nclass reprop:\n\n    def __init__(self, templatefile, inputstore, personality, encoding=None,\n                 remove_untranslated=False):\n        self.templatefile = templatefile\n        self.inputstore = inputstore\n        self.personality = properties.get_dialect(personality)\n        self.encoding = encoding\n        if self.encoding is None:\n            self.encoding = self.personality.default_encoding\n        self.remove_untranslated = remove_untranslated\n        self.mixer = accesskey.UnitMixer(properties.labelsuffixes,\n                                         properties.accesskeysuffixes)\n\n    def convertstore(self, includefuzzy=False):\n        self.includefuzzy = includefuzzy\n        self.inmultilinemsgid = False\n        self.inecho = False\n        self.inputstore.makeindex()\n        if self.personality.name == \"gaia\":\n            self._explode_gaia_plurals()\n        outputlines = []\n        # Readlines doesn't work for UTF-16, we read() and splitlines(keepends) instead\n        content = self.templatefile.read().decode(self.encoding)\n        for line in content.splitlines(True):\n            outputstr = self.convertline(line)\n            outputlines.append(outputstr)\n        return u\"\".join(outputlines).encode(self.encoding)\n\n    def _handle_accesskeys(self, inunit, currkey):\n        value = inunit.target\n        if self.personality.name == \"mozilla\":\n            keys = inunit.getlocations()\n            mixedkeys = self.mixer.match_entities(keys)\n            for key in keys:\n                if key == currkey and key in self.inputstore.locationindex:\n                    propunit = self.inputstore.locationindex[key]  # find the prop\n                    value = applytranslation(key, propunit, inunit, mixedkeys)\n                    break\n\n        return value\n\n    def _explode_gaia_plurals(self):\n        \"\"\"Explode the gaia plurals.\"\"\"\n        from translate.lang import data\n        for unit in self.inputstore.units:\n            if not unit.hasplural():\n                continue\n            if unit.isfuzzy() and not self.includefuzzy or not unit.istranslated():\n                continue\n\n            names = data.cldr_plural_categories\n            location = unit.getlocations()[0]\n            for category, text in zip(names, unit.target.strings):\n                # TODO: for now we assume all forms are present. We need to\n                # fill in the rest after mapping things to the proper CLDR names.\n                if category == 'zero':\n                    # [zero] cases are translated as separate units\n                    continue\n                new_unit = self.inputstore.addsourceunit(u\"fish\")  # not used\n                new_location = '%s[%s]' % (location, category)\n                new_unit.addlocation(new_location)\n                new_unit.target = text\n                self.inputstore.locationindex[new_location] = new_unit\n\n            # We don't want the plural marker to be translated:\n            del self.inputstore.locationindex[location]\n\n    def convertline(self, line):\n        returnline = u\"\"\n        # handle multiline msgid if we're in one\n        if self.inmultilinemsgid:\n            msgid = quote.rstripeol(line).strip()\n            # see if there's more\n            self.inmultilinemsgid = (msgid[-1:] == '\\\\')\n            # if we're echoing...\n            if self.inecho:\n                returnline = line\n        # otherwise, this could be a comment\n        elif line.strip()[:1] == '#':\n            returnline = quote.rstripeol(line) + eol\n        else:\n            line = quote.rstripeol(line)\n            delimiter_char, delimiter_pos = self.personality.find_delimiter(line)\n            if quote.rstripeol(line)[-1:] == '\\\\':\n                self.inmultilinemsgid = True\n            if delimiter_pos == -1:\n                key = self.personality.key_strip(line)\n                delimiter = \" %s \" % self.personality.delimiters[0]\n            else:\n                key = self.personality.key_strip(line[:delimiter_pos])\n                # Calculate space around the equal sign\n                prespace = line[line.find(' ', len(key)):delimiter_pos]\n                postspacestart = len(line[delimiter_pos+1:])\n                postspaceend = len(line[delimiter_pos+1:].lstrip())\n                postspace = line[delimiter_pos+1:delimiter_pos+(postspacestart-postspaceend)+1]\n                delimiter = prespace + delimiter_char + postspace\n            if key in self.inputstore.locationindex:\n                unit = self.inputstore.locationindex[key]\n                if unit is None or not unit.istranslated() and bool(unit.source) and self.remove_untranslated:\n                    returnline = u\"\"\n                    self.inecho = False\n                else:\n                    if unit.isfuzzy() and not self.includefuzzy or len(unit.target) == 0:\n                        value = unit.source\n                    else:\n                        value = self._handle_accesskeys(unit, key)\n                    self.inecho = False\n                    assert isinstance(value, str)\n                    returnline = \"%(key)s%(del)s%(value)s%(term)s%(eol)s\" % {\n                        \"key\": \"%s%s%s\" % (self.personality.key_wrap_char,\n                                           key,\n                                           self.personality.key_wrap_char),\n                        \"del\": delimiter,\n                        \"value\": \"%s%s%s\" % (self.personality.value_wrap_char,\n                                             self.personality.encode(value),\n                                             self.personality.value_wrap_char),\n                        \"term\": self.personality.pair_terminator,\n                        \"eol\": eol,\n                    }\n            else:\n                self.inecho = True\n                returnline = line + eol\n        assert isinstance(returnline, str)\n        return returnline\n\n\ndef convertstrings(inputfile, outputfile, templatefile, personality=\"strings\",\n                   includefuzzy=False, encoding=None, outputthreshold=None,\n                   remove_untranslated=False):\n    \"\"\".strings specific convertor function\"\"\"\n    return convertprop(inputfile, outputfile, templatefile,\n                       personality=\"strings\", includefuzzy=includefuzzy,\n                       encoding=encoding, outputthreshold=outputthreshold,\n                       remove_untranslated=remove_untranslated)\n\n\ndef convertmozillaprop(inputfile, outputfile, templatefile,\n                       includefuzzy=False, remove_untranslated=False,\n                       outputthreshold=None):\n    \"\"\"Mozilla specific convertor function\"\"\"\n    return convertprop(inputfile, outputfile, templatefile,\n                       personality=\"mozilla\", includefuzzy=includefuzzy,\n                       remove_untranslated=remove_untranslated,\n                       outputthreshold=outputthreshold)\n\n\ndef convertprop(inputfile, outputfile, templatefile, personality=\"java\",\n                includefuzzy=False, encoding=None, remove_untranslated=False,\n                outputthreshold=None):\n    inputstore = po.pofile(inputfile)\n\n    if not convert.should_output_store(inputstore, outputthreshold):\n        return False\n\n    if templatefile is None:\n        raise ValueError(\"must have template file for properties files\")\n        # convertor = po2prop()\n    else:\n        convertor = reprop(templatefile, inputstore, personality, encoding,\n                           remove_untranslated)\n    outputprop = convertor.convertstore(includefuzzy)\n    outputfile.write(outputprop)\n    return True\n\n\nformats = {\n    (\"po\", \"properties\"): (\"properties\", convertprop),\n    (\"po\", \"lang\"): (\"lang\", convertprop),\n    (\"po\", \"strings\"): (\"strings\", convertstrings),\n}\n\n\ndef main(argv=None):\n    # handle command line options\n    parser = convert.ConvertOptionParser(formats, usetemplates=True,\n                                         description=__doc__)\n    parser.add_option(\n        \"\", \"--personality\", dest=\"personality\",\n        default=properties.default_dialect, type=\"choice\",\n        choices=list(properties.dialects),\n        help=\"override the input file format: %s (for .properties files, default: %s)\" % (\n            \", \".join(properties.dialects), properties.default_dialect),\n        metavar=\"TYPE\")\n    parser.add_option(\n        \"\", \"--encoding\", dest=\"encoding\", default=None,\n        help=\"override the encoding set by the personality\",\n        metavar=\"ENCODING\")\n    parser.add_threshold_option()\n    parser.add_fuzzy_option()\n    parser.add_remove_untranslated_option()\n    parser.passthrough.append(\"personality\")\n    parser.passthrough.append(\"encoding\")\n    parser.run(argv)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "translate/convert/po2prop.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n#\n# Copyright 2002-2006 Zuza Software Foundation\n#\n# This file is part of translate.\n#\n# translate 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 2 of the License, or\n# (at your option) any later version.\n#\n# translate 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 <http://www.gnu.org/licenses/>.\n\n\"\"\"Convert Gettext PO localization files to Java/Mozilla .properties files.\n\nSee: http://docs.translatehouse.org/projects/translate-toolkit/en/latest/commands/prop2po.html\nfor examples and usage instructions.\n\"\"\"\n\nimport warnings\n\nfrom translate.convert import accesskey, convert\nfrom translate.misc import quote\nfrom translate.storage import po, properties\n\n\neol = u\"\\n\"\n\n\ndef applytranslation(key, propunit, inunit, mixedkeys):\n    \"\"\"applies the translation for key in the po unit to the prop unit\"\"\"\n    # this converts the po-style string to a prop-style string\n    value = inunit.target\n    # handle mixed keys\n    for labelsuffix in properties.labelsuffixes:\n        if key.endswith(labelsuffix):\n            if key in mixedkeys:\n                value, akey = accesskey.extract(value)\n                break\n    else:\n        for akeysuffix in properties.accesskeysuffixes:\n            if key.endswith(akeysuffix):\n                if key in mixedkeys:\n                    label, value = accesskey.extract(value)\n                    if not value:\n                        warnings.warn(\"Could not find accesskey for %s\" % key)\n                        # Use the source language accesskey\n                        label, value = accesskey.extract(inunit.source)\n                    else:\n                        original = propunit.source\n                        # For the sake of diffs we keep the case of the\n                        # accesskey the same if we know the translation didn't\n                        # change. Casing matters in XUL.\n                        if value == propunit.source and original.lower() == value.lower():\n                            if original.isupper():\n                                value = value.upper()\n                            elif original.islower():\n                                value = value.lower()\n    return value\n\n\nclass reprop:\n\n    def __init__(self, templatefile, inputstore, personality, encoding=None,\n                 remove_untranslated=False):\n        self.templatefile = templatefile\n        self.inputstore = inputstore\n        self.personality = properties.get_dialect(personality)\n        self.encoding = encoding\n        if self.encoding is None:\n            self.encoding = self.personality.default_encoding\n        self.remove_untranslated = remove_untranslated\n        self.mixer = accesskey.UnitMixer(properties.labelsuffixes,\n                                         properties.accesskeysuffixes)\n\n    def convertstore(self, includefuzzy=False):\n        self.includefuzzy = includefuzzy\n        self.inmultilinemsgid = False\n        self.inecho = False\n        self.inputstore.makeindex()\n        if self.personality.name == \"gaia\":\n            self._explode_gaia_plurals()\n        outputlines = []\n        # Readlines doesn't work for UTF-16, we read() and splitlines(keepends) instead\n        content = self.templatefile.read().decode(self.encoding)\n        for line in content.splitlines(True):\n            outputstr = self.convertline(line)\n            outputlines.append(outputstr)\n        return u\"\".join(outputlines).encode(self.encoding)\n\n    def _handle_accesskeys(self, inunit, currkey):\n        value = inunit.target\n        if self.personality.name == \"mozilla\":\n            keys = inunit.getlocations()\n            mixedkeys = self.mixer.match_entities(keys)\n            for key in keys:\n                if key == currkey and key in self.inputstore.locationindex:\n                    propunit = self.inputstore.locationindex[key]  # find the prop\n                    value = applytranslation(key, propunit, inunit, mixedkeys)\n                    break\n\n        return value\n\n    def _explode_gaia_plurals(self):\n        \"\"\"Explode the gaia plurals.\"\"\"\n        from translate.lang import data\n        for unit in self.inputstore.units:\n            if not unit.hasplural():\n                continue\n            if unit.isfuzzy() and not self.includefuzzy or not unit.istranslated():\n                continue\n\n            names = data.cldr_plural_categories\n            location = unit.getlocations()[0]\n            for category, text in zip(names, unit.target.strings):\n                # TODO: for now we assume all forms are present. We need to\n                # fill in the rest after mapping things to the proper CLDR names.\n                if category == 'zero':\n                    # [zero] cases are translated as separate units\n                    continue\n                new_unit = self.inputstore.addsourceunit(u\"fish\")  # not used\n                new_location = '%s[%s]' % (location, category)\n                new_unit.addlocation(new_location)\n                new_unit.target = text\n                self.inputstore.locationindex[new_location] = new_unit\n\n            # We don't want the plural marker to be translated:\n            del self.inputstore.locationindex[location]\n\n    def convertline(self, line):\n        returnline = u\"\"\n        # handle multiline msgid if we're in one\n        if self.inmultilinemsgid:\n            msgid = quote.rstripeol(line).strip()\n            # see if there's more\n            self.inmultilinemsgid = (msgid[-1:] == '\\\\')\n            # if we're echoing...\n            if self.inecho:\n                returnline = line\n        # otherwise, this could be a comment\n        elif line.strip()[:1] == '#':\n            returnline = quote.rstripeol(line) + eol\n        else:\n            line = quote.rstripeol(line)\n            delimiter_char, delimiter_pos = self.personality.find_delimiter(line)\n            if quote.rstripeol(line)[-1:] == '\\\\':\n                self.inmultilinemsgid = True\n            if delimiter_pos == -1:\n                key = self.personality.key_strip(line)\n                delimiter = \" %s \" % self.personality.delimiters[0]\n            else:\n                key = self.personality.key_strip(line[:delimiter_pos])\n                # Calculate space around the equal sign\n                prespace = line[line.find(' ', len(key)):delimiter_pos]\n                postspacestart = len(line[delimiter_pos+1:])\n                postspaceend = len(line[delimiter_pos+1:].lstrip())\n                postspace = line[delimiter_pos+1:delimiter_pos+(postspacestart-postspaceend)+1]\n                delimiter = prespace + delimiter_char + postspace\n            if key in self.inputstore.locationindex:\n                unit = self.inputstore.locationindex[key]\n                if unit is None or not unit.istranslated() and bool(unit.source) and self.remove_untranslated:\n                    returnline = u\"\"\n                    self.inecho = False\n                else:\n                    if unit.isfuzzy() and not self.includefuzzy or len(unit.target) == 0:\n                        value = unit.source\n                    else:\n                        value = self._handle_accesskeys(unit, key)\n                    self.inecho = False\n                    assert isinstance(value, str)\n                    returnline = \"%(key)s%(del)s%(value)s%(term)s%(eol)s\" % {\n                        \"key\": \"%s%s%s\" % (self.personality.key_wrap_char,\n                                           key,\n                                           self.personality.key_wrap_char),\n                        \"del\": delimiter if delimiter_pos != -1 or value else \"\",\n                        \"value\": \"%s%s%s\" % (self.personality.value_wrap_char,\n                                             self.personality.encode(value),\n                                             self.personality.value_wrap_char),\n                        \"term\": self.personality.pair_terminator,\n                        \"eol\": eol,\n                    }\n            else:\n                self.inecho = True\n                returnline = line + eol\n        assert isinstance(returnline, str)\n        return returnline\n\n\ndef convertstrings(inputfile, outputfile, templatefile, personality=\"strings\",\n                   includefuzzy=False, encoding=None, outputthreshold=None,\n                   remove_untranslated=False):\n    \"\"\".strings specific convertor function\"\"\"\n    return convertprop(inputfile, outputfile, templatefile,\n                       personality=\"strings\", includefuzzy=includefuzzy,\n                       encoding=encoding, outputthreshold=outputthreshold,\n                       remove_untranslated=remove_untranslated)\n\n\ndef convertmozillaprop(inputfile, outputfile, templatefile,\n                       includefuzzy=False, remove_untranslated=False,\n                       outputthreshold=None):\n    \"\"\"Mozilla specific convertor function\"\"\"\n    return convertprop(inputfile, outputfile, templatefile,\n                       personality=\"mozilla\", includefuzzy=includefuzzy,\n                       remove_untranslated=remove_untranslated,\n                       outputthreshold=outputthreshold)\n\n\ndef convertprop(inputfile, outputfile, templatefile, personality=\"java\",\n                includefuzzy=False, encoding=None, remove_untranslated=False,\n                outputthreshold=None):\n    inputstore = po.pofile(inputfile)\n\n    if not convert.should_output_store(inputstore, outputthreshold):\n        return False\n\n    if templatefile is None:\n        raise ValueError(\"must have template file for properties files\")\n        # convertor = po2prop()\n    else:\n        convertor = reprop(templatefile, inputstore, personality, encoding,\n                           remove_untranslated)\n    outputprop = convertor.convertstore(includefuzzy)\n    outputfile.write(outputprop)\n    return True\n\n\nformats = {\n    (\"po\", \"properties\"): (\"properties\", convertprop),\n    (\"po\", \"lang\"): (\"lang\", convertprop),\n    (\"po\", \"strings\"): (\"strings\", convertstrings),\n}\n\n\ndef main(argv=None):\n    # handle command line options\n    parser = convert.ConvertOptionParser(formats, usetemplates=True,\n                                         description=__doc__)\n    parser.add_option(\n        \"\", \"--personality\", dest=\"personality\",\n        default=properties.default_dialect, type=\"choice\",\n        choices=list(properties.dialects),\n        help=\"override the input file format: %s (for .properties files, default: %s)\" % (\n            \", \".join(properties.dialects), properties.default_dialect),\n        metavar=\"TYPE\")\n    parser.add_option(\n        \"\", \"--encoding\", dest=\"encoding\", default=None,\n        help=\"override the encoding set by the personality\",\n        metavar=\"ENCODING\")\n    parser.add_threshold_option()\n    parser.add_fuzzy_option()\n    parser.add_remove_untranslated_option()\n    parser.passthrough.append(\"personality\")\n    parser.passthrough.append(\"encoding\")\n    parser.run(argv)\n\n\nif __name__ == '__main__':\n    main()\n", "path": "translate/convert/po2prop.py"}]}