##// END OF EJS Templates
typing: add type hints to `i18n._msgcache`...
Matt Harbison -
r52610:f841de63 default
parent child Browse files
Show More
@@ -1,123 +1,126 b''
1 # i18n.py - internationalization support for mercurial
1 # i18n.py - internationalization support for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com>
3 # Copyright 2005, 2006 Olivia Mackall <olivia@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8
8
9 import gettext as gettextmod
9 import gettext as gettextmod
10 import locale
10 import locale
11 import os
11 import os
12 import sys
12 import sys
13
13
14 from typing import (
14 from typing import (
15 Dict,
15 List,
16 List,
16 )
17 )
17
18
18 from .utils import resourceutil
19 from .utils import resourceutil
19 from . import (
20 from . import (
20 encoding,
21 encoding,
21 pycompat,
22 pycompat,
22 )
23 )
23
24
24 # modelled after templater.templatepath:
25 # modelled after templater.templatepath:
25 if getattr(sys, 'frozen', None) is not None:
26 if getattr(sys, 'frozen', None) is not None:
26 module = pycompat.sysexecutable
27 module = pycompat.sysexecutable
27 else:
28 else:
28 module = pycompat.fsencode(__file__)
29 module = pycompat.fsencode(__file__)
29
30
30 _languages = None
31 _languages = None
31 if (
32 if (
32 pycompat.iswindows
33 pycompat.iswindows
33 and b'LANGUAGE' not in encoding.environ
34 and b'LANGUAGE' not in encoding.environ
34 and b'LC_ALL' not in encoding.environ
35 and b'LC_ALL' not in encoding.environ
35 and b'LC_MESSAGES' not in encoding.environ
36 and b'LC_MESSAGES' not in encoding.environ
36 and b'LANG' not in encoding.environ
37 and b'LANG' not in encoding.environ
37 ):
38 ):
38 # Try to detect UI language by "User Interface Language Management" API
39 # Try to detect UI language by "User Interface Language Management" API
39 # if no locale variables are set. Note that locale.getdefaultlocale()
40 # if no locale variables are set. Note that locale.getdefaultlocale()
40 # uses GetLocaleInfo(), which may be different from UI language.
41 # uses GetLocaleInfo(), which may be different from UI language.
41 # (See http://msdn.microsoft.com/en-us/library/dd374098(v=VS.85).aspx )
42 # (See http://msdn.microsoft.com/en-us/library/dd374098(v=VS.85).aspx )
42 try:
43 try:
43 import ctypes
44 import ctypes
44
45
45 # pytype: disable=module-attr
46 # pytype: disable=module-attr
46 langid = ctypes.windll.kernel32.GetUserDefaultUILanguage()
47 langid = ctypes.windll.kernel32.GetUserDefaultUILanguage()
47 # pytype: enable=module-attr
48 # pytype: enable=module-attr
48
49
49 _languages = [locale.windows_locale[langid]]
50 _languages = [locale.windows_locale[langid]]
50 except (ImportError, AttributeError, KeyError):
51 except (ImportError, AttributeError, KeyError):
51 # ctypes not found or unknown langid
52 # ctypes not found or unknown langid
52 pass
53 pass
53
54
54
55
55 datapath = pycompat.fsdecode(resourceutil.datapath)
56 datapath = pycompat.fsdecode(resourceutil.datapath)
56 localedir = os.path.join(datapath, 'locale')
57 localedir = os.path.join(datapath, 'locale')
57 t = gettextmod.translation('hg', localedir, _languages, fallback=True)
58 t = gettextmod.translation('hg', localedir, _languages, fallback=True)
58 try:
59 try:
59 _ugettext = t.ugettext # pytype: disable=attribute-error
60 _ugettext = t.ugettext # pytype: disable=attribute-error
60 except AttributeError:
61 except AttributeError:
61 _ugettext = t.gettext
62 _ugettext = t.gettext
62
63
63
64
64 _msgcache = {} # encoding: {message: translation}
65 _msgcache: Dict[
66 bytes, Dict[bytes, bytes]
67 ] = {} # encoding: {message: translation}
65
68
66
69
67 def gettext(message: bytes) -> bytes:
70 def gettext(message: bytes) -> bytes:
68 """Translate message.
71 """Translate message.
69
72
70 The message is looked up in the catalog to get a Unicode string,
73 The message is looked up in the catalog to get a Unicode string,
71 which is encoded in the local encoding before being returned.
74 which is encoded in the local encoding before being returned.
72
75
73 Important: message is restricted to characters in the encoding
76 Important: message is restricted to characters in the encoding
74 given by sys.getdefaultencoding() which is most likely 'ascii'.
77 given by sys.getdefaultencoding() which is most likely 'ascii'.
75 """
78 """
76 # If message is None, t.ugettext will return u'None' as the
79 # If message is None, t.ugettext will return u'None' as the
77 # translation whereas our callers expect us to return None.
80 # translation whereas our callers expect us to return None.
78 if message is None or not _ugettext:
81 if message is None or not _ugettext:
79 return message
82 return message
80
83
81 cache = _msgcache.setdefault(encoding.encoding, {})
84 cache = _msgcache.setdefault(encoding.encoding, {})
82 if message not in cache:
85 if message not in cache:
83 if type(message) is str:
86 if type(message) is str:
84 # goofy unicode docstrings in test
87 # goofy unicode docstrings in test
85 paragraphs: List[str] = message.split(u'\n\n')
88 paragraphs: List[str] = message.split(u'\n\n')
86 else:
89 else:
87 # should be ascii, but we have unicode docstrings in test, which
90 # should be ascii, but we have unicode docstrings in test, which
88 # are converted to utf-8 bytes on Python 3.
91 # are converted to utf-8 bytes on Python 3.
89 paragraphs = [p.decode("utf-8") for p in message.split(b'\n\n')]
92 paragraphs = [p.decode("utf-8") for p in message.split(b'\n\n')]
90 # Be careful not to translate the empty string -- it holds the
93 # Be careful not to translate the empty string -- it holds the
91 # meta data of the .po file.
94 # meta data of the .po file.
92 u = u'\n\n'.join([p and _ugettext(p) or u'' for p in paragraphs])
95 u = u'\n\n'.join([p and _ugettext(p) or u'' for p in paragraphs])
93 try:
96 try:
94 # encoding.tolocal cannot be used since it will first try to
97 # encoding.tolocal cannot be used since it will first try to
95 # decode the Unicode string. Calling u.decode(enc) really
98 # decode the Unicode string. Calling u.decode(enc) really
96 # means u.encode(sys.getdefaultencoding()).decode(enc). Since
99 # means u.encode(sys.getdefaultencoding()).decode(enc). Since
97 # the Python encoding defaults to 'ascii', this fails if the
100 # the Python encoding defaults to 'ascii', this fails if the
98 # translated string use non-ASCII characters.
101 # translated string use non-ASCII characters.
99 encodingstr = pycompat.sysstr(encoding.encoding)
102 encodingstr = pycompat.sysstr(encoding.encoding)
100 cache[message] = u.encode(encodingstr, "replace")
103 cache[message] = u.encode(encodingstr, "replace")
101 except LookupError:
104 except LookupError:
102 # An unknown encoding results in a LookupError.
105 # An unknown encoding results in a LookupError.
103 cache[message] = message
106 cache[message] = message
104 return cache[message]
107 return cache[message]
105
108
106
109
107 def _plain():
110 def _plain():
108 if (
111 if (
109 b'HGPLAIN' not in encoding.environ
112 b'HGPLAIN' not in encoding.environ
110 and b'HGPLAINEXCEPT' not in encoding.environ
113 and b'HGPLAINEXCEPT' not in encoding.environ
111 ):
114 ):
112 return False
115 return False
113 exceptions = encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
116 exceptions = encoding.environ.get(b'HGPLAINEXCEPT', b'').strip().split(b',')
114 return b'i18n' not in exceptions
117 return b'i18n' not in exceptions
115
118
116
119
117 if _plain():
120 if _plain():
118
121
119 def _(message: bytes) -> bytes:
122 def _(message: bytes) -> bytes:
120 return message
123 return message
121
124
122 else:
125 else:
123 _ = gettext
126 _ = gettext
General Comments 0
You need to be logged in to leave comments. Login now