Commit 7bd33c5e authored by Gustavo Niemeyer's avatar Gustavo Niemeyer

This change implements the following gettext features, as

discussed recently in python-dev:

In _locale module:

- bind_textdomain_codeset() binding

In gettext module:

- bind_textdomain_codeset() function
- lgettext(), lngettext(), ldgettext(), ldngettext(),
  which return translated strings encoded in
  preferred system encoding, if
  bind_textdomain_codeset() was not used.
- Added equivalent functionality in translate()
  function and catalog classes.

Every change was also documented.
parent 5980ff2d
...@@ -51,6 +51,14 @@ for \var{domain} is returned.\footnote{ ...@@ -51,6 +51,14 @@ for \var{domain} is returned.\footnote{
the start of your application.} the start of your application.}
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{bind_textdomain_codeset}{domain\optional{, codeset}}
Bind the \var{domain} to \var{codeset}, changing the encoding of
strings returned by the \function{gettext()} family of functions.
If \var{codeset} is omitted, then the current binding is returned.
\versionadded{2.4}
\end{funcdesc}
\begin{funcdesc}{textdomain}{\optional{domain}} \begin{funcdesc}{textdomain}{\optional{domain}}
Change or query the current global domain. If \var{domain} is Change or query the current global domain. If \var{domain} is
\code{None}, then the current global domain is returned, otherwise the \code{None}, then the current global domain is returned, otherwise the
...@@ -64,11 +72,27 @@ is usually aliased as \function{_} in the local namespace (see ...@@ -64,11 +72,27 @@ is usually aliased as \function{_} in the local namespace (see
examples below). examples below).
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{lgettext}{message}
Equivalent to \function{gettext()}, but the translation is returned
in the preferred system encoding, if no other encoding was explicitly
set with \function{bind_textdomain_codeset()}.
\versionadded{2.4}
\end{funcdesc}
\begin{funcdesc}{dgettext}{domain, message} \begin{funcdesc}{dgettext}{domain, message}
Like \function{gettext()}, but look the message up in the specified Like \function{gettext()}, but look the message up in the specified
\var{domain}. \var{domain}.
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{ldgettext}{domain, message}
Equivalent to \function{dgettext()}, but the translation is returned
in the preferred system encoding, if no other encoding was explicitly
set with \function{bind_textdomain_codeset()}.
\versionadded{2.4}
\end{funcdesc}
\begin{funcdesc}{ngettext}{singular, plural, n} \begin{funcdesc}{ngettext}{singular, plural, n}
Like \function{gettext()}, but consider plural forms. If a translation Like \function{gettext()}, but consider plural forms. If a translation
...@@ -87,6 +111,14 @@ formulas for a variety of languages. ...@@ -87,6 +111,14 @@ formulas for a variety of languages.
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{lngettext}{singular, plural, n}
Equivalent to \function{ngettext()}, but the translation is returned
in the preferred system encoding, if no other encoding was explicitly
set with \function{bind_textdomain_codeset()}.
\versionadded{2.4}
\end{funcdesc}
\begin{funcdesc}{dngettext}{domain, singular, plural, n} \begin{funcdesc}{dngettext}{domain, singular, plural, n}
Like \function{ngettext()}, but look the message up in the specified Like \function{ngettext()}, but look the message up in the specified
\var{domain}. \var{domain}.
...@@ -94,6 +126,15 @@ Like \function{ngettext()}, but look the message up in the specified ...@@ -94,6 +126,15 @@ Like \function{ngettext()}, but look the message up in the specified
\versionadded{2.3} \versionadded{2.3}
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{ldngettext}{domain, singular, plural, n}
Equivalent to \function{dngettext()}, but the translation is returned
in the preferred system encoding, if no other encoding was explicitly
set with \function{bind_textdomain_codeset()}.
\versionadded{2.4}
\end{funcdesc}
Note that GNU \program{gettext} also defines a \function{dcgettext()} Note that GNU \program{gettext} also defines a \function{dcgettext()}
method, but this was deemed not useful and so it is currently method, but this was deemed not useful and so it is currently
...@@ -152,8 +193,8 @@ they appear in the languages list or the environment variables. ...@@ -152,8 +193,8 @@ they appear in the languages list or the environment variables.
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{translation}{domain\optional{, localedir\optional{, \begin{funcdesc}{translation}{domain\optional{, localedir\optional{,
languages\optional{, languages\optional{, class_\optional{,
class_,\optional{fallback}}}}} fallback\optional{, codeset}}}}}}
Return a \class{Translations} instance based on the \var{domain}, Return a \class{Translations} instance based on the \var{domain},
\var{localedir}, and \var{languages}, which are first passed to \var{localedir}, and \var{languages}, which are first passed to
\function{find()} to get a list of the \function{find()} to get a list of the
...@@ -161,7 +202,8 @@ associated \file{.mo} file paths. Instances with ...@@ -161,7 +202,8 @@ associated \file{.mo} file paths. Instances with
identical \file{.mo} file names are cached. The actual class instantiated identical \file{.mo} file names are cached. The actual class instantiated
is either \var{class_} if provided, otherwise is either \var{class_} if provided, otherwise
\class{GNUTranslations}. The class's constructor must take a single \class{GNUTranslations}. The class's constructor must take a single
file object argument. file object argument. If provided, \var{codeset} will change the
charset used to encode translated strings.
If multiple files are found, later files are used as fallbacks for If multiple files are found, later files are used as fallbacks for
earlier ones. To allow setting the fallback, \function{copy.copy} earlier ones. To allow setting the fallback, \function{copy.copy}
...@@ -172,13 +214,17 @@ If no \file{.mo} file is found, this function raises ...@@ -172,13 +214,17 @@ If no \file{.mo} file is found, this function raises
\exception{IOError} if \var{fallback} is false (which is the default), \exception{IOError} if \var{fallback} is false (which is the default),
and returns a \class{NullTranslations} instance if \var{fallback} is and returns a \class{NullTranslations} instance if \var{fallback} is
true. true.
\versionchanged[Added the \var{codeset} parameter]{2.4}
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{install}{domain\optional{, localedir\optional{, unicode}}} \begin{funcdesc}{install}{domain\optional{, localedir\optional{, unicode
\optional{, codeset}}}}
This installs the function \function{_} in Python's builtin namespace, This installs the function \function{_} in Python's builtin namespace,
based on \var{domain}, and \var{localedir} which are passed to the based on \var{domain}, \var{localedir}, and \var{codeset} which are
function \function{translation()}. The \var{unicode} flag is passed to passed to the function \function{translation()}. The \var{unicode}
the resulting translation object's \method{install} method. flag is passed to the resulting translation object's \method{install}
method.
As seen below, you usually mark the strings in your application that are As seen below, you usually mark the strings in your application that are
candidates for translation, by wrapping them in a call to the candidates for translation, by wrapping them in a call to the
...@@ -191,6 +237,8 @@ print _('This string will be translated.') ...@@ -191,6 +237,8 @@ print _('This string will be translated.')
For convenience, you want the \function{_()} function to be installed in For convenience, you want the \function{_()} function to be installed in
Python's builtin namespace, so it is easily accessible in all modules Python's builtin namespace, so it is easily accessible in all modules
of your application. of your application.
\versionchanged[Added the \var{codeset} parameter]{2.4}
\end{funcdesc} \end{funcdesc}
\subsubsection{The \class{NullTranslations} class} \subsubsection{The \class{NullTranslations} class}
...@@ -223,25 +271,39 @@ provide a translation for a given message. ...@@ -223,25 +271,39 @@ provide a translation for a given message.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[NullTranslations]{gettext}{message} \begin{methoddesc}[NullTranslations]{gettext}{message}
If a fallback has been set, forward \method{gettext} to the fallback. If a fallback has been set, forward \method{gettext()} to the fallback.
Otherwise, return the translated message. Overridden in derived classes.
\end{methoddesc}
\begin{methoddesc}[NullTranslations]{lgettext}{message}
If a fallback has been set, forward \method{lgettext()} to the fallback.
Otherwise, return the translated message. Overridden in derived classes. Otherwise, return the translated message. Overridden in derived classes.
\versionadded{2.4}
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[NullTranslations]{ugettext}{message} \begin{methoddesc}[NullTranslations]{ugettext}{message}
If a fallback has been set, forward \method{ugettext} to the fallback. If a fallback has been set, forward \method{ugettext()} to the fallback.
Otherwise, return the translated message as a Unicode string. Otherwise, return the translated message as a Unicode string.
Overridden in derived classes. Overridden in derived classes.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[NullTranslations]{ngettext}{singular, plural, n} \begin{methoddesc}[NullTranslations]{ngettext}{singular, plural, n}
If a fallback has been set, forward \method{ngettext} to the fallback. If a fallback has been set, forward \method{ngettext()} to the fallback.
Otherwise, return the translated message. Overridden in derived classes. Otherwise, return the translated message. Overridden in derived classes.
\versionadded{2.3} \versionadded{2.3}
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[NullTranslations]{lngettext}{singular, plural, n}
If a fallback has been set, forward \method{ngettext()} to the fallback.
Otherwise, return the translated message. Overridden in derived classes.
\versionadded{2.4}
\end{methoddesc}
\begin{methoddesc}[NullTranslations]{ungettext}{singular, plural, n} \begin{methoddesc}[NullTranslations]{ungettext}{singular, plural, n}
If a fallback has been set, forward \method{ungettext} to the fallback. If a fallback has been set, forward \method{ungettext()} to the fallback.
Otherwise, return the translated message as a Unicode string. Otherwise, return the translated message as a Unicode string.
Overridden in derived classes. Overridden in derived classes.
...@@ -256,6 +318,20 @@ Return the ``protected'' \member{_info} variable. ...@@ -256,6 +318,20 @@ Return the ``protected'' \member{_info} variable.
Return the ``protected'' \member{_charset} variable. Return the ``protected'' \member{_charset} variable.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[NullTranslations]{output_charset}{}
Return the ``protected'' \member{_output_charset} variable, which
defines the encoding used to return translated messages.
\versionadded{2.4}
\end{methoddesc}
\begin{methoddesc}[NullTranslations]{set_output_charset}{charset}
Change the ``protected'' \member{_output_charset} variable, which
defines the encoding used to return translated messages.
\versionadded{2.4}
\end{methoddesc}
\begin{methoddesc}[NullTranslations]{install}{\optional{unicode}} \begin{methoddesc}[NullTranslations]{install}{\optional{unicode}}
If the \var{unicode} flag is false, this method installs If the \var{unicode} flag is false, this method installs
\method{self.gettext()} into the built-in namespace, binding it to \method{self.gettext()} into the built-in namespace, binding it to
...@@ -323,6 +399,14 @@ look up is forwarded to the fallback's \method{gettext()} method. ...@@ -323,6 +399,14 @@ look up is forwarded to the fallback's \method{gettext()} method.
Otherwise, the \var{message} id is returned. Otherwise, the \var{message} id is returned.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[GNUTranslations]{lgettext}{message}
Equivalent to \method{gettext()}, but the translation is returned
in the preferred system encoding, if no other encoding was explicitly
set with \method{set_output_charset()}.
\versionadded{2.4}
\end{methoddesc}
\begin{methoddesc}[GNUTranslations]{ugettext}{message} \begin{methoddesc}[GNUTranslations]{ugettext}{message}
Look up the \var{message} id in the catalog and return the Look up the \var{message} id in the catalog and return the
corresponding message string, as a Unicode string. If there is no corresponding message string, as a Unicode string. If there is no
...@@ -346,6 +430,14 @@ returned, and \var{plural} is returned in all other cases. ...@@ -346,6 +430,14 @@ returned, and \var{plural} is returned in all other cases.
\versionadded{2.3} \versionadded{2.3}
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[GNUTranslations]{lngettext}{singular, plural, n}
Equivalent to \method{gettext()}, but the translation is returned
in the preferred system encoding, if no other encoding was explicitly
set with \method{set_output_charset()}.
\versionadded{2.4}
\end{methoddesc}
\begin{methoddesc}[GNUTranslations]{ungettext}{singular, plural, n} \begin{methoddesc}[GNUTranslations]{ungettext}{singular, plural, n}
Do a plural-forms lookup of a message id. \var{singular} is used as Do a plural-forms lookup of a message id. \var{singular} is used as
the message id for purposes of lookup in the catalog, while \var{n} is the message id for purposes of lookup in the catalog, while \var{n} is
...@@ -495,7 +587,7 @@ you would put at the top of your module: ...@@ -495,7 +587,7 @@ you would put at the top of your module:
\begin{verbatim} \begin{verbatim}
import gettext import gettext
t = gettext.translation('spam', '/usr/share/locale') t = gettext.translation('spam', '/usr/share/locale')
_ = t.gettext _ = t.lgettext
\end{verbatim} \end{verbatim}
If your translators were providing you with Unicode strings in their If your translators were providing you with Unicode strings in their
...@@ -633,6 +725,21 @@ program to look for translatable strings marked with \function{N_()}. ...@@ -633,6 +725,21 @@ program to look for translatable strings marked with \function{N_()}.
\program{pygettext} and \program{xpot} both support this through the \program{pygettext} and \program{xpot} both support this through the
use of command line switches. use of command line switches.
\subsubsection{\function{gettext()} vs. \function{lgettext()}}
In Python 2.4 the \function{lgettext()} family of functions were
introduced. The intention of these functions is to provide an
alternative which is more compliant with the current
implementation of GNU gettext. Unlike \function{gettext()}, which
returns strings encoded with the same codeset used in the
translation file, \function{lgettext()} will return strings
encoded with the preferred system encoding, as returned by
\function{locale.getpreferredencoding()}. Also notice that
Python 2.4 introduces new functions to explicitly choose
the codeset used in translated strings. If a codeset is explicitly
set, even \function{lgettext()} will return translated strings in
the requested codeset, as would be expected in the GNU gettext
implementation.
\subsection{Acknowledgements} \subsection{Acknowledgements}
The following people contributed code, feedback, design suggestions, The following people contributed code, feedback, design suggestions,
...@@ -647,4 +754,5 @@ this module: ...@@ -647,4 +754,5 @@ this module:
\item Martin von L\"owis \item Martin von L\"owis
\item Fran\c cois Pinard \item Fran\c cois Pinard
\item Barry Warsaw \item Barry Warsaw
\item Gustavo Niemeyer
\end{itemize} \end{itemize}
...@@ -469,15 +469,16 @@ that the \module{_locale} module is not accessible as a shared library. ...@@ -469,15 +469,16 @@ that the \module{_locale} module is not accessible as a shared library.
The locale module exposes the C library's gettext interface on systems The locale module exposes the C library's gettext interface on systems
that provide this interface. It consists of the functions that provide this interface. It consists of the functions
\function{gettext()}, \function{dgettext()}, \function{dcgettext()}, \function{gettext()}, \function{dgettext()}, \function{dcgettext()},
\function{textdomain()}, and \function{bindtextdomain()}. These are \function{textdomain()}, \function{bindtextdomain()}, and
similar to the same functions in the \refmodule{gettext} module, but use \function{bind_textdomain_codeset()}. These are similar to the same
the C library's binary format for message catalogs, and the C functions in the \refmodule{gettext} module, but use the C library's
library's search algorithms for locating message catalogs. binary format for message catalogs, and the C library's search
algorithms for locating message catalogs.
Python applications should normally find no need to invoke these Python applications should normally find no need to invoke these
functions, and should use \refmodule{gettext} instead. A known functions, and should use \refmodule{gettext} instead. A known
exception to this rule are applications that link use additional C exception to this rule are applications that link use additional C
libraries which internally invoke \cfunction{gettext()} or libraries which internally invoke \cfunction{gettext()} or
\function{cdgettext()}. For these applications, it may be necessary to \function{dcgettext()}. For these applications, it may be necessary to
bind the text domain, so that the libraries can properly locate their bind the text domain, so that the libraries can properly locate their
message catalogs. message catalogs.
...@@ -46,7 +46,7 @@ internationalized, to the local language and cultural habits. ...@@ -46,7 +46,7 @@ internationalized, to the local language and cultural habits.
# find this format documented anywhere. # find this format documented anywhere.
import copy, os, re, struct, sys import locale, copy, os, re, struct, sys
from errno import ENOENT from errno import ENOENT
...@@ -171,6 +171,7 @@ class NullTranslations: ...@@ -171,6 +171,7 @@ class NullTranslations:
def __init__(self, fp=None): def __init__(self, fp=None):
self._info = {} self._info = {}
self._charset = None self._charset = None
self._output_charset = None
self._fallback = None self._fallback = None
if fp is not None: if fp is not None:
self._parse(fp) self._parse(fp)
...@@ -189,6 +190,11 @@ class NullTranslations: ...@@ -189,6 +190,11 @@ class NullTranslations:
return self._fallback.gettext(message) return self._fallback.gettext(message)
return message return message
def lgettext(self, message):
if self._fallback:
return self._fallback.lgettext(message)
return message
def ngettext(self, msgid1, msgid2, n): def ngettext(self, msgid1, msgid2, n):
if self._fallback: if self._fallback:
return self._fallback.ngettext(msgid1, msgid2, n) return self._fallback.ngettext(msgid1, msgid2, n)
...@@ -197,6 +203,14 @@ class NullTranslations: ...@@ -197,6 +203,14 @@ class NullTranslations:
else: else:
return msgid2 return msgid2
def lngettext(self, msgid1, msgid2, n):
if self._fallback:
return self._fallback.lngettext(msgid1, msgid2, n)
if n == 1:
return msgid1
else:
return msgid2
def ugettext(self, message): def ugettext(self, message):
if self._fallback: if self._fallback:
return self._fallback.ugettext(message) return self._fallback.ugettext(message)
...@@ -216,6 +230,12 @@ class NullTranslations: ...@@ -216,6 +230,12 @@ class NullTranslations:
def charset(self): def charset(self):
return self._charset return self._charset
def output_charset(self):
return self._output_charset
def set_output_charset(self, charset):
self._output_charset = charset
def install(self, unicode=False): def install(self, unicode=False):
import __builtin__ import __builtin__
__builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext __builtin__.__dict__['_'] = unicode and self.ugettext or self.gettext
...@@ -315,14 +335,29 @@ class GNUTranslations(NullTranslations): ...@@ -315,14 +335,29 @@ class GNUTranslations(NullTranslations):
return self._fallback.gettext(message) return self._fallback.gettext(message)
return message return message
# Encode the Unicode tmsg back to an 8-bit string, if possible # Encode the Unicode tmsg back to an 8-bit string, if possible
if self._charset: if self._output_charset:
return tmsg.encode(self._output_charset)
elif self._charset:
return tmsg.encode(self._charset) return tmsg.encode(self._charset)
return tmsg return tmsg
def lgettext(self, message):
missing = object()
tmsg = self._catalog.get(message, missing)
if tmsg is missing:
if self._fallback:
return self._fallback.lgettext(message)
return message
if self._output_charset:
return tmsg.encode(self._output_charset)
return tmsg.encode(locale.getpreferredencoding())
def ngettext(self, msgid1, msgid2, n): def ngettext(self, msgid1, msgid2, n):
try: try:
tmsg = self._catalog[(msgid1, self.plural(n))] tmsg = self._catalog[(msgid1, self.plural(n))]
if self._charset: if self._output_charset:
return tmsg.encode(self._output_charset)
elif self._charset:
return tmsg.encode(self._charset) return tmsg.encode(self._charset)
return tmsg return tmsg
except KeyError: except KeyError:
...@@ -333,6 +368,20 @@ class GNUTranslations(NullTranslations): ...@@ -333,6 +368,20 @@ class GNUTranslations(NullTranslations):
else: else:
return msgid2 return msgid2
def lngettext(self, msgid1, msgid2, n):
try:
tmsg = self._catalog[(msgid1, self.plural(n))]
if self._output_charset:
return tmsg.encode(self._output_charset)
return tmsg.encode(locale.getpreferredencoding())
except KeyError:
if self._fallback:
return self._fallback.lngettext(msgid1, msgid2, n)
if n == 1:
return msgid1
else:
return msgid2
def ugettext(self, message): def ugettext(self, message):
missing = object() missing = object()
tmsg = self._catalog.get(message, missing) tmsg = self._catalog.get(message, missing)
...@@ -397,7 +446,7 @@ def find(domain, localedir=None, languages=None, all=0): ...@@ -397,7 +446,7 @@ def find(domain, localedir=None, languages=None, all=0):
_translations = {} _translations = {}
def translation(domain, localedir=None, languages=None, def translation(domain, localedir=None, languages=None,
class_=None, fallback=False): class_=None, fallback=False, codeset=None):
if class_ is None: if class_ is None:
class_ = GNUTranslations class_ = GNUTranslations
mofiles = find(domain, localedir, languages, all=1) mofiles = find(domain, localedir, languages, all=1)
...@@ -414,9 +463,12 @@ def translation(domain, localedir=None, languages=None, ...@@ -414,9 +463,12 @@ def translation(domain, localedir=None, languages=None,
t = _translations.get(key) t = _translations.get(key)
if t is None: if t is None:
t = _translations.setdefault(key, class_(open(mofile, 'rb'))) t = _translations.setdefault(key, class_(open(mofile, 'rb')))
# Copy the translation object to allow setting fallbacks. # Copy the translation object to allow setting fallbacks and
# All other instance data is shared with the cached object. # output charset. All other instance data is shared with the
# cached object.
t = copy.copy(t) t = copy.copy(t)
if codeset:
t.set_output_charset(codeset)
if result is None: if result is None:
result = t result = t
else: else:
...@@ -424,13 +476,16 @@ def translation(domain, localedir=None, languages=None, ...@@ -424,13 +476,16 @@ def translation(domain, localedir=None, languages=None,
return result return result
def install(domain, localedir=None, unicode=False): def install(domain, localedir=None, unicode=False, codeset=None):
translation(domain, localedir, fallback=True).install(unicode) t = translation(domain, localedir, fallback=True, codeset=codeset)
t.install(unicode)
# a mapping b/w domains and locale directories # a mapping b/w domains and locale directories
_localedirs = {} _localedirs = {}
# a mapping b/w domains and codesets
_localecodesets = {}
# current global domain, `messages' used for compatibility w/ GNU gettext # current global domain, `messages' used for compatibility w/ GNU gettext
_current_domain = 'messages' _current_domain = 'messages'
...@@ -449,17 +504,33 @@ def bindtextdomain(domain, localedir=None): ...@@ -449,17 +504,33 @@ def bindtextdomain(domain, localedir=None):
return _localedirs.get(domain, _default_localedir) return _localedirs.get(domain, _default_localedir)
def bind_textdomain_codeset(domain, codeset=None):
global _localecodesets
if codeset is not None:
_localecodesets[domain] = codeset
return _localecodesets.get(domain)
def dgettext(domain, message): def dgettext(domain, message):
try: try:
t = translation(domain, _localedirs.get(domain, None)) t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
except IOError: except IOError:
return message return message
return t.gettext(message) return t.gettext(message)
def ldgettext(domain, message):
try:
t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
except IOError:
return message
return t.lgettext(message)
def dngettext(domain, msgid1, msgid2, n): def dngettext(domain, msgid1, msgid2, n):
try: try:
t = translation(domain, _localedirs.get(domain, None)) t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
except IOError: except IOError:
if n == 1: if n == 1:
return msgid1 return msgid1
...@@ -467,14 +538,28 @@ def dngettext(domain, msgid1, msgid2, n): ...@@ -467,14 +538,28 @@ def dngettext(domain, msgid1, msgid2, n):
return msgid2 return msgid2
return t.ngettext(msgid1, msgid2, n) return t.ngettext(msgid1, msgid2, n)
def ldngettext(domain, msgid1, msgid2, n):
try:
t = translation(domain, _localedirs.get(domain, None),
codeset=_localecodesets.get(domain))
except IOError:
if n == 1:
return msgid1
else:
return msgid2
return t.lngettext(msgid1, msgid2, n)
def gettext(message): def gettext(message):
return dgettext(_current_domain, message) return dgettext(_current_domain, message)
def lgettext(message):
return ldgettext(_current_domain, message)
def ngettext(msgid1, msgid2, n): def ngettext(msgid1, msgid2, n):
return dngettext(_current_domain, msgid1, msgid2, n) return dngettext(_current_domain, msgid1, msgid2, n)
def lngettext(msgid1, msgid2, n):
return ldngettext(_current_domain, msgid1, msgid2, n)
# dcgettext() has been deemed unnecessary and is not implemented. # dcgettext() has been deemed unnecessary and is not implemented.
......
...@@ -33,6 +33,8 @@ Core and builtins ...@@ -33,6 +33,8 @@ Core and builtins
will cause a TypeError to be raised. This matches the behavior of will cause a TypeError to be raised. This matches the behavior of
Jython. Jython.
- Implemented bind_textdomain_codeset() in locale module.
Extension modules Extension modules
----------------- -----------------
...@@ -112,6 +114,12 @@ Library ...@@ -112,6 +114,12 @@ Library
- Bug #990307: when keep_empty_values is True, cgi.parse_qsl() - Bug #990307: when keep_empty_values is True, cgi.parse_qsl()
no longer returns spurious empty fields. no longer returns spurious empty fields.
- Implemented bind_textdomain_codeset() in gettext module.
- Introduced in gettext module the l*gettext() family of functions,
which return translation strings encoded in the preferred encoding,
as informed by locale module's getpreferredencoding().
Tools/Demos Tools/Demos
----------- -----------
......
...@@ -649,6 +649,24 @@ PyIntl_bindtextdomain(PyObject* self,PyObject*args) ...@@ -649,6 +649,24 @@ PyIntl_bindtextdomain(PyObject* self,PyObject*args)
return PyString_FromString(dirname); return PyString_FromString(dirname);
} }
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
PyDoc_STRVAR(bind_textdomain_codeset__doc__,
"bind_textdomain_codeset(domain, codeset) -> string\n"
"Bind the C library's domain to codeset.");
static PyObject*
PyIntl_bind_textdomain_codeset(PyObject* self,PyObject*args)
{
char *domain,*codeset;
if (!PyArg_ParseTuple(args, "sz", &domain, &codeset))
return NULL;
codeset = bind_textdomain_codeset(domain, codeset);
if (codeset)
return PyString_FromString(codeset);
Py_RETURN_NONE;
}
#endif
#endif #endif
static struct PyMethodDef PyLocale_Methods[] = { static struct PyMethodDef PyLocale_Methods[] = {
...@@ -678,6 +696,10 @@ static struct PyMethodDef PyLocale_Methods[] = { ...@@ -678,6 +696,10 @@ static struct PyMethodDef PyLocale_Methods[] = {
textdomain__doc__}, textdomain__doc__},
{"bindtextdomain",(PyCFunction)PyIntl_bindtextdomain,METH_VARARGS, {"bindtextdomain",(PyCFunction)PyIntl_bindtextdomain,METH_VARARGS,
bindtextdomain__doc__}, bindtextdomain__doc__},
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
{"bind_textdomain_codeset",(PyCFunction)PyIntl_bind_textdomain_codeset,
METH_VARARGS, bind_textdomain_codeset__doc__},
#endif
#endif #endif
{NULL, NULL} {NULL, NULL}
}; };
......
...@@ -2044,8 +2044,8 @@ fi ...@@ -2044,8 +2044,8 @@ fi
AC_MSG_RESULT(MACHDEP_OBJS) AC_MSG_RESULT(MACHDEP_OBJS)
# checks for library functions # checks for library functions
AC_CHECK_FUNCS(alarm chown clock confstr ctermid execv \ AC_CHECK_FUNCS(alarm bind_textdomain_codeset chown clock confstr ctermid \
fork fpathconf ftime ftruncate \ execv fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getpwent getsid getwd \ getpriority getpwent getsid getwd \
kill killpg lchown lstat mkfifo mknod mktime \ kill killpg lchown lstat mkfifo mknod mktime \
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
/* Define this if your time.h defines altzone. */ /* Define this if your time.h defines altzone. */
#undef HAVE_ALTZONE #undef HAVE_ALTZONE
/* Define to 1 if you have the `bind_textdomain_codeset' function. */
#undef HAVE_BIND_TEXTDOMAIN_CODESET
/* Define to 1 if you have the <bluetooth/bluetooth.h> header file. */ /* Define to 1 if you have the <bluetooth/bluetooth.h> header file. */
#undef HAVE_BLUETOOTH_BLUETOOTH_H #undef HAVE_BLUETOOTH_BLUETOOTH_H
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment