Merge branch 'mediagoblin-upstream' into bug444_fix_utils_py_redux
[mediagoblin.git] / mediagoblin / tools / translate.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 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 from babel.localedata import exists
20 from babel.support import LazyProxy
21
22 from mediagoblin import mg_globals
23
24 ###################
25 # Translation tools
26 ###################
27
28
29 TRANSLATIONS_PATH = pkg_resources.resource_filename(
30 'mediagoblin', 'i18n')
31
32
33 def locale_to_lower_upper(locale):
34 """
35 Take a locale, regardless of style, and format it like "en-us"
36 """
37 if '-' in locale:
38 lang, country = locale.split('-', 1)
39 return '%s_%s' % (lang.lower(), country.upper())
40 elif '_' in locale:
41 lang, country = locale.split('_', 1)
42 return '%s_%s' % (lang.lower(), country.upper())
43 else:
44 return locale.lower()
45
46
47 def locale_to_lower_lower(locale):
48 """
49 Take a locale, regardless of style, and format it like "en_US"
50 """
51 if '_' in locale:
52 lang, country = locale.split('_', 1)
53 return '%s-%s' % (lang.lower(), country.lower())
54 else:
55 return locale.lower()
56
57
58 def get_locale_from_request(request):
59 """
60 Figure out what target language is most appropriate based on the
61 request
62 """
63 request_form = request.GET or request.POST
64
65 if request_form.has_key('lang'):
66 return locale_to_lower_upper(request_form['lang'])
67
68 accept_lang_matches = request.accept_language.best_matches()
69
70 # Your routing can explicitly specify a target language
71 matchdict = request.matchdict or {}
72
73 if matchdict.has_key('locale'):
74 target_lang = matchdict['locale']
75 elif request.session.has_key('target_lang'):
76 target_lang = request.session['target_lang']
77 # Pull the first acceptable language
78 elif accept_lang_matches:
79 target_lang = accept_lang_matches[0]
80 # Fall back to English
81 else:
82 target_lang = 'en'
83
84 return locale_to_lower_upper(target_lang)
85
86 SETUP_GETTEXTS = {}
87
88 def setup_gettext(locale):
89 """
90 Setup the gettext instance based on this locale
91 """
92 # Later on when we have plugins we may want to enable the
93 # multi-translations system they have so we can handle plugin
94 # translations too
95
96 # TODO: fallback nicely on translations from pt_PT to pt if not
97 # available, etc.
98 if SETUP_GETTEXTS.has_key(locale):
99 this_gettext = SETUP_GETTEXTS[locale]
100 else:
101 this_gettext = gettext.translation(
102 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True)
103 if exists(locale):
104 SETUP_GETTEXTS[locale] = this_gettext
105
106 mg_globals.setup_globals(
107 translations=this_gettext)
108
109
110 # Force en to be setup before anything else so that
111 # mg_globals.translations is never None
112 setup_gettext('en')
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.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.
133 """
134 return LazyProxy(pass_to_ugettext, *args, **kwargs)
135
136
137 def pass_to_ngettext(*args, **kwargs):
138 """
139 Pass a translation on to the appropriate ngettext method.
140
141 The reason we can't have a global ngettext method is because
142 mg_globals gets swapped out by the application per-request.
143 """
144 return mg_globals.translations.ngettext(
145 *args, **kwargs)
146
147
148 def lazy_pass_to_ngettext(*args, **kwargs):
149 """
150 Lazily pass to ngettext.
151
152 This is useful if you have to define a translation on a module
153 level but you need it to not translate until the time that it's
154 used as a string.
155 """
156 return LazyProxy(pass_to_ngettext, *args, **kwargs)
157
158
159 def fake_ugettext_passthrough(string):
160 """
161 Fake a ugettext call for extraction's sake ;)
162
163 In wtforms there's a separate way to define a method to translate
164 things... so we just need to mark up the text so that it can be
165 extracted, not so that it's actually run through gettext.
166 """
167 return string