for git v1.5.2 (and below): chdir to the directory of the target file before executin...
[translate_toolkit.git] / convert / test_dtd2po.py
blob9d3c64b7593865fc53c69610765c0af011156df5
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 from translate.convert import dtd2po
5 from translate.convert import test_convert
6 from translate.misc import wStringIO
7 from translate.storage import po
8 from translate.storage import dtd
10 class TestDTD2PO:
11 def dtd2po(self, dtdsource, dtdtemplate=None):
12 """helper that converts dtd source to po source without requiring files"""
13 inputfile = wStringIO.StringIO(dtdsource)
14 inputdtd = dtd.dtdfile(inputfile)
15 convertor = dtd2po.dtd2po()
16 if not dtdtemplate:
17 outputpo = convertor.convertstore(inputdtd)
18 else:
19 templatefile = wStringIO.StringIO(dtdtemplate)
20 templatedtd = dtd.dtdfile(templatefile)
21 outputpo = convertor.mergestore(templatedtd, inputdtd)
22 return outputpo
24 def convertdtd(self, dtdsource):
25 """call the convertdtd, return the outputfile"""
26 inputfile = wStringIO.StringIO(dtdsource)
27 outputfile = wStringIO.StringIO()
28 templatefile = None
29 assert dtd2po.convertdtd(inputfile, outputfile, templatefile)
30 return outputfile.getvalue()
32 def singleelement(self, pofile):
33 """checks that the pofile contains a single non-header element, and returns it"""
34 assert len(pofile.units) == 2
35 assert pofile.units[0].isheader()
36 print pofile.units[1]
37 return pofile.units[1]
39 def countelements(self, pofile):
40 """returns the number of non-header items"""
41 if pofile.units[0].isheader():
42 return len(pofile.units) - 1
43 else:
44 return len(pofile.units)
46 def test_simpleentity(self):
47 """checks that a simple dtd entity definition converts properly to a po entry"""
48 dtdsource = '<!ENTITY test.me "bananas for sale">\n'
49 pofile = self.dtd2po(dtdsource)
50 pounit = self.singleelement(pofile)
51 assert pounit.source == "bananas for sale"
52 assert pounit.target == ""
53 # Now with a template language
54 dtdtemplate = '<!ENTITY test.me "bananas for sale">\n'
55 dtdtranslated = '<!ENTITY test.me "piesangs te koop">\n'
56 pofile = self.dtd2po(dtdtranslated, dtdtemplate)
57 pounit = self.singleelement(pofile)
58 assert pounit.source == "bananas for sale"
59 assert pounit.target == "piesangs te koop"
61 def test_convertdtd(self):
62 """checks that the convertdtd function is working"""
63 dtdsource = '<!ENTITY saveas.label "Save As...">\n'
64 posource = self.convertdtd(dtdsource)
65 pofile = po.pofile(wStringIO.StringIO(posource))
66 unit = self.singleelement(pofile)
67 assert unit.source == "Save As..."
68 assert unit.target == ""
70 def test_apos(self):
71 """apostrophe should not break a single-quoted entity definition, bug 69"""
72 dtdsource = "<!ENTITY test.me 'bananas &apos; for sale'>\n"
73 pofile = self.dtd2po(dtdsource)
74 pounit = self.singleelement(pofile)
75 assert pounit.source == "bananas ' for sale"
77 def test_quotes(self):
78 """quotes should be handled in a single-quoted entity definition"""
79 dtdsource = """<!ENTITY test.metoo '"Bananas" for sale'>\n"""
80 pofile = self.dtd2po(dtdsource)
81 pounit = self.singleelement(pofile)
82 print str(pounit)
83 assert pounit.source == '"Bananas" for sale'
85 def test_emptyentity(self):
86 """checks that empty entity definitions survive into po file, bug 15"""
87 dtdsource = '<!ENTITY credit.translation "">\n'
88 pofile = self.dtd2po(dtdsource)
89 pounit = self.singleelement(pofile)
90 assert "credit.translation" in str(pounit)
92 def test_emptyentity_translated(self):
93 """checks that if we translate an empty entity it makes it into the PO, bug 101"""
94 dtdtemplate = '<!ENTITY credit.translation "">\n'
95 dtdsource = '<!ENTITY credit.translation "Translators Names">\n'
96 pofile = self.dtd2po(dtdsource, dtdtemplate)
97 unit = self.singleelement(pofile)
98 print unit
99 assert "credit.translation" in str(unit)
100 # We don't want this to simply be seen as a header:
101 assert len(unit.getid()) != 0
102 assert unit.target == "Translators Names"
104 def test_localisaton_note_simple(self):
105 """test the simple localisation more becomes a #. comment"""
106 dtdsource = '''<!-- LOCALIZATION NOTE (alwaysCheckDefault.height):
107 There's some sort of bug which makes wrapping checkboxes not properly reflow,
108 causing the bottom border of the groupbox to be cut off; set this
109 appropriately if your localization causes this checkbox to wrap.
111 <!ENTITY alwaysCheckDefault.height "3em">
113 pofile = self.dtd2po(dtdsource)
114 posource = str(pofile)
115 print posource
116 assert posource.count('#.') == 5 # 1 Header extracted from, 3 comment lines, 1 autoinserted comment
118 def test_localisation_note_merge(self):
119 """test that LOCALIZATION NOTES are added properly as #. comments and disambiguated with msgctxt entries"""
120 dtdtemplate = '<!--LOCALIZATION NOTE (%s): Some note -->\n' + \
121 '<!ENTITY %s "Source text">\n'
122 dtdsource = dtdtemplate % ("note1.label", "note1.label") + dtdtemplate % ("note2.label", "note2.label")
123 pofile = self.dtd2po(dtdsource)
124 posource = str(pofile.units[1]) + str(pofile.units[2])
125 print posource
126 assert posource.count('#.') == 2
127 assert posource.count('msgctxt') == 2
129 def test_donttranslate_simple(self):
130 """check that we handle DONT_TRANSLATE messages properly"""
131 dtdsource = '''<!-- LOCALIZATION NOTE (region.Altitude): DONT_TRANSLATE -->
132 <!ENTITY region.Altitude "Very High">'''
133 pofile = self.dtd2po(dtdsource)
134 assert self.countelements(pofile) == 0
135 dtdsource = '''<!-- LOCALIZATION NOTE (exampleOpenTag.label): DONT_TRANSLATE: they are text for HTML tagnames: "<i>" and "</i>" -->
136 <!ENTITY exampleOpenTag.label "&lt;i&gt;">'''
137 pofile = self.dtd2po(dtdsource)
138 assert self.countelements(pofile) == 0
139 dtdsource = '''<!-- LOCALIZATION NOTE (imapAdvanced.label): Do not translate "IMAP" -->
140 <!ENTITY imapAdvanced.label "Advanced IMAP Server Settings">'''
141 pofile = self.dtd2po(dtdsource)
142 assert self.countelements(pofile) == 1
144 def test_donttranslate_label(self):
145 """test strangeness when label entity is marked DONT_TRANSLATE and accesskey is not, bug 30"""
146 dtdsource = '<!--LOCALIZATION NOTE (editorCheck.label): DONT_TRANSLATE -->\n' + \
147 '<!ENTITY editorCheck.label "Composer">\n<!ENTITY editorCheck.accesskey "c">\n'
148 pofile = self.dtd2po(dtdsource)
149 posource = str(pofile)
150 # we need to decided what we're going to do here - see the comments in bug 30
151 # this tests the current implementation which is that the DONT_TRANSLATE string is removed, but the other remains
152 assert 'editorCheck.label' not in posource
153 assert 'editorCheck.accesskey' in posource
155 def test_donttranslate_onlyentity(self):
156 """if the entity is itself just another entity then it shouldn't appear in the output PO file"""
157 dtdsource = '''<!-- LOCALIZATION NOTE (mainWindow.title): DONT_TRANSLATE -->
158 <!ENTITY mainWindow.title "&brandFullName;">'''
159 pofile = self.dtd2po(dtdsource)
160 assert self.countelements(pofile) == 0
162 def test_donttranslate_commentedout(self):
163 """check that we don't process messages in <!-- comments -->: bug 102"""
164 dtdsource = '''<!-- commenting out until bug 38906 is fixed
165 <!ENTITY messagesHeader.label "Messages"> -->'''
166 pofile = self.dtd2po(dtdsource)
167 assert self.countelements(pofile) == 0
169 def test_spaces_at_start_of_dtd_lines(self):
170 """test that pretty print spaces at the start of subsequent DTD element lines are removed from the PO file, bug 79"""
171 # Space at the end of the line
172 dtdsource = '<!ENTITY noupdatesfound.intro "First line then \n' + \
173 ' next lines.">\n'
174 pofile = self.dtd2po(dtdsource)
175 pounit = self.singleelement(pofile)
176 # We still need to decide how we handle line line breaks in the DTD entities. It seems that we should actually
177 # drop the line break but this has not been implemented yet.
178 assert pounit.source == "First line then \nnext lines."
179 # No space at the end of the line
180 dtdsource = '<!ENTITY noupdatesfound.intro "First line then\n' + \
181 ' next lines.">\n'
182 pofile = self.dtd2po(dtdsource)
183 pounit = self.singleelement(pofile)
184 assert pounit.source == "First line then \nnext lines."
186 def test_accesskeys_folding(self):
187 """test that we fold accesskeys into message strings"""
188 dtdsource_template = '<!ENTITY fileSaveAs.%s "Save As...">\n<!ENTITY fileSaveAs.%s "S">\n'
189 lang_template = '<!ENTITY fileSaveAs.%s "Gcina ka...">\n<!ENTITY fileSaveAs.%s "G">\n'
190 for label in ("label", "title"):
191 for accesskey in ("accesskey", "accessKey", "akey"):
192 pofile = self.dtd2po(dtdsource_template % (label, accesskey))
193 pounit = self.singleelement(pofile)
194 assert pounit.source == "&Save As..."
195 # Test with template (bug 155)
196 pofile = self.dtd2po(lang_template % (label, accesskey), dtdsource_template % (label, accesskey))
197 pounit = self.singleelement(pofile)
198 assert pounit.source == "&Save As..."
199 assert pounit.target == "&Gcina ka..."
201 def test_accesskeys_mismatch(self):
202 """check that we can handle accesskeys that don't match and thus can't be folded into the .label entry"""
203 dtdsource = '<!ENTITY fileSave.label "Save">\n' + \
204 '<!ENTITY fileSave.accesskey "z">\n'
205 pofile = self.dtd2po(dtdsource)
206 assert self.countelements(pofile) == 2
208 def test_carriage_return_in_multiline_dtd(self):
209 """test that we create nice PO files when we find a \r\n in a multiline DTD element"""
210 dtdsource = '<!ENTITY noupdatesfound.intro "First line then \r\n' + \
211 ' next lines.">\n'
212 pofile = self.dtd2po(dtdsource)
213 unit = self.singleelement(pofile)
214 assert unit.source == "First line then \nnext lines."
216 def test_multiline_with_blankline(self):
217 """test that we can process a multiline entity that has a blank line in it, bug 331"""
218 dtdsource = '''
219 <!ENTITY multiline.text "
220 Some text
222 Some other text
223 ">'''
224 pofile = self.dtd2po(dtdsource)
225 unit = self.singleelement(pofile)
226 assert unit.source == "Some text \n \nSome other text"
228 def test_mulitline_closing_quotes(self):
229 """test that we support various styles and spaces after closing quotes on multiline entities"""
230 dtdsource = '''
231 <!ENTITY pref.plural '<span>opsies</span><span
232 class="noWin">preferences</span>' >
234 pofile = self.dtd2po(dtdsource)
235 unit = self.singleelement(pofile)
236 assert unit.source == '<span>opsies</span><span \nclass="noWin">preferences</span>'
238 def test_preserving_spaces(self):
239 """test that we preserve space that appear at the start of the first line of a DTD entity"""
240 # Space before first character
241 dtdsource = '<!ENTITY mainWindow.titlemodifiermenuseparator " - ">'
242 pofile = self.dtd2po(dtdsource)
243 unit = self.singleelement(pofile)
244 assert unit.source == " - "
245 # Double line and spaces
246 dtdsource = '<!ENTITY mainWindow.titlemodifiermenuseparator " - with a newline\n and more text">'
247 pofile = self.dtd2po(dtdsource)
248 unit = self.singleelement(pofile)
249 print repr(unit.source)
250 assert unit.source == " - with a newline \nand more text"
252 def test_escaping_newline_tabs(self):
253 """test that we handle all kinds of newline permutations"""
254 dtdsource = '<!ENTITY noupdatesfound.intro "A hard coded newline.\\nAnd tab\\t and a \\r carriage return.">\n'
255 converter = dtd2po.dtd2po()
256 thedtd = dtd.dtdunit()
257 thedtd.parse(dtdsource)
258 thepo = po.pounit()
259 converter.convertstrings(thedtd, thepo)
260 print thedtd
261 print thepo.source
262 # \n in a dtd should also appear as \n in the PO file
263 assert thepo.source == r"A hard coded newline.\nAnd tab\t and a \r carriage return."
265 def test_abandoned_accelerator(self):
266 """test that when a language DTD has an accelerator but the template DTD does not that we abandon the accelerator"""
267 dtdtemplate = '<!ENTITY test.label "Test">\n'
268 dtdlanguage = '<!ENTITY test.label "Toets">\n<!ENTITY test.accesskey "T">\n'
269 pofile = self.dtd2po(dtdlanguage, dtdtemplate)
270 unit = self.singleelement(pofile)
271 assert unit.source == "Test"
272 assert unit.target == "Toets"
274 def test_unassociable_accelerator(self):
275 """test to see that we can handle accelerator keys that cannot be associated correctly"""
276 dtdsource = '<!ENTITY managecerts.button "Manage Certificates...">\n<!ENTITY managecerts.accesskey "M">'
277 pofile = self.dtd2po(dtdsource)
278 assert pofile.units[1].source == "Manage Certificates..."
279 assert pofile.units[2].source == "M"
280 pofile = self.dtd2po(dtdsource, dtdsource)
281 assert pofile.units[1].target == "Manage Certificates..."
282 assert pofile.units[2].target == "M"
284 def test_changed_labels_and_accelerators(self):
285 """test to ensure that when the template changes an entity name we can still manage the accelerators"""
286 dtdtemplate = '''<!ENTITY managecerts.caption "Manage Certificates">
287 <!ENTITY managecerts.text "Use the Certificate Manager to manage your personal certificates, as well as those of other people and certificate authorities.">
288 <!ENTITY managecerts.button "Manage Certificates...">
289 <!ENTITY managecerts.accesskey "M">'''
290 dtdlanguage = '''<!ENTITY managecerts.label "ﺇﺩﺍﺭﺓ ﺎﻠﺸﻫﺍﺩﺎﺗ">
291 <!ENTITY managecerts.text "ﺎﺴﺘﺧﺪﻣ ﻡﺪﻳﺭ ﺎﻠﺸﻫﺍﺩﺎﺗ ﻹﺩﺍﺭﺓ ﺶﻫﺍﺩﺎﺘﻛ ﺎﻠﺸﺨﺼﻳﺓ، ﺏﺍﻺﺿﺎﻓﺓ ﻞﺘﻠﻛ ﺎﻠﺧﺎﺻﺓ ﺏﺍﻶﺧﺮﻴﻧ ﻭ ﺲﻠﻃﺎﺗ ﺎﻠﺸﻫﺍﺩﺎﺗ.">
292 <!ENTITY managecerts.button "ﺇﺩﺍﺭﺓ ﺎﻠﺸﻫﺍﺩﺎﺗ...">
293 <!ENTITY managecerts.accesskey "ﺩ">'''
294 pofile = self.dtd2po(dtdlanguage, dtdtemplate)
295 print pofile
296 assert pofile.units[3].source == "Manage Certificates..."
297 assert pofile.units[3].target == u"ﺇﺩﺍﺭﺓ ﺎﻠﺸﻫﺍﺩﺎﺗ..."
298 assert pofile.units[4].source == "M"
299 assert pofile.units[4].target == u"ﺩ"
301 def wtest_accelerator_keys_not_in_sentence(self):
302 """tests to ensure that we can manage accelerator keys that are not part of the transated sentence eg in Chinese"""
303 dtdtemplate = '''<!ENTITY useAutoScroll.label "Use autoscrolling">
304 <!ENTITY useAutoScroll.accesskey "a">'''
305 dtdlanguage = '''<!ENTITY useAutoScroll.label "使用自動捲動(Autoscrolling)">
306 <!ENTITY useAutoScroll.accesskey "a">'''
307 pofile = self.dtd2po(dtdlanguage, dtdtemplate)
308 print pofile
309 assert pofile.units[1].target == "使用自動捲動(&Autoscrolling)"
310 # We assume that accesskeys with no associated key should be done as follows "XXXX (&A)"
311 # TODO - check that we can unfold this from PO -> DTD
312 dtdlanguage = '''<!ENTITY useAutoScroll.label "使用自動捲動">
313 <!ENTITY useAutoScroll.accesskey "a">'''
314 pofile = self.dtd2po(dtdlanguage, dtdtemplate)
315 print pofile
316 assert pofile.units[1].target == "使用自動捲動 (&A)"
318 def test_exclude_entity_includes(self):
319 """test that we don't turn an include into a translatable string"""
320 dtdsource = '<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">'
321 pofile = self.dtd2po(dtdsource)
322 assert self.countelements(pofile) == 0
324 def test_linewraps(self):
325 """check that redundant line wraps are removed from the po file"""
326 dtdsource = '''<!ENTITY generic.longDesc "
327 <p>Test me.</p>
328 ">'''
329 pofile = self.dtd2po(dtdsource)
330 pounit = self.singleelement(pofile)
331 assert pounit.source == "<p>Test me.</p>"
333 def test_merging_with_new_untranslated(self):
334 """test that when we merge in new untranslated strings with existing translations we manage the encodings properly"""
335 # This should probably be in test_po.py but was easier to do here
336 dtdtemplate = '''<!ENTITY unreadFolders.label "Unread">\n<!ENTITY viewPickerUnread.label "Unread">\n<!ENTITY unreadColumn.label "Unread">'''
337 dtdlanguage = '''<!ENTITY viewPickerUnread.label "Непрочетени">\n<!ENTITY unreadFolders.label "Непрочетени">'''
338 pofile = self.dtd2po(dtdlanguage, dtdtemplate)
339 print pofile
340 assert pofile.units[1].source == "Unread"
342 def test_merge_without_template(self):
343 """test that we we manage the case where we merge and their is no template file"""
344 # If we supply a template file we should fail if the template file does not exist or is blank. We should
345 # not put the translation in as the source.
346 # TODO: this test fails, since line 16 checks for "not dtdtemplate"
347 # instead of checking for "dtdtemplate is None". What is correct?
348 dtdtemplate = ''
349 dtdsource = '<!ENTITY no.template "Target">'
350 pofile = self.dtd2po(dtdsource, dtdtemplate)
351 print pofile
352 assert self.countelements(pofile) == 0
354 class TestDTD2POCommand(test_convert.TestConvertCommand, TestDTD2PO):
355 """Tests running actual dtd2po commands on files"""
356 convertmodule = dtd2po
357 defaultoptions = {"progress": "none"}
359 def test_help(self):
360 """tests getting help"""
361 options = test_convert.TestConvertCommand.test_help(self)
362 options = self.help_check(options, "-P, --pot")
363 options = self.help_check(options, "-t TEMPLATE, --template=TEMPLATE")
364 options = self.help_check(options, "--duplicates=DUPLICATESTYLE", last=True)