GrampsLocale: Restructure so that multiple GrampsLocale objects can safely be created for different locales

Only the first dependent upon the environment
All may be created with parameters which override environment variables or OS settings.
See the GrampsLocale docstring for details.

svn: r21182
This commit is contained in:
John Ralls 2013-01-21 01:27:29 +00:00
parent 20289c20a3
commit 60729859f2
2 changed files with 161 additions and 87 deletions

View File

@ -47,54 +47,110 @@ from ..constfunc import mac, win, UNITYPE
#------------------------------------------------------------------------ #------------------------------------------------------------------------
class GrampsLocale(object): class GrampsLocale(object):
""" """
Encapsulate a locale Encapsulate a locale. This class is a sort-of-singleton: The
first instance created will query the environment and OSX defaults
for missing parameters (precedence is parameters passed to the
constructor, environment variables LANG, LC_COLLATE, LC_TIME,
etc., and LANGUAGE, OSX defaults settings when that's the
platform). Subsequent calls to the constructor with no or
identical parameters will return the same Grampslocale
object. Construction with different parameters will result in a
new GrampsLocale instance with the specified parameters, but any
parameters left out will be filled in from the first instance.
@localedir: The full path to the top level directory containing
the translation files. Defaults to sys.prefix/share/locale.
@lang: A single locale value which is used for unset locale.LC_FOO
settings.
@domain: The name of the applicable translation file. The default is
"gramps", indicating files in LC_MESSAGES named gramps.mo.
@languages: A list of two or five character codes corresponding to
subidrectries in the localedir, e.g. "fr" or "zh_CN".
""" """
def __init__(self): __first_instance = None
self.localedir = None def __new__(cls, localedir=None, lang=None, domain=None, languages=None):
self.lang = None if not GrampsLocale.__first_instance:
self.language = [] cls.__first_instance = super(GrampsLocale, cls).__new__(cls)
if ("GRAMPSI18N" in os.environ cls.__first_instance.initialized = False
and os.path.exists(os.environ["GRAMPSI18N"])): return cls.__first_instance
self.localedir = os.environ["GRAMPSI18N"]
elif os.path.exists(LOCALE_DIR): if not cls.__first_instance.initialized:
self.localedir = LOCALE_DIR raise RuntimeError("Second GrampsLocale created before first one was initialized")
elif os.path.exists(os.path.join(sys.prefix, "share", "locale")): if ((lang is None or lang == cls.__first_instance.lang)
self.localedir = os.path.join(sys.prefix, "share", "locale") and (localedir is None or localedir == cls.__first_instance.localedir)
and (domain is None or domain == cls.__first_instance.localedomain)
and (languages is None or len(languages) == 0 or
languages == cls.__first_instance.languages)):
return cls.__first_instance
return super(GrampsLocale, cls).__new__(cls)
def __init_first_instance(self, localedir=None, lang=None,
domain=None, language=None):
if localedir and os.path.exists(localedir):
self.localedir = localedir
else: else:
lang = os.environ.get('LANG', 'en') if ("GRAMPSI18N" in os.environ
if lang and lang[:2] == 'en': and os.path.exists(os.environ["GRAMPSI18N"])):
pass # No need to display warning, we're in English self.localedir = os.environ["GRAMPSI18N"]
elif os.path.exists(LOCALE_DIR):
self.localedir = LOCALE_DIR
elif os.path.exists(os.path.join(sys.prefix, "share", "locale")):
self.localedir = os.path.join(sys.prefix, "share", "locale")
else: else:
logging.warning('Locale dir does not exist at %s', LOCALE_DIR) if not lang:
logging.warning('Running python setup.py install --prefix=YourPrefixDir might fix the problem') lang = os.environ.get('LANG', 'en')
if lang and lang[:2] == 'en':
pass # No need to display warning, we're in English
else:
logging.warning('Locale dir does not exist at %s', LOCALE_DIR)
logging.warning('Running python setup.py install --prefix=YourPrefixDir might fix the problem')
if not self.localedir: if not self.localedir:
#No localization files, no point in continuing #No localization files, no point in continuing
return return
self.localedomain = 'gramps' if domain:
self.localedomain = domain
else:
self.localedomain = 'gramps'
if not language or not isinstance(language, list):
language = []
else:
language = [l for l in languages
if l in get_available_translations()]
if mac(): if mac():
from . import maclocale from . import maclocale
(self.lang, self.language) = maclocale.mac_setup_localization(self) (self.lang, self.language) = maclocale.mac_setup_localization(self, lang, language)
else: else:
self.lang = ' ' if not lang:
try: lang = ' '
self.lang = os.environ["LANG"]
except KeyError:
self.lang = locale.getlocale()[0]
if not self.lang:
try: try:
self.lang = locale.getdefaultlocale()[0] + '.UTF-8' lang = os.environ["LANG"]
except TypeError: except KeyError:
logging.warning('Unable to determine your Locale, using English') lang = locale.getlocale()[0]
self.lang = 'C.UTF-8' if not lang:
try:
lang = locale.getdefaultlocale()[0] + '.UTF-8'
except TypeError:
logging.warning('Unable to determine your Locale, using English')
lang = 'C.UTF-8'
self.lang = lang
if "LANGUAGE" in os.environ: if not language or len(language) == 0:
language = [l for l in os.environ["LANGUAGE"].split(":") if "LANGUAGE" in os.environ:
if l in self.get_available_translations()] language = [l for l in os.environ["LANGUAGE"].split(":")
self.language = language if l in get_available_translations()]
else: self.language = language
self.language = [self.lang[0:2]] elif not lang == "C.UTF-8":
self.language = [lang[0:2]]
else:
self.language = ["en"]
#GtkBuilder depends on reading Glade files as UTF-8 and crashes if it #GtkBuilder depends on reading Glade files as UTF-8 and crashes if it
#doesn't, so set $LANG to have a UTF-8 locale. NB: This does *not* #doesn't, so set $LANG to have a UTF-8 locale. NB: This does *not*
@ -105,55 +161,71 @@ class GrampsLocale(object):
self.lang = '.'.join((check_lang[0], 'UTF-8')) self.lang = '.'.join((check_lang[0], 'UTF-8'))
os.environ["LANG"] = self.lang os.environ["LANG"] = self.lang
# Set Gramps's translations # Set Gramps's translations
self.translation = self._get_translation(self.localedomain, self.localedir, self.language)
# Now set the locale for everything besides translations.
try: try:
# First try the environment to preserve individual variables # First try the environment to preserve individual variables
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
try: try:
#Then set LC_MESSAGES to self.lang #Then set LC_MESSAGES to lang
locale.setlocale(locale.LC_MESSAGES, self.lang) locale.setlocale(locale.LC_MESSAGES, lang)
except locale.Error: except locale.Error:
logging.warning("Unable to set translations to %s, locale not found.", self.lang) logging.warning("Unable to set translations to %s, locale not found.", lang)
except locale.Error: except locale.Error:
# That's not a valid locale -- on Linux, probably not installed. # That's not a valid locale -- on Linux, probably not installed.
try: try:
# First fallback is self.lang # First fallback is lang
locale.setlocale(locale.LC_ALL, self.lang) locale.setlocale(locale.LC_ALL, self.lang)
logging.warning("Setting locale to individual LC_ variables failed, falling back to %s.", self.lang) logging.warning("Setting locale to individual LC_ variables failed, falling back to %s.", lang)
except locale.Error: except locale.Error:
# No good, set the default encoding to C.UTF-8. Don't # No good, set the default encoding to C.UTF-8. Don't
# mess with anything else. # mess with anything else.
locale.setlocale(locale.LC_ALL, 'C.UTF-8') locale.setlocale(locale.LC_ALL, 'C.UTF-8')
logging.error("Failed to set locale %s, falling back to English", self.lang) logging.error("Failed to set locale %s, falling back to English", lang)
# $LANGUAGE is what sets the Gtk+ translations # $LANGUAGE is what sets the Gtk+ translations
os.environ["LANGUAGE"] = ':'.join(self.language) os.environ["LANGUAGE"] = ':'.join(self.language)
# GtkBuilder uses GLib's g_dgettext wrapper, which oddly is bound # GtkBuilder uses GLib's g_dgettext wrapper, which oddly is bound
# with locale instead of gettext. # with locale instead of gettext.
locale.bindtextdomain(self.localedomain, self.localedir) locale.bindtextdomain(self.localedomain, self.localedir)
#------------------------------------------------------------------------- self.initialized = True
#
# Public Functions
#
#-------------------------------------------------------------------------
def get_localedomain(self):
"""
Get the LOCALEDOMAIN used for the Gramps application.
Required by gui/glade.py to pass to Gtk.Builder
"""
return self.localedomain
def get_language_list(self): def __init__(self, lang=None, localedir=None, domain=None, languages=None):
""" """
Return the list of configured languages. Used by Init a GrampsLocale. Run __init_first_instance() to set up the
ViewManager.check_for_updates to select the language for the environement if this is the first run. Return __first_instance
addons descriptions. otherwise if called without arguments.
""" """
return self.language if self == self._GrampsLocale__first_instance:
if not self.initialized:
self._GrampsLocale__init_first_instance(lang, localedir,
domain, languages)
else:
return
else:
if domain:
self.localedomain = domain
else:
self.localedomain = self._GrampsLocale__first_instance.localedomain
if localedir:
self.localedir = localedir
else:
self.localedir = self._GrampsLocale__first_instance.localedir
self.language = []
if languages and len(languages) > 0:
self.language = [l for l in languages
if l in get_available_translations()]
if len(self.language) == 0:
self.language = self._GrampsLocale__first_instance.language
self.translation = self._get_translation(self.localedomain,
self.localedir, self.language)
def _get_translation(self, domain = None, def _get_translation(self, domain = None,
localedir = None, localedir = None,

View File

@ -73,7 +73,7 @@ locale, leaving $LANGUAGE unset (which is the same as setting it to
import sys, os, subprocess import sys, os, subprocess
def mac_setup_localization(glocale): def mac_setup_localization(glocale, lang, language):
""" """
Set up the localization parameters from OSX's "defaults" system, Set up the localization parameters from OSX's "defaults" system,
permitting environment variables to override the settings. permitting environment variables to override the settings.
@ -244,33 +244,35 @@ def mac_setup_localization(glocale):
if calendar and "LC_TIME" not in os.environ: if calendar and "LC_TIME" not in os.environ:
os.environ["LC_TIME"] = calendar os.environ["LC_TIME"] = calendar
if "LANG" in os.environ: if not lang:
lang = os.environ["LANG"] if "LANG" in os.environ:
else: lang = os.environ["LANG"]
lang = "en_US" else:
loc = mac_resolve_locale(loc) lang = "en_US"
if loc != None: loc = mac_resolve_locale(loc)
lang = loc if loc != None:
collation = mac_resolve_locale(collation) lang = loc
if "LC_COLLATE" not in os.environ and collation != None: collation = mac_resolve_locale(collation)
os.environ["LC_COLLATE"] = collation if "LC_COLLATE" not in os.environ and collation != None:
os.environ["LC_COLLATE"] = collation
elif len(collation) > 0: elif len(collation) > 0:
lang = mac_resolve_locale(collation) lang = mac_resolve_locale(collation)
if "LANGUAGE" in os.environ: if not language or len(language) == 0:
language = [l for l in os.environ["LANGUAGE"].split(":") if "LANGUAGE" in os.environ:
if l in available] language = [l for l in os.environ["LANGUAGE"].split(":")
elif "LANG" in os.environ: if l in available]
language = [lang[0:2]] elif lang != "en_US":
else: language = [lang[0:2]]
if len(translations) > 0: else:
language = translations if len(translations) > 0:
elif (len(loc) > 0 and loc in available language = translations
and not loc.startswith("en")): elif (len(loc) > 0 and loc in available
language = [loc] and not loc.startswith("en")):
elif (len(collation) > 0 and collation in available language = [loc]
and not collation.startswith("en")): elif (len(collation) > 0 and collation in available
language = [collation] and not collation.startswith("en")):
language = [collation]
return (lang, language) return (lang, language)