1515# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717#
18- # $Id: i18n.py,v 1.10 2004-07-14 07:27:21 a1s Exp $
18+ # $Id: i18n.py,v 1.11 2004-10-23 14:03:34 a1s Exp $
1919
2020"""
2121RoundUp Internationalization (I18N)
3838__docformat__ = 'restructuredtext'
3939
4040import errno
41+ import gettext as gettext_module
42+ import os
43+
44+ from roundup import msgfmt
4145
4246# Roundup text domain
4347DOMAIN = "roundup"
4448
45- # first, we try to import gettext; this probably never fails, but we make
46- # sure we survive this anyway
47- try :
48- import gettext as gettext_module
49- except ImportError :
50- gettext_module = None
51-
52- # use or emulate features of gettext_module
53- if not gettext_module :
54- # no gettext engine available.
55- # implement emulation for Translations class
56- # and find_translation() function
57- class RoundupNullTranslations :
58- """Dummy Translations class
59-
60- Only methods used by Roundup are implemented.
61-
62- """
63- def add_fallback (self , fallback ):
64- pass
65- def gettext (self , text ):
66- return text
67- def ugettext (self , text ):
68- return unicode (text )
69- def ngettext (self , singular , plural , count ):
70- if count == 1 : return singular
71- return plural
72- def ungettext (self , singular , plural , count ):
73- return unicode (self .ngettext (singular , plural , count ))
74-
75- RoundupTranslations = RoundupNullTranslations
76-
77- def find_translation (domain , localedir = None , languages = None , class_ = None ):
78- """Always raise IOError (no message catalogs available)"""
79- raise IOError (errno .ENOENT ,
80- "No translation file found for domain" , domain )
81-
82- elif not hasattr (gettext_module .GNUTranslations , "ngettext" ):
49+ if hasattr (gettext_module .GNUTranslations , "ngettext" ):
50+ # gettext_module has everything needed
51+ RoundupNullTranslations = gettext_module .NullTranslations
52+ RoundupTranslations = gettext_module .GNUTranslations
53+ else :
8354 # prior to 2.3, there was no plural forms. mix simple emulation in
8455 class PluralFormsMixIn :
8556 def ngettext (self , singular , plural , count ):
@@ -102,16 +73,84 @@ class RoundupTranslations(
10273 gettext_module .GNUTranslations , PluralFormsMixIn
10374 ):
10475 pass
105- # lookup function is available
106- find_translation = gettext_module .translation
107- else :
108- # gettext_module has everything needed
109- RoundupNullTranslations = gettext_module .NullTranslations
110- RoundupTranslations = gettext_module .GNUTranslations
111- find_translation = gettext_module .translation
11276
77+ def find_locales (language = None ):
78+ """Return normalized list of locale names to try for given language
79+
80+ If language is None, use OS environment variables as a starting point
11381
114- def get_translation (language = None , domain = DOMAIN ,
82+ """
83+ # body of this function is borrowed from gettext_module.find()
84+ if language is None :
85+ languages = []
86+ for envar in ('LANGUAGE' , 'LC_ALL' , 'LC_MESSAGES' , 'LANG' ):
87+ val = os .environ .get (envar )
88+ if val :
89+ languages = val .split (':' )
90+ break
91+ else :
92+ languages = [language ]
93+ # now normalize and expand the languages
94+ nelangs = []
95+ for lang in languages :
96+ for nelang in gettext_module ._expand_lang (lang ):
97+ if nelang not in nelangs :
98+ nelangs .append (nelang )
99+ return nelangs
100+
101+ def get_mofile (languages , localedir , domain = None ):
102+ """Return the first of .mo files found in localedir for languages
103+
104+ Parameters:
105+ languages:
106+ list of locale names to try
107+ localedir:
108+ path to directory containing locale files.
109+ Usually this is either gettext_module._default_localedir
110+ or 'locale' subdirectory in the tracker home.
111+ domain:
112+ optional name of messages domain.
113+ If omitted or None, work with simplified
114+ locale directory, as used in tracker homes:
115+ message catalogs are kept in files locale.po
116+ instead of locale/LC_MESSAGES/domain.po
117+
118+ Return the path of the first .mo file found.
119+ If nothing found, return None.
120+
121+ Automatically compile .po files if necessary.
122+
123+ """
124+ for locale in languages :
125+ if locale == "C" :
126+ break
127+ if domain :
128+ basename = os .path .join (localedir , locale , "LC_MESSAGES" , domain )
129+ else :
130+ basename = os .path .join (localedir , locale )
131+ # look for message catalog files, check timestamps
132+ mofile = basename + ".mo"
133+ if os .path .isfile (mofile ):
134+ motime = os .path .getmtime (mofile )
135+ else :
136+ motime = 0
137+ pofile = basename + ".po"
138+ if os .path .isfile (pofile ):
139+ potime = os .path .getmtime (pofile )
140+ else :
141+ potime = 0
142+ # see what we've found
143+ if motime < potime :
144+ # compile
145+ msgfmt .make (pofile , mofile )
146+ elif motime == 0 :
147+ # no files found - proceed to the next locale name
148+ continue
149+ # .mo file found or made
150+ return mofile
151+ return None
152+
153+ def get_translation (language = None , tracker_home = None ,
115154 translation_class = RoundupTranslations ,
116155 null_translation_class = RoundupNullTranslations
117156):
@@ -120,51 +159,37 @@ def get_translation(language=None, domain=DOMAIN,
120159 Arguments 'translation_class' and 'null_translation_class'
121160 specify the classes that are instantiated for existing
122161 and non-existing translations, respectively.
162+
123163 """
124- if language :
125- _languages = [language ]
164+ mofiles = []
165+ # locale directory paths
166+ system_locale = gettext_module ._default_localedir
167+ if tracker_home is not None :
168+ tracker_locale = os .path .join (tracker_home , "locale" )
126169 else :
127- # use OS environment
128- _languages = None
129- # except for english ("en") language, add english fallback if available
130- if language == "en" :
131- _fallback = None
170+ tracker_locale = None
171+ # get the list of locales
172+ locales = find_locales (language )
173+ # add mofiles found in the tracker, then in the system locale directory
174+ if tracker_locale :
175+ mofiles .append (get_mofile (locales , tracker_locale ))
176+ mofiles .append (get_mofile (locales , system_locale , DOMAIN ))
177+ # we want to fall back to english unless english is selected language
178+ if "en" not in locales :
179+ locales = find_locales ("en" )
180+ # add mofiles found in the tracker, then in the system locale directory
181+ if tracker_locale :
182+ mofiles .append (get_mofile (locales , tracker_locale ))
183+ mofiles .append (get_mofile (locales , system_locale , DOMAIN ))
184+ # filter out elements that are not found
185+ mofiles = filter (None , mofiles )
186+ if mofiles :
187+ translator = translation_class (open (mofiles [0 ], "rb" ))
188+ for mofile in mofiles :
189+ translator .add_fallback (translation_class (open (mofile , "rb" )))
132190 else :
133- try :
134- _fallback = find_translation (domain = domain , languages = ["en" ],
135- class_ = translation_class )
136- # gettext.translation returns a cached translation
137- # even if it is not of the desired class.
138- # This is a quick-and-dirty solution for this problem.
139- # It works with current codebase, because all translators
140- # inherit from respective base translation classes
141- # defined in the gettext module, i.e. have same internal data.
142- # The cached instance is not affected by this hack,
143- # 'cause gettext made a copy for us.
144- # XXX Consider making a copy of gettext.translation function
145- # with class bug fixed...
146- if _fallback .__class__ != translation_class :
147- _fallback .__class__ = translation_class
148- except IOError :
149- # no .mo files found
150- _fallback = None
151- # get the translation
152- try :
153- _translation = find_translation (domain = domain , languages = _languages ,
154- class_ = translation_class )
155- # XXX See the comment after first find_translation() call
156- if _translation .__class__ != translation_class :
157- _translation .__class__ = translation_class
158- except IOError :
159- _translation = None
160- # see what's found
161- if _translation and _fallback :
162- _translation .add_fallback (_fallback )
163- elif _fallback :
164- _translation = _fallback
165- elif not _translation :
166- _translation = null_translation_class ()
167- return _translation
191+ translator = null_translation_class ()
192+ return translator
168193
169194# static translations object
170195translation = get_translation ()
0 commit comments