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.
Download
Copy to clipboard
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!