docs: Document video resolution config.
[mediagoblin.git] / mediagoblin / tools / translate.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import gettext
18 import pkg_resources
19
20 import six
21
22 from babel import localedata
23 from babel.support import LazyProxy
24
25 from mediagoblin import mg_globals
26
27 ###################
28 # Translation tools
29 ###################
30
31 AVAILABLE_LOCALES = None
32 TRANSLATIONS_PATH = pkg_resources.resource_filename(
33 'mediagoblin', 'i18n')
34
35 # Known RTL languages
36 KNOWN_RTL = set(["ar", "fa", "he", "iw", "ur", "yi", "ji"])
37
38 def is_rtl(lang):
39 """Returns true when the local language is right to left"""
40 return lang in KNOWN_RTL
41
42 def set_available_locales():
43 """Set available locales for which we have translations"""
44 global AVAILABLE_LOCALES
45 locales=['en', 'en_US'] # these are available without translations
46 for locale in localedata.locale_identifiers():
47 if gettext.find('mediagoblin', TRANSLATIONS_PATH, [locale]):
48 locales.append(locale)
49 AVAILABLE_LOCALES = locales
50
51
52 class ReallyLazyProxy(LazyProxy):
53 """
54 Like LazyProxy, except that it doesn't cache the value ;)
55 """
56 def __init__(self, func, *args, **kwargs):
57 super(ReallyLazyProxy, self).__init__(func, *args, **kwargs)
58 object.__setattr__(self, '_is_cache_enabled', False)
59
60 def __repr__(self):
61 return "<%s for %s(%r, %r)>" % (
62 self.__class__.__name__,
63 self._func,
64 self._args,
65 self._kwargs)
66
67
68 def locale_to_lower_upper(locale):
69 """
70 Take a locale, regardless of style, and format it like "en_US"
71 """
72 if '-' in locale:
73 lang, country = locale.split('-', 1)
74 return '%s_%s' % (lang.lower(), country.upper())
75 elif '_' in locale:
76 lang, country = locale.split('_', 1)
77 return '%s_%s' % (lang.lower(), country.upper())
78 else:
79 return locale.lower()
80
81
82 def locale_to_lower_lower(locale):
83 """
84 Take a locale, regardless of style, and format it like "en_us"
85 """
86 if '_' in locale:
87 lang, country = locale.split('_', 1)
88 return '%s-%s' % (lang.lower(), country.lower())
89 else:
90 return locale.lower()
91
92
93 def get_locale_from_request(request):
94 """
95 Return most appropriate language based on prefs/request request
96 """
97 request_args = (request.args, request.form)[request.method=='POST']
98
99 if 'lang' in request_args:
100 # User explicitely demanded a language, normalize lower_uppercase
101 target_lang = locale_to_lower_upper(request_args['lang'])
102
103 elif 'target_lang' in request.session:
104 # TODO: Uh, ohh, this is never ever set anywhere?
105 target_lang = request.session['target_lang']
106 else:
107 # Pull the most acceptable language based on browser preferences
108 # This returns one of AVAILABLE_LOCALES which is aready case-normalized.
109 # Note: in our tests request.accept_languages is None, so we need
110 # to explicitely fallback to en here.
111 target_lang = request.accept_languages.best_match(AVAILABLE_LOCALES) \
112 or "en_US"
113
114 return target_lang
115
116 SETUP_GETTEXTS = {}
117
118 def get_gettext_translation(locale):
119 """
120 Return the gettext instance based on this locale
121 """
122 # Later on when we have plugins we may want to enable the
123 # multi-translations system they have so we can handle plugin
124 # translations too
125
126 # TODO: fallback nicely on translations from pt_PT to pt if not
127 # available, etc.
128 if locale in SETUP_GETTEXTS:
129 this_gettext = SETUP_GETTEXTS[locale]
130 else:
131 this_gettext = gettext.translation(
132 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True)
133 if localedata.exists(locale):
134 SETUP_GETTEXTS[locale] = this_gettext
135 return this_gettext
136
137
138 def set_thread_locale(locale):
139 """Set the current translation for this thread"""
140 mg_globals.thread_scope.translations = get_gettext_translation(locale)
141
142
143 def pass_to_ugettext(*args, **kwargs):
144 """
145 Pass a translation on to the appropriate ugettext method.
146
147 The reason we can't have a global ugettext method is because
148 mg_globals gets swapped out by the application per-request.
149 """
150 if six.PY2:
151 return mg_globals.thread_scope.translations.ugettext(*args, **kwargs)
152 return mg_globals.thread_scope.translations.gettext(*args, **kwargs)
153
154 def pass_to_ungettext(*args, **kwargs):
155 """
156 Pass a translation on to the appropriate ungettext method.
157
158 The reason we can't have a global ugettext method is because
159 mg_globals gets swapped out by the application per-request.
160 """
161 if six.PY2:
162 return mg_globals.thread_scope.translations.ungettext(*args, **kwargs)
163 return mg_globals.thread_scope.translations.ngettext(*args, **kwargs)
164
165
166 def lazy_pass_to_ugettext(*args, **kwargs):
167 """
168 Lazily pass to ugettext.
169
170 This is useful if you have to define a translation on a module
171 level but you need it to not translate until the time that it's
172 used as a string. For example, in:
173 def func(self, message=_('Hello boys and girls'))
174
175 you would want to use the lazy version for _.
176 """
177 return ReallyLazyProxy(pass_to_ugettext, *args, **kwargs)
178
179
180 def pass_to_ngettext(*args, **kwargs):
181 """
182 Pass a translation on to the appropriate ngettext method.
183
184 The reason we can't have a global ngettext method is because
185 mg_globals gets swapped out by the application per-request.
186 """
187 return mg_globals.thread_scope.translations.ngettext(
188 *args, **kwargs)
189
190
191 def lazy_pass_to_ngettext(*args, **kwargs):
192 """
193 Lazily pass to ngettext.
194
195 This is useful if you have to define a translation on a module
196 level but you need it to not translate until the time that it's
197 used as a string.
198 """
199 return ReallyLazyProxy(pass_to_ngettext, *args, **kwargs)
200
201 def lazy_pass_to_ungettext(*args, **kwargs):
202 """
203 Lazily pass to ungettext.
204
205 This is useful if you have to define a translation on a module
206 level but you need it to not translate until the time that it's
207 used as a string.
208 """
209 return ReallyLazyProxy(pass_to_ungettext, *args, **kwargs)
210
211
212 def fake_ugettext_passthrough(string):
213 """
214 Fake a ugettext call for extraction's sake ;)
215
216 In wtforms there's a separate way to define a method to translate
217 things... so we just need to mark up the text so that it can be
218 extracted, not so that it's actually run through gettext.
219 """
220 return string