Skip to content

Commit

Permalink
Extract win32 localatiztion to a new file win32locale.py.
Browse files Browse the repository at this point in the history
  • Loading branch information
jralls committed Jul 16, 2023
1 parent 52e5879 commit b297416
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 207 deletions.
217 changes: 13 additions & 204 deletions gramps/gen/utils/grampslocale.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@
from . maclocale import mac_setup_localization

if sys.platform == 'win32':
from ctypes import cdll
from .win32locale import (win32_locale_init_from_environment,
win32_locale_bindtextdomain)

from .win32locale import _LOCALE_NAMES

LOG = logging.getLogger("." + __name__)
LOG.propagate = True
Expand All @@ -76,101 +78,11 @@
ICU_LOCALES = None
if HAVE_ICU:
ICU_LOCALES = Locale.getAvailableLocales()

# Map of languages for converting to Microsoft locales and naming
# locales for display to the user. It's important to add to this list
# when a new translation is added. Note the dummy _(): That's just to
# get xgettext to include the string in gramps.pot; actual translation
# is done in _get_language_string() below.
# (The gramps officially-supported language list is ALL_LINGUAS in setup.py)
def _(msgid):
return msgid

_LOCALE_NAMES = {
'ar': ('Arabic_Saudi Arabia', '1256', _("Arabic")),
'bg': ('Bulgrian_Bulgaria', '1251', _("Bulgarian")),
#Windows has no translation for Breton
'br': (None, None, _("Breton")),
'ca': ('Catalan_Spain', '1252', _("Catalan")),
'cs': ('Czech_Czech Republic', '1250', _("Czech")),
'da': ('Danish_Denmark', '1252', _("Danish")),
'de': ('German_Germany', '1252', _("German")),
'de_AT': ('German_Austria', '1252', _("German (Austria)")),
'el': ('Greek_Greece', '1253', _("Greek")),
'en': ('English_United States', '1252', _("English (USA)")),
'en_GB': ('English_United Kingdom', '1252', _("English")),
#Windows has no translation for Esperanto
'eo': (None, None, _("Esperanto")),
'es': ('Spanish_Spain', '1252', _("Spanish")),
'fi': ('Finnish_Finland', '1252', _("Finnish")),
'fr': ('French_France', '1252', _("French")),
#Windows has no translation for Gaelic
'ga': (None, None, _("Gaelic")),
'he': ('Hebrew_Israel', '1255', _("Hebrew")),
'hr': ('Croatian_Croatia', '1250', _("Croatian")),
'hu': ('Hungarian_Hungary', '1250', _("Hungarian")),
'id': ('Indonesian', '1057', _("Indonesian")),
'is': ('Icelandic', '1252', _("Icelandic")),
'it': ('Italian_Italy', '1252', _("Italian")),
'ja': ('Japanese_Japan', '932', _("Japanese")),
'lt': ('Lithuanian_Lithuania', '1252', _("Lithuanian")),
#Windows has no translation for Macedonian
'mk': (None, None, _("Macedonian")),
'nb': ('Norwegian_Norway', '1252', _("Norwegian Bokmal")),
'nl': ('Dutch_Netherlands', '1252', _("Dutch")),
'nn': ('Norwegian-Nynorsk_Norway', '1252', _("Norwegian Nynorsk")),
'pl': ('Polish_Poland', '1250', _("Polish")),
'pt_BR': ('Portuguese_Brazil', '1252', _("Portuguese (Brazil)")),
'pt_PT': ('Portuguese_Portugal', '1252', _("Portuguese (Portugal)")),
'ro': ('Romanian_Romania', '1250', _("Romanian")),
'ru': ('Russian_Russia', '1251', _("Russian")),
'sk': ('Slovak_Slovakia', '1250', _("Slovak"),),
'sl': ('Slovenian_Slovenia', '1250', _("Slovenian")),
'sq': ('Albanian_Albania', '1250', _("Albanian")),
'sr': ('Serbian(Cyrillic)_Serbia and Montenegro', '1251', _("Serbian")),
'sv': ('Swedish_Sweden', '1252', _("Swedish")),
# Windows has no codepage for Tamil
'ta': (None, None, _("Tamil")),
'tr': ('Turkish_Turkey', '1254', _("Turkish")),
'uk': ('Ukrainian_Ukraine', '1251', _("Ukrainian")),
'vi': ('Vietnamese_Vietnam', '1258', _("Vietnamese")),
'zh_CN': ('Chinese_China', '936', _("Chinese (Simplified)")),
'zh_HK': ('Chinese_Hong Kong', '950', _("Chinese (Hong Kong)")),
'zh_TW': ('Chinese_Taiwan', '950', _("Chinese (Traditional)")),
}

