Commits

Konstantine Rybnikov committed eabb0b9

get much more functionality. compare of pofiles with modified entries not working, tests added

Comments (0)

Files changed (6)

fat_po_editor/diff_util.py

+# -*- coding: utf-8 -*-
+
+from polib import POEntry
+
+from fat_po_editor.util import isiterable
+from fat_po_editor import _
+from fat_po_editor.util import StringBuilder
+
+MAXLEN = 80
+
+class PODiff(object):
+    """This class implements storage of diff data between two po-files.
+
+    It:
+    
+    - knows which phrases were added, removed or changed.
+    - can display them in various formats.
+
+    Entries of diff should be in :class:`~polib.POEntry` format.
+
+    :internal:
+    """
+
+    def __init__(self):
+        self.entries_add = []
+        self.entries_rm = []
+        self.entries_modified = []
+
+        self.operation_to_storage_map = {
+            'add': self.entries_add,
+            'rm': self.entries_rm,
+            'modify': self.entries_modified
+        }
+        self.operation_types = self.operation_to_storage_map.keys()
+    
+    def append_entry(self, operation_type, entry):
+        """
+        ``operation_type``
+            one of ['add', 'rm', 'modify']
+        """
+        assert operation_type in self.operation_types
+        if operation_type == 'modify':
+            assert isiterable(entry)
+            entry = list(entry)
+            assert len(entry) == 2
+        else:
+            assert isinstance(entry, POEntry)
+        storage = self.operation_to_storage_map[operation_type]
+        storage.append(entry)
+
+    def get_entries(self, operation_type):
+        assert operation_type in self.operation_types
+        return self.operation_to_storage_map[operation_type]
+
+    def __repr__(self):
+        rv = StringBuilder(unicode(super(PODiff, self).__repr__()))
+        rv += u' (%(add_num)s add, %(rm_num)s rm, %(modify_num)s modify)' \
+            % dict(add_num=len(self.operation_to_storage_map['add']),
+                   rm_num=len(self.operation_to_storage_map['rm']),
+                   modify_num=len(self.operation_to_storage_map['modify']))
+        return unicode(rv)
+
+    def __unicode__(self):
+        """Gives a nice text representation of what happened."""
+        def esc_chunk(s, maxlen):
+            """Escapes chunk's ``\\n`` symbols etc. and returns a chunk
+            of that.
+            """
+            from polib import escape
+            
+            def chunk(s, maxlen):
+                if len(s) > maxlen - 3:
+                    return u"".join([s[:maxlen - 3], '...'])
+                return s
+
+            return chunk(escape(s), maxlen)
+        
+        rv = StringBuilder()
+        
+        list_prefix = u"    * "
+        if len(self.operation_to_storage_map['add']):
+            rv += _(u"Added phrases:\n")
+            for item in self.operation_to_storage_map['add']:
+                rv += u"%s%s\n" % (list_prefix,
+                                   esc_chunk(item.msgid,
+                                             MAXLEN - len(list_prefix)))
+        if len(self.operation_to_storage_map['rm']):
+            rv += _(u"Removed phrases:\n")
+            for item in self.operation_to_storage_map['rm']:
+                rv += u"%s%s\n" % (list_prefix,
+                                   esc_chunk(item.msgid,
+                                             MAXLEN - len(list_prefix)))
+        if len(self.operation_to_storage_map['modify']):
+            rv += _(u"Modified translation of phrases:\n")
+            for item in self.operation_to_storage_map['modify']:
+                rv += u"%s%s\n" % (list_prefix,
+                                   esc_chunk(item[0].msgid,
+                                             MAXLEN - len(list_prefix)))
+        return unicode(rv)
+    
+    def full_represent(self):
+        """Full represent should guaranty that it represent all the information
+        needed to fully describe PODiff. Used to make sure that two PODiff's are
+        equal.
+        """
+        def prefix_lines(prefix, text):
+            return u"\n".join(u"".join([prefix, line])
+                              for line in text.splitlines())
+        
+        rv = StringBuilder()
+        for item in self.operation_to_storage_map['add']:
+            rv += prefix_lines('+ ', unicode(item))
+            rv += u'\n'
+        for item in self.operation_to_storage_map['rm']:
+            rv += prefix_lines('- ', unicode(item))
+            rv += u'\n'
+        for item in self.operation_to_storage_map['modify']:
+            rv += prefix_lines('+ ', unicode(item[0]))
+            rv += u'\n'
+            rv += prefix_lines('- ', unicode(item[1]))
+            rv += u'\n'
+        return unicode(rv)
+
+    def __eq__(self, other):
+        if not isinstance(other, PODiff):
+            return False
+        return self.full_represent() == other.full_represent()
+
+def compare_two_pofiles(pofile1, pofile2):
+    """Returns :class:`~PODiff` object that represents difference between two po
+    files.
+
+    ``pofile1``, ``pofile2``
+        :class:`~polib.POFile` instances.
+    """
+
+    rv = PODiff()
+    
+    pofile1_msgid_to_poentry_map = dict(map(lambda entry: (entry.msgid, entry), pofile1))
+    pofile2_msgid_to_poentry_map = dict(map(lambda entry: (entry.msgid, entry), pofile2))
+    
+    for item in pofile2:
+        if item.msgid not in pofile1_msgid_to_poentry_map:
+            rv.append_entry('add', item)
+        else:
+            old_item = pofile1_msgid_to_poentry_map[item.msgid]
+            if item != old_item:
+                rv.append_entry('modify', [old_item, item])
+    
+    for item in pofile1:
+        if item.msgid not in pofile2_msgid_to_poentry_map:
+            rv.append_entry('rm', item)
+
+    return rv

