1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
22 from babel
import localedata
23 from babel
.support
import LazyProxy
25 from mediagoblin
import mg_globals
31 AVAILABLE_LOCALES
= None
32 TRANSLATIONS_PATH
= pkg_resources
.resource_filename(
33 'mediagoblin', 'i18n')
36 KNOWN_RTL
= set(["ar", "fa", "he", "iw", "ur", "yi", "ji"])
39 """Returns true when the local language is right to left"""
40 return lang
in KNOWN_RTL
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
52 class ReallyLazyProxy(LazyProxy
):
54 Like LazyProxy, except that it doesn't cache the value ;)
56 def __init__(self
, func
, *args
, **kwargs
):
57 super(ReallyLazyProxy
, self
).__init
__(func
, *args
, **kwargs
)
58 object.__setattr
__(self
, '_is_cache_enabled', False)
61 return "<%s for %s(%r, %r)>" % (
62 self
.__class
__.__name
__,
68 def locale_to_lower_upper(locale
):
70 Take a locale, regardless of style, and format it like "en_US"
73 lang
, country
= locale
.split('-', 1)
74 return '%s_%s' % (lang
.lower(), country
.upper())
76 lang
, country
= locale
.split('_', 1)
77 return '%s_%s' % (lang
.lower(), country
.upper())
82 def locale_to_lower_lower(locale
):
84 Take a locale, regardless of style, and format it like "en_us"
87 lang
, country
= locale
.split('_', 1)
88 return '%s-%s' % (lang
.lower(), country
.lower())
93 def get_locale_from_request(request
):
95 Return most appropriate language based on prefs/request request
97 request_args
= (request
.args
, request
.form
)[request
.method
=='POST']
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'])
103 elif 'target_lang' in request
.session
:
104 # TODO: Uh, ohh, this is never ever set anywhere?
105 target_lang
= request
.session
['target_lang']
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
) \
118 def get_gettext_translation(locale
):
120 Return the gettext instance based on this locale
122 # Later on when we have plugins we may want to enable the
123 # multi-translations system they have so we can handle plugin
126 # TODO: fallback nicely on translations from pt_PT to pt if not
128 if locale
in SETUP_GETTEXTS
:
129 this_gettext
= SETUP_GETTEXTS
[locale
]
131 this_gettext
= gettext
.translation(
132 'mediagoblin', TRANSLATIONS_PATH
, [locale
], fallback
=True)
133 if localedata
.exists(locale
):
134 SETUP_GETTEXTS
[locale
] = this_gettext
138 def set_thread_locale(locale
):
139 """Set the current translation for this thread"""
140 mg_globals
.thread_scope
.translations
= get_gettext_translation(locale
)
143 def pass_to_ugettext(*args
, **kwargs
):
145 Pass a translation on to the appropriate ugettext method.
147 The reason we can't have a global ugettext method is because
148 mg_globals gets swapped out by the application per-request.
151 return mg_globals
.thread_scope
.translations
.ugettext(*args
, **kwargs
)
152 return mg_globals
.thread_scope
.translations
.gettext(*args
, **kwargs
)
154 def pass_to_ungettext(*args
, **kwargs
):
156 Pass a translation on to the appropriate ungettext method.
158 The reason we can't have a global ugettext method is because
159 mg_globals gets swapped out by the application per-request.
162 return mg_globals
.thread_scope
.translations
.ungettext(*args
, **kwargs
)
163 return mg_globals
.thread_scope
.translations
.ngettext(*args
, **kwargs
)
166 def lazy_pass_to_ugettext(*args
, **kwargs
):
168 Lazily pass to ugettext.
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'))
175 you would want to use the lazy version for _.
177 return ReallyLazyProxy(pass_to_ugettext
, *args
, **kwargs
)
180 def pass_to_ngettext(*args
, **kwargs
):
182 Pass a translation on to the appropriate ngettext method.
184 The reason we can't have a global ngettext method is because
185 mg_globals gets swapped out by the application per-request.
187 return mg_globals
.thread_scope
.translations
.ngettext(
191 def lazy_pass_to_ngettext(*args
, **kwargs
):
193 Lazily pass to ngettext.
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
199 return ReallyLazyProxy(pass_to_ngettext
, *args
, **kwargs
)
201 def lazy_pass_to_ungettext(*args
, **kwargs
):
203 Lazily pass to ungettext.
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
209 return ReallyLazyProxy(pass_to_ungettext
, *args
, **kwargs
)
212 def fake_ugettext_passthrough(string
):
214 Fake a ugettext call for extraction's sake ;)
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.