Merge lp:~numerigraphe-team/ocb-server/7.0-po-targets-933496-vmt into lp:ocb-server

Proposed by Lionel Sausin - Initiatives/Numérigraphe
Status: Merged
Merged at revision: 5314
Proposed branch: lp:~numerigraphe-team/ocb-server/7.0-po-targets-933496-vmt
Merge into: lp:ocb-server
Diff against target: 379 lines (+270/-5)
10 files modified
openerp/tests/addons/test_translation_import/__init__.py (+2/-0)
openerp/tests/addons/test_translation_import/__openerp__.py (+15/-0)
openerp/tests/addons/test_translation_import/i18n/fr.po (+52/-0)
openerp/tests/addons/test_translation_import/i18n/test_translation_import.pot (+58/-0)
openerp/tests/addons/test_translation_import/models.py (+20/-0)
openerp/tests/addons/test_translation_import/tests.yml (+13/-0)
openerp/tests/addons/test_translation_import/tests/__init__.py (+9/-0)
openerp/tests/addons/test_translation_import/tests/test_term_count.py (+16/-0)
openerp/tests/addons/test_translation_import/view.xml (+23/-0)
openerp/tools/translate.py (+62/-5)
To merge this branch: bzr merge lp:~numerigraphe-team/ocb-server/7.0-po-targets-933496-vmt
Reviewer Review Type Date Requested Status
Loïc Bellier - Numérigraphe (community) code review Approve
Pedro Manuel Baeza code review Approve
Holger Brunn (Therp) Needs Information
Review via email: mp+209895@code.launchpad.net

Description of the change

This fixes a bug in translations that make them disappear when the string is removed from launchpad's current translation focus.
For example, when Launchpad's translation target becomes v8.0, all strings present in v7 but removed from v8 becomes untranslatable in v7.

This branch runs green on Runbot.
MP for stable 7.0 : https://code.launchpad.net/~numerigraphe-team/openobject-server/7.0-fix-po-targets-933496-vmt/+merge/215585

To post a comment you must log in.
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

I posted Vo Minh Thu's fix for 6.1 (https://code.launchpad.net/~therp-nl/ocb-server/6.1-lp933496/+merge/193011) but Pedro had an issue with I never managed to figure out. Pedro: Did you ever have a chance to dig into that? And do you have the same behavior with this patch and version 7.0?

review: Needs Information
Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

I have checked at last the problem and see that the problem doesn't come from patch itself, but from pot files, so you can unlock both MPs.

I give also my approval to this.

review: Approve (code review)
Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote :

This branch currently runs red on runbot, and the original branch by VMT runs orange - missing access rules on demo data. I'll check what the problem is as soon as I can.

Revision history for this message
Lionel Sausin - Initiatives/Numérigraphe (ls-initiatives) wrote :

Fixed, runs green on runbot again.

Revision history for this message
Loïc Bellier - Numérigraphe (lb-b) :
review: Approve (code review)
4799. By Numérigraphe

[DEL] remove VIM comments. Just because.

Revision history for this message
Pedro Manuel Baeza (pedro.baeza) wrote :

I proceed with the merge because Holger's comment, refered to me, has been answered.

