flufl.i18n-1.1.1/ 0000775 0001750 0001750 00000000000 11744113073 013663 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/setup.py 0000664 0001750 0001750 00000004377 11744112357 015414 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
from distribute_setup import use_setuptools
use_setuptools()
from setup_helpers import (
description, get_version, long_description, require_python)
from setuptools import setup, find_packages
require_python(0x20600f0)
__version__ = get_version('flufl/i18n/__init__.py')
setup(
name='flufl.i18n',
version=__version__,
namespace_packages=['flufl'],
packages=find_packages(),
include_package_data=True,
zip_safe=False,
maintainer='Barry Warsaw',
maintainer_email='barry@python.org',
description=description('README.rst'),
long_description=long_description('README.rst', 'flufl/i18n/NEWS.rst'),
license='LGPLv3',
url='https://launchpad.net/flufl.i18n',
download_url='https://launchpad.net/flufl.i18n/+download',
test_suite='flufl.i18n.tests',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: '
'GNU Lesser General Public License v3 or later (LGPLv3+)',
'Operating System :: POSIX',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Topic :: Software Development :: Internationalization',
'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Software Development :: Localization',
]
)
flufl.i18n-1.1.1/setup.cfg 0000664 0001750 0001750 00000000221 11744113073 015477 0 ustar barry barry 0000000 0000000 [build_sphinx]
source_dir = flufl/i18n
[upload_docs]
upload_dir = build/sphinx/html
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
flufl.i18n-1.1.1/setup_helpers.py 0000664 0001750 0001750 00000012042 11744107252 017120 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""setup.py helper functions."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'description',
'find_doctests',
'get_version',
'long_description',
'require_python',
]
import os
import re
import sys
DEFAULT_VERSION_RE = re.compile(r'(?P\d+\.\d+(?:\.\d+)?)')
EMPTYSTRING = ''
__version__ = '2.1'
def require_python(minimum):
"""Require at least a minimum Python version.
The version number is expressed in terms of `sys.hexversion`. E.g. to
require a minimum of Python 2.6, use::
>>> require_python(0x206000f0)
:param minimum: Minimum Python version supported.
:type minimum: integer
"""
if sys.hexversion < minimum:
hversion = hex(minimum)[2:]
if len(hversion) % 2 != 0:
hversion = '0' + hversion
split = list(hversion)
parts = []
while split:
parts.append(int(''.join((split.pop(0), split.pop(0))), 16))
major, minor, micro, release = parts
if release == 0xf0:
print('Python {0}.{1}.{2} or better is required'.format(
major, minor, micro))
else:
print('Python {0}.{1}.{2} ({3}) or better is required'.format(
major, minor, micro, hex(release)[2:]))
sys.exit(1)
def get_version(filename, pattern=None):
"""Extract the __version__ from a file without importing it.
While you could get the __version__ by importing the module, the very act
of importing can cause unintended consequences. For example, Distribute's
automatic 2to3 support will break. Instead, this searches the file for a
line that starts with __version__, and extract the version number by
regular expression matching.
By default, two or three dot-separated digits are recognized, but by
passing a pattern parameter, you can recognize just about anything. Use
the `version` group name to specify the match group.
:param filename: The name of the file to search.
:type filename: string
:param pattern: Optional alternative regular expression pattern to use.
:type pattern: string
:return: The version that was extracted.
:rtype: string
"""
if pattern is None:
cre = DEFAULT_VERSION_RE
else:
cre = re.compile(pattern)
with open(filename) as fp:
for line in fp:
if line.startswith('__version__'):
mo = cre.search(line)
assert mo, 'No valid __version__ string found'
return mo.group('version')
raise AssertionError('No __version__ assignment found')
def find_doctests(start='.', extension='.rst'):
"""Find separate-file doctests in the package.
This is useful for Distribute's automatic 2to3 conversion support. The
`setup()` keyword argument `convert_2to3_doctests` requires file names,
which may be difficult to track automatically as you add new doctests.
:param start: Directory to start searching in (default is cwd)
:type start: string
:param extension: Doctest file extension (default is .txt)
:type extension: string
:return: The doctest files found.
:rtype: list
"""
doctests = []
for dirpath, dirnames, filenames in os.walk(start):
doctests.extend(os.path.join(dirpath, filename)
for filename in filenames
if filename.endswith(extension))
return doctests
def long_description(*filenames):
"""Provide a long description."""
res = ['']
for filename in filenames:
with open(filename) as fp:
for line in fp:
res.append(' ' + line)
res.append('')
res.append('\n')
return EMPTYSTRING.join(res)
def description(filename):
"""Provide a short description."""
# This ends up in the Summary header for PKG-INFO and it should be a
# one-liner. It will get rendered on the package page just below the
# package version header but above the long_description, which ironically
# gets stuff into the Description header. It should not include reST, so
# pick out the first single line after the double header.
with open(filename) as fp:
for lineno, line in enumerate(fp):
if lineno < 3:
continue
line = line.strip()
if len(line) > 0:
return line
flufl.i18n-1.1.1/flufl/ 0000775 0001750 0001750 00000000000 11744113073 014773 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/__init__.py 0000664 0001750 0001750 00000001566 11706106035 017112 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n.
#
# flufl.i18n is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
# this is a namespace package
try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)
flufl.i18n-1.1.1/flufl/i18n/ 0000775 0001750 0001750 00000000000 11744113073 015552 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/_registry.py 0000664 0001750 0001750 00000003166 11706107523 020142 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Translation registry."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'registry',
]
from flufl.i18n._application import Application
class Registry:
"""A registry of application translation lookup strategies."""
def __init__(self):
# Map application names to Application instances.
self._registry = {}
def register(self, strategy):
"""Add an association between an application and a lookup strategy.
:param strategy: An application translation lookup strategy.
:type application: A callable object with a .name attribute
:return: An application instance which can be used to access the
language catalogs for the application.
:rtype: `Application`
"""
application = Application(strategy)
self._registry[strategy.name] = application
return application
registry = Registry()
flufl.i18n-1.1.1/flufl/i18n/docs/ 0000775 0001750 0001750 00000000000 11744113073 016502 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/docs/using.rst 0000664 0001750 0001750 00000016356 11706120614 020371 0 ustar barry barry 0000000 0000000 ============================
Using the flufl.i18n library
============================
Set up
======
There are two basic ways that your application can set up translations using
this library. The simple initialization will work for most applications,
where there is only one language context for the entire run of the
application. The more complex initialization works well for applications like
servers that may want to use multiple language contexts during their
execution.
Single language contexts
------------------------
If your application only needs one language context for its entire execution,
you can use the simple API to set things up.
>>> from flufl.i18n import initialize
The library by default uses the ``$LANG`` and ``$LOCPATH`` environment
variables to set things up::
>>> # The testing 'xx' language rot13's the source string. The
>>> # gettext catalogs are in this package directory.
>>> import os
>>> import flufl.i18n.testing.messages
>>> os.environ['LANG'] = 'xx'
>>> os.environ['LOCPATH'] = os.path.dirname(
... flufl.i18n.testing.messages.__file__)
Now you just need to call the ``initialize()`` function with the application's
name and you'll get an object back that you can assign to the ``_()`` function
for run-time translations.
>>> _ = initialize('flufl')
>>> print(_('A test message'))
N grfg zrffntr
It's probably best to just share this function through imports, but it does no
harm to call ``initialize()`` again.
>>> _ = initialize('flufl')
>>> print(_('A test message'))
N grfg zrffntr
..
>>> # Unregister the application domain used earlier. Also, clear the
>>> # environment settings from above.
>>> from flufl.i18n import registry
>>> registry._registry.clear()
>>> del os.environ['LANG']
>>> del os.environ['LOCPATH']
Multiple language contexts
--------------------------
Some applications, such as servers, are more complex; they need multiple
language contexts during their execution. To support this, there is a global
registry of catalog look up :doc:`strategies `. When a particular
language code is specified, the strategy is used to find the catalog that
provides that language's translations.
``flufl.i18n`` comes with a couple of fairly simple strategies, but you can of
course write your own. A convenient built-in strategy looks up catalogs from
within the package directory using `GNU gettext`_'s convention, where the base
directory for the catalogs is rooted in a subpackage.
>>> from flufl.i18n import registry
>>> from flufl.i18n import PackageStrategy
>>> strategy = PackageStrategy('flufl', flufl.i18n.testing.messages)
The first argument is the application name, which must be unique among all
registered strategies. The second argument is the package where the
translations can be found.
Once you have the desired strategy, register this with the global registry.
The registration process returns an application object which can be used to
look up language codes.
>>> application = registry.register(strategy)
The application object keeps track of a current translation catalog, and
exports a method which you can bind to the *underscore* function in your
module globals for convenient gettext usage. By doing so, at run time,
``_()`` will always translate the string argument to the current catalog's
language.
>>> _ = application._
By default the application just translates the source string back into the
source string. I.e. it is a null translator.
>>> print(_('A test message'))
A test message
And it has no language code.
>>> print(_.code)
None
You can temporarily push a new language context to the top of the stack, which
automatically rebinds the underscore function to the language's catalog.
>>> _.push('xx')
>>> print(_.code)
xx
>>> print(_('A test message'))
N grfg zrffntr
Pop the current language to return to the default. Once you're at the bottom
of the stack, more pops will just give you the default translation.
>>> _.pop()
>>> print(_.code)
None
>>> print(_('A test message'))
A test message
>>> _.pop()
>>> print(_.code)
None
>>> print(_('A test message'))
A test message
The underscore method has a context manager called ``using`` which can be used
to temporarily set a new language inside a ``with`` statement::
>>> with _.using('xx'):
... print(_('A test message'))
N grfg zrffntr
>>> print(_('A test message'))
A test message
These ``with`` statements are nestable::
>>> with _.using('xx'):
... print(_('A test message'))
... with _.using('yy'):
... print(_('A test message'))
... print(_('A test message'))
N grfg zrffntr
egassem tset A
N grfg zrffntr
>>> print(_('A test message'))
A test message
You can set the bottom language context, which replaces the default null
translation::
>>> _.default = 'xx'
>>> print(_('A test message'))
N grfg zrffntr
>>> _.pop()
>>> print(_.code)
xx
>>> print(_('A test message'))
N grfg zrffntr
>>> with _.using('yy'):
... print(_('A test message'))
egassem tset A
>>> print(_('A test message'))
N grfg zrffntr
Substitutions and placeholders
==============================
As you can see from the example above, using the library is very simple. You
just put the string to translate inside the underscore function. What if your
source strings need placeholders for other runtime information?
In that case, you use `PEP 292`_ style substitution strings as arguments to
the underscore function. Substitutions are taken from the locals and globals
of the function doing the translation, so that you don't have to repeat
yourself.
>>> ordinal = 'first'
>>> def print_it(name):
... print(_('The $ordinal test message $name'))
In this example, when ``print_it()`` is called, the ``$ordinal`` placeholder
is taken from globals, while the ``$name`` placeholder is taken from the
function locals (i.e. the arguments).
..
>>> # Reset the language context.
>>> del _.default
>>> print(_.code)
None
With no language context in place, the source string is printed unchanged,
except that the substitutions are made.
>>> print_it('Anne')
The first test message Anne
When a substitution is missing, rather than raise an exception, the
``$variable`` is used unchanged.
>>> del ordinal
>>> print_it('Bart')
The $ordinal test message Bart
When there is a language context in effect, the substitutions happen after
translation.
>>> ordinal = 'second'
>>> with _.using('xx'):
... print_it('Cris')
second si n grfg zrffntr Cris
Some languages change the order of the substitution variables, but of course
there is no problem with that.
>>> ordinal = 'third'
>>> with _.using('yy'):
... print_it('Dave')
Dave egassem tset third eht
Locals always take precedence over globals::
>>> def print_it(name, ordinal):
... print(_('The $ordinal test message $name'))
>>> with _.using('yy'):
... print_it('Elle', 'fourth')
Elle egassem tset fourth eht
.. _`GNU gettext`: http://www.gnu.org/software/gettext/manual/gettext.html
.. _`PEP 292`: http://www.python.org/dev/peps/pep-0292/
flufl.i18n-1.1.1/flufl/i18n/docs/__init__.py 0000664 0001750 0001750 00000000000 11706120443 020576 0 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/docs/expand.rst 0000664 0001750 0001750 00000001434 11706120424 020511 0 ustar barry barry 0000000 0000000 ===================
Expanding templates
===================
`PEP 292`_ defines a simplified string template, where substitution variables
are identified by a leading ``$``-sign. The substitution dictionary names the
keys and values that should be interpolated into the template::
>>> key_1 = 'key_1'
>>> key_2 = 'key_2'
>>> from flufl.i18n import expand
>>> # This may fail for Python < 2.6.5
>>> print(expand(
... '$key_2 is different than $key_1', {
... key_1: 'the first key',
... key_2: 'the second key',
... }))
the second key is different than the first key
See `issue 4978`_ for Python 2.6.x compatibility.
.. _`PEP 292`: http://www.python.org/dev/peps/pep-0292/
.. _`issue 4978`: http://bugs.python.org/issue4978
flufl.i18n-1.1.1/flufl/i18n/docs/strategies.rst 0000664 0001750 0001750 00000006117 11706122002 021401 0 ustar barry barry 0000000 0000000 ==================
Catalog strategies
==================
The way ``flufl.i18n`` finds its catalog for an application is extensible.
These are called *strategies*. ``flufl.i18n`` comes with a couple of fairly
simple strategies. The first locates catalog files from within a package's
directory. Inside the package directory, you still need the ``gettext``
standard layout of ``/LC_MESSAGES/.mo``.
Python package strategies
=========================
For example, to use the catalog in ``flufl.i18n``'s testing package, you would
use the ``PackageStrategy``.
>>> from flufl.i18n import PackageStrategy
>>> import flufl.i18n.testing.messages
By setting the ``$LANG`` environment variable, we can specify that the
application translates into that language automatically.
>>> # The testing 'xx' language rot13's the source string.
>>> import os
>>> os.environ['LANG'] = 'xx'
The first argument is the application name, which must be unique among all
registered strategies. The second argument is the package in which to search.
>>> strategy = PackageStrategy('flufl', flufl.i18n.testing.messages)
Once you have the desired strategy, register this with the global registry.
The registration process returns an application object which can be used to
look up language codes.
>>> from flufl.i18n import registry
>>> application = registry.register(strategy)
The application object keeps track of a current translation catalog, and
exports a method which you can bind to the 'underscore' function in your
module globals for convenient gettext usage.
>>> _ = application._
By doing so, at run time, ``_()`` will always translate the string argument to
the current catalog's language.
>>> print(_('A test message'))
N grfg zrffntr
..
>>> # Hack to unregister the previous strategy.
>>> registry._registry.clear()
Simple strategy
===============
There is also a simpler strategy that uses both the ``$LANG`` environment
variable, and the ``$LOCPATH`` environment variable to set things up::
>>> os.environ['LOCPATH'] = os.path.dirname(
... flufl.i18n.testing.messages.__file__)
>>> from flufl.i18n import SimpleStrategy
>>> strategy = SimpleStrategy('flufl')
>>> application = registry.register(strategy)
>>> _ = application._
>>> print(_('A test message'))
N grfg zrffntr
Calling with zero arguments
===========================
Strategies should be prepared to accept zero arguments when called, to produce
a *default* translation (usually the ``gettext.NullTranslator``). Here, we
look for the `ugettext()` method for Python 2 and the `gettext()` method for
Python 3::
>>> def get_ugettext(strategy):
... catalog = strategy()
... try:
... return catalog.ugettext
... except AttributeError:
... # Python 3
... return catalog.gettext
>>> print(get_ugettext(SimpleStrategy('example'))('A test message'))
A test message
>>> print(get_ugettext(PackageStrategy(
... 'example', flufl.i18n.testing.messages))('A test message'))
A test message
flufl.i18n-1.1.1/flufl/i18n/testing/ 0000775 0001750 0001750 00000000000 11744113073 017227 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/ 0000775 0001750 0001750 00000000000 11744113073 021036 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/__init__.py 0000664 0001750 0001750 00000000000 11706120377 023140 0 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/xx/ 0000775 0001750 0001750 00000000000 11744113073 021475 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/xx/LC_MESSAGES/ 0000775 0001750 0001750 00000000000 11744113073 023262 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/xx/LC_MESSAGES/flufl.mo 0000664 0001750 0001750 00000000772 11624413310 024727 0 ustar barry barry 0000000 0000000 4 L ` a p 9 A test message The $ordinal test message $name Project-Id-Version: PACKAGE VERSION
POT-Creation-Date: Sun Jan 8 15:53:47 2006
PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE
Last-Translator: FULL NAME
Language-Team: LANGUAGE
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
Generated-By: hand
N grfg zrffntr $ordinal si n grfg zrffntr $name flufl.i18n-1.1.1/flufl/i18n/testing/messages/xx/LC_MESSAGES/flufl.po 0000664 0001750 0001750 00000001247 11706106035 024734 0 ustar barry barry 0000000 0000000 # A testing catalog
# Copyright (C) 2009-2012 by Barry A. Warsaw
# Barry Warsaw , 2009, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: Sun Jan 8 15:53:47 2006\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: hand\n"
#: flufl/i18n/docs/readme.txt:39
msgid "A test message"
msgstr "N grfg zrffntr"
#: flufl/i18n/docs/readme.txt:40
msgid "The $ordinal test message $name"
msgstr "$ordinal si n grfg zrffntr $name"
flufl.i18n-1.1.1/flufl/i18n/testing/messages/yy/ 0000775 0001750 0001750 00000000000 11744113073 021477 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/yy/LC_MESSAGES/ 0000775 0001750 0001750 00000000000 11744113073 023264 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/testing/messages/yy/LC_MESSAGES/flufl.mo 0000664 0001750 0001750 00000000771 11624413325 024736 0 ustar barry barry 0000000 0000000 4 L ` a p 9 A test message The $ordinal test message $name Project-Id-Version: PACKAGE VERSION
POT-Creation-Date: Sun Jan 8 15:53:47 2006
PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE
Last-Translator: FULL NAME
Language-Team: LANGUAGE
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
Generated-By: hand
egassem tset A $name egassem tset $ordinal eht flufl.i18n-1.1.1/flufl/i18n/testing/messages/yy/LC_MESSAGES/flufl.po 0000664 0001750 0001750 00000001246 11706106035 024735 0 ustar barry barry 0000000 0000000 # A testing catalog
# Copyright (C) 2009-2012 by Barry A. Warsaw
# Barry Warsaw , 2009, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: Sun Jan 8 15:53:47 2006\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=iso-8859-1\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: hand\n"
#: flufl/i18n/docs/readme.txt:39
msgid "A test message"
msgstr "egassem tset A"
#: flufl/i18n/docs/readme.txt:40
msgid "The $ordinal test message $name"
msgstr "$name egassem tset $ordinal eht"
flufl.i18n-1.1.1/flufl/i18n/testing/__init__.py 0000664 0001750 0001750 00000000000 11706120371 021323 0 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/_strategy.py 0000664 0001750 0001750 00000006102 11706107571 020130 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Catalog search strategies."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'PackageStrategy',
'SimpleStrategy',
]
import os
import gettext
class _BaseStrategy:
"""Common code for strategies."""
def __init__(self, name):
"""Create a catalog lookup strategy.
:param name: The application's name.
:type name: string
"""
self.name = name
self._messages_dir = None
def __call__(self, language_code=None):
"""Find the catalog for the language.
:param language_code: The language code to find. If None, then the
default gettext language code lookup scheme is used.
:type language_code: string
:return: A `gettext` catalog.
:rtype: `gettext.NullTranslations` or subclass
"""
# gettext.translation() requires None or a sequence.
languages = (None if language_code is None else [language_code])
try:
return gettext.translation(
self.name, self._messages_dir, languages)
except IOError:
# Fall back to untranslated source language.
return gettext.NullTranslations()
class PackageStrategy(_BaseStrategy):
"""A strategy that finds catalogs based on package paths."""
def __init__(self, name, package):
"""Create a catalog lookup strategy.
:param name: The application's name.
:type name: string
:param package: The package path to the message catalogs. This
strategy uses the __file__ of the package path as the directory
containing `gettext` messages.
:type package_name: module
"""
super(PackageStrategy, self).__init__(name)
self._messages_dir = os.path.dirname(package.__file__)
class SimpleStrategy(_BaseStrategy):
"""A simpler strategy for getting translations."""
def __init__(self, name):
"""Create a catalog lookup strategy.
:param name: The application's name.
:type name: string
:param package: The package path to the message catalogs. This
strategy uses the __file__ of the package path as the directory
containing `gettext` messages.
:type package_name: module
"""
super(SimpleStrategy, self).__init__(name)
self._messages_dir = os.environ.get('LOCPATH')
flufl.i18n-1.1.1/flufl/i18n/_application.py 0000664 0001750 0001750 00000020520 11706107074 020567 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""An application."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'Application',
]
from flufl.i18n._translator import Translator
class _Using:
"""Context manager for _.using()."""
def __init__(self, application, language_code):
self._application = application
self._language_code = language_code
def __enter__(self):
self._application.push(self._language_code)
def __exit__(self, *exc_info):
self._application.pop()
# Do not suppress exceptions.
return False
class _Underscore:
"""The implementation of the _() function.
This class is internal representation only and has an incestuous
relationship with the Application class.
"""
def __init__(self, application):
self._application = application
def __call__(self, original, extras=None):
"""Translate the string into the language of the current context.
:param original: The original string to translate.
:type original: string
:param extras: Extra substitution mapping, elements of which override
the locals and globals.
:return: The translated string.
:rtype: string
"""
return self._application.current.translate(original, extras)
def using(self, language_code):
"""Create a context manager for temporary translation.
While in this context manager, translations use the given language
code. When the with statement exits, the original language is
restored. These are nestable.
"""
return _Using(self._application, language_code)
def push(self, language_code):
"""Push a new catalog onto the stack.
The translation catalog associated with the language code now becomes
the currently active translation context.
"""
self._application.push(language_code)
def pop(self):
"""Pop the current catalog off the translation stack.
No exception is raised for under-runs. In that case, pop() just
no-ops and the null translation becomes the current translation
context.
"""
self._application.pop()
@property
def default(self):
"""Return the default language code.
:return: The default language code.
:rtype: string or None if there is no default language
"""
return self._application.default
@default.setter
def default(self, language_code):
"""Set the default language code.
:param language_code: The language code for the default translator.
:type language_code: string
"""
self._application.default = language_code
@default.deleter
def default(self):
"""Reset the default language to the null translator."""
del self._application.default
@property
def code(self):
"""Return the language code currently in effect."""
code = self._application.code
if code is None:
return self.default
return code
class Application:
"""Manage all the catalogs for a particular application.
You can ask the application for a specific catalog based on the language
code. The Application requires a strategy for finding catalog files.
Attributes:
* dedent (default True) - controls whether translated strings are dedented
or not. This is passed through to the underlying `Translator`
instance.
* depth (default 2) - The number of stack frames to call sys._getframe()
with in the underlying `Translator` instance. Passed through to that
class's constructor.
"""
def __init__(self, strategy):
"""Create an `Application`.
Use the `dedent` attribute on this instance to control whether
translated strings are dedented or not. This is passed straight
through to the `Translator` instance created in the _() method.
:param strategy: A callable that can find catalog files for the
application based on the language code.
:type strategy: callable taking one string argument, the language code.
"""
self._strategy = strategy
# A mapping from language codes to catalogs.
self._catalogs = {}
self._stack = []
# Arguments to the Translator constructor.
self.dedent = True
self.depth = 2
# By default, the baseline translator is the null translator. Use our
# public API so that we share code.
self._default_language = None
self._default_translator = None
# This sets the _default_translator.
del self.default
@property
def name(self):
"""The application name.
:return: The application name.
:rtype: string
"""
return self._strategy.name
@property
def default(self):
"""Return the default language code.
:return: The default language code.
:rtype: string or None if there is no default language
"""
return self._default_language
@default.setter
def default(self, language_code):
"""Set the default language code.
:param language_code: The language code for the default translator.
:type language_code: string
"""
self._default_language = language_code
catalog = self.get(language_code)
self._default_translator = Translator(catalog, self.dedent, self.depth)
@default.deleter
def default(self):
"""Reset the default language to the null translator."""
self._default_language = None
self._default_translator = Translator(
self._strategy(), self.dedent, self.depth)
def get(self, language_code):
"""Get the catalog associated with the language code.
:param language_code: The language code.
:type language_code: string
:return: A `gettext` catalog.
:rtype: `gettext.NullTranslations` or subclass.
"""
missing = object()
catalog = self._catalogs.get(language_code, missing)
if catalog is missing:
catalog = self._strategy(language_code)
self._catalogs[language_code] = catalog
return catalog
@property
def _(self):
"""Return a translator object, tied to the current catalog.
:return: A translator context object for the current active catalog.
:rtype: `Translator`
"""
return _Underscore(self)
def push(self, language_code):
"""Push a new catalog onto the stack.
The translation catalog associated with the language code now becomes
the currently active translation context.
"""
catalog = self.get(language_code)
translator = Translator(catalog, self.dedent, self.depth)
self._stack.append((language_code, translator))
def pop(self):
"""Pop the current catalog off the translation stack.
No exception is raised for under-runs. In that case, pop() just
no-ops and the null translation becomes the current translation
context.
"""
if len(self._stack) > 0:
self._stack.pop()
@property
def current(self):
"""Return the current translator.
:return: The current translator.
:rtype: `Translator`
"""
if len(self._stack) == 0:
return self._default_translator
return self._stack[-1][1]
@property
def code(self):
"""Return the current language code.
:return: The current language code.
:rtype: string
"""
if len(self._stack) == 0:
return None
return self._stack[-1][0]
flufl.i18n-1.1.1/flufl/i18n/NEWS.rst 0000664 0001750 0001750 00000003332 11744112445 017063 0 ustar barry barry 0000000 0000000 =====================
NEWS for flufl.i18n
=====================
1.1.1 (2012-04-19)
==================
* Add classifiers to setup.py and make the long description more compatible
with the Cheeseshop.
* Other changes to make the Cheeseshop page look nicer. (LP: #680136)
* setup_helper.py version 2.1.
1.1 (2012-01-19)
================
* Support Python 3 without the need for 2to3.
1.0.4 (2010-12-06)
==================
* Restore missing line from MANIFEST.in to fix distribution tarball.
1.0.3 (2010-12-01)
==================
* Fix setup.py to not install myfixers artifact directory on install.
* Remove pylint.rc; we'll use pyflakes instead.
1.0.2 (2010-06-23)
==================
* Small documentation fix.
1.0.1 (2010-06-09)
==================
* Ditch the use of zc.buildout.
* Improved documentation.
1.0 (2010-04-24)
================
* Use Distribute instead of Setuptools.
* Port to Python 3 when used with 2to3.
* More documentation improvements.
0.6 (2010-04-21)
================
* Documentation and lint clean up.
0.5 (2010-04-20)
================
* Added a simplified initialization API for one-language-context
applications. This works much better for non-server applications.
* Added a SimpleStrategy which recognizes the $LOCPATH environment variable.
* Show how PEP 292 strings are supported automatically.
* When strategies are called with zero arguments, they supply the default
translation context, which is usually a NullTranslation. This is better
than hardcoding the NullTranslation in the Application.
0.4 (2010-03-04)
================
* Add the ability to get the current language code, via _.code
0.3 (2009-11-15)
================
* Initial release; refactored from Mailman 3.
flufl.i18n-1.1.1/flufl/i18n/__init__.py 0000664 0001750 0001750 00000002176 11744112374 017674 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Expose sub-module names in the package namespace."""
__version__ = '1.1.1'
from flufl.i18n._expand import expand
from flufl.i18n._registry import registry
from flufl.i18n._strategy import *
def initialize(domain):
"""A convenience function for setting up translation.
:param domain: The application's name.
:type domain: string
"""
strategy = SimpleStrategy(domain)
application = registry.register(strategy)
return application._
flufl.i18n-1.1.1/flufl/i18n/conf.py 0000664 0001750 0001750 00000015331 11706123006 017047 0 ustar barry barry 0000000 0000000 # -*- coding: utf-8 -*-
#
# flufl.i18n documentation build configuration file, created by
# sphinx-quickstart on Fri Apr 23 11:41:37 2010.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
from __future__ import print_function
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['../../_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'README'
# General information about the project.
project = 'flufl.i18n'
copyright = '2009-2012, Barry A. Warsaw'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
from flufl.i18n import __version__
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
#unused_docs = []
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = ['_build', 'build', 'flufl.i18n.egg-info', 'distribute-0.6.10']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['../../_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_use_modindex = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'flufli18ndoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('README.rst', 'flufli18n.tex', 'flufl.i18n Documentation',
'Barry A. Warsaw', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_use_modindex = True
import errno
def index_html():
cwd = os.getcwd()
try:
os.chdir('build/sphinx/html')
try:
os.symlink('README.html', 'index.html')
except OSError as error:
if error.errno != errno.EEXIST:
raise
print('index.html -> README.html')
finally:
os.chdir(cwd)
import atexit
atexit.register(index_html)
flufl.i18n-1.1.1/flufl/i18n/_expand.py 0000664 0001750 0001750 00000003044 11706107176 017550 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""String interpolation."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'expand',
]
import logging
from string import Template
log = logging.getLogger('flufl.i18n')
def expand(template, substitutions, template_class=Template):
"""Expand string template with substitutions.
:param template: A PEP 292 $-string template.
:type template: string
:param substitutions: The substitutions dictionary.
:type substitutions: dict
:param template_class: The template class to use.
:type template_class: class
:return: The substituted string.
:rtype: string
"""
try:
return template_class(template).safe_substitute(substitutions)
except (TypeError, ValueError):
# The template is really screwed up.
log.exception('broken template: %s', template)
flufl.i18n-1.1.1/flufl/i18n/_substitute.py 0000664 0001750 0001750 00000002514 11706107641 020502 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Substitutions."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'Template',
'attrdict',
]
import string
_missing = object()
class Template(string.Template):
"""Match any attribute path."""
idpattern = r'[_a-z][_a-z0-9.]*'
class attrdict(dict):
"""Follow attribute paths."""
def __getitem__(self, key):
parts = key.split('.')
value = super(attrdict, self).__getitem__(parts.pop(0))
while parts:
value = getattr(value, parts.pop(0), _missing)
if value is _missing:
raise KeyError(key)
return value
flufl.i18n-1.1.1/flufl/i18n/README.rst 0000664 0001750 0001750 00000005157 11706107425 017253 0 ustar barry barry 0000000 0000000 ======================================================
flufl.i18n - A high level API for internationalization
======================================================
This package provides a high level, convenient API for managing
internationalization translation contexts in Python application. There is a
simple API for single-context applications, such as command line scripts which
only need to translate into one language during the entire course of their
execution. There is a more flexible, but still convenient API for
multi-context applications, such as servers, which may need to switch language
contexts for different tasks.
Requirements
============
``flufl.i18n`` requires Python 2.6.5 or newer, and is compatible with Python 3.
Documentation
=============
A `simple guide`_ to using the library is available within this package, in
the form of doctests. The manual is also available online in the Cheeseshop
at:
http://packages.python.org/flufl.i18n
Project details
===============
The project home page is:
http://launchpad.net/flufl.i18n
You should report bugs at:
http://bugs.launchpad.net/flufl.i18n
You can download the latest version of the package either from the Cheeseshop:
http://pypi.python.org/pypi/flufl.i18n
or from the Launchpad page above. Of course you can also just install it with
``pip`` or ``easy_install`` from the command line::
% sudo pip flufl.i18n
% sudo easy_install flufl.i18n
You can grab the latest development copy of the code using Bazaar, from the
Launchpad home page above. See http://bazaar-vcs.org for details on the
Bazaar distributed revision control system. If you have Bazaar installed, you
can branch the code like this::
% bzr branch lp:flufl.i18n
You may contact the author via barry@python.org.
Copyright
=========
Copyright (C) 2009-2012 Barry A. Warsaw
This file is part of flufl.i18n
flufl.i18n is free software: you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) any
later version.
flufl.i18n is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License along
with flufl.i18n. If not, see .
Table of Contents
=================
.. toctree::
:glob:
docs/using
docs/*
NEWS
.. _`simple guide`: docs/using.html
flufl.i18n-1.1.1/flufl/i18n/tests/ 0000775 0001750 0001750 00000000000 11744113073 016714 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/tests/test_translator.py 0000664 0001750 0001750 00000010426 11706120237 022517 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Tests for the Translator class.
This cannot be a doctest because of the sys._getframe() manipulations. That
does not play well with the way doctest executes Python code. But see
translator.txt for a description of how this should work in real Python code.
"""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
]
import unittest
from flufl.i18n._translator import Translator
# Some globals for following tests.
purple = 'porpoises'
magenta = 'monkeys'
green = 'gerbil'
class Catalog:
"""Test catalog."""
def __init__(self):
self.translation = None
def ugettext(self, original):
"""Return the translation."""
return self.translation
# For Python 3.
gettext = ugettext
def charset(self):
"""Return the encoding."""
# The default is ascii.
return None
class TranslatorTests(unittest.TestCase):
"""Tests of the Translator class."""
def setUp(self):
self.catalog = Catalog()
# We need depth=1 because we're calling the translation at the same
# level as the locals we care about.
self.translator = Translator(self.catalog, depth=1)
def test_locals(self):
# Test that locals get properly substituted.
aqua = 'aardvarks'
blue = 'badgers'
cyan = 'cats'
self.catalog.translation = '$blue and $cyan and $aqua'
self.assertEqual(self.translator.translate('source string'),
'badgers and cats and aardvarks')
def test_globals(self):
# Test that globals get properly substituted.
self.catalog.translation = '$purple and $magenta and $green'
self.assertEqual(self.translator.translate('source string'),
'porpoises and monkeys and gerbil')
def test_dict_overrides_locals(self):
# Test that explicit mappings override locals.
aqua = 'aardvarks'
blue = 'badgers'
cyan = 'cats'
overrides = dict(blue='bats')
self.catalog.translation = '$blue and $cyan and $aqua'
self.assertEqual(self.translator.translate('source string', overrides),
'bats and cats and aardvarks')
def test_globals_with_overrides(self):
# Test that globals with overrides get properly substituted.
self.catalog.translation = '$purple and $magenta and $green'
overrides = dict(green='giraffe')
self.assertEqual(self.translator.translate('source string', overrides),
'porpoises and monkeys and giraffe')
def test_empty_string(self):
# The empty string is always translated as the empty string.
self.assertEqual(self.translator.translate(''), '')
def test_dedent(self):
# By default, the translated string is always dedented.
aqua = 'aardvarks'
blue = 'badgers'
cyan = 'cats'
self.catalog.translation = """\
These are the $blue
These are the $cyan
These are the $aqua
"""
for line in self.translator.translate('source string').splitlines():
self.assertTrue(line[:5], 'These')
def test_no_dedent(self):
# You can optionally suppress the dedent.
aqua = 'aardvarks'
blue = 'badgers'
cyan = 'cats'
self.catalog.translation = """\
These are the $blue
These are the $cyan
These are the $aqua
"""
translator = Translator(self.catalog, dedent=False)
for line in translator.translate('source string').splitlines():
self.assertTrue(line[:9], ' These')
flufl.i18n-1.1.1/flufl/i18n/tests/__init__.py 0000664 0001750 0001750 00000000000 11706120163 021007 0 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl/i18n/tests/test_documentation.py 0000664 0001750 0001750 00000004715 11706121234 023201 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Test harness for doctests."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'additional_tests',
]
import os
import atexit
import doctest
import unittest
from pkg_resources import (
resource_filename, resource_exists, resource_listdir, cleanup_resources)
DOCTEST_FLAGS = (
doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE |
doctest.REPORT_NDIFF)
def setup(testobj):
"""Test setup."""
# Make sure future statements in our doctests match the Python code. When
# run with 2to3, the future import gets removed and these names are not
# defined.
try:
testobj.globs['absolute_import'] = absolute_import
testobj.globs['print_function'] = print_function
testobj.globs['unicode_literals'] = unicode_literals
except NameError:
pass
# Ensure that environment variables affecting translation are neutralized.
for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'):
if envar in os.environ:
del os.environ[envar]
def additional_tests():
"Run the doc tests (README.txt and docs/*, if any exist)"
doctest_files = [
os.path.abspath(resource_filename('flufl.i18n', 'README.rst'))]
if resource_exists('flufl.i18n', 'docs'):
for name in resource_listdir('flufl.i18n', 'docs'):
if name.endswith('.rst'):
doctest_files.append(
os.path.abspath(
resource_filename('flufl.i18n', 'docs/%s' % name)))
kwargs = dict(module_relative=False,
optionflags=DOCTEST_FLAGS,
setUp=setup,
)
atexit.register(cleanup_resources)
return unittest.TestSuite((
doctest.DocFileSuite(*doctest_files, **kwargs)))
flufl.i18n-1.1.1/flufl/i18n/_translator.py 0000664 0001750 0001750 00000010370 11706120143 020447 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Basic translation context class."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
'Translator',
]
import sys
import textwrap
from flufl.i18n._substitute import attrdict, Template
from flufl.i18n._expand import expand
try:
unicode_type = unicode
except NameError:
# Python 3
unicode_type = str
class Translator:
"""A translation context."""
def __init__(self, catalog, dedent=True, depth=2):
"""Create a translation context.
:param catalog: The translation catalog.
:type catalog: `gettext.NullTranslations` or subclass
:param dedent: Whether the input string should be dedented.
:type dedent: bool
:param depth: Number of stack frames to call sys._getframe() with.
:type depth: int
"""
self._catalog = catalog
self.dedent = dedent
self.depth = depth
# Python 3's .gettext() returns unicodes.
try:
self._gettext = self._catalog.ugettext
except AttributeError:
# Must be Python 3, right?
self._gettext = self._catalog.gettext
def translate(self, original, extras=None):
"""Translate the string.
:param original: The original string to translate.
:type original: string
:param extras: Extra substitution mapping, elements of which override
the locals and globals.
:return: The translated string.
:rtype: string
"""
if original == '':
return ''
assert original, 'Cannot translate: {0}'.format(original)
# Because the original string is what the text extractors put into the
# catalog, we must first look up the original unadulterated string in
# the catalog. Use the global translation context for this.
#
# Translations must be unicode safe internally. The translation
# service is one boundary to the outside world, so to honor this
# constraint, make sure that all strings to come out of this are
# unicodes, even if the translated string or dictionary values are
# 8-bit strings.
tns = self._gettext(original)
charset = self._catalog.charset() or 'us-ascii'
# Do PEP 292 style $-string interpolation into the resulting string.
#
# This lets you write something like:
#
# now = time.ctime(time.time())
# print _('The current time is: $now')
#
# and have it Just Work. Key precedence is:
#
# extras > locals > globals
#
# Get the frame of the caller.
frame = sys._getframe(self.depth)
# Create the raw dictionary of substitutions.
raw_dict = frame.f_globals.copy()
raw_dict.update(frame.f_locals)
if extras is not None:
raw_dict.update(extras)
# Python 2 requires ** dictionaries to have str, not unicode keys.
# For our purposes, keys should always be ascii. Values though should
# be unicode.
translated_string = expand(tns, attrdict(raw_dict), Template)
# Use the bytes type here instead of str for better compatibility with
# 2to3, which transforms this code into trying to decode a unicode to
# a unicode.
if isinstance(translated_string, bytes):
translated_string = unicode_type(translated_string, charset)
# Dedent the string if so desired.
if self.dedent:
translated_string = textwrap.dedent(translated_string)
return translated_string
flufl.i18n-1.1.1/distribute_setup.py 0000664 0001750 0001750 00000035665 11706106035 017650 0 ustar barry barry 0000000 0000000 #!python
"""Bootstrap distribute installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from distribute_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
import sys
import time
import fnmatch
import tempfile
import tarfile
from distutils import log
try:
from site import USER_SITE
except ImportError:
USER_SITE = None
try:
import subprocess
def _python_cmd(*args):
args = (sys.executable,) + args
return subprocess.call(args) == 0
except ImportError:
# will be used for python 2.3
def _python_cmd(*args):
args = (sys.executable,) + args
# quoting arguments if windows
if sys.platform == 'win32':
def quote(arg):
if ' ' in arg:
return '"%s"' % arg
return arg
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
DEFAULT_VERSION = "0.6.10"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
SETUPTOOLS_PKG_INFO = """\
Metadata-Version: 1.0
Name: setuptools
Version: %s
Summary: xxxx
Home-page: xxx
Author: xxx
Author-email: xxx
License: xxx
Description: xxx
""" % SETUPTOOLS_FAKED_VERSION
def _install(tarball):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# installing
log.warn('Installing Distribute')
if not _python_cmd('setup.py', 'install'):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
finally:
os.chdir(old_wd)
def _build_egg(egg, tarball, to_dir):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# building an egg
log.warn('Building a Distribute egg in %s', to_dir)
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
finally:
os.chdir(old_wd)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
raise IOError('Could not build the egg.')
def _do_download(version, download_base, to_dir, download_delay):
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
% (version, sys.version_info[0], sys.version_info[1]))
if not os.path.exists(egg):
tarball = download_setuptools(version, download_base,
to_dir, download_delay)
_build_egg(egg, tarball, to_dir)
sys.path.insert(0, egg)
import setuptools
setuptools.bootstrap_install_from = egg
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, download_delay=15, no_fake=True):
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
was_imported = 'pkg_resources' in sys.modules or \
'setuptools' in sys.modules
try:
try:
import pkg_resources
if not hasattr(pkg_resources, '_distribute'):
if not no_fake:
_fake_setuptools()
raise ImportError
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
pkg_resources.require("distribute>="+version)
return
except pkg_resources.VersionConflict:
e = sys.exc_info()[1]
if was_imported:
sys.stderr.write(
"The required version of distribute (>=%s) is not available,\n"
"and can't be installed while this script is running. Please\n"
"install a more recent version first, using\n"
"'easy_install -U distribute'."
"\n\n(Currently using %r)\n" % (version, e.args[0]))
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return _do_download(version, download_base, to_dir,
download_delay)
except pkg_resources.DistributionNotFound:
return _do_download(version, download_base, to_dir,
download_delay)
finally:
if not no_fake:
_create_fake_setuptools_pkg_info(to_dir)
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download distribute from a specified location and return its filename
`version` should be a valid distribute version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download
attempt.
"""
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
tgz_name = "distribute-%s.tar.gz" % version
url = download_base + tgz_name
saveto = os.path.join(to_dir, tgz_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
log.warn("Downloading %s", url)
src = urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = src.read()
dst = open(saveto, "wb")
dst.write(data)
finally:
if src:
src.close()
if dst:
dst.close()
return os.path.realpath(saveto)
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
if existing_content == content:
# already patched
log.warn('Already patched.')
return False
log.warn('Patching...')
_rename_path(path)
f = open(path, 'w')
try:
f.write(content)
finally:
f.close()
return True
def _same_content(path, content):
return open(path).read() == content
def _no_sandbox(function):
def __no_sandbox(*args, **kw):
try:
from setuptools.sandbox import DirectorySandbox
def violation(*args):
pass
DirectorySandbox._old = DirectorySandbox._violation
DirectorySandbox._violation = violation
patched = True
except ImportError:
patched = False
try:
return function(*args, **kw)
finally:
if patched:
DirectorySandbox._violation = DirectorySandbox._old
del DirectorySandbox._old
return __no_sandbox
@_no_sandbox
def _rename_path(path):
new_name = path + '.OLD.%s' % time.time()
log.warn('Renaming %s into %s', path, new_name)
os.rename(path, new_name)
return new_name
def _remove_flat_installation(placeholder):
if not os.path.isdir(placeholder):
log.warn('Unkown installation at %s', placeholder)
return False
found = False
for file in os.listdir(placeholder):
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
found = True
break
if not found:
log.warn('Could not locate setuptools*.egg-info')
return
log.warn('Removing elements out of the way...')
pkg_info = os.path.join(placeholder, file)
if os.path.isdir(pkg_info):
patched = _patch_egg_dir(pkg_info)
else:
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
if not patched:
log.warn('%s already patched.', pkg_info)
return False
# now let's move the files out of the way
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
element = os.path.join(placeholder, element)
if os.path.exists(element):
_rename_path(element)
else:
log.warn('Could not find the %s element of the '
'Setuptools distribution', element)
return True
def _after_install(dist):
log.warn('After install bootstrap.')
placeholder = dist.get_command_obj('install').install_purelib
_create_fake_setuptools_pkg_info(placeholder)
@_no_sandbox
def _create_fake_setuptools_pkg_info(placeholder):
if not placeholder or not os.path.exists(placeholder):
log.warn('Could not find the install location')
return
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
(SETUPTOOLS_FAKED_VERSION, pyver)
pkg_info = os.path.join(placeholder, setuptools_file)
if os.path.exists(pkg_info):
log.warn('%s already exists', pkg_info)
return
log.warn('Creating %s', pkg_info)
f = open(pkg_info, 'w')
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
f.close()
pth_file = os.path.join(placeholder, 'setuptools.pth')
log.warn('Creating %s', pth_file)
f = open(pth_file, 'w')
try:
f.write(os.path.join(os.curdir, setuptools_file))
finally:
f.close()
def _patch_egg_dir(path):
# let's check if it's already patched
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
if os.path.exists(pkg_info):
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
log.warn('%s already patched.', pkg_info)
return False
_rename_path(path)
os.mkdir(path)
os.mkdir(os.path.join(path, 'EGG-INFO'))
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
f = open(pkg_info, 'w')
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
f.close()
return True
def _before_install():
log.warn('Before install bootstrap.')
_fake_setuptools()
def _under_prefix(location):
if 'install' not in sys.argv:
return True
args = sys.argv[sys.argv.index('install')+1:]
for index, arg in enumerate(args):
for option in ('--root', '--prefix'):
if arg.startswith('%s=' % option):
top_dir = arg.split('root=')[-1]
return location.startswith(top_dir)
elif arg == option:
if len(args) > index:
top_dir = args[index+1]
return location.startswith(top_dir)
elif option == '--user' and USER_SITE is not None:
return location.startswith(USER_SITE)
return True
def _fake_setuptools():
log.warn('Scanning installed packages')
try:
import pkg_resources
except ImportError:
# we're cool
log.warn('Setuptools or Distribute does not seem to be installed.')
return
ws = pkg_resources.working_set
try:
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
replacement=False))
except TypeError:
# old distribute API
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
if setuptools_dist is None:
log.warn('No setuptools distribution found')
return
# detecting if it was already faked
setuptools_location = setuptools_dist.location
log.warn('Setuptools installation detected at %s', setuptools_location)
# if --root or --preix was provided, and if
# setuptools is not located in them, we don't patch it
if not _under_prefix(setuptools_location):
log.warn('Not patching, --root or --prefix is installing Distribute'
' in another location')
return
# let's see if its an egg
if not setuptools_location.endswith('.egg'):
log.warn('Non-egg installation')
res = _remove_flat_installation(setuptools_location)
if not res:
return
else:
log.warn('Egg installation')
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
if (os.path.exists(pkg_info) and
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
log.warn('Already patched.')
return
log.warn('Patching...')
# let's create a fake egg replacing setuptools one
res = _patch_egg_dir(setuptools_location)
if not res:
return
log.warn('Patched done.')
_relaunch()
def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args))
def _extractall(self, path=".", members=None):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
directories afterwards. `path' specifies a different directory
to extract to. `members' is optional and must be a subset of the
list returned by getmembers().
"""
import copy
import operator
from tarfile import ExtractError
directories = []
if members is None:
members = self
for tarinfo in members:
if tarinfo.isdir():
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
if sys.version_info < (2, 4):
def sorter(dir1, dir2):
return cmp(dir1.name, dir2.name)
directories.sort(sorter)
directories.reverse()
else:
directories.sort(key=operator.attrgetter('name'), reverse=True)
# Set correct owner, mtime and filemode on directories.
for tarinfo in directories:
dirpath = os.path.join(path, tarinfo.name)
try:
self.chown(tarinfo, dirpath)
self.utime(tarinfo, dirpath)
self.chmod(tarinfo, dirpath)
except ExtractError:
e = sys.exc_info()[1]
if self.errorlevel > 1:
raise
else:
self._dbg(1, "tarfile: %s" % e)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
tarball = download_setuptools()
_install(tarball)
if __name__ == '__main__':
main(sys.argv[1:])
flufl.i18n-1.1.1/flufl.i18n.egg-info/ 0000775 0001750 0001750 00000000000 11744113073 017243 5 ustar barry barry 0000000 0000000 flufl.i18n-1.1.1/flufl.i18n.egg-info/SOURCES.txt 0000664 0001750 0001750 00000002037 11744113073 021131 0 ustar barry barry 0000000 0000000 COPYING-LESSER.txt
README.rst
distribute_setup.py
setup.cfg
setup.py
setup_helpers.py
template.py
flufl/__init__.py
flufl.i18n.egg-info/PKG-INFO
flufl.i18n.egg-info/SOURCES.txt
flufl.i18n.egg-info/dependency_links.txt
flufl.i18n.egg-info/namespace_packages.txt
flufl.i18n.egg-info/not-zip-safe
flufl.i18n.egg-info/top_level.txt
flufl/i18n/NEWS.rst
flufl/i18n/README.rst
flufl/i18n/__init__.py
flufl/i18n/_application.py
flufl/i18n/_expand.py
flufl/i18n/_registry.py
flufl/i18n/_strategy.py
flufl/i18n/_substitute.py
flufl/i18n/_translator.py
flufl/i18n/conf.py
flufl/i18n/docs/__init__.py
flufl/i18n/docs/expand.rst
flufl/i18n/docs/strategies.rst
flufl/i18n/docs/using.rst
flufl/i18n/testing/__init__.py
flufl/i18n/testing/messages/__init__.py
flufl/i18n/testing/messages/xx/LC_MESSAGES/flufl.mo
flufl/i18n/testing/messages/xx/LC_MESSAGES/flufl.po
flufl/i18n/testing/messages/yy/LC_MESSAGES/flufl.mo
flufl/i18n/testing/messages/yy/LC_MESSAGES/flufl.po
flufl/i18n/tests/__init__.py
flufl/i18n/tests/test_documentation.py
flufl/i18n/tests/test_translator.py flufl.i18n-1.1.1/flufl.i18n.egg-info/top_level.txt 0000664 0001750 0001750 00000000006 11744113073 021771 0 ustar barry barry 0000000 0000000 flufl
flufl.i18n-1.1.1/flufl.i18n.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 11744113073 023311 0 ustar barry barry 0000000 0000000
flufl.i18n-1.1.1/flufl.i18n.egg-info/namespace_packages.txt 0000664 0001750 0001750 00000000006 11744113073 023572 0 ustar barry barry 0000000 0000000 flufl
flufl.i18n-1.1.1/flufl.i18n.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 11706123111 021461 0 ustar barry barry 0000000 0000000
flufl.i18n-1.1.1/flufl.i18n.egg-info/PKG-INFO 0000664 0001750 0001750 00000011555 11744113073 020347 0 ustar barry barry 0000000 0000000 Metadata-Version: 1.1
Name: flufl.i18n
Version: 1.1.1
Summary: A high level API for Python internationalization.
Home-page: https://launchpad.net/flufl.i18n
Author: Barry Warsaw
Author-email: barry@python.org
License: LGPLv3
Download-URL: https://launchpad.net/flufl.i18n/+download
Description: ==========
flufl.i18n
==========
A high level API for Python internationalization.
The ``flufl.i18n`` library provides a convenient API for managing translation
contexts in Python applications. It provides facilities not only for
single-context applications like command line scripts, but also more
sophisticated management of multiple-context applications such as Internet
servers.
License
=======
This file is part of flufl.i18n.
flufl.i18n is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
flufl.i18n is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with flufl.i18n. If not, see .
=====================
NEWS for flufl.i18n
=====================
1.1.1 (2012-04-19)
==================
* Add classifiers to setup.py and make the long description more compatible
with the Cheeseshop.
* Other changes to make the Cheeseshop page look nicer. (LP: #680136)
* setup_helper.py version 2.1.
1.1 (2012-01-19)
================
* Support Python 3 without the need for 2to3.
1.0.4 (2010-12-06)
==================
* Restore missing line from MANIFEST.in to fix distribution tarball.
1.0.3 (2010-12-01)
==================
* Fix setup.py to not install myfixers artifact directory on install.
* Remove pylint.rc; we'll use pyflakes instead.
1.0.2 (2010-06-23)
==================
* Small documentation fix.
1.0.1 (2010-06-09)
==================
* Ditch the use of zc.buildout.
* Improved documentation.
1.0 (2010-04-24)
================
* Use Distribute instead of Setuptools.
* Port to Python 3 when used with 2to3.
* More documentation improvements.
0.6 (2010-04-21)
================
* Documentation and lint clean up.
0.5 (2010-04-20)
================
* Added a simplified initialization API for one-language-context
applications. This works much better for non-server applications.
* Added a SimpleStrategy which recognizes the $LOCPATH environment variable.
* Show how PEP 292 strings are supported automatically.
* When strategies are called with zero arguments, they supply the default
translation context, which is usually a NullTranslation. This is better
than hardcoding the NullTranslation in the Application.
0.4 (2010-03-04)
================
* Add the ability to get the current language code, via _.code
0.3 (2009-11-15)
================
* Initial release; refactored from Mailman 3.
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Internationalization
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Localization
flufl.i18n-1.1.1/COPYING-LESSER.txt 0000664 0001750 0001750 00000016725 11706106035 016540 0 ustar barry barry 0000000 0000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
flufl.i18n-1.1.1/PKG-INFO 0000664 0001750 0001750 00000011555 11744113073 014767 0 ustar barry barry 0000000 0000000 Metadata-Version: 1.1
Name: flufl.i18n
Version: 1.1.1
Summary: A high level API for Python internationalization.
Home-page: https://launchpad.net/flufl.i18n
Author: Barry Warsaw
Author-email: barry@python.org
License: LGPLv3
Download-URL: https://launchpad.net/flufl.i18n/+download
Description: ==========
flufl.i18n
==========
A high level API for Python internationalization.
The ``flufl.i18n`` library provides a convenient API for managing translation
contexts in Python applications. It provides facilities not only for
single-context applications like command line scripts, but also more
sophisticated management of multiple-context applications such as Internet
servers.
License
=======
This file is part of flufl.i18n.
flufl.i18n is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
flufl.i18n is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with flufl.i18n. If not, see .
=====================
NEWS for flufl.i18n
=====================
1.1.1 (2012-04-19)
==================
* Add classifiers to setup.py and make the long description more compatible
with the Cheeseshop.
* Other changes to make the Cheeseshop page look nicer. (LP: #680136)
* setup_helper.py version 2.1.
1.1 (2012-01-19)
================
* Support Python 3 without the need for 2to3.
1.0.4 (2010-12-06)
==================
* Restore missing line from MANIFEST.in to fix distribution tarball.
1.0.3 (2010-12-01)
==================
* Fix setup.py to not install myfixers artifact directory on install.
* Remove pylint.rc; we'll use pyflakes instead.
1.0.2 (2010-06-23)
==================
* Small documentation fix.
1.0.1 (2010-06-09)
==================
* Ditch the use of zc.buildout.
* Improved documentation.
1.0 (2010-04-24)
================
* Use Distribute instead of Setuptools.
* Port to Python 3 when used with 2to3.
* More documentation improvements.
0.6 (2010-04-21)
================
* Documentation and lint clean up.
0.5 (2010-04-20)
================
* Added a simplified initialization API for one-language-context
applications. This works much better for non-server applications.
* Added a SimpleStrategy which recognizes the $LOCPATH environment variable.
* Show how PEP 292 strings are supported automatically.
* When strategies are called with zero arguments, they supply the default
translation context, which is usually a NullTranslation. This is better
than hardcoding the NullTranslation in the Application.
0.4 (2010-03-04)
================
* Add the ability to get the current language code, via _.code
0.3 (2009-11-15)
================
* Initial release; refactored from Mailman 3.
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Internationalization
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Localization
flufl.i18n-1.1.1/template.py 0000664 0001750 0001750 00000001466 11706107063 016057 0 ustar barry barry 0000000 0000000 # Copyright (C) 2009-2012 by Barry A. Warsaw
#
# This file is part of flufl.i18n
#
# flufl.i18n is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# flufl.i18n is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with flufl.i18n. If not, see .
"""Module contents."""
from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
]
flufl.i18n-1.1.1/README.rst 0000664 0001750 0001750 00000001774 11744111344 015362 0 ustar barry barry 0000000 0000000 ==========
flufl.i18n
==========
A high level API for Python internationalization.
The ``flufl.i18n`` library provides a convenient API for managing translation
contexts in Python applications. It provides facilities not only for
single-context applications like command line scripts, but also more
sophisticated management of multiple-context applications such as Internet
servers.
License
=======
This file is part of flufl.i18n.
flufl.i18n is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 3 of the License.
flufl.i18n is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with flufl.i18n. If not, see .