1
2 """
3 Internationalization facilities
4
5 Authors: David Malcolm <dmalcolm@redhat.com>
6 """
7
8 __author__ = """David Malcolm <dmalcolm@redhat.com>, Zack Cerza <zcerza@redhat.com>"""
9
10 import config
11
12 import os
13 import re
14 import gettext
15
16 from logging import debugLogger as logger
17 from __builtin__ import unicode
18
19
21 try:
22 string = string.decode('utf-8', 'replace')
23 except UnicodeEncodeError:
24 string = string.encode('utf-8', 'replace')
25 return string
26
27
30
31
32 """
33 Singleton list of TranslationDb instances, to be initialized by the script with
34 whatever translation databases it wants.
35 """
36 translationDbs = []
37
38
40
41 """
42 Abstract base class representing a database of translations
43 """
44
46 """
47 Pure virtual method to look up the translation of a string.
48 Returns a list of candidate strings (the translation), empty if not found.
49
50 Note that a source string can map to multiple translated strings. For
51 example, in the French translation of Evolution, the string "Forward" can
52 translate to both
53 (i) "Faire suivre" for forwarding an email, and
54 (ii) "Suivant" for the next page in a wizard.
55 """
56 raise NotImplementedError
57
58
59 -class GettextTranslationDb(TranslationDb):
60
61 """
62 Implementation of TranslationDb which leverages gettext, using a single
63 translation mo-file.
64 """
65
66 - def __init__(self, moFile):
67 self.__moFile = moFile
68 self.__gnutranslations = gettext.GNUTranslations(open(moFile))
69
70 - def getTranslationsOf(self, srcName):
71 srcName = safeDecode(srcName)
72
73
74 results = {}
75 result = self.__gnutranslations.ugettext(srcName)
76 if result != srcName:
77 results[result] = None
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92 for index in range(len(srcName)):
93 candidate = srcName[:index] + "_" + srcName[index:]
94 result = self.__gnutranslations.ugettext(candidate)
95 if result != candidate:
96
97 results[result.replace('_', '')] = True
98
99 return results.keys()
100
101
103 """
104 Look up srcString in the various translation databases (if any), returning
105 a list of all matches found (potentially the empty list)
106 """
107
108 results = {}
109
110 for translationDb in translationDbs:
111 for result in translationDb.getTranslationsOf(srcString):
112 result = safeDecode(result)
113 results[result] = True
114
115
116 if len(results) == 0:
117 if config.config.debugTranslation:
118 logger.log('Translation not found for "%s"' % srcString)
119 return results.keys()
120
121
123
124 """
125 Class representing a string that we want to match strings against, handling
126 translation for us, by looking it up once at construction time.
127 """
128
129 - def __init__(self, untranslatedString):
130 """
131 Constructor looks up the string in all of the translation databases, storing
132 the various translations it finds.
133 """
134 untranslatedString = safeDecode(untranslatedString)
135 self.untranslatedString = untranslatedString
136 self.translatedStrings = translate(untranslatedString)
137
139 """
140 Compare the test string against either the translation of the original
141 string (or simply the original string, if no translation was found).
142 """
143
144 def stringsMatch(inS, outS):
145 """
146 Compares a regular expression to a string
147
148 inS: the regular expression (or normal string)
149 outS: the normal string to be compared against
150 """
151 inString = str(inS)
152 outString = outS
153 if inString == outString:
154 return True
155 inString = inString + '$'
156 inString = safeDecode(inString)
157 outString = safeDecode(outString)
158 if inString[0] == '*':
159 inString = "\\" + inString
160
161 inString = re.sub('([\(\)])', r'\\\1', inString)
162 match = re.match(inString, outString)
163 matched = match is not None
164 return matched
165
166 matched = False
167
168
169
170
171 for translatedString in self.translatedStrings:
172
173 matched = stringsMatch(translatedString, string)
174 if not matched:
175 matched = translatedString == string
176 if matched:
177 return matched
178
179 return stringsMatch(self.untranslatedString, string)
180
182 """
183 Provide a meaningful debug version of the string (and the translation in
184 use)
185 """
186 if len(self.translatedStrings) > 0:
187
188 translations = ""
189 for tString in self.translatedStrings:
190 translations += u'"%s", ' % safeDecode(tString)
191 result = u'"%s" (%s)' % (
192 safeDecode(self.untranslatedString), translations)
193 return safeDecode(result)
194 else:
195 return '"%s"' % (self.untranslatedString)
196
197
199 """
200 Does the given filename look like a gettext mo file?
201
202 Optionally: Does the file also contain translations for a certain language,
203 for example 'ja'?
204 """
205 if re.match('(.*)\\.mo$', filename):
206 if not language:
207 return True
208 elif re.match('/usr/share/locale(.*)/%s(.*)/LC_MESSAGES/(.*)\\.mo$' %
209 language, filename):
210 return True
211 else:
212 return False
213 else:
214 return False
215
216
221
222
224 """
225 Look up the named package and find all gettext mo files within it and its
226 dependencies. It is possible to restrict the results to those of a certain
227 language, for example 'ja'.
228 """
229 import distro
230
231 result = []
232 for filename in distro.packageDb.getFiles(packageName):
233 if isMoFile(filename, language):
234 result.append(filename)
235
236 if getDependencies:
237
238 for dep in distro.packageDb.getDependencies(packageName):
239
240
241 result.extend(getMoFilesForPackage(dep, language, False))
242
243 return result
244
245
247 """
248 Helper function which appends all of the gettext translation mo-files used by
249 the package (and its dependencies) to the translation database list.
250 """
251
252 moFiles = {}
253
254 def load(packageName, language='', getDependencies=True):
255 for moFile in getMoFilesForPackage(packageName, language, getDependencies):
256
257
258
259 if not('popt.mo' in moFile or moFile in moFiles):
260 try:
261 translationDbs.append(GettextTranslationDb(moFile))
262 moFiles[moFile] = None
263 except (AttributeError, IndexError):
264 if config.config.debugTranslation:
265
266
267 logger.log(
268 "Warning: Failed to load mo-file for translation: " + moFile)
269
270
271
272
273
274
275
276
277 import distro
278 language = os.environ.get('LANGUAGE', os.environ['LANG'])[0:2]
279 if isinstance(distro.distro, distro.Ubuntu):
280 load('language-pack-gnome-%s' % language, language)
281 load(packageName, language, getDependencies)
282