Regards.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'openerp/tests/addons/test_translation_import'
2=== added file 'openerp/tests/addons/test_translation_import/__init__.py'
3--- openerp/tests/addons/test_translation_import/__init__.py 1970-01-01 00:00:00 +0000
4+++ openerp/tests/addons/test_translation_import/__init__.py 2014-04-18 10:25:50 +0000
5@@ -0,0 +1,2 @@
6+# -*- coding: utf-8 -*-
7+import models
8
9=== added file 'openerp/tests/addons/test_translation_import/__openerp__.py'
10--- openerp/tests/addons/test_translation_import/__openerp__.py 1970-01-01 00:00:00 +0000
11+++ openerp/tests/addons/test_translation_import/__openerp__.py 2014-04-18 10:25:50 +0000
12@@ -0,0 +1,15 @@
13+# -*- coding: utf-8 -*-
14+{
15+ 'name': 'test-translation-import',
16+ 'version': '0.1',
17+ 'category': 'Tests',
18+ 'description': """A module to test translation import.""",
19+ 'author': 'OpenERP SA',
20+ 'maintainer': 'OpenERP SA',
21+ 'website': 'http://www.openerp.com',
22+ 'depends': ['base'],
23+ 'data': ['view.xml'],
24+ 'test': ['tests.yml'],
25+ 'installable': True,
26+ 'auto_install': False,
27+}
28
29=== added directory 'openerp/tests/addons/test_translation_import/i18n'
30=== added file 'openerp/tests/addons/test_translation_import/i18n/fr.po'
31--- openerp/tests/addons/test_translation_import/i18n/fr.po 1970-01-01 00:00:00 +0000
32+++ openerp/tests/addons/test_translation_import/i18n/fr.po 2014-04-18 10:25:50 +0000
33@@ -0,0 +1,52 @@
34+# This is a test PO file, not a true french translation.
35+# See the POT file for further information.
36+msgid ""
37+msgstr ""
38+"Project-Id-Version: OpenERP Server 6.1\n"
39+"Report-Msgid-Bugs-To: \n"
40+"POT-Creation-Date: 2012-10-17 12:36+0000\n"
41+"PO-Revision-Date: 2012-10-17 12:36+0000\n"
42+"Last-Translator: <>\n"
43+"Language-Team: \n"
44+"MIME-Version: 1.0\n"
45+"Content-Type: text/plain; charset=UTF-8\n"
46+"Content-Transfer-Encoding: \n"
47+"Plural-Forms: \n"
48+
49+# Note: there is normally an additional line:
50+# #: code:addons/test_translation_import/models.py:17
51+# This line is present in the POT and removed here to test the translation
52+# import behavior.
53+#. module: test_translation_import
54+#: field:test.translation.import,name:0
55+#, python-format
56+msgid "1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB"
57+msgstr "1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB in french"
58+
59+#. module: test_translation_import
60+#: code:addons/test_translation_import/models.py:14
61+#, python-format
62+msgid "Ijkl"
63+msgstr "Ijkl in french"
64+
65+#. module: test_translation_import
66+#: model:ir.model,name:test_translation_import.model_test_translation_import
67+msgid "test.translation.import"
68+msgstr "test.translation.import in french"
69+
70+#. module: test_translation_import
71+#: help:test.translation.import,name:0
72+msgid "Efgh"
73+msgstr "Efgh in french"
74+
75+#. module: test_translation_import
76+#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import
77+#: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import
78+msgid "Test translation import"
79+msgstr "Test translation import in french"
80+
81+#. module: test_translation_import
82+#: model:ir.ui.menu,name:test_translation_import.menu_test_translation
83+msgid "Test translation"
84+msgstr "Test translation in french"
85+
86
87=== added file 'openerp/tests/addons/test_translation_import/i18n/test_translation_import.pot'
88--- openerp/tests/addons/test_translation_import/i18n/test_translation_import.pot 1970-01-01 00:00:00 +0000
89+++ openerp/tests/addons/test_translation_import/i18n/test_translation_import.pot 2014-04-18 10:25:50 +0000
90@@ -0,0 +1,58 @@
91+# This is a test POT file, not a true template. It is manually maintained
92+# to test the import translation behavior of OpenERP.
93+#
94+# In particular, the
95+# `1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB` source is
96+# given with two targets (the #: comments): `code` and `field`. The code one is
97+# removed in the fr.po file. Still, the import should generate a database entry
98+# for the `code` one. I.e. the targets defined in the POT must be added to the
99+# targets defined in the PO file. This was done to fix a bug, as reported by
100+# lp:933496.
101+#
102+msgid ""
103+msgstr ""
104+"Project-Id-Version: OpenERP Server 6.1\n"
105+"Report-Msgid-Bugs-To: \n"
106+"POT-Creation-Date: 2012-10-17 12:36+0000\n"
107+"PO-Revision-Date: 2012-10-17 12:36+0000\n"
108+"Last-Translator: <>\n"
109+"Language-Team: \n"
110+"MIME-Version: 1.0\n"
111+"Content-Type: text/plain; charset=UTF-8\n"
112+"Content-Transfer-Encoding: \n"
113+"Plural-Forms: \n"
114+
115+#. module: test_translation_import
116+#: code:addons/test_translation_import/models.py:17
117+#: field:test.translation.import,name:0
118+#, python-format
119+msgid "1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB"
120+msgstr ""
121+
122+#. module: test_translation_import
123+#: code:addons/test_translation_import/models.py:14
124+#, python-format
125+msgid "Ijkl"
126+msgstr ""
127+
128+#. module: test_translation_import
129+#: model:ir.model,name:test_translation_import.model_test_translation_import
130+msgid "test.translation.import"
131+msgstr ""
132+
133+#. module: test_translation_import
134+#: help:test.translation.import,name:0
135+msgid "Efgh"
136+msgstr ""
137+
138+#. module: test_translation_import
139+#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import
140+#: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import
141+msgid "Test translation import"
142+msgstr ""
143+
144+#. module: test_translation_import
145+#: model:ir.ui.menu,name:test_translation_import.menu_test_translation
146+msgid "Test translation"
147+msgstr ""
148+
149
150=== added file 'openerp/tests/addons/test_translation_import/models.py'
151--- openerp/tests/addons/test_translation_import/models.py 1970-01-01 00:00:00 +0000
152+++ openerp/tests/addons/test_translation_import/models.py 2014-04-18 10:25:50 +0000
153@@ -0,0 +1,20 @@
154+# -*- coding: utf-8 -*-
155+import openerp
156+from openerp.tools.translate import _
157+
158+class m(openerp.osv.orm.TransientModel):
159+ """ A model to provide source strings.
160+ """
161+ _name = 'test.translation.import'
162+
163+ _columns = {
164+ 'name': openerp.osv.fields.char(
165+ '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB',
166+ size=32, help='Efgh'),
167+ }
168+
169+ _('Ijkl')
170+
171+ # With the name label above, this source string should be generated twice.
172+ _('1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB')
173+
174
175=== added directory 'openerp/tests/addons/test_translation_import/tests'
176=== added file 'openerp/tests/addons/test_translation_import/tests.yml'
177--- openerp/tests/addons/test_translation_import/tests.yml 1970-01-01 00:00:00 +0000
178+++ openerp/tests/addons/test_translation_import/tests.yml 2014-04-18 10:25:50 +0000
179@@ -0,0 +1,13 @@
180+-
181+ Load the french translation.
182+-
183+ !python {model: ir.translation }: |
184+ import openerp
185+ openerp.tools.trans_load(cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False)
186+-
187+ Assert we have loaded the correct number of entries for the given source string.
188+-
189+ !python {model: ir.translation }: |
190+ ids = self.search(cr, uid,
191+ [('src', '=', '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB')])
192+ assert len(ids) == 2, "2 entries are expected, got %s instead." % len(ids)
193
194=== added file 'openerp/tests/addons/test_translation_import/tests/__init__.py'
195--- openerp/tests/addons/test_translation_import/tests/__init__.py 1970-01-01 00:00:00 +0000
196+++ openerp/tests/addons/test_translation_import/tests/__init__.py 2014-04-18 10:25:50 +0000
197@@ -0,0 +1,9 @@
198+# -*- coding: utf-8 -*-
199+import unittest2
200+
201+import test_term_count
202+
203+suite = [
204+ test_term_count
205+ ]
206+
207
208=== added file 'openerp/tests/addons/test_translation_import/tests/test_term_count.py'
209--- openerp/tests/addons/test_translation_import/tests/test_term_count.py 1970-01-01 00:00:00 +0000
210+++ openerp/tests/addons/test_translation_import/tests/test_term_count.py 2014-04-18 10:25:50 +0000
211@@ -0,0 +1,16 @@
212+# -*- coding: utf-8 -*-
213+
214+import openerp
215+from openerp.tests import common
216+
217+class TestTermCount(common.TransactionCase):
218+
219+ def test_count_term(self):
220+ """
221+ Just make sure we have as many translation entries as we wanted.
222+ """
223+ openerp.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False)
224+ ids = self.registry('ir.translation').search(self.cr, self.uid,
225+ [('src', '=', '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB')])
226+ self.assertEqual(len(ids), 2)
227+
228
229=== added file 'openerp/tests/addons/test_translation_import/view.xml'
230--- openerp/tests/addons/test_translation_import/view.xml 1970-01-01 00:00:00 +0000
231+++ openerp/tests/addons/test_translation_import/view.xml 2014-04-18 10:25:50 +0000
232@@ -0,0 +1,23 @@
233+<?xml version="1.0" encoding="utf-8"?>
234+<openerp>
235+ <data>
236+
237+ <record id="action_test_translation_import" model="ir.actions.act_window">
238+ <field name="name">Test translation import</field>
239+ <field name="type">ir.actions.act_window</field>
240+ <field name="res_model">test.translation.import</field>
241+ <field name="view_type">form</field>
242+ <field name="view_mode">tree,form</field>
243+ <field name="target">current</field>
244+ </record>
245+
246+ <menuitem icon="STOCK_PREFERENCES" id="base.menu_tests" name="Tests"/>
247+
248+ <menuitem id="menu_test_translation" parent="base.menu_tests" name="Test translation"/>
249+
250+ <menuitem id="menu_test_translation_import"
251+ name="Test translation import"
252+ action="action_test_translation_import"
253+ parent="menu_test_translation"/>
254+ </data>
255+</openerp>
256
257=== modified file 'openerp/tools/translate.py'
258--- openerp/tools/translate.py 2014-03-17 08:06:23 +0000
259+++ openerp/tools/translate.py 2014-04-18 10:25:50 +0000
260@@ -313,6 +313,10 @@
261 if not line.startswith('module:'):
262 comments.append(line)
263 elif line.startswith('#:'):
264+ # Process the `reference` comments. Each line can specify
265+ # multiple targets (e.g. model, view, code, selection,
266+ # ...). For each target, we will return an additional
267+ # entry.
268 for lpart in line[2:].strip().split(' '):
269 trans_info = lpart.strip().split(':',2)
270 if trans_info and len(trans_info) == 2:
271@@ -362,6 +366,9 @@
272 line = self.lines.pop(0).strip()
273
274 if targets and not fuzzy:
275+ # Use the first target for the current entry (returned at the
276+ # end of this next() call), and keep the others to generate
277+ # additional entries (returned the next next() calls).
278 trans_type, name, res_id = targets.pop(0)
279 for t, n, r in targets:
280 if t == trans_type == 'code': continue
281@@ -950,6 +957,10 @@
282 # lets create the language with locale information
283 lang_obj.load_lang(cr, SUPERUSER_ID, lang=lang, lang_name=lang_name)
284
285+ # Parse also the POT: it will possibly provide additional targets.
286+ # (Because the POT comments are correct on Launchpad but not the
287+ # PO comments due to a Launchpad limitation.)
288+ pot_reader = []
289
290 # now, the serious things: we read the language file
291 fileobj.seek(0)
292@@ -962,19 +973,42 @@
293 elif fileformat == 'po':
294 reader = TinyPoFile(fileobj)
295 f = ['type', 'name', 'res_id', 'src', 'value', 'comments']
296+
297+ # Make a reade for the POT file and be somewhat defensive for the
298+ # stable branch.
299+ if fileobj.name.endswith('.po'):
300+ try:
301+ # Normally the path looks like /path/to/xxx/i18n/lang.po
302+ # and we try to find the corresponding
303+ # /path/to/xxx/i18n/xxx.pot file.
304+ head, tail = os.path.split(fileobj.name)
305+ head2, tail2 = os.path.split(head)
306+ head3, tail3 = os.path.split(head2)
307+ pot_handle = misc.file_open(os.path.join(head3, tail3, 'i18n', tail3 + '.pot'))
308+ pot_reader = TinyPoFile(pot_handle)
309+ except:
310+ pass
311+
312 else:
313 _logger.error('Bad file format: %s', fileformat)
314 raise Exception(_('Bad file format'))
315
316+ # Read the POT `reference` comments, and keep them indexed by source
317+ # string.
318+ pot_targets = {}
319+ for type, name, res_id, src, _, comments in pot_reader:
320+ if type is not None:
321+ pot_targets.setdefault(src, {'value': None, 'targets': []})
322+ pot_targets[src]['targets'].append((type, name, res_id))
323+
324 # read the rest of the file
325- line = 1
326 irt_cursor = trans_obj._get_import_cursor(cr, SUPERUSER_ID, context=context)
327
328- for row in reader:
329- line += 1
330+ def process_row(row):
331+ """Process a single PO (or POT) entry."""
332 # skip empty rows and rows where the translation field (=last fiefd) is empty
333 #if (not row) or (not row[-1]):
334- # continue
335+ # return
336
337 # dictionary which holds values for this line of the csv file
338 # {'lang': ..., 'type': ..., 'name': ..., 'res_id': ...,
339@@ -984,9 +1018,17 @@
340 for i, field in enumerate(f):
341 dic[field] = row[i]
342
343+ # Get the `reference` comments from the POT.
344+ src = row[3]
345+ if pot_reader and src in pot_targets:
346+ pot_targets[src]['targets'] = filter(lambda x: x != row[:3], pot_targets[src]['targets'])
347+ pot_targets[src]['value'] = row[4]
348+ if not pot_targets[src]['targets']:
349+ del pot_targets[src]
350+
351 # This would skip terms that fail to specify a res_id
352 if not dic.get('res_id'):
353- continue
354+ return
355
356 res_id = dic.pop('res_id')
357 if res_id and isinstance(res_id, (int, long)) \
358@@ -1007,6 +1049,21 @@
359
360 irt_cursor.push(dic)
361
362+ # First process the entries from the PO file (doing so also fill/remove
363+ # the entries from the POT file).
364+ for row in reader:
365+ process_row(row)
366+
367+ # Then process the entries implied by the POT file (which is more
368+ # correct w.r.t. the targets) if some of them remain.
369+ pot_rows = []
370+ for src in pot_targets:
371+ value = pot_targets[src]['value']
372+ for type, name, res_id in pot_targets[src]['targets']:
373+ pot_rows.append((type, name, res_id, src, value, comments))
374+ for row in pot_rows:
375+ process_row(row)
376+
377 irt_cursor.finish()
378 trans_obj.clear_caches()
379 if verbose:

Subscribers

People subscribed via source and target branches

to status/vote changes: