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

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: Updated accessors, tests and added base.py & test_base.py Created 1 year, 4 months ago
Right Patch Set: Fixed minor issues 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
140 137
141 # Message (set methods) 138 # Message (set methods)
142 gpo.po_message_set_comments.argtypes = [c_int, STRING] 139 gpo.po_message_set_comments.argtypes = [c_int, STRING]
143 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING] 140 gpo.po_message_set_extracted_comments.argtypes = [c_int, STRING]
144 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int] 141 gpo.po_message_set_fuzzy.argtypes = [c_int, c_int]
145 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING] 142 gpo.po_message_set_msgctxt.argtypes = [c_int, STRING]
146 gpo.po_message_set_prev_msgctxt.argtypes = [c_int, STRING] 143 gpo.po_message_set_prev_msgctxt.argtypes = [c_int, STRING]
147 gpo.po_message_set_prev_msgid.argtypes = [c_int, STRING] 144 gpo.po_message_set_prev_msgid.argtypes = [c_int, STRING]
148 gpo.po_message_set_prev_msgid_plural.argtypes = [c_int, STRING] 145 gpo.po_message_set_prev_msgid_plural.argtypes = [c_int, STRING]
149 146
150 # Setup the po_xerror_handler 147 # Setup the po_xerror_handler
151 xerror_handler = po_xerror_handler() 148 xerror_handler = po_xerror_handler()
152 xerror_handler.xerror = xerror_prototype(xerror_cb) 149 xerror_handler.xerror = xerror_prototype(xerror_cb)
153 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb) 150 xerror_handler.xerror2 = xerror2_prototype(xerror2_cb)
154 151
155 def escapeforpo(text): 152 def escapeforpo(text):
156 return pypo.escapeforpo(text) 153 return pypo.escapeforpo(text)
157 154
158 def quoteforpo(text): 155 def quoteforpo(text):
159 return pypo.quoteforpo(text) 156 return pypo.quoteforpo(text)
160 157
161 def unquotefrompo(postr, joinwithlinebreak=False): 158 def unquotefrompo(postr, joinwithlinebreak=False):
162 return pypo.unquotefrompo(postr, joinwithlinebreak) 159 return pypo.unquotefrompo(postr, joinwithlinebreak)
163 160
164 def encodingToUse(encoding): 161 def encodingToUse(encoding):
165 return pypo.encodingToUse(encoding) 162 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
166 177
167 class pounit(pocommon.pounit): 178 class pounit(pocommon.pounit):
168 def __init__(self, source=None, encoding='utf-8', gpo_message=None): 179 def __init__(self, source=None, encoding='utf-8', gpo_message=None):
169 self._encoding = encoding 180 self._encoding = encoding
170 if not gpo_message: 181 if not gpo_message:
171 self._gpo_message = gpo.po_message_create() 182 self._gpo_message = gpo.po_message_create()
172 if source or source == "": 183 if source or source == "":
173 self.source = source 184 self.source = source
174 self.target = "" 185 self.target = ""
175 elif gpo_message: 186 elif gpo_message:
176 self._gpo_message = gpo_message 187 self._gpo_message = gpo_message
177 188
178 def setmsgidcomment(self, msgidcomment): 189 def setmsgidcomment(self, msgidcomment):
179 if msgidcomment: 190 if msgidcomment:
180 newsource = "_: " + msgidcomment + "\n" + self.source 191 newsource = "_: " + msgidcomment + "\n" + self.source
181 self.source = newsource 192 self.source = newsource
182 msgidcomment = property(None, setmsgidcomment) 193 msgidcomment = property(None, setmsgidcomment)
183 194
184 def setmsgid_plural(self, msgid_plural): 195 def setmsgid_plural(self, msgid_plural):
185 if isinstance(msgid_plural, list): 196 if isinstance(msgid_plural, list):
186 msgid_plural = "".join(msgid_plural) 197 msgid_plural = "".join(msgid_plural)
187 gpo.po_message_set_msgid_plural(self._gpo_message, msgid_plural) 198 gpo.po_message_set_msgid_plural(self._gpo_message, msgid_plural)
188 msgid_plural = property(None, setmsgid_plural) 199 msgid_plural = property(None, setmsgid_plural)
189 200
190 def setprev_context(self, msgid_context): 201 def setprev_context(self, context):
dwaynebailey 2008/07/17 12:59:27 Can we avoid references to msgid but stick to gene
191 """Set the previous context for this message""" 202 """Set the previous context for this message"""
192 if self.isfuzzy(): 203 if self.isfuzzy():
193 return 204 return
194 gpo.po_message_set_prev_msgctxt(self._gpo_message, msgid_context) 205 gpo.po_message_set_prev_msgctxt(self._gpo_message, context)
195 206
196 def getprev_context(self): 207 def getprev_context(self):
197 """Get the previous context for this message, if any """ 208 """Get the previous context for this message, if any """
198 prev_ctxt = gpo.po_message_prev_msgctxt(self._gpo_message) 209 prev_ctxt = gpo.po_message_prev_msgctxt(self._gpo_message)
199 if not prev_ctxt: 210 if not prev_ctxt:
200 return "" 211 return ""
201 return prev_ctxt 212 return prev_ctxt
202 213
203 prev_context = property(getprev_context, setprev_context) 214 prev_context = property(getprev_context, setprev_context)
204 215
205 def setprev_source(self, prev_source): 216 def setprev_source(self, prev_source):
206 """Set msgid as a previous msgid for this message""" 217 """Set msgid as a previous msgid for this message"""
207 if self.isfuzzy(): 218 if self.isfuzzy():
219 return
220 if not self.source:
208 return 221 return
209 if isinstance(prev_source, multistring): 222 if isinstance(prev_source, multistring):
210 prev_source = prev_source.strings 223 prev_source = prev_source.strings
211 if isinstance(prev_source, unicode): 224 if isinstance(prev_source, unicode):
212 prev_source = prev_source.encode(self._encoding) 225 prev_source = prev_source.encode(self._encoding)
213 if isinstance(prev_source, list): 226 if isinstance(prev_source, list):
214 if len(prev_source) > 1: 227 if len(prev_source) > 1:
215 msgid_plural = str(prev_source[1]) 228 msgid_plural = str(prev_source[1])
216 if isinstance(msgid_plural, list): 229 if isinstance(msgid_plural, list):
217 msgid_plural = "".join(msgid_plural) 230 msgid_plural = "".join(msgid_plural)
218 231
219 gpo.po_message_set_prev_msgid_plural(self._gpo_message, 232 gpo.po_message_set_prev_msgid_plural(self._gpo_message,
220 msgid_plural) 233 msgid_plural)
221 prev_source = str(prev_source[0]) 234 prev_source = str(prev_source[0])
222 235
223 self.source = ""
224 gpo.po_message_set_prev_msgid(self._gpo_message, prev_source) 236 gpo.po_message_set_prev_msgid(self._gpo_message, prev_source)
225 237
226 def getprev_source(self): 238 def getprev_source(self):
227 """Get the previous msgid for this message, if any""" 239 """Get the previous msgid for this message, if any"""
228 prev_source = gpo.po_message_prev_msgid(self._gpo_message) 240 prev_source = gpo.po_message_prev_msgid(self._gpo_message)
229 if not prev_source: 241 if not prev_source:
230 return multistring(u"") 242 return multistring(u"")
231 243
232 prev_source = multistring(prev_source, self._encoding) 244 prev_source = multistring(prev_source, self._encoding)
233 prev_plural = gpo.po_message_prev_msgid_plural(self._gpo_message) 245 prev_plural = gpo.po_message_prev_msgid_plural(self._gpo_message)
234 if prev_plural: 246 if prev_plural:
235 prev_source.strings.append(prev_plural.decode(self._encoding)) 247 prev_source.strings.append(prev_plural.decode(self._encoding))
236 return prev_source 248 return prev_source
237 249
238 prev_source = property(getprev_source, setprev_source) 250 prev_source = property(getprev_source, setprev_source)
239 251
240 def set_as_previous(self): 252 def set_as_previous(self):
241 """Sets the current msgid as previous message. Then, the current 253 """Sets the current msgid as previous message. Then, the current
242 msgid could be filled with a new msgid. Note that, if a unit 254 msgid could be filled with a new msgid. Note that, if a unit
243 contains a previous msgid, it also is marked as fuzzy (to keep the 255 contains a previous msgid, it also is marked as fuzzy (to keep the
244 same behavior as msgmerge). 256 same behavior as msgmerge).
245 """ 257 """
246 if not self.target: 258 if not self.target:
247 return 259 return
248 260 # since the context accessor might also return KDE style comments, I'm
249 # the 'pure' context string 261 # just using the libgettextpo accessor to retrive only the context
250 self.prev_context = gpo.po_message_msgctxt(self._gpo_message) 262 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
251 self.prev_source = self.source 263 self.prev_source = self.source
264 self.source = ""
252 self.markfuzzy() 265 self.markfuzzy()
253 266
254 def getsource(self): 267 def getsource(self):
255 def remove_msgid_comments(text): 268 def remove_msgid_comments(text):
256 if not text: 269 if not text:
257 return text 270 return text
258 if text.startswith("_:"): 271 if text.startswith("_:"):
259 remainder = re.search(r"_: .*\n(.*)", text) 272 remainder = re.search(r"_: .*\n(.*)", text)
260 if remainder: 273 if remainder:
261 return remainder.group(1) 274 return remainder.group(1)
262 else: 275 else:
263 return u"" 276 return u""
264 else: 277 else:
265 return text 278 return text
266 singular = remove_msgid_comments(gpo.po_message_msgid(self._gpo_message) ) 279 singular = remove_msgid_comments(gpo.po_message_msgid(self._gpo_message) )
267 if singular: 280 if singular:
268 multi = multistring(singular, self._encoding) 281 multi = multistring(singular, self._encoding)
269 if self.hasplural(): 282 if self.hasplural():
270 pluralform = gpo.po_message_msgid_plural(self._gpo_message) 283 pluralform = gpo.po_message_msgid_plural(self._gpo_message)
271 if isinstance(pluralform, str): 284 if isinstance(pluralform, str):
272 pluralform = pluralform.decode(self._encoding) 285 pluralform = pluralform.decode(self._encoding)
273 multi.strings.append(pluralform) 286 multi.strings.append(pluralform)
274 return multi 287 return multi
275 else: 288 else:
276 return u"" 289 return u""
277 290
278 def setsource(self, source): 291 def setsource(self, source):
279 if isinstance(source, multistring): 292 if isinstance(source, multistring):
280 source = source.strings 293 source = source.strings
281 if isinstance(source, unicode): 294 if isinstance(source, unicode):
282 source = source.encode(self._encoding) 295 source = source.encode(self._encoding)
283 if isinstance(source, list): 296 if isinstance(source, list):
284 gpo.po_message_set_msgid(self._gpo_message, str(source[0])) 297 gpo.po_message_set_msgid(self._gpo_message, str(source[0]))
285 if len(source) > 1: 298 if len(source) > 1:
286 gpo.po_message_set_msgid_plural(self._gpo_message, str(source[1] )) 299 gpo.po_message_set_msgid_plural(self._gpo_message, str(source[1] ))
287 else: 300 else:
288 gpo.po_message_set_msgid(self._gpo_message, source) 301 gpo.po_message_set_msgid(self._gpo_message, source)
289 gpo.po_message_set_msgid_plural(self._gpo_message, None) 302 gpo.po_message_set_msgid_plural(self._gpo_message, None)
290 303
291 source = property(getsource, setsource) 304 source = property(getsource, setsource)
292 305
293 def gettarget(self): 306 def gettarget(self):
294 if self.hasplural(): 307 if self.hasplural():
295 plurals = [] 308 plurals = []
296 nplural = 0 309 nplural = 0
297 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural) 310 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural)
298 while plural: 311 while plural:
299 plurals.append(plural) 312 plurals.append(plural)
300 nplural += 1 313 nplural += 1
301 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural ) 314 plural = gpo.po_message_msgstr_plural(self._gpo_message, nplural )
302 if plurals: 315 if plurals:
303 multi = multistring(plurals, encoding=self._encoding) 316 multi = multistring(plurals, encoding=self._encoding)
304 else: 317 else:
305 multi = multistring(u"") 318 multi = multistring(u"")
306 else: 319 else:
307 multi = multistring(gpo.po_message_msgstr(self._gpo_message) or u"", encoding=self._encoding) 320 multi = multistring(gpo.po_message_msgstr(self._gpo_message) or u"", encoding=self._encoding)
308 return multi 321 return multi
309 322
310 def settarget(self, target): 323 def settarget(self, target):
311 # for plural strings: convert 'target' into a list 324 # for plural strings: convert 'target' into a list
312 if self.hasplural(): 325 if self.hasplural():
313 if isinstance(target, multistring): 326 if isinstance(target, multistring):
314 target = target.strings 327 target = target.strings
315 elif isinstance(target, basestring): 328 elif isinstance(target, basestring):
316 target = [target] 329 target = [target]
317 # for non-plurals: check number of items in 'target' 330 # for non-plurals: check number of items in 'target'
318 elif isinstance(target,(dict, list)): 331 elif isinstance(target,(dict, list)):
319 if len(target) == 1: 332 if len(target) == 1:
320 target = target[0] 333 target = target[0]
321 else: 334 else:
322 raise ValueError("po msgid element has no plural but msgstr has %d elements (%s)" % (len(target), target)) 335 raise ValueError("po msgid element has no plural but msgstr has %d elements (%s)" % (len(target), target))
323 # empty the previous list of messages 336 # empty the previous list of messages
324 # TODO: the "pypo" implementation does not remove the previous items of 337 # TODO: the "pypo" implementation does not remove the previous items of
325 # the target, if self.target == target (essentially: comparing only 338 # the target, if self.target == target (essentially: comparing only
326 # the first item of a plural string with the single new string) 339 # the first item of a plural string with the single new string)
327 # Maybe this behaviour should be unified. 340 # Maybe this behaviour should be unified.
328 if isinstance(target, (dict, list)): 341 if isinstance(target, (dict, list)):
329 i = 0 342 i = 0
330 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 343 message = gpo.po_message_msgstr_plural(self._gpo_message, i)
331 while message is not None: 344 while message is not None:
332 gpo.po_message_set_msgstr_plural(self._gpo_message, i, None) 345 gpo.po_message_set_msgstr_plural(self._gpo_message, i, None)
333 i += 1 346 i += 1
334 message = gpo.po_message_msgstr_plural(self._gpo_message, i) 347 message = gpo.po_message_msgstr_plural(self._gpo_message, i)
335 # add the items of a list 348 # add the items of a list
336 if isinstance(target, list): 349 if isinstance(target, list):
337 for i in range(len(target)): 350 for i in range(len(target)):
338 targetstring = target[i] 351 targetstring = target[i]
339 if isinstance(targetstring, unicode): 352 if isinstance(targetstring, unicode):
340 targetstring = targetstring.encode(self._encoding) 353 targetstring = targetstring.encode(self._encoding)
341 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing) 354 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing)
342 # add the values of a dict 355 # add the values of a dict
343 elif isinstance(target, dict): 356 elif isinstance(target, dict):
344 for i, targetstring in enumerate(target.itervalues()): 357 for i, targetstring in enumerate(target.itervalues()):
345 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing) 358 gpo.po_message_set_msgstr_plural(self._gpo_message, i, targetstr ing)
346 # add a single string 359 # add a single string
347 else: 360 else:
348 if isinstance(target, unicode): 361 if isinstance(target, unicode):
349 target = target.encode(self._encoding) 362 target = target.encode(self._encoding)
350 if target is None: 363 if target is None:
351 gpo.po_message_set_msgstr(self._gpo_message, "") 364 gpo.po_message_set_msgstr(self._gpo_message, "")
352 else: 365 else:
353 gpo.po_message_set_msgstr(self._gpo_message, target) 366 gpo.po_message_set_msgstr(self._gpo_message, target)
354 target = property(gettarget, settarget) 367 target = property(gettarget, settarget)
355 368
356 def getid(self): 369 def getid(self):
357 """The unique identifier for this unit according to the convensions in 370 """The unique identifier for this unit according to the convensions in
358 .mo files.""" 371 .mo files."""
359 id = gpo.po_message_msgid(self._gpo_message) 372 id = gpo.po_message_msgid(self._gpo_message)
360 # Gettext does not consider the plural to determine duplicates, only 373 # Gettext does not consider the plural to determine duplicates, only
361 # the msgid. For generation of .mo files, we might want to use this 374 # the msgid. For generation of .mo files, we might want to use this
362 # code to generate the entry for the hash table, but for now, it is 375 # code to generate the entry for the hash table, but for now, it is
363 # commented out for conformance to gettext. 376 # commented out for conformance to gettext.
364 # plural = gpo.po_message_msgid_plural(self._gpo_message) 377 # plural = gpo.po_message_msgid_plural(self._gpo_message)
365 # if not plural is None: 378 # if not plural is None:
366 # id = '%s\0%s' % (id, plural) 379 # id = '%s\0%s' % (id, plural)
367 context = gpo.po_message_msgctxt(self._gpo_message) 380 context = gpo.po_message_msgctxt(self._gpo_message)
368 if context: 381 if context:
369 id = "%s\04%s" % (context, id) 382 id = "%s\04%s" % (context, id)
370 return id or "" 383 return id or ""
371 384
372 def getnotes(self, origin=None): 385 def getnotes(self, origin=None):
373 if origin == None: 386 if origin == None:
374 comments = gpo.po_message_comments(self._gpo_message) + \ 387 comments = gpo.po_message_comments(self._gpo_message) + \
375 gpo.po_message_extracted_comments(self._gpo_message) 388 gpo.po_message_extracted_comments(self._gpo_message)
376 elif origin == "translator": 389 elif origin == "translator":
377 comments = gpo.po_message_comments(self._gpo_message) 390 comments = gpo.po_message_comments(self._gpo_message)
378 elif origin in ["programmer", "developer", "source code"]: 391 elif origin in ["programmer", "developer", "source code"]:
379 comments = gpo.po_message_extracted_comments(self._gpo_message) 392 comments = gpo.po_message_extracted_comments(self._gpo_message)
380 else: 393 else:
381 raise ValueError("Comment type not valid") 394 raise ValueError("Comment type not valid")
382 # FIXME this fixes a bug in Gettext that returns leading space with comm ents 395
383 if comments: 396 if comments and get_libgettextpo_version() < (0, 17, 0):
384 comments = "\n".join([line.strip() for line in comments.split("\n")] ) 397 comments = "\n".join([line.strip() for line in comments.split("\n")] )
385 # Let's drop the last newline 398 # Let's drop the last newline
386 return comments[:-1].decode(self._encoding) 399 return comments[:-1].decode(self._encoding)
387 400
388 def addnote(self, text, origin=None, position="append"): 401 def addnote(self, text, origin=None, position="append"):
389 # ignore empty strings and strings without non-space characters 402 # ignore empty strings and strings without non-space characters
390 if (not text) or (not text.strip()): 403 if (not text) or (not text.strip()):
391 return 404 return
392 text = data.forceunicode(text) 405 text = data.forceunicode(text)
393 oldnotes = self.getnotes(origin) 406 oldnotes = self.getnotes(origin)
394 newnotes = None 407 newnotes = None
395 if oldnotes: 408 if oldnotes:
396 if position == "append": 409 if position == "append":
397 newnotes = oldnotes + "\n" + text 410 newnotes = oldnotes + "\n" + text
398 elif position == "merge": 411 elif position == "merge":
399 if oldnotes != text: 412 if oldnotes != text:
400 oldnoteslist = oldnotes.split("\n") 413 oldnoteslist = oldnotes.split("\n")
401 for newline in text.split("\n"): 414 for newline in text.split("\n"):
402 newline = newline.rstrip() 415 newline = newline.rstrip()
403 # avoid duplicate comment lines (this might cause some p roblems) 416 # avoid duplicate comment lines (this might cause some p roblems)
404 if newline not in oldnotes or len(newline) < 5: 417 if newline not in oldnotes or len(newline) < 5:
405 oldnoteslist.append(newline) 418 oldnoteslist.append(newline)
406 newnotes = "\n".join(oldnoteslist) 419 newnotes = "\n".join(oldnoteslist)
407 else: 420 else:
408 newnotes = text + '\n' + oldnotes 421 newnotes = text + '\n' + oldnotes
409 else: 422 else:
410 newnotes = "\n".join([line.rstrip() for line in text.split("\n")]) 423 newnotes = "\n".join([line.rstrip() for line in text.split("\n")])
411 # FIXME; workaround the need for leading spaces when adding comments to PO files in libgettexpo 424
412 if newnotes: 425 if newnotes:
413 newlines = [] 426 newlines = []
427 needs_space = get_libgettextpo_version() < (0, 17, 0)
414 for line in newnotes.split("\n"): 428 for line in newnotes.split("\n"):
415 if line: 429 if line and needs_space:
416 newlines.append(" " + line) 430 newlines.append(" " + line)
417 else: 431 else:
418 newlines.append(line) 432 newlines.append(line)
419 newnotes = "\n".join(newlines) 433 newnotes = "\n".join(newlines)
420 if origin in ["programmer", "developer", "source code"]: 434 if origin in ["programmer", "developer", "source code"]:
421 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s) 435 gpo.po_message_set_extracted_comments(self._gpo_message, newnote s)
422 else: 436 else:
423 gpo.po_message_set_comments(self._gpo_message, newnotes) 437 gpo.po_message_set_comments(self._gpo_message, newnotes)
424 438
425 def removenotes(self): 439 def removenotes(self):
426 gpo.po_message_set_comments(self._gpo_message, "") 440 gpo.po_message_set_comments(self._gpo_message, "")
427 441
428 def copy(self): 442 def copy(self):
429 newpo = self.__class__() 443 newpo = self.__class__()
430 newpo._gpo_message = self._gpo_message 444 newpo._gpo_message = self._gpo_message
431 return newpo 445 return newpo
432 446
433 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ): 447 def merge(self, otherpo, overwrite=False, comments=True, authoritative=False ):
434 """Merges the otherpo (with the same msgid) into this one. 448 """Merges the otherpo (with the same msgid) into this one.
435 449
436 Overwrite non-blank self.msgstr only if overwrite is True 450 Overwrite non-blank self.msgstr only if overwrite is True
437 merge comments only if comments is True 451 merge comments only if comments is True
438 452
439 """ 453 """
440 454
441 if not isinstance(otherpo, pounit): 455 if not isinstance(otherpo, pounit):
442 super(pounit, self).merge(otherpo, overwrite, comments) 456 super(pounit, self).merge(otherpo, overwrite, comments)
443 return 457 return
444 if comments: 458 if comments:
445 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge") 459 self.addnote(otherpo.getnotes("translator"), origin="translator", po sition="merge")
446 # FIXME mergelists(self.typecomments, otherpo.typecomments) 460 # FIXME mergelists(self.typecomments, otherpo.typecomments)
447 if not authoritative: 461 if not authoritative:
448 # We don't bring across otherpo.automaticcomments as we consider ourself 462 # We don't bring across otherpo.automaticcomments as we consider ourself
449 # to be the the authority. Same applies to otherpo.msgidcomment s 463 # to be the the authority. Same applies to otherpo.msgidcomment s
450 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge") 464 self.addnote(otherpo.getnotes("developer"), origin="developer", position="merge")
451 self.msgidcomment = otherpo._extract_msgidcomments() or None 465 self.msgidcomment = otherpo._extract_msgidcomments() or None
452 self.addlocations(otherpo.getlocations()) 466 self.addlocations(otherpo.getlocations())
453 if not self.istranslated() or overwrite: 467 if not self.istranslated() or overwrite:
454 # Remove kde-style comments from the translation (if any). 468 # Remove kde-style comments from the translation (if any).
455 if self._extract_msgidcomments(otherpo.target): 469 if self._extract_msgidcomments(otherpo.target):
456 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '') 470 otherpo.target = otherpo.target.replace('_: ' + otherpo._extract _msgidcomments()+ '\n', '')
457 self.target = otherpo.target 471 self.target = otherpo.target
458 if self.source != otherpo.source: 472 if self.source != otherpo.source:
459 self.markfuzzy() 473 self.markfuzzy()
460 else: 474 else:
461 self.markfuzzy(otherpo.isfuzzy()) 475 self.markfuzzy(otherpo.isfuzzy())
462 elif not otherpo.istranslated(): 476 elif not otherpo.istranslated():
463 if self.source != otherpo.source: 477 if self.source != otherpo.source:
464 self.markfuzzy() 478 self.markfuzzy()
465 else: 479 else:
(...skipping 224 matching lines...) Show 10 above Show 10 below
690 posrc = input.read() 704 posrc = input.read()
691 input.close() 705 input.close()
692 input = posrc 706 input = posrc
693 707
694 needtmpfile = not os.path.isfile(input) 708 needtmpfile = not os.path.isfile(input)
695 if needtmpfile: 709 if needtmpfile:
696 # This is not a file - we write the string to a temporary file 710 # This is not a file - we write the string to a temporary file
697 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po') 711 fd, fname = tempfile.mkstemp(prefix='translate', suffix='.po')
698 os.write(fd, input) 712 os.write(fd, input)
699 input = fname 713 input = fname
700 os.close(fd) 714 os.close(fd)
701 715
702 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler) 716 self._gpo_memory_file = gpo.po_file_read_v3(input, xerror_handler)
703 if self._gpo_memory_file is None: 717 if self._gpo_memory_file is None:
704 print >> sys.stderr, "Error:" 718 print >> sys.stderr, "Error:"
705 719
706 if needtmpfile: 720 if needtmpfile:
707 os.remove(input) 721 os.remove(input)
708 722
709 # Handle xerrors here 723 # Handle xerrors here
710 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None) 724 self._header = gpo.po_file_domain_header(self._gpo_memory_file, None)
711 if self._header: 725 if self._header:
712 charset = gpo.po_header_field(self._header, "Content-Type") 726 charset = gpo.po_header_field(self._header, "Content-Type")
713 if charset: 727 if charset:
714 charset = re.search("charset=([^\\s]+)", charset).group(1) 728 charset = re.search("charset=([^\\s]+)", charset).group(1)
715 self._encoding = encodingToUse(charset) 729 self._encoding = encodingToUse(charset)
716 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None) 730 self._gpo_message_iterator = gpo.po_message_iterator(self._gpo_memory_fi le, None)
717 newmessage = gpo.po_next_message(self._gpo_message_iterator) 731 newmessage = gpo.po_next_message(self._gpo_message_iterator)
718 while newmessage: 732 while newmessage:
719 newunit = pounit(gpo_message=newmessage) 733 newunit = pounit(gpo_message=newmessage)
720 self.units.append(newunit) 734 self.units.append(newunit)
721 newmessage = gpo.po_next_message(self._gpo_message_iterator) 735 newmessage = gpo.po_next_message(self._gpo_message_iterator)
722 self._free_iterator() 736 self._free_iterator()
723 737
724 def __del__(self): 738 def __del__(self):
725 # We currently disable this while we still get segmentation faults. 739 # We currently disable this while we still get segmentation faults.
726 # Note that this is definitely leaking memory because of this. 740 # Note that this is definitely leaking memory because of this.
727 return 741 return
728 self._free_iterator() 742 self._free_iterator()
729 if self._gpo_memory_file is not None: 743 if self._gpo_memory_file is not None:
730 gpo.po_file_free(self._gpo_memory_file) 744 gpo.po_file_free(self._gpo_memory_file)
731 self._gpo_memory_file = None 745 self._gpo_memory_file = None
732 746
733 def _free_iterator(self): 747 def _free_iterator(self):
734 # We currently disable this while we still get segmentation faults. 748 # We currently disable this while we still get segmentation faults.
735 # Note that this is definitely leaking memory because of this. 749 # Note that this is definitely leaking memory because of this.
736 return 750 return
737 if self._gpo_message_iterator is not None: 751 if self._gpo_message_iterator is not None:
738 gpo.po_message_iterator_free(self._gpo_message_iterator) 752 gpo.po_message_iterator_free(self._gpo_message_iterator)
739 self._gpo_message_iterator = None 753 self._gpo_message_iterator = None
LEFTRIGHT

Powered by Google App Engine
This is Rietveld r159