Injecting _() in the __builtin__ module in order to inject global functions _() and N_() is common in applications which need i18n. This is a variation on the theme that does this in a multi-threaded environment, using threading.local from Python-2.4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | #!/usr/bin/env python
"""
Usage of threaded.local as gettext() to implement the __builtin__
I18n _() kludge in a multi-threaded environment. This is a simple
proof-of-concept that demonstrates the kludge.
"""
import threading, time, __builtin__, random
# Create a translator function that can be used in __builtin__ to
# fetch translations from the gettext catalogs and will work in a
# multi-threaded environment. This gets setup once.
catalogs = threading.local()
def gettext_getfunc( lang ):
"""
Returns a function used to translate to a specific catalog.
"""
# Note: you would get the gettext catalog here and install it in the
# closure.
def tr( s ):
# Note: we do not really translate here, we just prepend the
# language, but you get the idea.
return '[%s] %s' % (lang, s)
return tr
def gettext_translate( s ):
"""
Thread-safe version of _().
We look up the thread-local translation function.
"""
return catalogs.translate(s)
# Inject the _() function in the builtins. You could also inject a N_()
# function for noop translation markers.
__builtin__._ = gettext_translate
def handle_request():
"""
Treat a request, with i18n strings.
"""
# Fetch and return a translated string.
# This is the interesting bit, from a client's point-of-view.
print _('bli'), _('bla'), _('blo')
# Do something else.
time.sleep(random.random())
# A thread class. This would be provided by the web framework,
# normally.
class Thread(threading.Thread):
def __init__( self, num ):
threading.Thread.__init__(self)
self.num = num
def run( self ):
while 1: # many requests
# Setup a request.
# Get the request's language (we just select a random
# language here for demonstration purposes).
reqlang = random.choice(['en', 'fr', 'es', 'de',
'pt', 'si', 'dk', 'it'])
# Setup current language for thread with a closure that
# efficiently implement the desired catalog lookup.
catalogs.translate = gettext_getfunc(reqlang)
# Dispatch a request to be handled.
handle_request()
# Create and launch test threads.
threads = []
for x in xrange(5):
t = Thread(x)
t.start()
|
I was toying with the idea of converting my web application framework to multi-threaded rather than prefork (mod_python/apache2 prefork), so I wondered how I would solve the _() builtin kludge problem. This is a proof-of-concept. Comments welcome.
P.S. I purposefully do not include actual gettext code here, because I want the test program to be easy to setup (i.e. no catalogs needed) and this same trick could work with other i18n catalog libraries.
This code is pure beauty. I had no idea about threading.local(), nor being able to implement my own interception of the builtin gettext "_" function. Solve all my multi-threaded internationalization problems.
Thanks for posting!