# locales with right-to-left text
_RTL_LOCALES = ('ar', 'he')

# locales with less than 70% currently translated
INCOMPLETE_TRANSLATIONS = ('ar', 'bg', 'sq', 'ta', 'tr', 'zh_HK', 'zh_TW')

def _check_mswin_locale(loc):
msloc = None
try:
msloc = _LOCALE_NAMES[loc[:5]][:2]
newloc = loc[:5]
except KeyError:
try:
msloc = _LOCALE_NAMES[loc[:2]][:2]
newloc = loc[:2]
except KeyError:
#US English is the outlier, all other English locales want
#real English:
if loc[:2] == ('en') and loc[:5] != 'en_US':
return ('en_GB', '1252')
return (None, None)
return (newloc, msloc)

def _check_mswin_locale_reverse(loc):
for (newloc, msloc) in _LOCALE_NAMES.items():
if msloc and newloc == msloc[0]:
return (newloc, msloc[1])
#US English is the outlier, all other English locales want real English:
if loc.startswith('English') and loc != 'English_United States':
return ('en_GB', '1252')
return (None, None)

def _check_gformat():
"""
Some OS environments do not support the locale.nl_langinfo() method
Expand Down Expand Up @@ -264,96 +176,8 @@ def _matches(self, localedir, lang, domain, languages):
matches = matches and (lang is None or self.lang == lang)
matches = matches and (domain is None or self.localedomain == domain)
return matches and (languages is None or len(languages) == 0 or
self.languages == languages)

def _win_init_environment(self):
"""
The Windows implementation of Python ignores environment
variables when setting the locale; it only pays attention to
the control panel language settings -- which for practical
purposes limits one to the language for which one purchased
Windows. This function enables using alternative
localizations.
"""

if 'LANG' in os.environ:
(lang, loc) = _check_mswin_locale(os.environ['LANG'])
if loc:
locale.setlocale(locale.LC_ALL, '.'.join(loc))
self.lang = lang
self.encoding = loc[1]
else:
LOG.debug("%%LANG%% value %s not usable", os.environ['LANG'])
if not self.lang:
locale.setlocale(locale.LC_ALL, '')
locale_tuple = locale.getlocale()
lang = locale_tuple[0]
loc = _check_mswin_locale_reverse(lang)
if loc[0]:
self.lang = loc[0]
self.encoding = loc[1]
else:
(lang, loc) = _check_mswin_locale(locale.getlocale()[0])
if lang:
self.lang = lang
self.encoding = loc[1]
else:
LOG.debug("No usable locale found in environment")

if not self.lang:
self.lang = 'C'
self.encoding = 'cp1252'
self.language == languages)

if 'LC_MESSAGES' in os.environ:
lang = self.check_available_translations(os.environ['LC_MESSAGES'])
if lang:
self.language = [lang]
else:
LOG.debug("No translation for %%LC_MESSAGES%% locale")
if 'LANGUAGE' in os.environ:
language = [x for x in [self.check_available_translations(l)
for l in os.environ["LANGUAGE"].split(":")]
if x]
if language:
self.language = language
else:
LOG.debug("No languages with translations found "
"in %%LANGUAGES%%")
if not self.language:
self.language = [self.lang[:5]]

if 'COLLATION' in os.environ:
coll = os.environ['COLLATION']
if HAVE_ICU:
if coll[:2] in ICU_LOCALES:
self.collation = coll
else:
self.collation = self.lang
else:
(coll, loc) = _check_mswin_locale(coll)
if not loc:
(coll, loc) = _check_mswin_locale(self.lang)
self.collation = '.'.join(loc)
locale.setlocale(locale.LC_COLLATE, self.collation )
else:
if HAVE_ICU:
self.collation = self.lang
else:
(coll, loc) = _check_mswin_locale(self.lang)
if loc:
self.collation = '.'.join(loc)
else:
self.collation = 'C'
locale.setlocale(locale.LC_COLLATE, self.collation )

# We can't import datahandler stuff or we'll get a circular
# dependency, so we rely on the available translations list
if 'LC_TIME' in os.environ:
self.calendar = (
self.check_available_translations(os.environ['LC_TIME']) or
self.lang)
else:
self.calendar = self.lang

def _init_from_environment(self):

Expand Down Expand Up @@ -439,21 +263,6 @@ def _check_locale(loc):
LOG.debug("The locale tformat for '%s' is '%s'",
self.lang, _check_gformat())

def _win_bindtextdomain(self, localedomain, localedir):
"""
Help routine for loading and setting up libintl attributes
Returns libintl
"""
try:
libintl = cdll.LoadLibrary('libintl-8')
libintl.bindtextdomain(localedomain, localedir)
libintl.textdomain(localedomain)
libintl.bind_textdomain_codeset(localedomain, "UTF-8")

except WindowsError:
LOG.warning("Localization library libintl not on %PATH%, "
"localization will be incomplete")

def __init_first_instance(self):
"""
Initialize the primary locale from whatever might be
Expand All @@ -477,7 +286,7 @@ def __init_first_instance(self):
if sys.platform == 'darwin':
mac_setup_localization(self)
elif sys.platform == 'win32':
self._win_init_environment()
win32_locale_init_from_environment(self, HAVE_ICU, ICU_LOCALES)
else:
self._init_from_environment()
else:
Expand Down Expand Up @@ -540,13 +349,13 @@ def __init_first_instance(self):
# GtkBuilder uses GLib's g_dgettext wrapper, which oddly is bound
# with locale instead of gettext. Win32 doesn't support bindtextdomain.
if self.localedir:
if sys.platform != 'win32':
if sys.platform == 'win32':
win32_locale_bindtextdomain(self.localedomain.encode('utf-8'),
self.localedir.encode('utf-8'))
else:
# bug12278, _build_popup_ui() under linux and macOS
locale.textdomain(self.localedomain)
locale.bindtextdomain(self.localedomain, self.localedir)
else:
self._win_bindtextdomain(self.localedomain.encode('utf-8'),
self.localedir.encode('utf-8'))

self.rtl_locale = False
if self.language[0] in _RTL_LOCALES:
Expand Down Expand Up @@ -895,21 +704,21 @@ def check_available_translations(self, loc):
"""
if not self.localedir:
return None
#Note that this isn't a typo for self.language; self.languages
#Note that this isn't a typo for self.language; self.language
#is cached so we don't have to query the file system every
#time this function is called.
if not hasattr(self, 'languages'):
self.languages = self.get_available_translations()
self.language = self.get_available_translations()

if not loc:
return None

if loc[:5] in self.languages:
if loc[:5] in self.language:
return loc[:5]
#US English is the outlier, all other English locales want real English:
if loc[:2] == 'en' and loc[:5] != 'en_US':
return 'en_GB'
if loc[:2] in self.languages:
if loc[:2] in self.language:
return loc[:2]
return None

Expand Down
4 changes: 1 addition & 3 deletions gramps/gen/utils/grampstranslation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2009 Brian G. Matherly
# Copyright (C) 2013 John Ralls
# Copyright (C) 2023 John Ralls
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down
Loading

0 comments on commit b297416

Please sign in to comment.