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