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