fat_po_editor/diffutil.py

-# -*- coding: utf-8 -*-
-
-from polib import POEntry
-
-from fat_po_editor.util import isiterable
-from fat_po_editor import _
-from fat_po_editor.util import StringBuilder
-
-MAXLEN = 80
-
-class PoDiff(object):
-    """This class implements storage of diff data between two po-files.
-
-    It:
-    
-    - knows which phrases were added, removed or changed.
-    - can display them in various formats.
-
-    Entries of diff should be in :class:`~polib.POEntry` format.
-
-    :internal:
-    """
-
-    def __init__(self):
-        self.entries_add = []
-        self.entries_rm = []
-        self.entries_modified = []
-
-        self.operation_to_storage_map = {
-            'add': self.entries_add,
-            'rm': self.entries_rm,
-            'modify': self.entries_modified
-        }
-        self.operation_types = self.operation_to_storage_map.keys()
-    
-    def append_entry(self, operation_type, entry):
-        """
-        ``operation_type``
-            one of ['add', 'rm', 'modify']
-        """
-        assert operation_type in self.operation_types
-        if operation_type == 'modify':
-            assert isiterable(entry)
-            entry = list(entry)
-            assert len(entry) == 2
-        else:
-            assert isinstance(entry, POEntry)
-        storage = self.operation_to_storage_map[operation_type]
-        storage.append(entry)
-
-    def get_entries(self, operation_type):
-        assert operation_type in self.operation_types
-        return self.operation_to_storage_map[operation_type]
-
-    def __repr__(self):
-        rv = StringBuilder(unicode(super(PoDiff, self).__repr__()))
-        rv += u' (%(add_num)s add, %(rm_num)s rm, %(modify_num)s modify)' \
-            % dict(add_num=len(self.operation_to_storage_map['add']),
-                   rm_num=len(self.operation_to_storage_map['rm']),
-                   modify_num=len(self.operation_to_storage_map['modify']))
-        return unicode(rv)
-
-    def __unicode__(self):
-        """Gives a nice text representation of what happened."""
-        def esc_chunk(s, maxlen):
-            """Escapes chunk's ``\\n`` symbols etc. and returns a chunk
-            of that.
-            """
-            from polib import escape
-            
-            def chunk(s, maxlen):
-                if len(s) > maxlen - 3:
-                    return u"".join([s[:maxlen - 3], '...'])
-                return s
-
-            return chunk(escape(s), maxlen)
-        
-        rv = StringBuilder()
-        
-        list_prefix = u"    * "
-        if len(self.operation_to_storage_map['add']):
-            rv += _(u"Added phrases:\n")
-            for item in self.operation_to_storage_map['add']:
-                rv += u"%s%s\n" % (list_prefix,
-                                   esc_chunk(item.msgid,
-                                             MAXLEN - len(list_prefix)))
-        if len(self.operation_to_storage_map['rm']):
-            rv += _(u"Removed phrases:\n")
-            for item in self.operation_to_storage_map['rm']:
-                rv += u"%s%s\n" % (list_prefix,
-                                   esc_chunk(item.msgid,
-                                             MAXLEN - len(list_prefix)))
-        if len(self.operation_to_storage_map['modify']):
-            rv += _(u"Modified translation of phrases:\n")
-            for item in self.operation_to_storage_map['modify']:
-                rv += u"%s%s\n" % (list_prefix,
-                                   esc_chunk(item[0].msgid,
-                                             MAXLEN - len(list_prefix)))
-        return unicode(rv)
-    
-    def full_represent(self):
-        """Full represent should guaranty that it represent all the information
-        needed to fully describe PoDiff. Used to make sure that two PoDiff's are
-        equal.
-        """
-        def prefix_lines(prefix, text):
-            return u"\n".join(u"".join([prefix, line])
-                              for line in text.splitlines())
-        
-        rv = StringBuilder()
-        for item in self.operation_to_storage_map['add']:
-            rv += prefix_lines('+ ', unicode(item))
-            rv += u'\n'
-        for item in self.operation_to_storage_map['rm']:
-            rv += prefix_lines('- ', unicode(item))
-            rv += u'\n'
-        for item in self.operation_to_storage_map['modify']:
-            rv += prefix_lines('+ ', unicode(item[0]))
-            rv += u'\n'
-            rv += prefix_lines('- ', unicode(item[1]))
-            rv += u'\n'
-        return unicode(rv)
-
-    def __eq__(self, other):
-        if not isinstance(other, PoDiff):
-            return False
-        return self.full_represent() == other.full_represent()
-
-def compare_two_pofiles(pofile1, pofile2):
-    """Returns :class:`~PoDiff` object that represents difference between two po
-    files.
-
-    ``pofile1``, ``pofile2``
-        :class:`~polib.POFile` instances.
-    """
-
-    rv = PoDiff()
-    
-    pofile1_msgid_to_poentry_map = dict(map(lambda entry: (entry.msgid, entry), pofile1))
-    pofile2_msgid_to_poentry_map = dict(map(lambda entry: (entry.msgid, entry), pofile2))
-    
-    for item in pofile2:
-        if item.msgid not in pofile1_msgid_to_poentry_map:
-            rv.append_entry('add', item)
-        else:
-            old_item = pofile1_msgid_to_poentry_map[item.msgid]
-            if item != old_item:
-                rv.append_entry('modify', [old_item, item])
-    
-    for item in pofile1:
-        if item.msgid not in pofile2_msgid_to_poentry_map:
-            rv.append_entry('rm', item)
-
-    return rv

