Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(71)

Delta Between Two Patch Sets: translate/storage/cpo.py

Issue 62: CPO support for previous messages SVN Base: https://translate.svn.sourceforge.net/svnroot/translate/src/trunk/
Left Patch Set: Fixed minor issues Created 1 year, 4 months ago
Right Patch Set: Updated accessors, tests and added base.py & test_base.py Created 1 year, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: View regular side by side diff
Right: View regular side by side diff
LEFTRIGHT
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 # 3 #
4 # Copyright 2002-2007 Zuza Software Foundation 4 # Copyright 2002-2007 Zuza Software Foundation
5 # 5 #
6 # This file is part of translate. 6 # This file is part of translate.
7 # 7 #
8 # translate is free software; you can redistribute it and/or modify 8 # translate is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by 9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or 10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version. 11 # (at your option) any later version.
12 # 12 #
13 # translate is distributed in the hope that it will be useful, 13 # translate is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details. 16 # GNU General Public License for more details.
17 # 17 #
18 # You should have received a copy of the GNU General Public License 18 # You should have received a copy of the GNU General Public License
19 # along with translate; if not, write to the Free Software 19 # along with translate; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 21
22 """Classes that hold units of .po files (pounit) or entire files (pofile). 22 """Classes that hold units of .po files (pounit) or entire files (pofile).
23 23
24 Gettext-style .po (or .pot) files are used in translations for KDE, GNOME and 24 Gettext-style .po (or .pot) files are used in translations for KDE, GNOME and
25 many other projects. 25 many other projects.
26 26
27 This uses libgettextpo from the gettext package. Any version before 0.17 will 27 This uses libgettextpo from the gettext package. Any version before 0.17 will
28 at least cause some subtle bugs or may not work at all. Developers might want 28 at least cause some subtle bugs or may not work at all. Developers might want
29 to have a look at gettext-tools/libgettextpo/gettext-po.h from the gettext 29 to have a look at gettext-tools/libgettextpo/gettext-po.h from the gettext
30 package for the public API of the library. 30 package for the public API of the library.
31 """ 31 """
32 32
33 from translate.misc.multistring import multistring 33 from translate.misc.multistring import multistring
34 from translate.storage import pocommon 34 from translate.storage import pocommon
35 from translate.misc import quote 35 from translate.misc import quote
36 from translate.lang import data 36 from translate.lang import data
37 from ctypes import * 37 from ctypes import *
38 import ctypes.util 38 import ctypes.util
39 try: 39 try:
40 import cStringIO as StringIO 40 import cStringIO as StringIO
41 except ImportError: 41 except ImportError:
42 import StringIO 42 import StringIO
43 import os 43 import os
44 import pypo 44 import pypo
45 import re 45 import re
46 import sys 46 import sys
47 import tempfile 47 import tempfile
48 48
49 lsep = " " 49 lsep = " "
50 """Seperator for #: entries""" 50 """Seperator for #: entries"""
51 51
52 STRING = c_char_p 52 STRING = c_char_p
53 53
54 # Structures 54 # Structures
55 class po_message(Structure): 55 class po_message(Structure):
56 _fields_ = [] 56 _fields_ = []
57 57
58 # Function prototypes 58 # Function prototypes
59 xerror_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c _uint, c_int, STRING) 59 xerror_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c _uint, c_int, STRING)
60 xerror2_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRIN G) 60 xerror2_prototype = CFUNCTYPE(None, c_int, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRING, POINTER(po_message), STRING, c_uint, c_uint, c_int, STRIN G)
61 61
62 62
63 # Structures (error handler) 63 # Structures (error handler)
64 class po_xerror_handler(Structure): 64 class po_xerror_handler(Structure):
65 _fields_ = [('xerror', xerror_prototype), 65 _fields_ = [('xerror', xerror_prototype),
66 ('xerror2', xerror2_prototype)] 66 ('xerror2', xerror2_prototype)]
67 67
68 class po_error_handler(Structure): 68 class po_error_handler(Structure):
69 _fields_ = [ 69 _fields_ = [
70 ('error', CFUNCTYPE(None, c_int, c_int, STRING)), 70 ('error', CFUNCTYPE(None, c_int, c_int, STRING)),
71 ('error_at_line', CFUNCTYPE(None, c_int, c_int, STRING, c_uint, STRING)), 71 ('error_at_line', CFUNCTYPE(None, c_int, c_int, STRING, c_uint, STRING)),
72 ('multiline_warning', CFUNCTYPE(None, STRING, STRING)), 72 ('multiline_warning', CFUNCTYPE(None, STRING, STRING)),
73 ('multiline_error', CFUNCTYPE(None, STRING, STRING)), 73 ('multiline_error', CFUNCTYPE(None, STRING, STRING)),
74 ] 74 ]
75 75
76 # Callback functions for po_xerror_handler 76 # Callback functions for po_xerror_handler
77 def xerror_cb(severity, message, filename, lineno, column, multilint_p, message_ text): 77 def xerror_cb(severity, message, filename, lineno, column, multilint_p, message_ text):
78 print >> sys.stderr, "xerror_cb", severity, message, filename, lineno, colum n, multilint_p, message_text 78 print >> sys.stderr, "xerror_cb", severity, message, filename, lineno, colum n, multilint_p, message_text
79 if severity >= 1: 79 if severity >= 1:
80 raise ValueError(message_text) 80 raise ValueError(message_text)
81 81
82 def xerror2_cb(severity, message1, filename1, lineno1, column1, multiline_p1, me ssage_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2) : 82 def xerror2_cb(severity, message1, filename1, lineno1, column1, multiline_p1, me ssage_text1, message2, filename2, lineno2, column2, multiline_p2, message_text2) :
83 print >> sys.stderr, "xerror2_cb", severity, message1, filename1, lineno1, c olumn1, multiline_p1, message_text1, message2, filename2, lineno2, column2, mult iline_p2, message_text2 83 print >> sys.stderr, "xerror2_cb", severity, message1, filename1, lineno1, c olumn1, multiline_p1, message_text1, message2, filename2, lineno2, column2, mult iline_p2, message_text2
84 if severity >= 1: 84 if severity >= 1:
85 raise ValueError(message_text1) 85 raise ValueError(message_text1)
86 86
87 87
88 88
89 # Load libgettextpo 89 # Load libgettextpo
90 gpo = None 90 gpo = None
91 # 'gettextpo' is recognised on Unix, while only 'libgettextpo' is recognised on 91 # 'gettextpo' is recognised on Unix, while only 'libgettextpo' is recognised on
92 # windows. Therefore we test both. 92 # windows. Therefore we test both.
93 names = ['gettextpo', 'libgettextpo'] 93 names = ['gettextpo', 'libgettextpo']
94 for name in names: 94 for name in names:
95 lib_location = ctypes.util.find_library(name) 95 lib_location = ctypes.util.find_library(name)
96 if lib_location: 96 if lib_location:
97 gpo = cdll.LoadLibrary(lib_location) 97 gpo = cdll.LoadLibrary(lib_location)
98 if gpo: 98 if gpo:
99 break 99 break
100 else: 100 else:
101 # Now we are getting desperate, so let's guess a unix type DLL that might 101 # Now we are getting desperate, so let's guess a unix type DLL that might
102 # be in LD_LIBRARY_PATH or loaded with LD_PRELOAD 102 # be in LD_LIBRARY_PATH or loaded with LD_PRELOAD
103 try: 103 try:
104 gpo = cdll.LoadLibrary('libgettextpo.so') 104 gpo = cdll.LoadLibrary('libgettextpo.so')
105 except OSError, e: 105 except OSError, e:
106 raise ImportError("gettext PO library not found") 106 raise ImportError("gettext PO library not found")
107 107
108 # Setup return and paramater types 108 # Setup return and paramater types
109 # File access 109 # File access
110 gpo.po_file_read_v3.argtypes = [STRING, POINTER(po_xerror_handler)] 110 gpo.po_file_read_v3.argtypes = [STRING, POINTER(po_xerror_handler)]
111 gpo.po_file_write_v2.argtypes = [c_int, STRING, POINTER(po_xerror_handler)] 111 gpo.po_file_write_v2.argtypes = [c_int, STRING, POINTER(po_xerror_handler)]
112 gpo.po_file_write_v2.retype = c_int 112 gpo.po_file_write_v2.retype = c_int
113 113
114 # Header 114 # Header
115 gpo.po_file_domain_header.restype = STRING 115 gpo.po_file_domain_header.restype = STRING
116 gpo.po_header_field.restype = STRING 116 gpo.po_header_field.restype = STRING
117 gpo.po_header_field.argtypes = [STRING, STRING] 117 gpo.po_header_field.argtypes = [STRING, STRING]
118 118
119 # Locations (filepos) 119 # Locations (filepos)
120 gpo.po_filepos_file.restype = STRING 120 gpo.po_filepos_file.restype = STRING
121 gpo.po_message_filepos.restype = c_int 121 gpo.po_message_filepos.restype = c_int
122 gpo.po_message_filepos.argtypes = [c_int, c_int] 122 gpo.po_message_filepos.argtypes = [c_int, c_int]
123 gpo.po_message_add_filepos.argtypes = [c_int, STRING, c_int] 123 gpo.po_message_add_filepos.argtypes = [c_int, STRING, c_int]
124 124
125 # Message (get methods) 125 # Message (get methods)
126 gpo.po_message_comments.restype = STRING 126 gpo.po_message_comments.restype = STRING
127 gpo.po_message_extracted_comments.restype = STRING 127 gpo.po_message_extracted_comments.restype = STRING
128 gpo.po_message_prev_msgctxt.restype = STRING 128 gpo.po_message_prev_msgctxt.restype = STRING
129 gpo.po_message_prev_msgid.restype = STRING 129 gpo.po_message_prev_msgid.restype = STRING
130 gpo.po_message_prev_msgid_plural.restype = STRING 130 gpo.po_message_prev_msgid_plural.restype = STRING
131 gpo.po_message_is_format.restype = c_int 131 gpo.po_message_is_format.restype = c_int
132 gpo.po_message_msgctxt.restype = STRING 132 gpo.po_message_msgctxt.restype = STRING
133 gpo.po_message_msgid.restype = STRING 133 gpo.po_message_msgid.restype = STRING
134 gpo.po_message_msgid_plural.restype = STRING 134 gpo.po_message_msgid_plural.restype = STRING
135 gpo.po_message_msgstr.restype = STRING 135 gpo.po_message_msgstr.restype = STRING
136 gpo.po_message_msgstr_plural.restype = STRING 136 gpo.po_message_msgstr_plural.restype = STRING
137 gpo.po_message_prev_msgid.restype = STRING
dwaynebailey 2008/07/17 12:59:27 I assume this should be previous msgctxt? I know
georgeyk 2008/07/18 05:15:49 On 2008/07/17 12:59:27, dwaynebailey wrote: > I as
138 gpo.po_message_prev_msgid.restype = STRING
139 gpo.po_message_prev_msgid_plural.restype = STRING
137 140
138 # Message (set methods) 141 # Message (set methods)
139 gpo.po_message_set_comments.argtypes = [c_int, STRING] 142 gpo.po_message_set_comments.argtypes = [c_int, STRING]
140 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING] 143 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING]
141 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int] 144 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int]
142 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING] 145 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING]
143 gpo.po_message_set_prev_msgctxt.argtypes = [c_int, STRING] 146 gpo.po_message_set_prev_msgctxt.argtypes = [c_int, STRING]
144 gpo.po_message_set_prev_msgid.argtypes = [c_int, STRING] 147 gpo.po_message_set_prev_msgid.argtypes = [c_int, STRING]
145 gpo.po_message_set_prev_msgid_plural.argtypes = [c_int, STRING] 148 gpo.po_message_set_prev_msgid_plural.argtypes = [c_int, STRING]
146 149
147 # Setup the po_xerror_handler 150 # Setup the po_xerror_handler
148 xerror_handler = po_xerror_handler() 151 xerror_handler = po_xerror_handler()
149 xerror_handler.xerror = xerror_prototype(xerror_cb) 152 xerror_handler.xerror = xerror_prototype(xerror_cb)
150 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb) 153 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb)
151 154
152 def escapeforpo(text): 155 def escapeforpo(text):
153 return pypo.escapeforpo(text) 156 return pypo.escapeforpo(text)
154 157
155 def quoteforpo(text): 158 def quoteforpo(text):
156 return pypo.quoteforpo(text) 159 return pypo.quoteforpo(text)
157 160
158 def unquotefrompo(postr, joinwithlinebreak=False): 161 def unquotefrompo(postr, joinwithlinebreak=False):
159 return pypo.unquotefrompo(postr, joinwithlinebreak) 162 return pypo.unquotefrompo(postr, joinwithlinebreak)
160 163
161 def encodingToUse(encoding): 164 def encodingToUse(encoding):
162 return pypo.encodingToUse(encoding) 165 return pypo.encodingToUse(encoding)
163
164 def get_libgettextpo_version():
165 """Returns the libgettextpo version
166
167 @return: a three-value tuple containing the libgettextpo version in the
168 following format:
169 (major version, minor version, subminor version)
170 """
171 libversion = c_long.in_dll(gpo, 'libgettextpo_version')
172 major = libversion.value >> 16
173 minor = libversion.value >> 8
174 subminor = libversion.value - (major << 16) - (minor << 8)
175 return major, minor, subminor
176
177 166
178 class pounit(pocommon.pounit): 167 class pounit(pocommon.pounit):
179 def __init__(self, source=None, encoding='utf-8', gpo_message=None): 168 def __init__(self, source=None, encoding='utf-8', gpo_message=None):
180 self._encoding = encoding 169 self._encoding = encoding
181 if not gpo_message: 170 if not gpo_message:
182 self._gpo_message = gpo.po_message_create() 171 self._gpo_message = gpo.po_message_create()
183 if source or source == "": 172 if source or source == "":
184 self.source = source 173 self.source = source
185 self.target = "" 174 self.target = ""
186 elif gpo_message: 175 elif gpo_message:
187 self._gpo_message = gpo_message 176 self._gpo_message = gpo_message
188 177
189 def setmsgidcomment(self, msgidcomment): 178 def setmsgidcomment(self, msgidcomment):
190 if msgidcomment: 179 if msgidcomment:
191 newsource = "_: " + msgidcomment + "\n" + self.source 180 newsource = "_: " + msgidcomment + "\n" + self.source
192 self.source = newsource 181 self.source = newsource
193 msgidcomment = property(None, setmsgidcomment) 182 msgidcomment = property(None, setmsgidcomment)
194 183
195 def setmsgid_plural(self, msgid_plural): 184 def setmsgid_plural(self, msgid_plural):
196 if isinstance(msgid_plural, list): 185 if isinstance(msgid_plural, list):
197 msgid_plural = "".join(msgid_plural) 186 msgid_plural = "".join(msgid_plural)
198 gpo.po_message_set_msgid_plural(self._gpo_message, msgid_plural) 187 gpo.po_message_set_msgid_plural(self._gpo_message, msgid_plural)
199 msgid_plural = property(None, setmsgid_plural) 188 msgid_plural = property(None, setmsgid_plural)
200 189
201 def setprev_context(self, context): 190 def setprev_context(self, msgid_context):
dwaynebailey 2008/07/17 12:59:27 Can we avoid references to msgid but stick to gene
202 """Set the previous context for this message""" 191 """Set the previous context for this message"""
203 if self.isfuzzy(): 192 if self.isfuzzy():
204 return 193 return
205 gpo.po_message_set_prev_msgctxt(self._gpo_message, context) 194 gpo.po_message_set_prev_msgctxt(self._gpo_message, msgid_context)
206 195
207 def getprev_context(self): 196 def getprev_context(self):
208 """Get the previous context for this message, if any """ 197 """Get the previous context for this message, if any """
209 prev_ctxt = gpo.po_message_prev_msgctxt(self._gpo_message) 198 prev_ctxt = gpo.po_message_prev_msgctxt(self._gpo_message)
210 if not prev_ctxt: 199 if not prev_ctxt:
211 return "" 200 return ""
212 return prev_ctxt 201 return prev_ctxt
213 202
214 prev_context = property(getprev_context, setprev_context) 203 prev_context = property(getprev_context, setprev_context)
215 204
216 def setprev_source(self, prev_source): 205 def setprev_source(self, prev_source):
217 """Set msgid as a previous msgid for this message""" 206 """Set msgid as a previous msgid for this message"""
218 if self.isfuzzy(): 207 if self.isfuzzy():
219 return
220 if not self.source:
221 return 208 return
222 if isinstance(prev_source, multistring): 209 if isinstance(prev_source, multistring):
223 prev_source = prev_source.strings 210 prev_source = prev_source.strings
224 if isinstance(prev_source, unicode): 211 if isinstance(prev_source, unicode):
225 prev_source = prev_source.encode(self._encoding) 212 prev_source = prev_source.encode(self._encoding)
226 if isinstance(prev_source, list): 213 if isinstance(prev_source, list):
227 if len(prev_source) > 1: 214 if len(prev_source) > 1:
228 msgid_plural = str(prev_source[1]) 215 msgid_plural = str(prev_source[1])
229 if isinstance(msgid_plural, list): 216 if isinstance(msgid_plural, list):
230 msgid_plural = "".join(msgid_plural) 217 msgid_plural = "".join(msgid_plural)
231 218
232 gpo.po_message_set_prev_msgid_plural(self._gpo_message, 219 gpo.po_message_set_prev_msgid_plural(self._gpo_message,
233 msgid_plural) 220 msgid_plural)
234 prev_source = str(prev_source[0]) 221 prev_source = str(prev_source[0])
235 222
223 self.source = ""
236 gpo.po_message_set_prev_msgid(self._gpo_message, prev_source) 224 gpo.po_message_set_prev_msgid(self._gpo_message, prev_source)
237 225
238 def getprev_source(self): 226 def getprev_source(self):
239 """Get the previous msgid for this message, if any""" 227 """Get the previous msgid for this message, if any"""
240 prev_source = gpo.po_message_prev_msgid(self._gpo_message) 228 prev_source = gpo.po_message_prev_msgid(self._gpo_message)
241 if not prev_source: 229 if not prev_source:
242 return multistring(u"") 230 return multistring(u"")
243 231
244 prev_source = multistring(prev_source, self._encoding) 232 prev_source = multistring(prev_source, self._encoding)
245 prev_plural = gpo.po_message_prev_msgid_plural(self._gpo_message) 233 prev_plural = gpo.po_message_prev_msgid_plural(self._gpo_message)
246 if prev_plural: 234 if prev_plural:
247 prev_source.strings.append(prev_plural.decode(self._encoding)) 235 prev_source.strings.append(prev_plural.decode(self._encoding))
248 return prev_source 236 return prev_source
249 237
250 prev_source = property(getprev_source, setprev_source) 238 prev_source = property(getprev_source, setprev_source)
251 239
252 def set_as_previous(self): 240 def set_as_previous(self):
253 """Sets the current msgid as previous message. Then, the current 241 """Sets the current msgid as previous message. Then, the current
254 msgid could be filled with a new msgid. Note that, if a unit 242 msgid could be filled with a new msgid. Note that, if a unit
255 contains a previous msgid, it also is marked as fuzzy (to keep the 243 contains a previous msgid, it also is marked as fuzzy (to keep the
256 same behavior as msgmerge). 244 same behavior as msgmerge).
257 """ 245 """
258 if not self.target: 246 if not self.target:
259 return 247 return
260 # since the context accessor might also return KDE style comments, I'm 248
261 # just using the libgettextpo accessor to retrive only the context 249 # the 'pure' context string
262 self.prev_context = gpo.po_message_msgctxt(self._gpo_message) 250 self.prev_context = gpo.po_message_msgctxt(self._gpo_message)
dwaynebailey 2008/07/17 12:59:27 Shouldn't this use an accesors like self.context t
georgeyk 2008/07/18 05:15:49 On 2008/07/17 12:59:27, dwaynebailey wrote: > Shou
263 self.prev_source = self.source 251 self.prev_source = self.source
264 self.source = ""
265 self.markfuzzy() 252 self.markfuzzy()
266 253
267 def getsource(self): 254 def getsource(self):
268 def remove_msgid_comments(text): 255 def remove_msgid_comments(text):
269 if not text: 256 if not text:
270 return text 257 return text
271 if text.startswith("_:"): 258 if text.startswith("_:"):
272 remainder = re.search(r"_: .*\n(.*)", text) 259 remainder = re.search(r"_: .*\n(.*)", text)
273 if remainder: 260 if remainder:
274 return remainder.group(1) 261 return remainder.group(1)
275 else: 262 else:
276 return u"" 263 return u""
277 else: 264 else:
278 return text 265 return text
279 singular = remove_msgid_comments(gpo.po_message_msgid(self._gpo_message) ) 266 singular = remove_msgid_comments(gpo.po_message_msgid(self._gpo_message) )
280 if singular: 267 if singular:
281 multi = multistring(singular, self._encoding) 268 multi = multistring(singular, self._encoding)
282 if self.hasplural(): 269 if self.hasplural():
283 pluralform = gpo.po_message_msgid_plural(self._gpo_message) 270 pluralform = gpo.po_message_msgid_plural(self._gpo_message)
284 if isinstance(pluralform, str): 271 if isinstance(pluralform, str):
285 pluralform = pluralform.decode(self._encoding) 272 pluralform = pluralform.decode(self._encoding)
286 multi.strings.append(pluralform) 273 multi.strings.append(pluralform)
287 return multi 274 return multi
288 else: 275 else:
289 return u"" 276 return u""
290 277
291 def setsource(self, source): 278 def setsource(self, source):
292 if isinstance(source, multistring): 279 if isinstance(source, multistring):
293 source = source.strings 280 source = source.strings
294 if isinstance(source, unicode): 281 if isinstance(source, unicode):
295 source = source.encode(self._encoding) 282 source = source.encode(self._encoding)
296 if isinstance(source, list): 283 if isinstance(source, list):
297 gpo.po_message_set_msgid(self._gpo_message, str(source[0])) 284 gpo.po_message_set_msgid(self._gpo_message, str(source[0]))
298 if len(source) > 1: 285 if len(source) > 1:
299 gpo.po_message_set_msgid_plural(self._gpo_message, str(source[1] )) 286 gpo.po_message_set_msgid_plural(self._gpo_message, str(source[1] ))
300 else: 287 else:
301 gpo.po_message_set_msgid(self._gpo_message, source) 288 gpo.po_message_set_msgid(self._gpo_message, source)
302 gpo.po_message_set_msgid_plural(self._gpo_message, None) 289 gpo.po_message_set_msgid_plural(self._gpo_message, None)
303 290
304 source = property(getsource, setsource) 291 source = property(getsource, setsource)
305 292
306 def gettarget(self): 293 def gettarget(self):
307 if self.hasplural(): 294 if self.hasplural():
308 plurals = [] 295 plurals = []
309 nplural = 0 296 nplural = 0
310 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural) 297 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural)
311 while plural: 298 while plural:
312 plurals.append(plural) 299 plurals.append(plural)
313 nplural += 1 300 nplural += 1
314 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural ) 301 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural )
315 if plurals: 302 if plurals:
316 multi = multistring(plurals, encoding=self._encoding) 303 multi = multistring(plurals, encoding=self._encoding)
317 else: 304 else:
318 multi = multistring(u"") 305 multi = multistring(u"")
319 else: 306 else:
320 multi = multistring(gpo.po_message_msgstr(self._gpo_message) or u"", encoding=self._encoding) 307 multi = multistring(gpo.po_message_msgstr(self._gpo_message) or u"", encoding=self._encoding)
321 return multi 308 return multi
322 309
323 def settarget(self, target): 310 def settarget(self, target):
324 # for plural strings: convert 'target' into a list 311 # for plural strings: convert 'target' into a list
325 if self.hasplural(): 312 if self.hasplural():
326 if isinstance(target, multistring): 313 if isinstance(target, multistring):
327 target = target.strings 314 target = target.strings
328 elif isinstance(target, basestring): 315 elif isinstance(target, basestring):
329 target = [target] 316 target = [target]
330 # for non-plurals: check number of items in 'target' 317 # for non-plurals: check number of items in 'target'
331 elif isinstance(target,(dict, list)): 318 elif isinstance(target,(dict, list)):
332 if len(target) == 1: 319 if len(target) == 1:
333 target = target[0] 320 target = target[0]
334 else: 321 else:
335 raise ValueError("po msgid element has no plural but msgstr has %d elements (%s)" % (len(target), target)) 322 raise ValueError("po msgid element has no plural but msgstr has %d elements (%s)" % (len(target), target))
336 # empty the previous list of messages 323 # empty the previous list of messages
337 # TODO: the "pypo" implementation does not remove the previous items of 324 # TODO: the "pypo" implementation does not remove the previous items of
338 # the target, if self.target == target (essentially: comparing only 325 # the target, if self.target == target (essentially: comparing only
339 # the first item of a plural string with the single new string) 326 # the first item of a plural string with the single new string)
340 # Maybe this behaviour should be unified. 327 # Maybe this behaviour should be unified.
341 if isinstance(target, (dict, list)): 328 if isinstance(target, (dict, list)):
342 i = 0 329 i = 0
343 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 330 message = gpo.po_message_msgstr_plural(self._gpo_message, i)
344 while message is not None: 331 while message is not None:
345 gpo.po_message_set_msgstr_plural(self._gpo_message, i, None) 332 gpo.po_message_set_msgstr_plural(self._gpo_message, i, None)
346 i += 1 333 i += 1
347 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 334 message = gpo.po_message_msgstr_plural(self._gpo_message, i)
348 # add the items of a list 335 # add the items of a list
349 if isinstance(target, list): 336 if isinstance(target, list):
350 for i in range(len(target)): 337 for i in range(len(target)):
351 targetstring = target[i] 338 targetstring = target[i]
352 if isinstance(targetstring, unicode): 339 if isinstance(targetstring, unicode):
353 targetstring = targetstring.encode(self._encoding) 340 targetstring = targetstring.encode(self._encoding)
354 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing) 341 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing)
355 # add the values of a dict 342 # add the values of a dict
356 elif isinstance(target, dict): 343 elif isinstance(target, dict):
357 for i, targetstring in enumerate(target.itervalues()): 344 for i, targetstring in enumerate(target.itervalues()):
358 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing) 345 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing)
359 # add a single string 346 # add a single string
360 else: 347 else:
361 if isinstance(target, unicode): 348 if isinstance(target, unicode):
362 target = target.encode(self._encoding) 349 target = target.encode(self._encoding)
363 if target is None: 350 if target is None:
364 gpo.po_message_set_msgstr(self._gpo_message, "") 351 gpo.po_message_set_msgstr(self._gpo_message, "")
365 else: 352 else:
366 gpo.po_message_set_msgstr(self._gpo_message, target) 353 gpo.po_message_set_msgstr(self._gpo_message, target)
367 target = property(gettarget, settarget) 354 target = property(gettarget, settarget)
368 355
369 def getid(self): 356 def getid(self):
370 """The unique identifier for this unit according to the convensions in 357 """The unique identifier for this unit according to the convensions in
371 .mo files.""" 358 .mo files."""
372 id = gpo.po_message_msgid(self._gpo_message) 359 id = gpo.po_message_msgid(self._gpo_message)
373 # Gettext does not consider the plural to determine duplicates, only 360 # Gettext does not consider the plural to determine duplicates, only
374 # the msgid. For generation of .mo files, we might want to use this 361 # the msgid. For generation of .mo files, we might want to use this
375 # code to generate the entry for the hash table, but for now, it is 362 # code to generate the entry for the hash table, but for now, it is
376 # commented out for conformance to gettext. 363 # commented out for conformance to gettext.
377 # plural = gpo.po_message_msgid_plural(self._gpo_message) 364 # plural = gpo.po_message_msgid_plural(self._gpo_message)
378 # if not plural is None: 365 # if not plural is None:
379 # id = '%s\0%s' % (id, plural) 366 # id = '%s\0%s' % (id, plural)
380 context = gpo.po_message_msgctxt(self._gpo_message) 367 context = gpo.po_message_msgctxt(self._gpo_message)
381 if context: 368 if context:
382 id = "%s\04%s" % (context, id) 369 id = "%s\04%s" % (context, id)
383 return id or "" 370 return id or ""
384 371
385 def getnotes(self, origin=None): 372 def getnotes(self, origin=None):
386 if origin == None: 373 if origin == None:
387 comments = gpo.po_message_comments(self._gpo_message) + \ 374 comments = gpo.po_message_comments(self._gpo_message) + \
388 gpo.po_message_extracted_comments(self._gpo_message) 375 gpo.po_message_extracted_comments(self._gpo_message)
389 elif origin == "translator": 376 elif origin == "translator":
390 comments = gpo.po_message_comments(self._gpo_message) 377 comments = gpo.po_message_comments(self._gpo_message)
391 elif origin in ["programmer", "developer", "source code"]: 378 elif origin in ["programmer", "developer", "source code"]:
392 comments = gpo.po_message_extracted_comments(self._gpo_message) 379 comments = gpo.po_message_extracted_comments(self._gpo_message)
393 else: 380 else:
394 raise ValueError("Comment type not valid") 381 raise ValueError("Comment type not valid")
395 382 # FIXME this fixes a bug in Gettext that returns leading space with comm ents
396 if comments and get_libgettextpo_version() < (0, 17, 0): 383 if comments:
397 comments = "\n".join([line.strip() for line in comments.split("\n")] ) 384 comments = "\n".join([line.strip() for line in comments.split("\n")] )
398 # Let's drop the last newline 385 # Let's drop the last newline
399 return comments[:-1].decode(self._encoding) 386 return comments[:-1].decode(self._encoding)
400 387
401 def addnote(self, text, origin=None, position="append"): 388 def addnote(self, text, origin=None, position="append"):
402 # ignore empty strings and strings without non-space characters 389 # ignore empty strings and strings without non-space characters
403 if (not text) or (not text.strip()): 390 if (not text) or (not text.strip()):
404 return 391 return
405 text = data.forceunicode(text) 392 text = data.forceunicode(text)
406 oldnotes = self.getnotes(origin) 393 oldnotes = self.getnotes(origin)
407 newnotes = None 394 newnotes = None
408 if oldnotes: 395 if oldnotes:
409 if position == "append": 396 if position == "append":
410 newnotes = oldnotes + "\n" + text 397 newnotes = oldnotes + "\n" + text
411 elif position == "merge": 398 elif position == "merge":
412 if oldnotes != text: 399 if oldnotes != text:
413 oldnoteslist = oldnotes.split("\n") 400 oldnoteslist = oldnotes.split("\n")
414 for newline in text.split("\n"): 401 for newline in text.split("\n"):
415 newline = newline.rstrip() 402 newline = newline.rstrip()
416 # avoid duplicate comment lines (this might cause some p roblems) 403 # avoid duplicate comment lines (this might cause some p roblems)
417 if newline not in oldnotes or len(newline) < 5: 404 if newline not in oldnotes or len(newline) < 5:
418 oldnoteslist.append(newline) 405 oldnoteslist.append(newline)
419 newnotes = "\n".join(oldnoteslist) 406 newnotes = "\n".join(oldnoteslist)
420 else: 407 else:
421 newnotes = text + '\n' + oldnotes 408 newnotes = text + '\n' + oldnotes
422 else: 409 else:
423 newnotes = "\n".join([line.rstrip() for line in text.split("\n")]) 410 newnotes = "\n".join([line.rstrip() for line in text.split("\n")])
424 411 # FIXME; workaround the need for leading spaces when adding comments to PO files in libgettexpo
425 if newnotes: 412 if newnotes:
426 newlines = [] 413 newlines = []
427 needs_space = get_libgettextpo_version() < (0, 17, 0)
428 for line in newnotes.split("\n"): 414 for line in newnotes.split("\n"):
429 if line and needs_space: 415 if line:
430 newlines.append(" " + line) 416 newlines.append(" " + line)
431 else: 417 else:
432 newlines.append(line) 418 newlines.append(line)
433 newnotes = "\n".join(newlines) 419 newnotes = "\n".join(newlines)
434 if origin in ["programmer", "developer", "source code"]: 420 if origin in ["programmer", "developer", "source code"]:
435 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s) 421 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s)
436 else: 422 else:
437 gpo.po_message_set_comments(self._gpo_message, newnotes) 423 gpo.po_message_set_comments(self._gpo_message, newnotes)
438 424
439 def removenotes(self): 425 def removenotes(self):
440 gpo.po_message_set_comments(self._gpo_message, "") 426 gpo.po_message_set_comments(self._gpo_message, "")
441 427
442 def copy(self): 428 def copy(self):
443 newpo = self.__class__() 429 newpo = self.__class__()
444 newpo._gpo_message = self._gpo_message 430 newpo._gpo_message = self._gpo_message
445 return newpo 431 return newpo
446 432
447 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ): 433 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ):
448 """Merges the otherpo (with the same msgid) into this one. 434 """Merges the otherpo (with the same msgid) into this one.
449 435
450 Overwrite non-blank self.msgstr only if overwrite is True 436 Overwrite non-blank self.msgstr only if overwrite is True
451 merge comments only if comments is True 437 merge comments only if comments is True
452 438
453 """ 439 """
454 440
455 if not isinstance(otherpo, pounit): 441 if not isinstance(otherpo, pounit):
456 super(pounit, self).merge(otherpo, overwrite, comments) 442 super(pounit, self).merge(otherpo, overwrite, comments)
457 return 443 return
458 if comments: 444 if comments:
459 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge") 445 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge")
460 # FIXME mergelists(self.typecomments, otherpo.typecomments) 446 # FIXME mergelists(self.typecomments, otherpo.typecomments)
461 if not authoritative: 447 if not authoritative:
462 # We don't bring across otherpo.automaticcomments as we consider ourself 448 # We don't bring across otherpo.automaticcomments as we consider ourself
463 # to be the the authority. Same applies to otherpo.msgidcomment s 449 # to be the the authority. Same applies to otherpo.msgidcomment s
464 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 450 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge")
465 self.msgidcomment = otherpo._extract_msgidcomments() or None 451 self.msgidcomment = otherpo._extract_msgidcomments() or None
466 self.addlocations(otherpo.getlocations()) 452 self.addlocations(otherpo.getlocations())
467 if not self.istranslated() or overwrite: 453 if not self.istranslated() or overwrite:
468 # Remove kde-style comments from the translation (if any). 454 # Remove kde-style comments from the translation (if any).
469 if self._extract_msgidcomments(otherpo.target): 455 if self._extract_msgidcomments(otherpo.target):
470 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '') 456 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '')
471 self.target = otherpo.target 457 self.target = otherpo.target
472 if self.source != otherpo.source: 458 if self.source != otherpo.source:
473 self.markfuzzy() 459 self.markfuzzy()
474 else: 460 else:
475 self.markfuzzy(otherpo.isfuzzy()) 461 self.markfuzzy(otherpo.isfuzzy())
476 elif not otherpo.istranslated(): 462 elif not otherpo.istranslated():
477 if self.source != otherpo.source: 463 if self.source != otherpo.source:
478 self.markfuzzy() 464 self.markfuzzy()
479 else: 465 else:
(...skipping 224 matching lines...) Show 10 above Show 10 below
704 posrc = input.read() 690 posrc = input.read()
705 input.close() 691 input.close()
706 input = posrc 692 input = posrc
707 693
708 needtmpfile = not os.path.isfile(input) 694 needtmpfile = not os.path.isfile(input)
709 if needtmpfile: 695 if needtmpfile:
710 # This is not a file - we write the string to a temporary file 696 # This is not a file - we write the string to a temporary file
711 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 697 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po')
712 os.write(fd, input) 698 os.write(fd, input)
713 input = fname 699 input = fname
714 os.close(fd) 700 os.close(fd)
715 701
716 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler) 702 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler)
717 if self._gpo_memory_file is None: 703 if self._gpo_memory_file is None:
718 print >> sys.stderr, "Error:" 704 print >> sys.stderr, "Error:"
719 705
720 if needtmpfile: 706 if needtmpfile:
721 os.remove(input) 707 os.remove(input)
722 708
723 # Handle xerrors here 709 # Handle xerrors here
724 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None) 710 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None)
725 if self._header: 711 if self._header:
726 charset = gpo.po_header_field(self._header, "Content-Type") 712 charset = gpo.po_header_field(self._header, "Content-Type")
727 if charset: 713 if charset:
728 charset = re.search("charset=([^\\s]+)", charset).group(1) 714 charset = re.search("charset=([^\\s]+)", charset).group(1)
729 self._encoding = encodingToUse(charset) 715 self._encoding = encodingToUse(charset)
730 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None) 716 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None)
731 newmessage = gpo.po_next_message(self._gpo_message_iterator) 717 newmessage = gpo.po_next_message(self._gpo_message_iterator)
732 while newmessage: 718 while newmessage:
733 newunit = pounit(gpo_message=newmessage) 719 newunit = pounit(gpo_message=newmessage)
734 self.units.append(newunit) 720 self.units.append(newunit)
735 newmessage = gpo.po_next_message(self._gpo_message_iterator) 721 newmessage = gpo.po_next_message(self._gpo_message_iterator)
736 self._free_iterator() 722 self._free_iterator()
737 723
738 def __del__(self): 724 def __del__(self):
739 # We currently disable this while we still get segmentation faults. 725 # We currently disable this while we still get segmentation faults.
740 # Note that this is definitely leaking memory because of this. 726 # Note that this is definitely leaking memory because of this.
741 return 727 return
742 self._free_iterator() 728 self._free_iterator()
743 if self._gpo_memory_file is not None: 729 if self._gpo_memory_file is not None:
744 gpo.po_file_free(self._gpo_memory_file) 730 gpo.po_file_free(self._gpo_memory_file)
745 self._gpo_memory_file = None 731 self._gpo_memory_file = None
746 732
747 def _free_iterator(self): 733 def _free_iterator(self):
748 # We currently disable this while we still get segmentation faults. 734 # We currently disable this while we still get segmentation faults.
749 # Note that this is definitely leaking memory because of this. 735 # Note that this is definitely leaking memory because of this.
750 return 736 return
751 if self._gpo_message_iterator is not None: 737 if self._gpo_message_iterator is not None:
752 gpo.po_message_iterator_free(self._gpo_message_iterator) 738 gpo.po_message_iterator_free(self._gpo_message_iterator)
753 self._gpo_message_iterator = None 739 self._gpo_message_iterator = None
LEFTRIGHT

Powered by Google App Engine
This is Rietveld r159