Merge branch 'master' into joar-skip_transcoding
[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
21 from babel import localedata
22 from babel.support import LazyProxy
23
24 from mediagoblin import mg_globals
25
26 ###################
27 # Translation tools
28 ###################
29
30 AVAILABLE_LOCALES = None
31 TRANSLATIONS_PATH = pkg_resources.resource_filename(
32 'mediagoblin', 'i18n')
33
34
35 def set_available_locales():
36 """Set available locales for which we have translations"""
37 global AVAILABLE_LOCALES
38 locales=['en', 'en_US'] # these are available without translations
39 for locale in localedata.list():
40 if gettext.find('mediagoblin', TRANSLATIONS_PATH, [locale]):
41 locales.append(locale)
42 AVAILABLE_LOCALES = locales
43
44
45 def locale_to_lower_upper(locale):
46 """
47 Take a locale, regardless of style, and format it like "en_US"
48 """
49 if '-' in locale:
50 lang, country = locale.split('-', 1)
51 return '%s_%s' % (lang.lower(), country.upper())
52 elif '_' in locale:
53 lang, country = locale.split('_', 1)
54 return '%s_%s' % (lang.lower(), country.upper())
55 else:
56 return locale.lower()
57
58
59 def locale_to_lower_lower(locale):
60 """
61 Take a locale, regardless of style, and format it like "en_us"
62 """
63 if '_' in locale:
64 lang, country = locale.split('_', 1)
65 return '%s-%s' % (lang.lower(), country.lower())
66 else:
67 return locale.lower()
68
69
70 def get_locale_from_request(request):
71 """
72 Return most appropriate language based on prefs/request request
73 """
74 request_args = (request.args, request.form)[request.method=='POST']
75
76 if 'lang' in request_args:
77 # User explicitely demanded a language, normalize lower_uppercase
78 target_lang = locale_to_lower_upper(request_args['lang'])
79
80 elif 'target_lang' in request.session:
81 # TODO: Uh, ohh, this is never ever set anywhere?
82 target_lang = request.session['target_lang']
83 else:
84 # Pull the most acceptable language based on browser preferences
85 # This returns one of AVAILABLE_LOCALES which is aready case-normalized.
86 # Note: in our tests request.accept_languages is None, so we need
87 # to explicitely fallback to en here.
88 target_lang = request.accept_languages.best_match(AVAILABLE_LOCALES) \
89 or "en_US"
90
91 return target_lang
92
93 SETUP_GETTEXTS = {}
94
95 def get_gettext_translation(locale):
96 """
97 Return the gettext instance based on this locale
98 """
99 # Later on when we have plugins we may want to enable the
100 # multi-translations system they have so we can handle plugin
101 # translations too
102
103 # TODO: fallback nicely on translations from pt_PT to pt if not
104 # available, etc.
105 if locale in SETUP_GETTEXTS:
106 this_gettext = SETUP_GETTEXTS[locale]
107 else:
108 this_gettext = gettext.translation(
109 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True)
110 if localedata.exists(locale):
111 SETUP_GETTEXTS[locale] = this_gettext
112 return this_gettext
113
114
115 def pass_to_ugettext(*args, **kwargs):
116 """
117 Pass a translation on to the appropriate ugettext method.
118
119 The reason we can't have a global ugettext method is because
120 mg_globals gets swapped out by the application per-request.
121 """
122 return mg_globals.thread_scope.translations.ugettext(
123 *args, **kwargs)
124
125
126 def lazy_pass_to_ugettext(*args, **kwargs):
127 """
128 Lazily pass to ugettext.
129
130 This is useful if you have to define a translation on a module
131 level but you need it to not translate until the time that it's
132 used as a string. For example, in:
133 def func(self, message=_('Hello boys and girls'))
134
135 you would want to use the lazy version for _.
136 """
137 return LazyProxy(pass_to_ugettext, *args, **kwargs)
138
139
140 def pass_to_ngettext(*args, **kwargs):
141 """
142 Pass a translation on to the appropriate ngettext method.
143
144 The reason we can't have a global ngettext method is because
145 mg_globals gets swapped out by the application per-request.
146 """
147 return mg_globals.thread_scope.translations.ngettext(
148 *args, **kwargs)
149
150
151 def lazy_pass_to_ngettext(*args, **kwargs):
152 """
153 Lazily pass to ngettext.
154
155 This is useful if you have to define a translation on a module
156 level but you need it to not translate until the time that it's
157 used as a string.
158 """
159 return LazyProxy(pass_to_ngettext, *args, **kwargs)
160
161
162 def fake_ugettext_passthrough(string):
163 """
164 Fake a ugettext call for extraction's sake ;)
165
166 In wtforms there's a separate way to define a method to translate
167 things... so we just need to mark up the text so that it can be
168 extracted, not so that it's actually run through gettext.
169 """
170 return string