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# 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. 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 19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 21# This module is copied from Bazaar Explorer and modified for brz. 22 23"""i18n and l10n support for Bazaar.""" 24 25import gettext as _gettext 26import os 27import sys 28 29 30_translations = None 31 32 33def gettext(message): 34 """Translate message. 35 36 :returns: translated message as unicode. 37 """ 38 install() 39 try: 40 return _translations.ugettext(message) 41 except AttributeError: 42 return _translations.gettext(message) 43 44 45def ngettext(singular, plural, number): 46 """Translate message with plural forms based on `number`. 47 48 :param singular: English language message in singular form 49 :param plural: English language message in plural form 50 :param number: the number this message should be translated for 51 52 :returns: translated message as unicode. 53 """ 54 install() 55 try: 56 return _translations.ungettext(singular, plural, number) 57 except AttributeError: 58 return _translations.ngettext(singular, plural, number) 59 60 61def N_(msg): 62 """Mark message for translation but don't translate it right away.""" 63 return msg 64 65 66def gettext_per_paragraph(message): 67 """Translate message per paragraph. 68 69 :returns: concatenated translated message as unicode. 70 """ 71 install() 72 paragraphs = message.split(u'\n\n') 73 # Be careful not to translate the empty string -- it holds the 74 # meta data of the .po file. 75 return u'\n\n'.join(gettext(p) if p else u'' for p in paragraphs) 76 77 78def disable_i18n(): 79 """Do not allow i18n to be enabled. Useful for third party users 80 of breezy.""" 81 global _translations 82 _translations = _gettext.NullTranslations() 83 84 85def installed(): 86 """Returns whether translations are in use or not.""" 87 return _translations is not None 88 89 90def install(lang=None): 91 """Enables gettext translations in brz.""" 92 global _translations 93 if installed(): 94 return 95 _translations = install_translations(lang) 96 97 98def install_translations(lang=None, domain='brz', locale_base=None): 99 """Create a gettext translation object. 100 101 :param lang: language to install. 102 :param domain: translation domain to install. 103 :param locale_base: plugins can specify their own directory. 104 105 :returns: a gettext translations object to use 106 """ 107 if lang is None: 108 lang = _get_current_locale() 109 if lang is not None: 110 languages = lang.split(':') 111 else: 112 languages = None 113 translation = _gettext.translation( 114 domain, 115 localedir=_get_locale_dir(locale_base), 116 languages=languages, 117 fallback=True) 118 return translation 119 120 121def add_fallback(fallback): 122 """ 123 Add a fallback translations object. Typically used by plugins. 124 125 :param fallback: gettext.GNUTranslations object 126 """ 127 install() 128 _translations.add_fallback(fallback) 129 130 131def uninstall(): 132 """Disables gettext translations.""" 133 global _translations 134 _translations = None 135 136 137def _get_locale_dir(base): 138 """Returns directory to find .mo translations file in, either local or system 139 140 :param base: plugins can specify their own local directory 141 """ 142 if getattr(sys, 'frozen', False): 143 if base is None: 144 base = os.path.dirname(sys.executable) 145 return os.path.join(base, u'locale') 146 else: 147 if base is None: 148 base = os.path.dirname(__file__) 149 dirpath = os.path.realpath(os.path.join(base, u'locale')) 150 if os.path.exists(dirpath): 151 return dirpath 152 return os.path.join(sys.prefix, u"share", u"locale") 153 154 155def _check_win32_locale(): 156 for i in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): 157 if os.environ.get(i): 158 break 159 else: 160 lang = None 161 import locale 162 try: 163 import ctypes 164 except ImportError: 165 # use only user's default locale 166 lang = locale.getdefaultlocale()[0] 167 else: 168 # using ctypes to determine all locales 169 lcid_user = ctypes.windll.kernel32.GetUserDefaultLCID() 170 lcid_system = ctypes.windll.kernel32.GetSystemDefaultLCID() 171 if lcid_user != lcid_system: 172 lcid = [lcid_user, lcid_system] 173 else: 174 lcid = [lcid_user] 175 lang = [locale.windows_locale.get(i) for i in lcid] 176 lang = ':'.join([i for i in lang if i]) 177 # set lang code for gettext 178 if lang: 179 os.environ['LANGUAGE'] = lang 180 181 182def _get_current_locale(): 183 if not os.environ.get('LANGUAGE'): 184 from . import config 185 lang = config.GlobalStack().get('language') 186 if lang: 187 os.environ['LANGUAGE'] = lang 188 return lang 189 if sys.platform == 'win32': 190 _check_win32_locale() 191 for i in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): 192 lang = os.environ.get(i) 193 if lang: 194 return lang 195 return None 196 197 198def load_plugin_translations(domain): 199 """Load the translations for a specific plugin. 200 201 :param domain: Gettext domain name (usually 'brz-PLUGINNAME') 202 """ 203 locale_base = os.path.dirname(__file__) 204 translation = install_translations(domain=domain, 205 locale_base=locale_base) 206 add_fallback(translation) 207 return translation 208