1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Manage the Haiku catkeys translation format
22
23 The Haiku catkeys format is the translation format used for localisation of
24 the U{Haiku<http://www.haiku-os.org/>} operating system.
25
26 It is a bilingual base class derived format with L{CatkeysFile} and
27 L{CatkeysUnit} providing file and unit level access. The file format is
28 described here:
29 http://www.haiku-os.org/blog/pulkomandy/2009-09-24_haiku_locale_kit_translator_handbook
30
31 Implementation
32 ==============
33 The implementation covers the full requirements of a catkeys file. The
34 files are simple Tab Separated Value (TSV) files that can be read
35 by Microsoft Excel and other spreadsheet programs. They use the .txt
36 extension which does make it more difficult to automatically identify
37 such files.
38
39 The dialect of the TSV files is specified by L{CatkeysDialect}.
40
41 Encoding
42 --------
43 The files are UTF-8 encoded.
44
45 Header
46 ------
47 L{CatkeysHeader} provides header management support.
48
49 Escaping
50 --------
51 catkeys seem to escape things like in C++ (strings are just extracted from
52 the source code unchanged, it seems.
53
54 Functions allow for L{escaping<_escape>} and L{unescaping<_unescape>}.
55 """
56
57 import csv
58 import sys
59
60 from translate.lang import data
61 from translate.storage import base
62
63 FIELDNAMES_HEADER = ["version", "language", "mimetype", "checksum"]
64 """Field names for the catkeys header"""
65
66 FIELDNAMES = ["source", "context", "comment", "target"]
67 """Field names for a catkeys TU"""
68
69 FIELDNAMES_HEADER_DEFAULTS = {
70 "version": "1",
71 "language": "",
72 "mimetype": "",
73 "checksum": "",
74 }
75 """Default or minimum header entries for a catkeys file"""
76
77 _unescape_map = {"\\r": "\r", "\\t": "\t", '\\n': '\n', '\\\\': '\\'}
78 _escape_map = dict([(value, key) for (key, value) in _unescape_map.items()])
79
80
81
82
83
85 if string:
86 string = string.replace(r"\n", r"\\n").replace("\n", "\\n").replace("\t", "\\t")
87 return string
88
89
91 if string:
92 string = string.replace("\\n", "\n").replace("\\t", "\t").replace(r"\n", r"\\n")
93 return string
94
95
109 csv.register_dialect("catkeys", CatkeysDialect)
110
111
113 """A catkeys translation memory header"""
114
121
126
128 """Set a human readable target language"""
129 if not newlang or newlang not in data.languages:
130 return
131
132 self._header_dict['language'] = data.languages[newlang][0].lower()
133 targetlanguage = property(None, settargetlanguage)
134
135
137 """A catkeys translation memory unit"""
138
144
146 """Get the dictionary of values for a catkeys line"""
147 return self._dict
148
150 """Set the dictionary of values for a catkeys line
151
152 @param newdict: a new dictionary with catkeys line elements
153 @type newdict: Dict
154 """
155
156 self._dict = newdict
157 dict = property(getdict, setdict)
158
160 if self._dict.get(key, None) is None:
161 return None
162 elif self._dict[key]:
163 return _unescape(self._dict[key]).decode('utf-8')
164 else:
165 return ""
166
168 if newvalue is None:
169 self._dict[key] = None
170 if isinstance(newvalue, unicode):
171 newvalue = newvalue.encode('utf-8')
172 newvalue = _escape(newvalue)
173 if not key in self._dict or newvalue != self._dict[key]:
174 self._dict[key] = newvalue
175
178
182 source = property(getsource, setsource)
183
186
190 target = property(gettarget, settarget)
191
193 if not origin or origin in ["programmer", "developer", "source code"]:
194 return self._dict["comment"].decode('utf-8')
195 return u""
196
197 - def getcontext(self):
198 return self._dict["context"].decode('utf-8')
199
209
211 if present:
212 self.target = u""
213
215 self._dict['target-lang'] = newlang
216 targetlang = property(None, settargetlang)
217
219 return str(self._dict)
220
222 if not self._dict.get('source', None):
223 return False
224 return bool(self._dict.get('target', None))
225
226 - def merge(self, otherunit, overwrite=False, comments=True,
227 authoritative=False):
234
235
237 """A catkeys translation memory file"""
238 Name = _("Haiku catkeys file")
239 Mimetypes = ["application/x-catkeys"]
240 Extensions = ["catkeys"]
241
243 """Construct a catkeys store, optionally reading in from inputfile."""
244 self.UnitClass = unitclass
245 base.TranslationStore.__init__(self, unitclass=unitclass)
246 self.filename = ''
247 self.header = CatkeysHeader()
248 self._encoding = 'utf-8'
249 if inputfile is not None:
250 self.parse(inputfile)
251
254
256 """parsse the given file or file source string"""
257 if hasattr(input, 'name'):
258 self.filename = input.name
259 elif not getattr(self, 'filename', ''):
260 self.filename = ''
261 if hasattr(input, "read"):
262 tmsrc = input.read()
263 input.close()
264 input = tmsrc
265 for header in csv.DictReader(input.split("\n")[:1], fieldnames=FIELDNAMES_HEADER, dialect="catkeys"):
266 self.header = CatkeysHeader(header)
267 lines = csv.DictReader(input.split("\n")[1:], fieldnames=FIELDNAMES, dialect="catkeys")
268 for line in lines:
269 newunit = CatkeysUnit()
270 newunit.dict = line
271 self.addunit(newunit)
272
274 output = csv.StringIO()
275 writer = csv.DictWriter(output, fieldnames=FIELDNAMES_HEADER, dialect="catkeys")
276 writer.writerow(self.header._header_dict)
277 writer = csv.DictWriter(output, fieldnames=FIELDNAMES, dialect="catkeys")
278 for unit in self.units:
279 writer.writerow(unit.dict)
280 return output.getvalue()
281