~bzr-pqm/bzr/bzr.dev

5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
1
# -*- coding: utf-8 -*-
2
#
3
# Copyright (C) 2007 Lukáš Lalinský <lalinsky@gmail.com>
4
# Copyright (C) 2007,2009 Alexander Belchenko <bialix@ukr.net>
5
# Copyright (C) 2011 Canonical Ltd
6
#
5875.2.11 by Vincent Ladeuil
Slightly different wrapping of the GPL header broke test_source :-/
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2 of the License, or
10
# (at your option) any later version.
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
11
#
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
5875.2.11 by Vincent Ladeuil
Slightly different wrapping of the GPL header broke test_source :-/
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
20
21
# This module is copied from Bazaar Explorer and modified for bzr.
22
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
23
"""i18n and l10n support for Bazaar."""
24
6379.6.3 by Jelmer Vernooij
Use absolute_import.
25
from __future__ import absolute_import
26
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
27
import gettext as _gettext
28
import os
29
import sys
30
6162.4.7 by Jonathan Riddell
two empty lines between top level items
31
6133.3.10 by Jonathan Riddell
default _translations back to None so we can tell if it gets installed
32
_translations = None
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
33
34
5875.2.7 by INADA Naoki
Change order of functions.
35
def gettext(message):
36
    """Translate message. 
5875.2.9 by Vincent Ladeuil
Cleanup some PEP8 issues and move test code in test module, test_multiline still failing.
37
    
38
    :returns: translated message as unicode.
39
    """
6131.2.1 by Jonathan Riddell
install translations whenever they are used
40
    install()
5875.3.25 by Vincent Ladeuil
Fix test failures and make sure we don't rely on a default translation.
41
    return _translations.ugettext(message)
5875.2.7 by INADA Naoki
Change order of functions.
42
5875.2.9 by Vincent Ladeuil
Cleanup some PEP8 issues and move test code in test module, test_multiline still failing.
43
5971.1.24 by Jonathan Riddell
fix translations for plural forms
44
def ngettext(singular, plural, number):
45
    """Translate message with plural forms based on `number`.
46
47
    :param singular: English language message in singular form
48
    :param plural: English language message in plural form
49
    :param number: the number this message should be translated for
5875.2.9 by Vincent Ladeuil
Cleanup some PEP8 issues and move test code in test module, test_multiline still failing.
50
51
    :returns: translated message as unicode.
52
    """
6131.2.1 by Jonathan Riddell
install translations whenever they are used
53
    install()
5971.1.77 by Jonathan Riddell
merge in trunk
54
    return _translations.ungettext(singular, plural, number)
5875.2.7 by INADA Naoki
Change order of functions.
55
5875.2.9 by Vincent Ladeuil
Cleanup some PEP8 issues and move test code in test module, test_multiline still failing.
56
5875.2.7 by INADA Naoki
Change order of functions.
57
def N_(msg):
58
    """Mark message for translation but don't translate it right away."""
59
    return msg
60
5875.2.9 by Vincent Ladeuil
Cleanup some PEP8 issues and move test code in test module, test_multiline still failing.
61
5875.2.7 by INADA Naoki
Change order of functions.
62
def gettext_per_paragraph(message):
63
    """Translate message per paragraph.
64
5875.2.9 by Vincent Ladeuil
Cleanup some PEP8 issues and move test code in test module, test_multiline still failing.
65
    :returns: concatenated translated message as unicode.
66
    """
6133.3.14 by Jonathan Riddell
install() translations for per_paragraph too
67
    install()
5875.2.7 by INADA Naoki
Change order of functions.
68
    paragraphs = message.split(u'\n\n')
5875.3.25 by Vincent Ladeuil
Fix test failures and make sure we don't rely on a default translation.
69
    ugettext = _translations.ugettext
5875.2.7 by INADA Naoki
Change order of functions.
70
    # Be careful not to translate the empty string -- it holds the
71
    # meta data of the .po file.
72
    return u'\n\n'.join(ugettext(p) if p else u'' for p in paragraphs)
73
74
6131.2.3 by Jonathan Riddell
rename to disable_i18n() to follow convention
75
def disable_i18n():
6131.2.2 by Jonathan Riddell
add disableI18n() function so bzrlib users can chose not to use i18n
76
    """Do not allow i18n to be enabled.  Useful for third party users
77
    of bzrlib."""
6133.3.13 by Jonathan Riddell
vila sorts it out, fix disable_i18n() and use it in tests.__init__
78
    global _translations
6133.3.10 by Jonathan Riddell
default _translations back to None so we can tell if it gets installed
79
    _translations = _gettext.NullTranslations()
6131.2.2 by Jonathan Riddell
add disableI18n() function so bzrlib users can chose not to use i18n
80
6133.3.13 by Jonathan Riddell
vila sorts it out, fix disable_i18n() and use it in tests.__init__
81
5875.3.25 by Vincent Ladeuil
Fix test failures and make sure we don't rely on a default translation.
82
def installed():
6133.3.10 by Jonathan Riddell
default _translations back to None so we can tell if it gets installed
83
    """Returns whether translations are in use or not."""
84
    return _translations is not None
5875.3.25 by Vincent Ladeuil
Fix test failures and make sure we don't rely on a default translation.
85
86
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
87
def install(lang=None):
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
88
    """Enables gettext translations in bzr."""
5875.3.25 by Vincent Ladeuil
Fix test failures and make sure we don't rely on a default translation.
89
    global _translations
6112.1.1 by Jonathan Riddell
check for installed i18n before doing install
90
    if installed():
