mercurial/i18n.py
author Gregory Szorc <gregory.szorc@gmail.com>
Thu, 01 Mar 2018 08:24:54 -0800
changeset 36557 72e487851a53
parent 34660 d00ec62d156f
child 36717 aeaf9c7f7528
permissions -rw-r--r--
debugcommands: add debugwireproto command We currently don't have a low-level mechanism for sending arbitrary wire protocol commands. Having a generic and robust mechanism for sending wire protocol commands, examining wire data, etc would make it vastly easier to test the wire protocol and debug server operation. This is a problem I've wanted a solution for numerous times, especially recently as I've been hacking on a new version of the wire protocol. This commit establishes a `hg debugwireproto` command for sending data to a peer. The command invents a mini language for specifying actions to take. This will enable a lot of flexibility for issuing commands and testing variations for how commands are sent. Right now, we only support low-level raw sends and receives. These are probably the least valuable commands to intended users of this command. But they are the most useful commands to implement to bootstrap the feature (I've chosen to reimplement test-ssh-proto.t using this command to prove its usefulness). My eventual goal of `hg debugwireproto` is to allow calling wire protocol commands with a human-friendly interface. Essentially, people can type in a command name and arguments and `hg debugwireproto` will figure out how to send that on the wire. I'd love to eventually be able to save the server's raw response to a file. This would allow us to e.g. call "getbundle" wire protocol commands easily. test-ssh-proto.t has been updated to use the new command in lieu of piping directly to a server process. As part of the transition, test behavior improved. Before, we piped all request data to the server at once. Now, we have explicit control over the ordering of operations. e.g. we can send one command, receive its response, then send another command. This will allow us to more robustly test race conditions, buffering behavior, etc. There were some subtle changes in test behavior. For example, previous behavior would often send trailing newlines to the server. The new mechanism doesn't treat literal newlines specially and requires newlines be escaped in the payload. Because the new logging code is very low level, it is easy to introduce race conditions in tests. For example, the number of bytes returned by a read() may vary depending on load. This is why tests make heavy use of "readline" for consuming data: the result of that operation should be deterministic and not subject to race conditions. There are still some uses of "readavailable." However, those are only for reading from stderr. I was able to reproduce timing issues with my system under load when using "readavailable" globally. But if I "readline" to grab stdout, "readavailable" appears to work deterministically for stderr. I think this is because the server writes to stderr first. As long as the OS delivers writes to pipes in the same order they were made, this should work. If there are timing issues, we can introduce a mechanism to readline from stderr. Differential Revision: https://phab.mercurial-scm.org/D2392
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
8226
8b2cd04a6e97 put license and copyright info into comment blocks
Martin Geisler <mg@lazybytes.net>
parents: 8225
diff changeset
     1
# i18n.py - internationalization support for mercurial
8b2cd04a6e97 put license and copyright info into comment blocks
Martin Geisler <mg@lazybytes.net>
parents: 8225
diff changeset
     2
#
8b2cd04a6e97 put license and copyright info into comment blocks
Martin Geisler <mg@lazybytes.net>
parents: 8225
diff changeset
     3
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
8b2cd04a6e97 put license and copyright info into comment blocks
Martin Geisler <mg@lazybytes.net>
parents: 8225
diff changeset
     4
#
8b2cd04a6e97 put license and copyright info into comment blocks
Martin Geisler <mg@lazybytes.net>
parents: 8225
diff changeset
     5
# This software may be used and distributed according to the terms of the
10263
25e572394f5c Update license to GPLv2+
Matt Mackall <mpm@selenic.com>
parents: 9538
diff changeset
     6
# GNU General Public License version 2 or any later version.
1400
cf9a1233738a i18n first part: make '_' available for files who need it
Benoit Boissinot <benoit.boissinot@ens-lyon.org
parents:
diff changeset
     7
25955
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
     8
from __future__ import absolute_import
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
     9
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
    10
import gettext as gettextmod
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
    11
import locale
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
    12
import os
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
    13
import sys
2c07c6884394 i18n: use absolute_import
Gregory Szorc <gregory.szorc@gmail.com>
parents: 23031
diff changeset
    14
30050
d229be12e256 py3: convert to unicode to pass into encode()
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
    15
from . import (
d229be12e256 py3: convert to unicode to pass into encode()
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
    16
    encoding,
d229be12e256 py3: convert to unicode to pass into encode()
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
    17
    pycompat,
d229be12e256 py3: convert to unicode to pass into encode()
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
    18
)
7650
85ae7aaf08e9 i18n: lookup .mo files in private locale/ directory
Martin Geisler <mg@daimi.au.dk>
parents: 3888
diff changeset
    19
85ae7aaf08e9 i18n: lookup .mo files in private locale/ directory
Martin Geisler <mg@daimi.au.dk>
parents: 3888
diff changeset
    20