fat_po_editor/vcs_util.py

 import vcs
 
 from .util import StringBuilder
+from .diff_util import compare_two_pofiles
 
 def is_useful_changeset(changeset):
     """Returns if changeset is useful for fat_po_edito"""
         rv += u"Affected pofiles: [%s]\n" % u", ".join(self.affected_pofiles)
         return unicode(rv)
 
+    def generate_podiff(self, pofile_path):
+        """
+        :returns:
+            Returns a :class:`~fat_po_editor.diffutil.PoDiff` instance for
+            this changeset and it's parent (first parent when multiple).
+        :raises: :class:`~vcs.exceptions.ChangesetError` when file not found.
+        """
+        from polib import pofile
+        contents = pofile(self.changeset.get_file_content(pofile_path))
+        contents_old = pofile(self.changeset.parents[0].get_file_content(pofile_path))
+        return compare_two_pofiles(contents_old, contents)
+
+
 class RepoWithPofiles(object):
     def __init__(self, path=None):
         self.path = path
               for r in useful_revisions]
         return rv
 
-    def generate_podiff_for_changeset(self):
-        """Returns :class:`~fat_po_editor.diffutil.PoDiff` instance for this changeset"""
-        pass
+    def get_bound_changeset_info_for_revision(self, revision):
+        """Build new :class:`~BoundChangesetInfo` instance from revision
+        given as a string.
+
+        :param revision: revision id as a string
+        """
+        c = self.vcs_repo.get_changeset(revision)
+        return BoundChangesetInfo(c)

