"""Translation/Localization functions.

Provides ``gettext`` translation functions via an app's ``pylons.translator``
and get/set_lang for changing the language translated to.
"""
import os
from gettext import NullTranslations, translation

import pylons

__all__ = ['_', 'add_fallback', 'get_lang', 'gettext', 'gettext_noop',
           'lazy_gettext', 'lazy_ngettext', 'lazy_ugettext', 'lazy_ungettext',
           'ngettext', 'set_lang', 'ugettext', 'ungettext', 'LanguageError',
           'N_']

class LanguageError(Exception):
    """Exception raised when a problem occurs with changing languages"""
    pass


class LazyString(object):
    """Has a number of lazily evaluated functions replicating a string. Just
    override the eval() method to produce the actual value.

    This method copied from TurboGears.
    """

    def __init__(self, func, *args, **kwargs):
        self.func = func
        self.args = args
        self.kwargs = kwargs

    def eval(self):
        return self.func(*self.args, **self.kwargs)

    def __unicode__(self):
        return unicode(self.eval())

    def __str__(self):
        return str(self.eval())

    def __mod__(self, other):
        return self.eval() % other


def lazify(func):
    """Decorator to return a lazy-evaluated version of the original"""
    def newfunc(*args, **kwargs):
        return LazyString(func, *args, **kwargs)
    try:
        newfunc.__name__ = 'lazy_%s' % func.__name__
    except TypeError: # Python < 2.4
        pass
    newfunc.__doc__ = 'Lazy-evaluated version of the %s function\n\n%s' % \
        (func.__name__, func.__doc__)
    return newfunc


def gettext_noop(value):
    """Mark a string for translation without translating it. Returns value.

    Used for global strings, e.g.:

    .. code-block:: Python

        foo = N_('Hello')

        class Bar:
            def __init__(self):
                self.local_foo = _(foo)

        h.set_lang('fr')
        assert Bar().local_foo == 'Bonjour'
        h.set_lang('es')
        assert Bar().local_foo == 'Hola'
        assert foo == 'Hello'
    """
    return value
N_ = gettext_noop


def gettext(value):
    """Mark a string for translation. Returns the localized string of value.

    Mark a string to be localized as follows:

    .. code-block:: Python

        gettext('This should be in lots of languages')
    """
    return pylons.translator.gettext(value)
lazy_gettext = lazify(gettext)


def ugettext(value):
    """Mark a string for translation. Returns the localized unicode string of
    value.

    Mark a string to be localized as follows:

    .. code-block:: Python

        _('This should be in lots of languages')
    """
    return pylons.translator.ugettext(value)
_ = ugettext
lazy_ugettext = lazify(ugettext)


def ngettext(singular, plural, n):
    """Mark a string for translation. Returns the localized string of the
    pluralized value.

    This does a plural-forms lookup of a message id. ``singular`` is used as
    the message id for purposes of lookup in the catalog, while ``n`` is used
    to determine which plural form to use. The returned message is a string.

    Mark a string to be localized as follows:

    .. code-block:: Python

        ngettext('There is %(num)d file here', 'There are %(num)d files here',
                 n) % {'num': n}
    """
    return pylons.translator.ngettext(singular, plural, n)
lazy_ngettext = lazify(ngettext)


def ungettext(singular, plural, n):
    """Mark a string for translation. Returns the localized unicode string of
    the pluralized value.

    This does a plural-forms lookup of a message id. ``singular`` is used as
    the message id for purposes of lookup in the catalog, while ``n`` is used
    to determine which plural form to use. The returned message is a Unicode
    string.

    Mark a string to be localized as follows:

    .. code-block:: Python

        ungettext('There is %(num)d file here', 'There are %(num)d files here',
                  n) % {'num': n}
    """
    return pylons.translator.ungettext(singular, plural, n)
lazy_ungettext = lazify(ungettext)


def _get_translator(lang, **kwargs):
    """Utility method to get a valid translator object from a language name"""
    conf = pylons.config.current_conf()
    # XXX: root_path is deprecated
    rootdir = conf['pylons.paths'].get('root',
                                       conf['pylons.paths'].get('root_path'))
    localedir = os.path.join(rootdir, 'i18n')
    if not isinstance(lang, list):
        lang = [lang]
    try:
        translator = translation(conf['pylons.package'], localedir,
                                 languages=lang, **kwargs)
    except IOError, ioe:
        raise LanguageError('IOError: %s' % ioe)
    translator.pylons_lang = lang
    return translator


def set_lang(lang, **kwargs):
    """Set the i18n language used"""
    registry = pylons.request.environ['paste.registry']
    if not lang:
        registry.replace(pylons.translator, NullTranslations())
    else:
        translator = _get_translator(lang, **kwargs)
        registry.replace(pylons.translator, translator)


def get_lang():
    """Return the current i18n language used"""
    return getattr(pylons.translator, 'pylons_lang', None)


def add_fallback(lang, **kwargs):
    """Add a fallback language from which words not matched in other languages
    will be translated to.
    """
    return pylons.translator.add_fallback(_get_translator(lang, **kwargs))
