Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
637a33b9
Commit
637a33b9
authored
Nov 07, 2018
by
Cheryl Sabella
Committed by
Serhiy Storchaka
Nov 07, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-2504: Add pgettext() and variants to gettext. (GH-7253)
parent
5598cc90
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
304 additions
and
52 deletions
+304
-52
Doc/library/gettext.rst
Doc/library/gettext.rst
+57
-1
Doc/whatsnew/3.8.rst
Doc/whatsnew/3.8.rst
+6
-0
Lib/gettext.py
Lib/gettext.py
+75
-9
Lib/test/test_gettext.py
Lib/test/test_gettext.py
+139
-25
Misc/ACKS
Misc/ACKS
+1
-0
Misc/NEWS.d/next/Library/2018-05-30-16-00-06.bpo-2504.BynUvU.rst
...WS.d/next/Library/2018-05-30-16-00-06.bpo-2504.BynUvU.rst
+1
-0
Tools/i18n/msgfmt.py
Tools/i18n/msgfmt.py
+25
-17
No files found.
Doc/library/gettext.rst
View file @
637a33b9
...
...
@@ -96,6 +96,18 @@ class-based API instead.
Like :func:`ngettext`, but look the message up in the specified *domain*.
.. function:: pgettext(context, message)
.. function:: dpgettext(domain, context, message)
.. function:: npgettext(context, singular, plural, n)
.. function:: dnpgettext(domain, context, singular, plural, n)
Similar to the corresponding functions without the ``p`` in the prefix (that
is, :func:`gettext`, :func:`dgettext`, :func:`ngettext`, :func:`dngettext`),
but the translation is restricted to the given message *context*.
.. versionadded:: 3.8
.. function:: lgettext(message)
.. function:: ldgettext(domain, message)
.. function:: lngettext(singular, plural, n)
...
...
@@ -266,6 +278,22 @@ are the methods of :class:`!NullTranslations`:
Overridden in derived classes.
.. method:: pgettext(context, message)
If a fallback has been set, forward :meth:`pgettext` to the fallback.
Otherwise, return the translated message. Overridden in derived classes.
.. versionadded:: 3.8
.. method:: npgettext(context, singular, plural, n)
If a fallback has been set, forward :meth:`npgettext` to the fallback.
Otherwise, return the translated message. Overridden in derived classes.
.. versionadded:: 3.8
.. method:: lgettext(message)
.. method:: lngettext(singular, plural, n)
...
...
@@ -316,7 +344,7 @@ are the methods of :class:`!NullTranslations`:
If the *names* parameter is given, it must be a sequence containing the
names of functions you want to install in the builtins namespace in
addition to :func:`_`. Supported names are ``'gettext'``, ``'ngettext'``,
``'
lgettext'``
and ``'lngettext'``.
``'
pgettext'``, ``'npgettext'``, ``'lgettext'``,
and ``'lngettext'``.
Note that this is only one way, albeit the most convenient way, to make
the :func:`_` function available to your application. Because it affects
...
...
@@ -331,6 +359,9 @@ are the methods of :class:`!NullTranslations`:
This puts :func:`_` only in the module's global namespace and so only
affects calls within this module.
.. versionchanged:: 3.8
Added ``'pgettext'`` and ``'npgettext'``.
The :class:`GNUTranslations` class
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
...
@@ -394,6 +425,31 @@ unexpected, or if other problems occur while reading the file, instantiating a
n) % {'num': n}
.. method:: pgettext(context, message)
Look up the *context* and *message* id in the catalog and return the
corresponding message string, as a Unicode string. If there is no
entry in the catalog for the *message* id and *context*, and a fallback
has been set, the look up is forwarded to the fallback's
:meth:`pgettext` method. Otherwise, the *message* id is returned.
.. versionadded:: 3.8
.. method:: npgettext(context, singular, plural, n)
Do a plural-forms lookup of a message id. *singular* is used as the
message id for purposes of lookup in the catalog, while *n* is used to
determine which plural form to use.
If the message id for *context* is not found in the catalog, and a
fallback is specified, the request is forwarded to the fallback's
:meth:`npgettext` method. Otherwise, when *n* is 1 *singular* is
returned, and *plural* is returned in all other cases.
.. versionadded:: 3.8
.. method:: lgettext(message)
.. method:: lngettext(singular, plural, n)
...
...
Doc/whatsnew/3.8.rst
View file @
637a33b9
...
...
@@ -131,6 +131,12 @@ asyncio
On Windows, the default event loop is now :class:`~asyncio.ProactorEventLoop`.
gettext
-------
Added :func:`~gettext.pgettext` and its variants.
(Contributed by Franz Glasner, Éric Araujo, and Cheryl Sabella in :issue:`2504`.)
gzip
----
...
...
Lib/gettext.py
View file @
637a33b9
...
...
@@ -57,6 +57,7 @@ __all__ = ['NullTranslations', 'GNUTranslations', 'Catalog',
'bind_textdomain_codeset'
,
'dgettext'
,
'dngettext'
,
'gettext'
,
'lgettext'
,
'ldgettext'
,
'ldngettext'
,
'lngettext'
,
'ngettext'
,
'pgettext'
,
'dpgettext'
,
'npgettext'
,
'dnpgettext'
,
]
_default_localedir
=
os
.
path
.
join
(
sys
.
base_prefix
,
'share'
,
'locale'
)
...
...
@@ -311,6 +312,19 @@ class NullTranslations:
return
tmsg
.
encode
(
self
.
_output_charset
)
return
tmsg
.
encode
(
locale
.
getpreferredencoding
())
def
pgettext
(
self
,
context
,
message
):
if
self
.
_fallback
:
return
self
.
_fallback
.
pgettext
(
context
,
message
)
return
message
def
npgettext
(
self
,
context
,
msgid1
,
msgid2
,
n
):
if
self
.
_fallback
:
return
self
.
_fallback
.
npgettext
(
context
,
msgid1
,
msgid2
,
n
)
if
n
==
1
:
return
msgid1
else
:
return
msgid2
def
info
(
self
):
return
self
.
_info
...
...
@@ -332,15 +346,11 @@ class NullTranslations:
def
install
(
self
,
names
=
None
):
import
builtins
builtins
.
__dict__
[
'_'
]
=
self
.
gettext
if
hasattr
(
names
,
"__contains__"
):
if
"gettext"
in
names
:
builtins
.
__dict__
[
'gettext'
]
=
builtins
.
__dict__
[
'_'
]
if
"ngettext"
in
names
:
builtins
.
__dict__
[
'ngettext'
]
=
self
.
ngettext
if
"lgettext"
in
names
:
builtins
.
__dict__
[
'lgettext'
]
=
self
.
lgettext
if
"lngettext"
in
names
:
builtins
.
__dict__
[
'lngettext'
]
=
self
.
lngettext
if
names
is
not
None
:
allowed
=
{
'gettext'
,
'lgettext'
,
'lngettext'
,
'ngettext'
,
'npgettext'
,
'pgettext'
}
for
name
in
allowed
&
set
(
names
):
builtins
.
__dict__
[
name
]
=
getattr
(
self
,
name
)
class
GNUTranslations
(
NullTranslations
):
...
...
@@ -348,6 +358,10 @@ class GNUTranslations(NullTranslations):
LE_MAGIC
=
0x950412de
BE_MAGIC
=
0xde120495
# The encoding of a msgctxt and a msgid in a .mo file is
# msgctxt + "\x04" + msgid (gettext version >= 0.15)
CONTEXT
=
"%s
\
x04
%s"
# Acceptable .mo versions
VERSIONS
=
(
0
,
1
)
...
...
@@ -493,6 +507,29 @@ class GNUTranslations(NullTranslations):
tmsg
=
msgid2
return
tmsg
def
pgettext
(
self
,
context
,
message
):
ctxt_msg_id
=
self
.
CONTEXT
%
(
context
,
message
)
missing
=
object
()
tmsg
=
self
.
_catalog
.
get
(
ctxt_msg_id
,
missing
)
if
tmsg
is
missing
:
if
self
.
_fallback
:
return
self
.
_fallback
.
pgettext
(
context
,
message
)
return
message
return
tmsg
def
npgettext
(
self
,
context
,
msgid1
,
msgid2
,
n
):
ctxt_msg_id
=
self
.
CONTEXT
%
(
context
,
msgid1
)
try
:
tmsg
=
self
.
_catalog
[
ctxt_msg_id
,
self
.
plural
(
n
)]
except
KeyError
:
if
self
.
_fallback
:
return
self
.
_fallback
.
npgettext
(
context
,
msgid1
,
msgid2
,
n
)
if
n
==
1
:
tmsg
=
msgid1
else
:
tmsg
=
msgid2
return
tmsg
# Locate a .mo file using the gettext strategy
def
find
(
domain
,
localedir
=
None
,
languages
=
None
,
all
=
False
):
...
...
@@ -672,6 +709,26 @@ def ldngettext(domain, msgid1, msgid2, n):
DeprecationWarning
)
return
t
.
lngettext
(
msgid1
,
msgid2
,
n
)
def
dpgettext
(
domain
,
context
,
message
):
try
:
t
=
translation
(
domain
,
_localedirs
.
get
(
domain
,
None
))
except
OSError
:
return
message
return
t
.
pgettext
(
context
,
message
)
def
dnpgettext
(
domain
,
context
,
msgid1
,
msgid2
,
n
):
try
:
t
=
translation
(
domain
,
_localedirs
.
get
(
domain
,
None
))
except
OSError
:
if
n
==
1
:
return
msgid1
else
:
return
msgid2
return
t
.
npgettext
(
context
,
msgid1
,
msgid2
,
n
)
def
gettext
(
message
):
return
dgettext
(
_current_domain
,
message
)
...
...
@@ -696,6 +753,15 @@ def lngettext(msgid1, msgid2, n):
DeprecationWarning
)
return
ldngettext
(
_current_domain
,
msgid1
,
msgid2
,
n
)
def
pgettext
(
context
,
message
):
return
dpgettext
(
_current_domain
,
context
,
message
)
def
npgettext
(
context
,
msgid1
,
msgid2
,
n
):
return
dnpgettext
(
_current_domain
,
context
,
msgid1
,
msgid2
,
n
)
# dcgettext() has been deemed unnecessary and is not implemented.
# James Henstridge's Catalog constructor from GNOME gettext. Documented usage
...
...
Lib/test/test_gettext.py
View file @
637a33b9
...
...
@@ -15,23 +15,27 @@ from test import support
# - Tests should have only one assert.
GNU_MO_DATA
=
b'''
\
3hIElQAAAAAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj
AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD
AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh
eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU
aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u
CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh
Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51
ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt
MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k
YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN
SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4
NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0
ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0
d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo
eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn
IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1
ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
3hIElQAAAAAJAAAAHAAAAGQAAAAAAAAArAAAAAAAAACsAAAAFQAAAK0AAAAjAAAAwwAAAKEAAADn
AAAAMAAAAIkBAAAHAAAAugEAABYAAADCAQAAHAAAANkBAAALAAAA9gEAAEIBAAACAgAAFgAAAEUD
AAAeAAAAXAMAAKEAAAB7AwAAMgAAAB0EAAAFAAAAUAQAABsAAABWBAAAIQAAAHIEAAAJAAAAlAQA
AABSYXltb25kIEx1eHVyeSBZYWNoLXQAVGhlcmUgaXMgJXMgZmlsZQBUaGVyZSBhcmUgJXMgZmls
ZXMAVGhpcyBtb2R1bGUgcHJvdmlkZXMgaW50ZXJuYXRpb25hbGl6YXRpb24gYW5kIGxvY2FsaXph
dGlvbgpzdXBwb3J0IGZvciB5b3VyIFB5dGhvbiBwcm9ncmFtcyBieSBwcm92aWRpbmcgYW4gaW50
ZXJmYWNlIHRvIHRoZSBHTlUKZ2V0dGV4dCBtZXNzYWdlIGNhdGFsb2cgbGlicmFyeS4AV2l0aCBj
b250ZXh0BFRoZXJlIGlzICVzIGZpbGUAVGhlcmUgYXJlICVzIGZpbGVzAG11bGx1c2sAbXkgY29u
dGV4dARudWRnZSBudWRnZQBteSBvdGhlciBjb250ZXh0BG51ZGdlIG51ZGdlAG51ZGdlIG51ZGdl
AFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDMtMDQtMTEgMTQ6
MzItMDQwMApMYXN0LVRyYW5zbGF0b3I6IEouIERhdmlkIEliYW5leiA8ai1kYXZpZEBub29zLmZy
PgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpNSU1FLVZlcnNpb246
IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4NTktMQpDb250ZW50
LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CkdlbmVyYXRlZC1CeTogcHlnZXR0ZXh0LnB5IDEuMQpQ
bHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0d29iYmxlciBNYW5n
cm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFoeXIgY2ViaXZxcmYg
dmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVnIHNiZSBsYmhlIENs
Z3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1ciBUQUgKdHJnZ3Jr
ZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4ASGF5ICVzIGZpY2hlcm8gKGNvbnRleHQpAEhheSAl
cyBmaWNoZXJvcyAoY29udGV4dCkAYmFjb24Ad2luayB3aW5rIChpbiAibXkgY29udGV4dCIpAHdp
bmsgd2luayAoaW4gIm15IG90aGVyIGNvbnRleHQiKQB3aW5rIHdpbmsA
'''
# This data contains an invalid major version number (5)
...
...
@@ -84,13 +88,13 @@ ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA==
UMO_DATA
=
b'''
\
3hIElQAAAAA
CAAAAHAAAACwAAAAFAAAAPAAAAAAAAABQAAAABAAAAFEAAAAPAQAAVgAAAAQAAABm
A
QAAAQAAAAIAAAAAAAAAAAAAAAAAAAAAYWLDngBQcm9qZWN0LUlkLVZlcnNpb246IDIuMApQTy1S
ZXZpc2lvbi1EYXRlOiAyMDAzLTA0LTExIDEyOjQyLTA0MDAKTGFzdC1UcmFuc2xhdG9yOiBCYXJy
eSBBLiBXQXJzYXcgPGJhcnJ5QHB5dGhvbi5vcmc+Ckxhbmd1YWdlLVRlYW06IFhYIDxweXRob24
t
ZGV2QHB5dGhvbi5vcmc+Ck1JTUUtVmVyc2lvbjogMS4wCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFp
bjsgY2hhcnNldD11dGYtOApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiA3Yml0CkdlbmVyYXRl
ZC1CeTogbWFudWFsbHkKAMKkeXo
A
3hIElQAAAAA
DAAAAHAAAADQAAAAAAAAAAAAAAAAAAABMAAAABAAAAE0AAAAQAAAAUgAAAA8BAABj
A
AAABAAAAHMBAAAWAAAAeAEAAABhYsOeAG15Y29udGV4dMOeBGFiw54AUHJvamVjdC1JZC1WZXJz
aW9uOiAyLjAKUE8tUmV2aXNpb24tRGF0ZTogMjAwMy0wNC0xMSAxMjo0Mi0wNDAwCkxhc3QtVHJh
bnNsYXRvcjogQmFycnkgQS4gV0Fyc2F3IDxiYXJyeUBweXRob24ub3JnPgpMYW5ndWFnZS1UZWF
t
OiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpNSU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5
cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzog
N2JpdApHZW5lcmF0ZWQtQnk6IG1hbnVhbGx5CgDCpHl6AMKkeXogKGNvbnRleHQgdmVyc2lvbik
A
'''
MMO_DATA
=
b'''
\
...
...
@@ -147,7 +151,7 @@ class GettextTestCase1(GettextBaseTest):
GettextBaseTest
.
setUp
(
self
)
self
.
localedir
=
os
.
curdir
self
.
mofile
=
MOFILE
gettext
.
install
(
'gettext'
,
self
.
localedir
)
gettext
.
install
(
'gettext'
,
self
.
localedir
,
names
=
[
'pgettext'
]
)
def
test_some_translations
(
self
):
eq
=
self
.
assertEqual
...
...
@@ -157,6 +161,13 @@ class GettextTestCase1(GettextBaseTest):
eq
(
_
(
r'Raymond Luxury Yach-t'
),
'Throatwobbler Mangrove'
)
eq
(
_
(
r'nudge nudge'
),
'wink wink'
)
def
test_some_translations_with_context
(
self
):
eq
=
self
.
assertEqual
eq
(
pgettext
(
'my context'
,
'nudge nudge'
),
'wink wink (in "my context")'
)
eq
(
pgettext
(
'my other context'
,
'nudge nudge'
),
'wink wink (in "my other context")'
)
def
test_double_quotes
(
self
):
eq
=
self
.
assertEqual
# double quotes
...
...
@@ -251,6 +262,20 @@ class GettextTestCase2(GettextBaseTest):
eq
(
self
.
_
(
r'Raymond Luxury Yach-t'
),
'Throatwobbler Mangrove'
)
eq
(
self
.
_
(
r'nudge nudge'
),
'wink wink'
)
def
test_some_translations_with_context
(
self
):
eq
=
self
.
assertEqual
eq
(
gettext
.
pgettext
(
'my context'
,
'nudge nudge'
),
'wink wink (in "my context")'
)
eq
(
gettext
.
pgettext
(
'my other context'
,
'nudge nudge'
),
'wink wink (in "my other context")'
)
def
test_some_translations_with_context_and_domain
(
self
):
eq
=
self
.
assertEqual
eq
(
gettext
.
dpgettext
(
'gettext'
,
'my context'
,
'nudge nudge'
),
'wink wink (in "my context")'
)
eq
(
gettext
.
dpgettext
(
'gettext'
,
'my other context'
,
'nudge nudge'
),
'wink wink (in "my other context")'
)
def
test_double_quotes
(
self
):
eq
=
self
.
assertEqual
# double quotes
...
...
@@ -298,6 +323,15 @@ class PluralFormsTestCase(GettextBaseTest):
x
=
gettext
.
ngettext
(
'There is %s file'
,
'There are %s files'
,
2
)
eq
(
x
,
'Hay %s ficheros'
)
def
test_plural_context_forms1
(
self
):
eq
=
self
.
assertEqual
x
=
gettext
.
npgettext
(
'With context'
,
'There is %s file'
,
'There are %s files'
,
1
)
eq
(
x
,
'Hay %s fichero (context)'
)
x
=
gettext
.
npgettext
(
'With context'
,
'There is %s file'
,
'There are %s files'
,
2
)
eq
(
x
,
'Hay %s ficheros (context)'
)
def
test_plural_forms2
(
self
):
eq
=
self
.
assertEqual
with
open
(
self
.
mofile
,
'rb'
)
as
fp
:
...
...
@@ -307,6 +341,17 @@ class PluralFormsTestCase(GettextBaseTest):
x
=
t
.
ngettext
(
'There is %s file'
,
'There are %s files'
,
2
)
eq
(
x
,
'Hay %s ficheros'
)
def
test_plural_context_forms2
(
self
):
eq
=
self
.
assertEqual
with
open
(
self
.
mofile
,
'rb'
)
as
fp
:
t
=
gettext
.
GNUTranslations
(
fp
)
x
=
t
.
npgettext
(
'With context'
,
'There is %s file'
,
'There are %s files'
,
1
)
eq
(
x
,
'Hay %s fichero (context)'
)
x
=
t
.
npgettext
(
'With context'
,
'There is %s file'
,
'There are %s files'
,
2
)
eq
(
x
,
'Hay %s ficheros (context)'
)
# Examples from http://www.gnu.org/software/gettext/manual/gettext.html
def
test_ja
(
self
):
...
...
@@ -646,6 +691,7 @@ class UnicodeTranslationsTest(GettextBaseTest):
with
open
(
UMOFILE
,
'rb'
)
as
fp
:
self
.
t
=
gettext
.
GNUTranslations
(
fp
)
self
.
_
=
self
.
t
.
gettext
self
.
pgettext
=
self
.
t
.
pgettext
def
test_unicode_msgid
(
self
):
self
.
assertIsInstance
(
self
.
_
(
''
),
str
)
...
...
@@ -653,6 +699,53 @@ class UnicodeTranslationsTest(GettextBaseTest):
def
test_unicode_msgstr
(
self
):
self
.
assertEqual
(
self
.
_
(
'ab
\
xde
'
),
'
\
xa4
yz'
)
def
test_unicode_context_msgstr
(
self
):
t
=
self
.
pgettext
(
'mycontext
\
xde
'
,
'ab
\
xde
'
)
self
.
assertTrue
(
isinstance
(
t
,
str
))
self
.
assertEqual
(
t
,
'
\
xa4
yz (context version)'
)
class
UnicodeTranslationsPluralTest
(
GettextBaseTest
):
def
setUp
(
self
):
GettextBaseTest
.
setUp
(
self
)
with
open
(
MOFILE
,
'rb'
)
as
fp
:
self
.
t
=
gettext
.
GNUTranslations
(
fp
)
self
.
ngettext
=
self
.
t
.
ngettext
self
.
npgettext
=
self
.
t
.
npgettext
def
test_unicode_msgid
(
self
):
unless
=
self
.
assertTrue
unless
(
isinstance
(
self
.
ngettext
(
''
,
''
,
1
),
str
))
unless
(
isinstance
(
self
.
ngettext
(
''
,
''
,
2
),
str
))
def
test_unicode_context_msgid
(
self
):
unless
=
self
.
assertTrue
unless
(
isinstance
(
self
.
npgettext
(
''
,
''
,
''
,
1
),
str
))
unless
(
isinstance
(
self
.
npgettext
(
''
,
''
,
''
,
2
),
str
))
def
test_unicode_msgstr
(
self
):
eq
=
self
.
assertEqual
unless
=
self
.
assertTrue
t
=
self
.
ngettext
(
"There is %s file"
,
"There are %s files"
,
1
)
unless
(
isinstance
(
t
,
str
))
eq
(
t
,
"Hay %s fichero"
)
unless
(
isinstance
(
t
,
str
))
t
=
self
.
ngettext
(
"There is %s file"
,
"There are %s files"
,
5
)
unless
(
isinstance
(
t
,
str
))
eq
(
t
,
"Hay %s ficheros"
)
def
test_unicode_msgstr_with_context
(
self
):
eq
=
self
.
assertEqual
unless
=
self
.
assertTrue
t
=
self
.
npgettext
(
"With context"
,
"There is %s file"
,
"There are %s files"
,
1
)
unless
(
isinstance
(
t
,
str
))
eq
(
t
,
"Hay %s fichero (context)"
)
t
=
self
.
npgettext
(
"With context"
,
"There is %s file"
,
"There are %s files"
,
5
)
unless
(
isinstance
(
t
,
str
))
eq
(
t
,
"Hay %s ficheros (context)"
)
class
WeirdMetadataTest
(
GettextBaseTest
):
def
setUp
(
self
):
...
...
@@ -750,6 +843,14 @@ msgstr ""
msgid "nudge nudge"
msgstr "wink wink"
msgctxt "my context"
msgid "nudge nudge"
msgstr "wink wink (in
\
"
my context
\
"
)"
msgctxt "my other context"
msgid "nudge nudge"
msgstr "wink wink (in
\
"
my other context
\
"
)"
#: test_gettext.py:16 test_gettext.py:22 test_gettext.py:28 test_gettext.py:34
#: test_gettext.py:77 test_gettext.py:83 test_gettext.py:89 test_gettext.py:95
msgid "albatross"
...
...
@@ -782,6 +883,14 @@ msgid "There is %s file"
msgid_plural "There are %s files"
msgstr[0] "Hay %s fichero"
msgstr[1] "Hay %s ficheros"
# Manually added, as neither pygettext nor xgettext support plural forms
# and context in Python.
msgctxt "With context"
msgid "There is %s file"
msgid_plural "There are %s files"
msgstr[0] "Hay %s fichero (context)"
msgstr[1] "Hay %s ficheros (context)"
'''
# Here's the second example po file example, used to generate the UMO_DATA
...
...
@@ -806,6 +915,11 @@ msgstr ""
#: nofile:0
msgid "ab
\
xc3
\
x9e
"
msgstr "
\
xc2
\
xa4
yz"
#: nofile:1
msgctxt "mycontext
\
xc3
\
x9e
"
msgid "ab
\
xc3
\
x9e
"
msgstr "
\
xc2
\
xa4
yz (context version)"
'''
# Here's the third example po file, used to generate MMO_DATA
...
...
Misc/ACKS
View file @
637a33b9
...
...
@@ -559,6 +559,7 @@ Julian Gindi
Yannick Gingras
Neil Girdhar
Matt Giuca
Franz Glasner
Wim Glenn
Michael Goderbauer
Karan Goel
...
...
Misc/NEWS.d/next/Library/2018-05-30-16-00-06.bpo-2504.BynUvU.rst
0 → 100644
View file @
637a33b9
Add gettext.pgettext() and variants.
Tools/i18n/msgfmt.py
View file @
637a33b9
...
...
@@ -5,7 +5,8 @@
This program converts a textual Uniforum-style message catalog (.po file) into
a binary GNU catalog (.mo file). This is essentially the same function as the
GNU msgfmt program, however, it is a simpler implementation.
GNU msgfmt program, however, it is a simpler implementation. Currently it
does not handle plural forms but it does handle message contexts.
Usage: msgfmt.py [OPTIONS] filename.po
...
...
@@ -32,12 +33,11 @@ import struct
import
array
from
email.parser
import
HeaderParser
__version__
=
"1.
1
"
__version__
=
"1.
2
"
MESSAGES
=
{}
def
usage
(
code
,
msg
=
''
):
print
(
__doc__
,
file
=
sys
.
stderr
)
if
msg
:
...
...
@@ -45,15 +45,16 @@ def usage(code, msg=''):
sys
.
exit
(
code
)
def
add
(
id
,
str
,
fuzzy
):
def
add
(
ctxt
,
id
,
str
,
fuzzy
):
"Add a non-fuzzy translation to the dictionary."
global
MESSAGES
if
not
fuzzy
and
str
:
MESSAGES
[
id
]
=
str
if
ctxt
is
None
:
MESSAGES
[
id
]
=
str
else
:
MESSAGES
[
b"%b
\
x04
%b"
%
(
ctxt
,
id
)]
=
str
def
generate
():
"Return the generated output."
global
MESSAGES
...
...
@@ -95,10 +96,10 @@ def generate():
return
output
def
make
(
filename
,
outfile
):
ID
=
1
STR
=
2
CTXT
=
3
# Compute .mo name from .po name and arguments
if
filename
.
endswith
(
'.po'
):
...
...
@@ -115,7 +116,7 @@ def make(filename, outfile):
print
(
msg
,
file
=
sys
.
stderr
)
sys
.
exit
(
1
)
section
=
None
section
=
msgctxt
=
None
fuzzy
=
0
# Start off assuming Latin-1, so everything decodes without failure,
...
...
@@ -129,8 +130,8 @@ def make(filename, outfile):
lno
+=
1
# If we get a comment line after a msgstr, this is a new entry
if
l
[
0
]
==
'#'
and
section
==
STR
:
add
(
msgid
,
msgstr
,
fuzzy
)
section
=
None
add
(
msg
ctxt
,
msg
id
,
msgstr
,
fuzzy
)
section
=
msgctxt
=
None
fuzzy
=
0
# Record a fuzzy mark
if
l
[:
2
]
==
'#,'
and
'fuzzy'
in
l
:
...
...
@@ -138,10 +139,16 @@ def make(filename, outfile):
# Skip comments
if
l
[
0
]
==
'#'
:
continue
# Now we are in a msgid section, output previous section
if
l
.
startswith
(
'msgid'
)
and
not
l
.
startswith
(
'msgid_plural'
):
# Now we are in a msgid or msgctxt section, output previous section
if
l
.
startswith
(
'msgctxt'
):
if
section
==
STR
:
add
(
msgctxt
,
msgid
,
msgstr
,
fuzzy
)
section
=
CTXT
l
=
l
[
7
:]
msgctxt
=
b''
elif
l
.
startswith
(
'msgid'
)
and
not
l
.
startswith
(
'msgid_plural'
):
if
section
==
STR
:
add
(
msgid
,
msgstr
,
fuzzy
)
add
(
msg
ctxt
,
msg
id
,
msgstr
,
fuzzy
)
if
not
msgid
:
# See whether there is an encoding declaration
p
=
HeaderParser
()
...
...
@@ -183,7 +190,9 @@ def make(filename, outfile):
if
not
l
:
continue
l
=
ast
.
literal_eval
(
l
)
if
section
==
ID
:
if
section
==
CTXT
:
msgctxt
+=
l
.
encode
(
encoding
)
elif
section
==
ID
:
msgid
+=
l
.
encode
(
encoding
)
elif
section
==
STR
:
msgstr
+=
l
.
encode
(
encoding
)
...
...
@@ -194,7 +203,7 @@ def make(filename, outfile):
sys
.
exit
(
1
)
# Add last entry
if
section
==
STR
:
add
(
msgid
,
msgstr
,
fuzzy
)
add
(
msg
ctxt
,
msg
id
,
msgstr
,
fuzzy
)
# Compute output
output
=
generate
()
...
...
@@ -206,7 +215,6 @@ def make(filename, outfile):
print
(
msg
,
file
=
sys
.
stderr
)
def
main
():
try
:
opts
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
'hVo:'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment