Premiere version : mise en route du suivi.
[auf_roundup.git] / roundup / .svn / text-base / i18n.py.svn-base
1 #
2 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
3 # This module is free software, and you may redistribute it and/or modify
4 # under the same terms as Python, so long as this copyright message and
5 # disclaimer are retained in their original form.
6 #
7 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
8 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
9 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
10 # POSSIBILITY OF SUCH DAMAGE.
11 #
12 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17 #
18 # $Id: i18n.py,v 1.15 2005-06-14 05:33:32 a1s Exp $
19
20 """
21 RoundUp Internationalization (I18N)
22
23 To use this module, the following code should be used::
24
25     from roundup.i18n import _
26     ...
27     print _("Some text that can be translated")
28
29 Note that to enable re-ordering of inserted texts in formatting strings
30 (which can easily happen if a sentence has to be re-ordered due to
31 grammatical changes), translatable formats should use named format specs::
32
33     ... _('Index of %(classname)s') % {'classname': cn} ...
34
35 Also, this eases the job of translators since they have some context what
36 the dynamic portion of a message really means.
37 """
38 __docformat__ = 'restructuredtext'
39
40 import errno
41 import gettext as gettext_module
42 import os
43
44 from roundup import msgfmt
45
46 # List of directories for mo file search (see SF bug 1219689)
47 LOCALE_DIRS = [
48     gettext_module._default_localedir,
49 ]
50 # compute mo location relative to roundup installation directory
51 # (prefix/lib/python/site-packages/roundup/msgfmt.py on posix systems,
52 # prefix/lib/site-packages/roundup/msgfmt.py on windows).
53 # locale root is prefix/share/locale.
54 if os.name == "nt":
55     _mo_path = [".."] * 4 + ["share", "locale"]
56 else:
57     _mo_path = [".."] * 5 + ["share", "locale"]
58 _mo_path = os.path.normpath(os.path.join(msgfmt.__file__, *_mo_path))
59 if _mo_path not in LOCALE_DIRS:
60     LOCALE_DIRS.append(_mo_path)
61 del _mo_path
62
63 # Roundup text domain
64 DOMAIN = "roundup"
65
66 if hasattr(gettext_module.GNUTranslations, "ngettext"):
67     # gettext_module has everything needed
68     RoundupNullTranslations = gettext_module.NullTranslations
69     RoundupTranslations = gettext_module.GNUTranslations
70 else:
71     # prior to 2.3, there was no plural forms.  mix simple emulation in
72     class PluralFormsMixIn:
73         def ngettext(self, singular, plural, count):
74             if count == 1:
75                 _msg = singular
76             else:
77                 _msg = plural
78             return self.gettext(_msg)
79         def ungettext(self, singular, plural, count):
80             if count == 1:
81                 _msg = singular
82             else:
83                 _msg = plural
84             return self.ugettext(_msg)
85     class RoundupNullTranslations(
86         gettext_module.NullTranslations, PluralFormsMixIn
87     ):
88         pass
89     class RoundupTranslations(
90         gettext_module.GNUTranslations, PluralFormsMixIn
91     ):
92         pass
93
94 def find_locales(language=None):
95     """Return normalized list of locale names to try for given language
96
97     Argument 'language' may be a single language code or a list of codes.
98     If 'language' is omitted or None, use locale settings in OS environment.
99
100     """
101     # body of this function is borrowed from gettext_module.find()
102     if language is None:
103         languages = []
104         for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
105             val = os.environ.get(envar)
106             if val:
107                 languages = val.split(':')
108                 break
109     elif isinstance(language, str) or  isinstance(language, unicode):
110         languages = [language]
111     else:
112         # 'language' must be iterable
113         languages = language
114     # now normalize and expand the languages
115     nelangs = []
116     for lang in languages:
117         for nelang in gettext_module._expand_lang(lang):
118             if nelang not in nelangs:
119                 nelangs.append(nelang)
120     return nelangs
121
122 def get_mofile(languages, localedir, domain=None):
123     """Return the first of .mo files found in localedir for languages
124
125     Parameters:
126         languages:
127             list of locale names to try
128         localedir:
129             path to directory containing locale files.
130             Usually this is either gettext_module._default_localedir
131             or 'locale' subdirectory in the tracker home.
132         domain:
133             optional name of messages domain.
134             If omitted or None, work with simplified
135             locale directory, as used in tracker homes:
136             message catalogs are kept in files locale.po
137             instead of locale/LC_MESSAGES/domain.po
138
139     Return the path of the first .mo file found.
140     If nothing found, return None.
141
142     Automatically compile .po files if necessary.
143
144     """
145     for locale in languages:
146         if locale == "C":
147             break
148         if domain:
149             basename = os.path.join(localedir, locale, "LC_MESSAGES", domain)
150         else:
151             basename = os.path.join(localedir, locale)
152         # look for message catalog files, check timestamps
153         mofile = basename + ".mo"
154         if os.path.isfile(mofile):
155             motime = os.path.getmtime(mofile)
156         else:
157             motime = 0
158         pofile = basename + ".po"
159         if os.path.isfile(pofile):
160             potime = os.path.getmtime(pofile)
161         else:
162             potime = 0
163         # see what we've found
164         if motime < potime:
165             # compile
166             msgfmt.make(pofile, mofile)
167         elif motime == 0:
168             # no files found - proceed to the next locale name
169             continue
170         # .mo file found or made
171         return mofile
172     return None
173
174 def get_translation(language=None, tracker_home=None,
175     translation_class=RoundupTranslations,
176     null_translation_class=RoundupNullTranslations
177 ):
178     """Return Translation object for given language and domain
179
180     Argument 'language' may be a single language code or a list of codes.
181     If 'language' is omitted or None, use locale settings in OS environment.
182
183     Arguments 'translation_class' and 'null_translation_class'
184     specify the classes that are instantiated for existing
185     and non-existing translations, respectively.
186
187     """
188     mofiles = []
189     # locale directory paths
190     if tracker_home is None:
191         tracker_locale = None
192     else:
193         tracker_locale = os.path.join(tracker_home, "locale")
194     # get the list of locales
195     locales = find_locales(language)
196     # add mofiles found in the tracker, then in the system locale directory
197     if tracker_locale:
198         mofiles.append(get_mofile(locales, tracker_locale))
199     for system_locale in LOCALE_DIRS:
200         mofiles.append(get_mofile(locales, system_locale, DOMAIN))
201     # we want to fall back to english unless english is selected language
202     if "en" not in locales:
203         locales = find_locales("en")
204         # add mofiles found in the tracker, then in the system locale directory
205         if tracker_locale:
206             mofiles.append(get_mofile(locales, tracker_locale))
207         for system_locale in LOCALE_DIRS:
208             mofiles.append(get_mofile(locales, system_locale, DOMAIN))
209     # filter out elements that are not found
210     mofiles = filter(None, mofiles)
211     if mofiles:
212         translator = translation_class(open(mofiles[0], "rb"))
213         for mofile in mofiles[1:]:
214             # note: current implementation of gettext_module
215             #   always adds fallback to the end of the fallback chain.
216             translator.add_fallback(translation_class(open(mofile, "rb")))
217     else:
218         translator = null_translation_class()
219     return translator
220
221 # static translations object
222 translation = get_translation()
223 # static translation functions
224 _ = gettext = translation.gettext
225 ugettext = translation.ugettext
226 ngettext = translation.ngettext
227 ungettext = translation.ungettext
228
229 # vim: set filetype=python sts=4 sw=4 et si :