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