Merge lp:~jr/bzr/i18n-errors into lp:bzr

Proposed by Jonathan Riddell
Status: Merged
Approved by: Jelmer Vernooij
Approved revision: no longer in the source branch.
Merged at revision: 6133
Proposed branch: lp:~jr/bzr/i18n-errors
Merge into: lp:bzr
Diff against target: 161 lines (+45/-14)
4 files modified
bzrlib/errors.py (+9/-2)
bzrlib/pack.py (+1/-1)
bzrlib/tests/test_i18n.py (+32/-11)
doc/en/release-notes/bzr-2.5.txt (+3/-0)
To merge this branch: bzr merge lp:~jr/bzr/i18n-errors
Reviewer Review Type Date Requested Status
Martin Packman (community) Needs Fixing
Jelmer Vernooij (community) Approve
Review via email: mp+73547@code.launchpad.net

Commit message

Turn on translations for errors.

Description of the change

Turn on translations for errors

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

It seems like this could install the i18n support more than once, wouldn't it need to be protected against that, like in the command help ?

It would be nice to have a smoke test for this (and perhaps also the command help i18n support), especially given none of the core developers uses i18n.

review: Needs Fixing
Revision history for this message
Jonathan Riddell (jr) wrote :

>It seems like this could install the i18n support more than once, wouldn't it need to be protected against that, like in the command help ?

This is no longer the case now that this branch is in trunk https://code.launchpad.net/~jr/bzr/i18n-install-once

>It would be nice to have a smoke test for this (and perhaps also the command help i18n support), especially given none of the core developers uses i18n.

Added

Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

sent to pqm by email

Revision history for this message
Martin Packman (gz) wrote :

Summarizing from IRC conversion, the error module has a lot of pre-existing problems that have been mostly safe to ignore because:

* All _fmt strings are ascii str, which can be upcast to either non-ascii bytes or unicode (but still break when trying to do both, see bug 273978 for instance).
* Many users have utf-8 consoles so printing utf-8 encoded error messages rather than encoding unicode appropriately appears fine.

In making the _fmt string a localised utf-8 message, these existing confusions with string types are exposed.

+ unicode_fmt = osutils.safe_unicode(fmt)
+ if type(fmt) == unicode:
+ trace.mutter("Unicode strings in error.fmt are deprecated")

I'd replace this with just unicode(fmt) and add a comment that all _fmt strings should be ascii.

+ return gettext(unicode_fmt).encode('utf-8')

Remove the encode here. That will fix exceptions that include paths, or any other unicode strings in the message. On the other hand, it breaks exceptions that think it's safe to interpolate arbitrary bytes into exception messages, but they're already bogus and need fixing anyway.

+ self.overrideAttr(i18n, '_translations', ZzzTranslations())
+ try:
+ workingtree.WorkingTree.open('./foo')
+ except errors.NotBranchError,e:
+ err = str(e)
+ self.assertContainsRe(err, "zz{{Not a branch: .*}}")

If we can make ZzzTranslations return more than just ascii it would be more realistic, and this test would then fail with a UnicodeError.

review: Needs Fixing
Revision history for this message
Martin Packman (gz) wrote :

Actually, NotBranchError has some special-casing to prevent breakage, but many other PathError subclasses do not.