91
        return
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
92
    _translations = install_translations(lang)
93
6162.4.7 by Jonathan Riddell
two empty lines between top level items
94
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
95
def install_translations(lang=None, domain='bzr', locale_base=None):
96
    """Create a gettext translation object.
97
    
98
    :param lang: language to install.
99
    :param domain: translation domain to install.
100
    :param locale_base: plugins can specify their own directory.
101
102
    :returns: a gettext translations object to use
103
    """
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
104
    if lang is None:
105
        lang = _get_current_locale()
6025.1.1 by Jelmer Vernooij
Fix i18n use when no environment variables are set.
106
    if lang is not None:
107
        languages = lang.split(':')
108
    else:
109
        languages = None
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
110
    translation = _gettext.translation(
111
            domain,
112
            localedir=_get_locale_dir(locale_base),
6025.1.1 by Jelmer Vernooij
Fix i18n use when no environment variables are set.
113
            languages=languages,
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
114
            fallback=True)
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
115
    return translation
5875.2.7 by INADA Naoki
Change order of functions.
116
6162.4.7 by Jonathan Riddell
two empty lines between top level items
117
6162.4.2 by Jonathan Riddell
add add_fallback() method to i18n
118
def add_fallback(fallback):
119
    """
120
    Add a fallback translations object.  Typically used by plugins.
121
122
    :param fallback: gettext.GNUTranslations object
123
    """
124
    install()
125
    _translations.add_fallback(fallback)
126
6162.4.7 by Jonathan Riddell
two empty lines between top level items
127
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
128
def uninstall():
6133.3.10 by Jonathan Riddell
default _translations back to None so we can tell if it gets installed
129
    """Disables gettext translations."""
5875.3.25 by Vincent Ladeuil
Fix test failures and make sure we don't rely on a default translation.
130
    global _translations
6133.3.10 by Jonathan Riddell
default _translations back to None so we can tell if it gets installed
131
    _translations = None
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
132
133
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
134
def _get_locale_dir(base):
135
    """Returns directory to find .mo translations file in, either local or system
136
137
    :param base: plugins can specify their own local directory
138
    """
6437.56.1 by Martin Packman
Fall back to sys.prefix not /usr when looking for .mo files
139
    fs_enc = sys.getfilesystemencoding()
140
    if getattr(sys, 'frozen', False):
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
141
        if base is None:
6437.56.1 by Martin Packman
Fall back to sys.prefix not /usr when looking for .mo files
142
            base = os.path.dirname(unicode(sys.executable, fs_enc))
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
143
        return os.path.join(base, u'locale')
144
    else:
6162.4.1 by Jonathan Riddell
alter code to make it more usable by plugins, add install_translations()
145
        if base is None:
6437.56.1 by Martin Packman
Fall back to sys.prefix not /usr when looking for .mo files
146
            base = os.path.dirname(unicode(__file__, fs_enc))
5875.2.4 by INADA Naoki
Search '/usr/share/locale' when bzrlib/locale does not exist.
147
        dirpath = os.path.realpath(os.path.join(base, u'locale'))
148
        if os.path.exists(dirpath):
149
            return dirpath
6437.56.1 by Martin Packman
Fall back to sys.prefix not /usr when looking for .mo files
150
    return os.path.join(unicode(sys.prefix, fs_enc), u"share", u"locale")
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
151
152
153
def _check_win32_locale():
154
    for i in ('LANGUAGE','LC_ALL','LC_MESSAGES','LANG'):
155
        if os.environ.get(i):
156
            break
157
    else:
158
        lang = None
159
        import locale
160
        try:
161
            import ctypes
162
        except ImportError:
163
            # use only user's default locale
164
            lang = locale.getdefaultlocale()[0]
165
        else:
166
            # using ctypes to determine all locales
167
            lcid_user = ctypes.windll.kernel32.GetUserDefaultLCID()
168
            lcid_system = ctypes.windll.kernel32.GetSystemDefaultLCID()
169
            if lcid_user != lcid_system:
170
                lcid = [lcid_user, lcid_system]
171
            else:
172
                lcid = [lcid_user]
173
            lang = [locale.windows_locale.get(i) for i in lcid]
174
            lang = ':'.join([i for i in lang if i])
175
        # set lang code for gettext
176
        if lang:
177
            os.environ['LANGUAGE'] = lang
178
179
180
def _get_current_locale():
181
    if not os.environ.get('LANGUAGE'):
182
        from bzrlib import config
6056.2.3 by Vincent Ladeuil
Migrate language.
183
        lang = config.GlobalStack().get('language')
5875.2.1 by INADA Naoki
Add bzrlib.i18n module.
184
        if lang:
185
            os.environ['LANGUAGE'] = lang
186
            return lang
187
    if sys.platform == 'win32':
188
        _check_win32_locale()
189
    for i in ('LANGUAGE','LC_ALL','LC_MESSAGES','LANG'):
190
        lang = os.environ.get(i)
191
        if lang:
192
            return lang
193
    return None
6189.1.1 by Jelmer Vernooij
Add a load_plugin_translations method.
194
195
196
def load_plugin_translations(domain):
197
    """Load the translations for a specific plugin.
198
199
    :param domain: Gettext domain name (usually 'bzr-PLUGINNAME')
200
    """
201
    locale_base = os.path.dirname(
202
        unicode(__file__, sys.getfilesystemencoding()))
203
    translation = install_translations(domain=domain,
204
        locale_base=locale_base)
205
    add_fallback(translation)
206
    return translation