themes: use the core module for io
[git-cola.git] / cola / i18n.py
blobdf7833ce81d3603e08eb19026bcefbdcefef86fe
1 """i18n and l10n support for git-cola"""
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 import locale
4 import os
5 import sys
7 from . import compat
8 from . import core
9 from . import polib
10 from . import resources
13 class NullTranslation(object):
14 """This is a pass-through object that does nothing"""
16 def gettext(self, value):
17 return value
20 class State(object):
21 """The application-wide current translation state"""
23 translation = NullTranslation()
25 @classmethod
26 def reset(cls):
27 cls.translation = NullTranslation()
29 @classmethod
30 def update(cls, lang):
31 cls.translation = Translation(lang)
33 @classmethod
34 def gettext(cls, value):
35 """Return a translated value"""
36 return cls.translation.gettext(value)
39 class Translation(object):
40 def __init__(self, lang):
41 self.lang = lang
42 self.messages = {}
43 self.filename = get_filename_for_locale(lang)
44 if self.filename:
45 self.load()
47 def load(self):
48 """Read the pofile content into memory"""
49 po = polib.pofile(self.filename, encoding='utf-8')
50 messages = self.messages
51 for entry in po.translated_entries():
52 messages[entry.msgid] = entry.msgstr
54 def gettext(self, value):
55 return self.messages.get(value, value)
58 def gettext(value):
59 """Translate a string"""
60 txt = State.gettext(value)
61 # handle @@verb / @@noun
62 if txt[-6:-4] == '@@':
63 txt = txt.replace('@@verb', '').replace('@@noun', '')
64 return txt
67 def N_(value):
68 """Marker function for translated values
70 N_("Some string value") is used to mark strings for translation.
71 """
72 return gettext(value)
75 def get_filename_for_locale(name):
76 """Return the .po file for the specified locale"""
77 # When <name> is "foo_BAR.UTF-8", the name is truncated to "foo_BAR".
78 # When <name> is "foo_BAR", the <short_name> is "foo"
79 # Try the following locations:
80 # cola/i18n/<name>.po
81 # cola/i18n/<short_name>.po
82 if not name: # If no locale was specified then try the current locale.
83 name = locale.getdefaultlocale()[0]
85 name = name.split('.', 1)[0] # foo_BAR.UTF-8 -> foo_BAR
87 filename = resources.i18n('%s.po' % name)
88 if os.path.exists(filename):
89 return filename
91 short_name = name.split('_', 1)[0]
92 filename = resources.i18n('%s.po' % short_name)
93 if os.path.exists(filename):
94 return filename
95 return None
98 def install(locale):
99 # pylint: disable=global-statement
100 if sys.platform == 'win32':
101 _check_win32_locale()
102 if locale:
103 _set_language(locale)
104 _install_custom_language()
106 State.update(locale)
109 def uninstall():
110 # pylint: disable=global-statement
111 State.reset()
114 def _install_custom_language():
115 """Allow a custom language to be set in ~/.config/git-cola/language"""
116 lang_file = resources.config_home('language')
117 if not core.exists(lang_file):
118 return
119 try:
120 locale = core.read(lang_file).strip()
121 except (OSError, IOError):
122 return
123 if locale:
124 _set_language(locale)
127 def _set_language(locale):
128 compat.setenv('LANGUAGE', locale)
129 compat.setenv('LANG', locale)
130 compat.setenv('LC_ALL', locale)
131 compat.setenv('LC_MESSAGES', locale)
134 def _check_win32_locale():
135 for i in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
136 if os.environ.get(i):
137 break
138 else:
139 lang = None
140 try:
141 import ctypes # pylint: disable=all
142 except ImportError:
143 # use only user's default locale
144 lang = locale.getdefaultlocale()[0]
145 else:
146 # using ctypes to determine all locales
147 lcid_user = ctypes.windll.kernel32.GetUserDefaultLCID()
148 lcid_system = ctypes.windll.kernel32.GetSystemDefaultLCID()
149 if lcid_user != lcid_system:
150 lcid = [lcid_user, lcid_system]
151 else:
152 lcid = [lcid_user]
153 lang = [locale.windows_locale.get(i) for i in lcid]
154 lang = ':'.join([i for i in lang if i])
155 # set lang code for gettext
156 if lang:
157 compat.setenv('LANGUAGE', lang)