Merge branch 'master' into joar-skip_transcoding
[mediagoblin.git] / mediagoblin / tools / translate.py
CommitLineData
ae3bc7fa 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
ae3bc7fa
AW
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
17import gettext
18import pkg_resources
6ef75af5
SS
19
20
21from babel import localedata
ae3bc7fa
AW
22from babel.support import LazyProxy
23
24from mediagoblin import mg_globals
25
26###################
27# Translation tools
28###################
29
826919c9 30AVAILABLE_LOCALES = None
ae3bc7fa
AW
31TRANSLATIONS_PATH = pkg_resources.resource_filename(
32 'mediagoblin', 'i18n')
33
34
826919c9
SS
35def 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
6ef75af5
SS
39 for locale in localedata.list():
40 if gettext.find('mediagoblin', TRANSLATIONS_PATH, [locale]):
41 locales.append(locale)
826919c9 42 AVAILABLE_LOCALES = locales
6ef75af5
SS
43
44
ae3bc7fa
AW
45def locale_to_lower_upper(locale):
46 """
7b9f9d1e 47 Take a locale, regardless of style, and format it like "en_US"
ae3bc7fa
AW
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
59def locale_to_lower_lower(locale):
60 """
9d3e56d5 61 Take a locale, regardless of style, and format it like "en_us"
ae3bc7fa
AW
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
70def get_locale_from_request(request):
71 """
c39b9afc 72 Return most appropriate language based on prefs/request request
ae3bc7fa 73 """
c39b9afc 74 request_args = (request.args, request.form)[request.method=='POST']
ae3bc7fa 75
04453ccf 76 if 'lang' in request_args:
c39b9afc
SS
77 # User explicitely demanded a language, normalize lower_uppercase
78 target_lang = locale_to_lower_upper(request_args['lang'])
ae3bc7fa 79
7b9f9d1e
SS
80 elif 'target_lang' in request.session:
81 # TODO: Uh, ohh, this is never ever set anywhere?
ae3bc7fa 82 target_lang = request.session['target_lang']
ae3bc7fa 83 else:
c39b9afc
SS
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
ae3bc7fa
AW
92
93SETUP_GETTEXTS = {}
94
6ef75af5 95def get_gettext_translation(locale):
ae3bc7fa 96 """
6ef75af5 97 Return the gettext instance based on this locale
ae3bc7fa
AW
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.
c80982c7 105 if locale in SETUP_GETTEXTS:
ae3bc7fa
AW
106 this_gettext = SETUP_GETTEXTS[locale]
107 else:
108 this_gettext = gettext.translation(
109 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True)
6ef75af5 110 if localedata.exists(locale):
ae3bc7fa 111 SETUP_GETTEXTS[locale] = this_gettext
6ef75af5 112 return this_gettext
ae3bc7fa
AW
113
114
ae3bc7fa
AW
115def 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 """
c80982c7 122 return mg_globals.thread_scope.translations.ugettext(
ae3bc7fa
AW
123 *args, **kwargs)
124
125
126def 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
c843de8a
SS
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 _.
ae3bc7fa
AW
136 """
137 return LazyProxy(pass_to_ugettext, *args, **kwargs)
138
139
140def 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 """
c80982c7 147 return mg_globals.thread_scope.translations.ngettext(
ae3bc7fa
AW
148 *args, **kwargs)
149
150
151def 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
162def 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