# modelled after templater.templatepath:
14975
b64538363dbe i18n: use getattr instead of hasattr
Augie Fackler <durin42@gmail.com>
parents: 13849
diff changeset
    21
if getattr(sys, 'frozen', None) is not None:
30672
10b17ed9b591 py3: replace sys.executable with pycompat.sysexecutable
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30644
diff changeset
    22
    module = pycompat.sysexecutable
7650
85ae7aaf08e9 i18n: lookup .mo files in private locale/ directory
Martin Geisler <mg@daimi.au.dk>
parents: 3888
diff changeset
    23
else:
31091
2912b06905dc py3: use pycompat.fsencode() to convert __file__ to bytes
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30672
diff changeset
    24
    module = pycompat.fsencode(__file__)
7650
85ae7aaf08e9 i18n: lookup .mo files in private locale/ directory
Martin Geisler <mg@daimi.au.dk>
parents: 3888
diff changeset
    25
28674
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    26
try:
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    27
    unicode
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    28
except NameError:
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    29
    unicode = str
7650
85ae7aaf08e9 i18n: lookup .mo files in private locale/ directory
Martin Geisler <mg@daimi.au.dk>
parents: 3888
diff changeset
    30
21987
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    31
_languages = None
34645
75979c8d4572 codemod: use pycompat.iswindows
Jun Wu <quark@fb.com>
parents: 31091
diff changeset
    32
if (pycompat.iswindows
30035
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
    33
    and 'LANGUAGE' not in encoding.environ
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
    34
    and 'LC_ALL' not in encoding.environ
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
    35
    and 'LC_MESSAGES' not in encoding.environ
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
    36
    and 'LANG' not in encoding.environ):
21987
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    37
    # Try to detect UI language by "User Interface Language Management" API
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    38
    # if no locale variables are set. Note that locale.getdefaultlocale()
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    39
    # uses GetLocaleInfo(), which may be different from UI language.
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    40
    # (See http://msdn.microsoft.com/en-us/library/dd374098(v=VS.85).aspx )
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    41
    try:
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    42
        import ctypes
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    43
        langid = ctypes.windll.kernel32.GetUserDefaultUILanguage()
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    44
        _languages = [locale.windows_locale[langid]]
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    45
    except (ImportError, AttributeError, KeyError):
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    46
        # ctypes not found or unknown langid
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    47
        pass
4953cd193e84 i18n: detect UI language without POSIX-style locale variable on Windows (BC)
Yuya Nishihara <yuya@tcha.org>
parents: 21746
diff changeset
    48
22638
0d0350cfc7ab i18n: use datapath for i18n like for templates and help
Mads Kiilerich <madski@unity3d.com>
parents: 21987
diff changeset
    49
_ugettext = None
0d0350cfc7ab i18n: use datapath for i18n like for templates and help
Mads Kiilerich <madski@unity3d.com>
parents: 21987
diff changeset
    50
0d0350cfc7ab i18n: use datapath for i18n like for templates and help
Mads Kiilerich <madski@unity3d.com>
parents: 21987
diff changeset
    51
def setdatapath(datapath):
30314
8321b083a83d py3: make util.datapath a bytes variable
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30085
diff changeset
    52
    datapath = pycompat.fsdecode(datapath)
30085
2bde971474d2 i18n: make the locale directory name the same string type as the datapath
Augie Fackler <augie@google.com>
parents: 30050
diff changeset
    53
    localedir = os.path.join(datapath, pycompat.sysstr('locale'))
22638
0d0350cfc7ab i18n: use datapath for i18n like for templates and help
Mads Kiilerich <madski@unity3d.com>
parents: 21987
diff changeset
    54
    t = gettextmod.translation('hg', localedir, _languages, fallback=True)
0d0350cfc7ab i18n: use datapath for i18n like for templates and help
Mads Kiilerich <madski@unity3d.com>
parents: 21987
diff changeset
    55
    global _ugettext
28674
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    56
    try:
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    57
        _ugettext = t.ugettext
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    58
    except AttributeError:
03d1ecbbd81e py3: handle ugettext + unicode in i18n
timeless <timeless@mozdev.org>
parents: 25955
diff changeset
    59
        _ugettext = t.gettext
7651
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    60
34660
d00ec62d156f i18n: cache translated messages per encoding
Yuya Nishihara <yuya@tcha.org>
parents: 34645
diff changeset
    61
_msgcache = {}  # encoding: {message: translation}
23031
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    62
7651
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    63
def gettext(message):
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    64
    """Translate message.
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    65
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    66
    The message is looked up in the catalog to get a Unicode string,
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    67
    which is encoded in the local encoding before being returned.
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    68
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    69
    Important: message is restricted to characters in the encoding
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    70
    given by sys.getdefaultencoding() which is most likely 'ascii'.
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    71
    """
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    72
    # If message is None, t.ugettext will return u'None' as the
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    73
    # translation whereas our callers expect us to return None.
