browse: display errors when saving blobs
[git-cola.git] / cola / i18n.py
blob15eaf2de12acdecd6d1c2e41f0d050b0051c72f8
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 core
8 from . import polib
9 from . import resources
12 class NullTranslation(object):
13 """This is a pass-through object that does nothing"""
15 def gettext(self, value):
16 return value
19 class State(object):
20 """The application-wide current translation state"""
22 translation = NullTranslation()
24 @classmethod
25 def reset(cls):
26 cls.translation = NullTranslation()
28 @classmethod
29 def update(cls, lang):
30 cls.translation = Translation(lang)
32 @classmethod
33 def gettext(cls, value):
34 """Return a translated value"""
35 return cls.translation.gettext(value)
38 class Translation(object):
39 def __init__(self, lang):
40 self.lang = lang
41 self.messages = {}
42 self.filename = get_filename_for_locale(lang)
43 if self.filename:
44 self.load()
46 def load(self):
47 """Read the pofile content into memory"""
48 po = polib.pofile(self.filename, encoding='utf-8')
49 messages = self.messages
50 for entry in po.translated_entries():
51 messages[entry.msgid] = entry.msgstr
53 def gettext(self, value):
54 return self.messages.get(value, value)
57 def gettext(value):
58 """Translate a string"""
59 txt = State.gettext(value)
60 # handle @@verb / @@noun
61 if txt[-6:-4] == '@@':
62 txt = txt.replace('@@verb', '').replace('@@noun', '')
63 return txt
66 def N_(value):
67 """Marker function for translated values
69 N_("Some string value") is used to mark strings for translation.
70 """
71 return gettext(value)
74 def get_filename_for_locale(name):
75 """Return the .po file for the specified locale"""
76 # When <name> is "foo_BAR.UTF-8", the name is truncated to "foo_BAR".
77 # When <name> is "foo_BAR", the <short_name> is "foo"
78 # Try the following locations:
79 # cola/i18n/<name>.po
80 # cola/i18n/<short_name>.po
81 if not name: # If no locale was specified then try the current locale.
82 name = locale.getdefaultlocale()[0]
84 if not name:
85 return None
87 name = name.split('.', 1)[0] # foo_BAR.UTF-8 -> foo_BAR
89 filename = resources.i18n('%s.po' % name)
90 if os.path.exists(filename):
91 return filename
93 short_name = name.split('_', 1)[0]
94 filename = resources.i18n('%s.po' % short_name)
95 if os.path.exists(filename):
96 return filename
97 return None
100 def install(lang):
101 # pylint: disable=global-statement
102 if sys.platform == 'win32' and not lang:
103 lang = _get_win32_default_locale()
104 lang = _install_custom_language(lang)
105 State.update(lang)
108 def uninstall():
109 # pylint: disable=global-statement
110 State.reset()
113 def _install_custom_language(lang):
114 """Allow a custom language to be set in ~/.config/git-cola/language"""
115 lang_file = resources.config_home('language')
116 if not core.exists(lang_file):
117 return lang
118 try:
119 lang = core.read(lang_file).strip()
120 except (OSError, IOError):
121 return lang
122 return lang
125 def _get_win32_default_locale():
126 """Get the default locale on Windows"""
127 for name in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
128 lang = os.environ.get(name)
129 if lang:
130 return lang
131 try:
132 import ctypes # pylint: disable=all
133 except ImportError:
134 # use only user's default locale
135 return locale.getdefaultlocale()[0]
136 # using ctypes to determine all locales
137 lcid_user = ctypes.windll.kernel32.GetUserDefaultLCID()
138 lcid_system = ctypes.windll.kernel32.GetSystemDefaultLCID()
139 lang_user = locale.windows_locale.get(lcid_user)
140 lang_system = locale.windows_locale.get(lcid_system)
141 if lang_user:
142 lang = lang_user
143 else:
144 lang = lang_system
145 return lang