tests/test_diff_util.py

+# -*- coding: utf-8 -*-
+
+import unittest
+
+from polib import POEntry, POFile
+
+from fat_po_editor.diff_util import PODiff, compare_two_pofiles
+
+class TestDiffUtilPODiff(unittest.TestCase):
+    def test_append_simple(self):
+        diff = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff.append_entry('add', entry)
+
+        self.assertEquals(
+            diff.get_entries('add'),
+            [entry])
+
+    def test_representation_simple(self):
+        diff = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff.append_entry('add', entry)
+
+        self.assertEquals(
+            unicode(diff),
+            (u"Added phrases:\n"
+             u"    * First msgid\n"))
+
+    def test_representation_more_complex(self):
+        diff = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff.append_entry('add', entry)
+        entry = POEntry(msgid=u"Second msgid",
+                        msgstr=u"Second msgstr")
+        diff.append_entry('rm', entry)
+        entry = [POEntry(msgid=u"Third msgid",
+                         msgstr=u"Third msgstr"),
+                 POEntry(msgid=u"Third msgid",
+                         msgstr=u"Third msgstr")]
+        diff.append_entry('modify', entry)
+        
+        self.assertEquals(
+            unicode(diff),
+            (u"Added phrases:\n"
+             u"    * First msgid\n"
+             u"Removed phrases:\n"
+             u"    * Second msgid\n"
+             u"Modified translation of phrases:\n"
+             u"    * Third msgid\n"))
+
+    def test_representation_too_long(self):
+        diff = PODiff()
+        entry = POEntry(msgid=u"F" * 81,
+                        msgstr=u"First msgstr")
+        diff.append_entry('add', entry)
+
+        self.assertEquals(
+            unicode(diff),
+            (u"Added phrases:\n"
+             u"    * %s...\n") % (u"F" * 71))
+
+    def test_representation_with_newline(self):
+        diff = PODiff()
+        entry = POEntry(msgid=u"First \nmsgid",
+                        msgstr=u"First msgstr")
+        diff.append_entry('add', entry)
+
+        self.assertEquals(
+            unicode(diff),
+            (u"Added phrases:\n"
+             u"    * First \\nmsgid\n"))
+
+    def test_full_represent(self):
+        diff = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff.append_entry('add', entry)
+        entry = POEntry(msgid=u"Second msgid",
+                        msgstr=u"Second msgstr")
+        diff.append_entry('rm', entry)
+        entry = POEntry(msgid=u"Third msgid",
+                        msgstr=u"Third msgstr")
+        diff.append_entry('modify', [entry, entry])
+
+        self.assertEquals(
+            diff.full_represent(),
+            (u'''+ msgid "First msgid"\n'''
+             u'''+ msgstr "First msgstr"\n'''
+             u'''- msgid "Second msgid"\n'''
+             u'''- msgstr "Second msgstr"\n'''
+             u'''+ msgid "Third msgid"\n'''
+             u'''+ msgstr "Third msgstr"\n'''
+             u'''- msgid "Third msgid"\n'''
+             u'''- msgstr "Third msgstr"\n'''))
+
+    def test_eq_simple(self):
+        diff1 = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff1.append_entry('add', entry)
+        diff2 = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff2.append_entry('add', entry)
+        
+        self.assertEquals(diff1 == diff2, True)
+
+    def test_eq_simple_noneq(self):
+        diff1 = PODiff()
+        entry = POEntry(msgid=u"First msgid",
+                        msgstr=u"First msgstr")
+        diff1.append_entry('add', entry)
+        diff2 = PODiff()
+        entry = POEntry(msgid=u"Second msgid",
+                        msgstr=u"Second msgstr")
+        diff2.append_entry('add', entry)
+        
+        self.assertEquals(diff1 == diff2, False)
+
+    def test_eq_empty(self):
+        diff1 = PODiff()
+        diff2 = PODiff()
+        
+        self.assertEquals(diff1 == diff2, True)
+
+class TestCompareTwoPofiles(unittest.TestCase):
+    def test_empty(self):
+        f1 = POFile()
+        f2 = POFile()
+
+        self.assertEquals(compare_two_pofiles(f1, f2),
+                          PODiff())
+
+    def test_simple(self):
+        f1 = POFile()
+        f2 = POFile()
+        expected = PODiff()
+        
+        e = POEntry(msgid=u"First msgid",
+                    msgstr=u"First msgstr")
+        f1.append(e)
+        f2.append(e)
+        e = POEntry(msgid=u"Second msgid",
+                    msgstr=u"Second msgstr")
+        f1.append(e)
+        f2.append(e)
+        e = POEntry(msgid=u"Third msgid",
+                    msgstr=u"Third msgstr")
+        f2.append(e)
+        expected.append_entry('add', e)
+        
+        self.assertEquals(compare_two_pofiles(f1, f2),
+                          expected)
+        
+
+    def test_modified(self):
+        f1 = POFile()
+        f2 = POFile()
+        expected = PODiff()
+
+        e_old = POEntry(msgid=u'First msgid',
+                        msgstr=u'')
+        f1.append(e_old)
+        e_new = POEntry(msgid=u'First msgid',
+                        msgstr=u'First msgstr')
+        f2.append(e_new)
+        expected.append_entry(
+            'modify',
+            [e_old, e_new])
+
+        self.assertEquals(compare_two_pofiles(f1, f2),
+                          expected)
+        