22638
0d0350cfc7ab i18n: use datapath for i18n like for templates and help
Mads Kiilerich <madski@unity3d.com>
parents: 21987
diff changeset
    74
    if message is None or not _ugettext:
7651
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    75
        return message
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    76
34660
d00ec62d156f i18n: cache translated messages per encoding
Yuya Nishihara <yuya@tcha.org>
parents: 34645
diff changeset
    77
    cache = _msgcache.setdefault(encoding.encoding, {})
d00ec62d156f i18n: cache translated messages per encoding
Yuya Nishihara <yuya@tcha.org>
parents: 34645
diff changeset
    78
    if message not in cache:
23031
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    79
        if type(message) is unicode:
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    80
            # goofy unicode docstrings in test
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    81
            paragraphs = message.split(u'\n\n')
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    82
        else:
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    83
            paragraphs = [p.decode("ascii") for p in message.split('\n\n')]
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    84
        # Be careful not to translate the empty string -- it holds the
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    85
        # meta data of the .po file.
29415
47fb4beb992b i18n: use unicode literal
Gregory Szorc <gregory.szorc@gmail.com>
parents: 28674
diff changeset
    86
        u = u'\n\n'.join([p and _ugettext(p) or u'' for p in paragraphs])
23031
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    87
        try:
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    88
            # encoding.tolocal cannot be used since it will first try to
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    89
            # decode the Unicode string. Calling u.decode(enc) really
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    90
            # means u.encode(sys.getdefaultencoding()).decode(enc). Since
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    91
            # the Python encoding defaults to 'ascii', this fails if the
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    92
            # translated string use non-ASCII characters.
30050
d229be12e256 py3: convert to unicode to pass into encode()
Pulkit Goyal <7895pulkit@gmail.com>
parents: 30035
diff changeset
    93
            encodingstr = pycompat.sysstr(encoding.encoding)
34660
d00ec62d156f i18n: cache translated messages per encoding
Yuya Nishihara <yuya@tcha.org>
parents: 34645
diff changeset
    94
            cache[message] = u.encode(encodingstr, "replace")
23031
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    95
        except LookupError:
3c0983cc279e i18n: cache the result of every gettext call
Augie Fackler <raf@durin42.com>
parents: 22638
diff changeset
    96
            # An unknown encoding results in a LookupError.
34660
d00ec62d156f i18n: cache translated messages per encoding
Yuya Nishihara <yuya@tcha.org>
parents: 34645
diff changeset
    97
            cache[message] = message
d00ec62d156f i18n: cache translated messages per encoding
Yuya Nishihara <yuya@tcha.org>
parents: 34645
diff changeset
    98
    return cache[message]
7651
5b5036ef847a i18n: encode output in user's local encoding
Martin Geisler <mg@daimi.au.dk>
parents: 7650
diff changeset
    99
13849
9f97de157aad HGPLAIN: allow exceptions to plain mode, like i18n, via HGPLAINEXCEPT
Brodie Rao <brodie@bitheap.org>
parents: 11403
diff changeset
   100
def _plain():
30035
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
   101
    if ('HGPLAIN' not in encoding.environ
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
   102
        and 'HGPLAINEXCEPT' not in encoding.environ):
13849
9f97de157aad HGPLAIN: allow exceptions to plain mode, like i18n, via HGPLAINEXCEPT
Brodie Rao <brodie@bitheap.org>
parents: 11403
diff changeset
   103
        return False
30035
02328b5d775d py3: make i18n use encoding.environ
Yuya Nishihara <yuya@tcha.org>
parents: 29415
diff changeset
   104
    exceptions = encoding.environ.get('HGPLAINEXCEPT', '').strip().split(',')
13849
9f97de157aad HGPLAIN: allow exceptions to plain mode, like i18n, via HGPLAINEXCEPT
Brodie Rao <brodie@bitheap.org>
parents: 11403
diff changeset
   105
    return 'i18n' not in exceptions
9f97de157aad HGPLAIN: allow exceptions to plain mode, like i18n, via HGPLAINEXCEPT
Brodie Rao <brodie@bitheap.org>
parents: 11403
diff changeset
   106
9f97de157aad HGPLAIN: allow exceptions to plain mode, like i18n, via HGPLAINEXCEPT
Brodie Rao <brodie@bitheap.org>
parents: 11403
diff changeset
   107
if _plain():
10455
40dfd46d098f ui: add HGPLAIN environment variable for easier scripting
Brodie Rao <me+hg@dackz.net>
parents: 10263
diff changeset
   108
    _ = lambda message: message
40dfd46d098f ui: add HGPLAIN environment variable for easier scripting
Brodie Rao <me+hg@dackz.net>
parents: 10263
diff changeset
   109
else:
40dfd46d098f ui: add HGPLAIN environment variable for easier scripting
Brodie Rao <me+hg@dackz.net>
parents: 10263
diff changeset
   110
    _ = gettext