Repository: python-babel/babel
Pull request #279 (2015-11-04 18:14:56)

Allow file locations without line numbers.
I'm proposing that locations without line numbers should work. Currently they are just swallowed. Other tools like msgmerge from gettext keep them as they are. Locations without line numbers are useful to reduce the number of differences when templates and code are edited without changing any of the translations.
The current implementation might break backward compatibility for other code that uses pofile from Babel.
Another possible solution would be to just keep unparsed locations as regular comments, but that is quite a bit more complex to handle correctly in my opinion.


Patch:
diff --git a/babel/messages/pofile.py b/babel/messages/pofile.py
index 3d7dc32..41fad0a 100644
--- a/babel/messages/pofile.py
+++ b/babel/messages/pofile.py
@@ -218,6 +218,8 @@ def read_po(fileobj, locale=None, domain=None, ignore_obsolete=False, charset=No
                         except ValueError:
                             continue
                         locations.append((location[:pos], lineno))
+                    else:
+                        locations.append((location, None))
             elif line[1:].startswith(','):
                 for flag in line[2:].lstrip().split(','):
                     flags.append(flag.strip())
@@ -449,9 +451,13 @@ def write_po(fileobj, catalog, width=76, no_location=False, omit_header=False,
             _write_comment(comment, prefix='.')
 
         if not no_location:
-            locs = u' '.join([u'%s:%d' % (filename.replace(os.sep, '/'), lineno)
-                              for filename, lineno in message.locations])
-            _write_comment(locs, prefix=':')
+            locs = []
+            for filename, lineno in message.locations:
+                if lineno:
+                    locs.append(u'%s:%d' % (filename.replace(os.sep, '/'), lineno))
+                else:
+                    locs.append(u'%s' % filename.replace(os.sep, '/'))
+            _write_comment(' '.join(locs), prefix=':')
         if message.flags:
             _write('#%s\n' % ', '.join([''] + sorted(message.flags)))
 


Test patch:
diff --git a/tests/messages/test_pofile.py b/tests/messages/test_pofile.py
index 3bb0557..4e991c6 100644
--- a/tests/messages/test_pofile.py
+++ b/tests/messages/test_pofile.py
@@ -513,6 +513,21 @@ msgstr[0] "Voh"
 msgstr[1] "Voeh"''' in value
         assert value.find(b'msgid ""') < value.find(b'msgid "bar"') < value.find(b'msgid "foo"')
 
+    def test_file_with_no_lineno(self):
+        catalog = Catalog()
+        catalog.add(u'bar', locations=[('utils.py', None)],
+                    user_comments=['Comment About `bar` with',
+                                   'multiple lines.'])
+        buf = BytesIO()
+        pofile.write_po(buf, catalog, sort_output=True)
+        value = buf.getvalue().strip()
+        assert b'''\
+# Comment About `bar` with
+# multiple lines.
+#: utils.py
+msgid "bar"
+msgstr ""''' in value
+
     def test_silent_location_fallback(self):
         buf = BytesIO(b'''\
 #: broken_file.py
@@ -523,7 +538,7 @@ msgstr ""
 msgid "broken line number"
 msgstr ""''')
         catalog = pofile.read_po(buf)
-        self.assertEqual(catalog['missing line number'].locations, [])
+        self.assertEqual(catalog['missing line number'].locations, [(u'broken_file.py', None)])
         self.assertEqual(catalog['broken line number'].locations, [])