tests/test_diffutil.py

-# -*- coding: utf-8 -*-
-
-import unittest
-
-from polib import POEntry, POFile
-
-from fat_po_editor.diffutil import PoDiff, compare_two_pofiles
-
-class TestDiffUtilPoDiff(unittest.TestCase):
-    def test_append_simple(self):
-        diff = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff.append_entry('add', entry)
-
-        self.assertEquals(
-            diff.get_entries('add'),
-            [entry])
-
-    def test_representation_simple(self):
-        diff = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff.append_entry('add', entry)
-
-        self.assertEquals(
-            unicode(diff),
-            (u"Added phrases:\n"
-             u"    * First msgid\n"))
-
-    def test_representation_more_complex(self):
-        diff = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff.append_entry('add', entry)
-        entry = POEntry(msgid=u"Second msgid",
-                        msgstr=u"Second msgstr")
-        diff.append_entry('rm', entry)
-        entry = [POEntry(msgid=u"Third msgid",
-                         msgstr=u"Third msgstr"),
-                 POEntry(msgid=u"Third msgid",
-                         msgstr=u"Third msgstr")]
-        diff.append_entry('modify', entry)
-        
-        self.assertEquals(
-            unicode(diff),
-            (u"Added phrases:\n"
-             u"    * First msgid\n"
-             u"Removed phrases:\n"
-             u"    * Second msgid\n"
-             u"Modified translation of phrases:\n"
-             u"    * Third msgid\n"))
-
-    def test_representation_too_long(self):
-        diff = PoDiff()
-        entry = POEntry(msgid=u"F" * 81,
-                        msgstr=u"First msgstr")
-        diff.append_entry('add', entry)
-
-        self.assertEquals(
-            unicode(diff),
-            (u"Added phrases:\n"
-             u"    * %s...\n") % (u"F" * 71))
-
-    def test_representation_with_newline(self):
-        diff = PoDiff()
-        entry = POEntry(msgid=u"First \nmsgid",
-                        msgstr=u"First msgstr")
-        diff.append_entry('add', entry)
-
-        self.assertEquals(
-            unicode(diff),
-            (u"Added phrases:\n"
-             u"    * First \\nmsgid\n"))
-
-    def test_full_represent(self):
-        diff = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff.append_entry('add', entry)
-        entry = POEntry(msgid=u"Second msgid",
-                        msgstr=u"Second msgstr")
-        diff.append_entry('rm', entry)
-        entry = POEntry(msgid=u"Third msgid",
-                        msgstr=u"Third msgstr")
-        diff.append_entry('modify', [entry, entry])
-
-        self.assertEquals(
-            diff.full_represent(),
-            (u'''+ msgid "First msgid"\n'''
-             u'''+ msgstr "First msgstr"\n'''
-             u'''- msgid "Second msgid"\n'''
-             u'''- msgstr "Second msgstr"\n'''
-             u'''+ msgid "Third msgid"\n'''
-             u'''+ msgstr "Third msgstr"\n'''
-             u'''- msgid "Third msgid"\n'''
-             u'''- msgstr "Third msgstr"\n'''))
-
-    def test_eq_simple(self):
-        diff1 = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff1.append_entry('add', entry)
-        diff2 = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff2.append_entry('add', entry)
-        
-        self.assertEquals(diff1 == diff2, True)
-
-    def test_eq_simple_noneq(self):
-        diff1 = PoDiff()
-        entry = POEntry(msgid=u"First msgid",
-                        msgstr=u"First msgstr")
-        diff1.append_entry('add', entry)
-        diff2 = PoDiff()
-        entry = POEntry(msgid=u"Second msgid",
-                        msgstr=u"Second msgstr")
-        diff2.append_entry('add', entry)
-        
-        self.assertEquals(diff1 == diff2, False)
-
-    def test_eq_empty(self):
-        diff1 = PoDiff()
-        diff2 = PoDiff()
-        
-        self.assertEquals(diff1 == diff2, True)
-
-class TestCompareTwoPofiles(unittest.TestCase):
-    def test_empty(self):
-        f1 = POFile()
-        f2 = POFile()
-
-        self.assertEquals(compare_two_pofiles(f1, f2),
-                          PoDiff())
-
-    def test_simple(self):
-        f1 = POFile()
-        f2 = POFile()
-        expected = PoDiff()
-        
-        e = POEntry(msgid=u"First msgid",
-                    msgstr=u"First msgstr")
-        f1.append(e)
-        f2.append(e)
-        e = POEntry(msgid=u"Second msgid",
-                    msgstr=u"Second msgstr")
-        f1.append(e)
-        f2.append(e)
-        e = POEntry(msgid=u"Third msgid",
-                    msgstr=u"Third msgstr")
-        f2.append(e)
-        expected.append_entry('add', e)
-        
-        self.assertEquals(compare_two_pofiles(f1, f2),
-                          expected)
-        