Revision history for this message
Jonathan Riddell (jr) wrote :

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/errors.py'
2--- bzrlib/errors.py 2011-09-05 10:28:30 +0000
3+++ bzrlib/errors.py 2011-09-07 11:50:38 +0000
4@@ -20,7 +20,10 @@
5 from bzrlib import (
6 osutils,
7 symbol_versioning,
8+ i18n,
9+ trace,
10 )
11+from bzrlib.i18n import gettext
12 from bzrlib.patches import (
13 MalformedHunkHeader,
14 MalformedLine,
15@@ -140,7 +143,11 @@
16 """Return format string for this exception or None"""
17 fmt = getattr(self, '_fmt', None)
18 if fmt is not None:
19- return fmt
20+ i18n.install()
21+ unicode_fmt = unicode(fmt) #_fmt strings should be ascii
22+ if type(fmt) == unicode:
23+ trace.mutter("Unicode strings in error.fmt are deprecated")
24+ return gettext(unicode_fmt)
25 fmt = getattr(self, '__doc__', None)
26 if fmt is not None:
27 symbol_versioning.warn("%s uses its docstring as a format, "
28@@ -2792,7 +2799,7 @@
29 _fmt = "Container has multiple records with the same name: %(name)s"
30
31 def __init__(self, name):
32- self.name = name
33+ self.name = name.decode("utf-8")
34
35
36 class NoDestinationAddress(InternalBzrError):
37
38=== modified file 'bzrlib/pack.py'
39--- bzrlib/pack.py 2011-04-08 14:45:25 +0000
40+++ bzrlib/pack.py 2011-09-07 11:50:38 +0000
41@@ -333,7 +333,7 @@
42 # risk that the same unicode string has been encoded two
43 # different ways.
44 if name_tuple in all_names:
45- raise errors.DuplicateRecordNameError(name_tuple)
46+ raise errors.DuplicateRecordNameError(name_tuple[0])
47 all_names.add(name_tuple)
48 excess_bytes = self.reader_func(1)
49 if excess_bytes != '':
50
51=== modified file 'bzrlib/tests/test_i18n.py'
52--- bzrlib/tests/test_i18n.py 2011-08-25 10:33:14 +0000
53+++ bzrlib/tests/test_i18n.py 2011-09-07 11:50:38 +0000
54@@ -16,7 +16,11 @@
55
56 """Tests for bzrlib.i18n"""
57
58-from bzrlib import i18n, tests
59+from bzrlib import (i18n,
60+ tests,
61+ errors,
62+ workingtree,
63+ )
64
65
66 class ZzzTranslations(object):
67@@ -28,7 +32,7 @@
68 _null_translation = i18n._gettext.NullTranslations()
69
70 def zzz(self, s):
71- return u'zz{{%s}}' % s
72+ return u'zz\xe5{{%s}}' % s
73
74 def ugettext(self, s):
75 return self.zzz(self._null_translation.ugettext(s))
76@@ -47,18 +51,18 @@
77 trans = ZzzTranslations()
78
79 t = trans.zzz('msg')
80- self._check_exact(u'zz{{msg}}', t)
81+ self._check_exact(u'zz\xe5{{msg}}', t)
82
83 t = trans.ugettext('msg')
84- self._check_exact(u'zz{{msg}}', t)
85+ self._check_exact(u'zz\xe5{{msg}}', t)
86
87 t = trans.ungettext('msg1', 'msg2', 0)
88- self._check_exact(u'zz{{msg2}}', t)
89+ self._check_exact(u'zz\xe5{{msg2}}', t)
90 t = trans.ungettext('msg1', 'msg2', 2)
91- self._check_exact(u'zz{{msg2}}', t)
92+ self._check_exact(u'zz\xe5{{msg2}}', t)
93
94 t = trans.ungettext('msg1', 'msg2', 1)
95- self._check_exact(u'zz{{msg1}}', t)
96+ self._check_exact(u'zz\xe5{{msg1}}', t)
97
98
99 class TestGetText(tests.TestCase):
100@@ -68,11 +72,11 @@
101 self.overrideAttr(i18n, '_translations', ZzzTranslations())
102
103 def test_oneline(self):
104- self.assertEqual(u"zz{{spam ham eggs}}",
105+ self.assertEqual(u"zz\xe5{{spam ham eggs}}",
106 i18n.gettext("spam ham eggs"))
107
108 def test_multiline(self):
109- self.assertEqual(u"zz{{spam\nham\n\neggs\n}}",
110+ self.assertEqual(u"zz\xe5{{spam\nham\n\neggs\n}}",
111 i18n.gettext("spam\nham\n\neggs\n"))
112
113
114@@ -83,11 +87,11 @@
115 self.overrideAttr(i18n, '_translations', ZzzTranslations())
116
117 def test_oneline(self):
118- self.assertEqual(u"zz{{spam ham eggs}}",
119+ self.assertEqual(u"zz\xe5{{spam ham eggs}}",
120 i18n.gettext_per_paragraph("spam ham eggs"))
121
122 def test_multiline(self):
123- self.assertEqual(u"zz{{spam\nham}}\n\nzz{{eggs\n}}",
124+ self.assertEqual(u"zz\xe5{{spam\nham}}\n\nzz\xe5{{eggs\n}}",
125 i18n.gettext_per_paragraph("spam\nham\n\neggs\n"))
126
127
128@@ -104,3 +108,20 @@
129 self.overrideEnv('LC_MESSAGES', None)
130 self.overrideEnv('LANG', None)
131 i18n.install()
132+
133+class TestTranslate(tests.TestCaseWithTransport):
134+
135+ def setUp(self):
136+ super(TestTranslate, self).setUp()
137+ self.overrideAttr(i18n, '_translations', ZzzTranslations())
138+
139+ def test_error_message_translation(self):
140+ """do errors get translated?"""
141+ err = None
142+ tree = self.make_branch_and_tree('.')
143+ self.overrideAttr(i18n, '_translations', ZzzTranslations())
144+ try:
145+ workingtree.WorkingTree.open('./foo')
146+ except errors.NotBranchError,e:
147+ err = str(e)
148+ self.assertContainsRe(err, u"zz\xe5{{Not a branch: .*}}".encode("utf-8"))
149
150=== modified file 'doc/en/release-notes/bzr-2.5.txt'
151--- doc/en/release-notes/bzr-2.5.txt 2011-09-06 13:35:07 +0000
152+++ doc/en/release-notes/bzr-2.5.txt 2011-09-07 11:50:38 +0000
153@@ -92,6 +92,9 @@
154 * bzr now ships with translations for command help. (Jonathan
155 Riddell, INADA Naoki, #83941)
156
157+* bzr now ships with translations for command errors. (Jonathan
158+ Riddell, INADA Naoki)
159+
160 Improvements
161 ************
162