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