tests/test_vcs_util.py

                            u"Affected pofiles: [pofiles/first.po]\n"))
 
 class TestBoundChangesetInfo(unittest.TestCase):
-    pass
+    def setUp(self):
+        self.testdata_path = os.path.join(get_testdata_path(),
+                                          'repo2')
+        self.repo = RepoWithPofiles(self.testdata_path)
+    
+    def test_generate_podiff_simple(self):
+        bound_changeset_info = (
+            self.repo.get_bound_changeset_info_for_revision('7d03985ecb3c'))
+        podiff = bound_changeset_info.generate_podiff('pofiles/first.po')
+        self.assertEquals(
+            podiff.full_represent(),
+            u'+ msgid "third msgid"\n'
+            u'+ msgstr "third msgstr"\n')
+        
+    def test_generate_podiff_added_msgplural(self):
+        bound_changeset_info = (
+            self.repo.get_bound_changeset_info_for_revision('578db7c04ee4'))
+        podiff = bound_changeset_info.generate_podiff('pofiles/first.po')
+        self.assertEquals(
+            podiff.full_represent(),
+            u'+ msgid "ngettext msgid"\n'
+            u'+ msgid_plural "nsgettext msgid_plural"\n'
+            u'+ msgstr ""\n')
+
+    def test_generate_podiff_translated_msgplural(self):
+        bound_changeset_info = (
+            self.repo.get_bound_changeset_info_for_revision('6714f3f0d989'))
+        podiff = bound_changeset_info.generate_podiff('pofiles/first.po')
+        self.assertEquals(
+            podiff.full_represent(),
+            u'+ msgid "ngettext msgid"\n'
+            u'+ msgid_plural "nsgettext msgid_plural"\n'
+            u'+ msgstr[0] "first form"\n'
+            u'+ msgstr[1] "second form"\n'
+            u'+ msgstr[2] "third form"\n'
+            u'- msgid "ngettext msgid"\n'
+            u'- msgid_plural "nsgettext msgid_plural"\n'
+            u'- msgstr ""\n')