1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """internationalization helpers
19 """
20
21 import os
22 import gettext
23
24 from twisted.spread import pb
25
26 from flumotion.common import log
27 from flumotion.configure import configure
28
29 __version__ = "$Rev: 6693 $"
30
31
32
33
34
35
36
38 compareAttributes = ()
39
41 if not self.compareAttributes:
42 return self is other
43
44 if not isinstance(other, self.__class__):
45 return False
46 for attr in self.compareAttributes:
47 if hasattr(self, attr):
48 if not hasattr(other, attr):
49 return False
50 elif not getattr(self, attr) == getattr(other, attr):
51 return False
52 elif hasattr(other, attr):
53 return False
54 return True
55
57 return not self.__eq__(other)
58
59
60 -def N_(formatString):
61 """
62 Mark a singular string for translation, without translating it.
63 """
64 return formatString
65
66
67 -def ngettext(singular, plural, count):
68 """
69 Mark a plural string for translation, without translating it.
70 """
71 return (singular, plural, count)
72
73
75 """
76 Return a function that takes a format string or tuple, and additional
77 format args,
78 and creates a L{Translatable} from it.
79
80 Example::
81
82 T_ = gettexter('flumotion')
83 t = T_(N_("Could not find '%s'."), file)
84
85 @param domain: the gettext domain to create translatables for.
86 """
87
88 def create(formatString, *args):
89 if isinstance(formatString, str):
90 return TranslatableSingular(domain, formatString, *args)
91 else:
92 return TranslatablePlural(domain, formatString, *args)
93
94 return lambda *args: create(*args)
95
96
98 """
99 I represent a serializable translatable gettext msg.
100 """
101 domain = None
102
103
104
105
106
107
108
109
110
111
113 """
114 I represent a translatable gettext msg in the singular form.
115 """
116
117 compareAttributes = ["domain", "format", "args"]
118
119 - def __init__(self, domain, formatString, *args):
120 """
121 @param domain: the text domain for translations of this message
122 @param formatString: a format string
123 @param args: any arguments to the format string
124 """
125 self.domain = domain
126 self.format = formatString
127 self.args = args
128
130 if self.args:
131 result = self.format % self.args
132 else:
133 result = self.format
134 return result
135 pb.setUnjellyableForClass(TranslatableSingular, TranslatableSingular)
136
137
139 """
140 I represent a translatable gettext msg in the plural form.
141 """
142
143 compareAttributes = ["domain", "singular", "plural", "count", "args"]
144
145 - def __init__(self, domain, formatString, *args):
146 """
147 @param domain: the text domain for translations of this message
148 @param formatString: a (singular, plural, count) tuple
149 @param args: any arguments to the format string
150 """
151 singular, plural, count = formatString
152 self.domain = domain
153 self.singular = singular
154 self.plural = plural
155 self.count = count
156 self.args = args
157
159 if self.args:
160 result = self.singular % self.args
161 else:
162 result = self.singular
163 return result
164 pb.setUnjellyableForClass(TranslatablePlural, TranslatablePlural)
165
166
168 """
169 I translate translatables and messages.
170 I need to be told where locale directories can be found for all domains
171 I need to translate for.
172 """
173
174 logCategory = "translator"
175
177 self._localedirs = {}
178
180 """
181 Add a locale directory for the given text domain.
182 """
183 if not domain in self._localedirs.keys():
184 self._localedirs[domain] = []
185
186 if not dir in self._localedirs[domain]:
187 self.debug('Adding localedir %s for domain %s' % (dir, domain))
188 self._localedirs[domain].append(dir)
189
191 """
192 Translate a translatable object, in the given language.
193
194 @param lang: language code (or the current locale if None)
195 """
196
197 domain = translatable.domain
198 t = None
199 if domain in self._localedirs.keys():
200
201 for localedir in self._localedirs[domain]:
202 try:
203 t = gettext.translation(domain, localedir, lang)
204 except IOError:
205 pass
206 else:
207 self.debug('no locales for domain %s' % domain)
208
209 formatString = None
210 if not t:
211
212 self.debug('no translation found, falling back to C')
213 if isinstance(translatable, TranslatableSingular):
214 formatString = translatable.format
215 elif isinstance(translatable, TranslatablePlural):
216 if translatable.count == 1:
217 formatString = translatable.singular
218 else:
219 formatString = translatable.plural
220 else:
221 raise NotImplementedError('Cannot translate translatable %r' %
222 translatable)
223 else:
224
225 if isinstance(translatable, TranslatableSingular):
226 formatString = t.gettext(translatable.format)
227 elif isinstance(translatable, TranslatablePlural):
228 formatString = t.ngettext(translatable.singular,
229 translatable.plural,
230 translatable.count)
231 else:
232 raise NotImplementedError('Cannot translate translatable %r' %
233 translatable)
234
235 if translatable.args:
236 return formatString % translatable.args
237 else:
238 return formatString
239
241 """
242 Translate a message, in the given language.
243 """
244 strings = []
245 for t in message.translatables:
246 strings.append(self.translateTranslatable(t, lang))
247 return "".join(strings)
248
249
251 """
252 Return the (at most) two-letter language code set for message translation.
253 """
254
255
256 language = os.environ.get('LANGUAGE', None)
257 if language != None:
258 LL = language[:2]
259 else:
260 lang = os.environ.get('LANG', 'en')
261 LL = lang[:2]
262
263 return LL
264
265
267 """
268 Sets up gettext so that the program gets translated.
269 Use this in any Flumotion end-user application that needs translations.
270 """
271 import locale
272
273 localedir = os.path.join(configure.localedatadir, 'locale')
274 log.debug("locale", "Loading locales from %s" % localedir)
275 gettext.bindtextdomain(configure.PACKAGE, localedir)
276 gettext.textdomain(configure.PACKAGE)
277
278
279
280
281 if hasattr(locale, 'bindtextdomain'):
282 locale.bindtextdomain(configure.PACKAGE, localedir)
283 if hasattr(locale, 'textdomain'):
284 locale.textdomain(configure.PACKAGE)
285