diff --git a/src/__init__.py b/src/__init__.py index 1586a2b52..0b4e922e4 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -33,10 +33,7 @@ from . import dynamicScope from . import i18n -builtins = (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__) -builtins['supybotInternationalization'] = i18n.PluginInternationalization() from . import utils -del builtins['supybotInternationalization'] (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['format'] = utils.str.format diff --git a/src/i18n.py b/src/i18n.py index 31488609f..81a8a3a0e 100644 --- a/src/i18n.py +++ b/src/i18n.py @@ -113,7 +113,6 @@ def getLocalePath(name, localeName, extension): i18nClasses = weakref.WeakValueDictionary() internationalizedCommands = weakref.WeakValueDictionary() -internationalizedFunctions = [] # No need to know their name def reloadLocalesIfRequired(): global currentLocale @@ -124,12 +123,13 @@ def reloadLocalesIfRequired(): reloadLocales() def reloadLocales(): + import supybot.utils as utils + for pluginClass in i18nClasses.values(): pluginClass.loadLocale() for command in list(internationalizedCommands.values()): internationalizeDocstring(command) - for function in internationalizedFunctions: - function.loadLocale() + utils.str._relocalizeFunctions(PluginInternationalization()) def normalize(string, removeNewline=False): import supybot.utils as utils @@ -201,7 +201,6 @@ def parse(translationFile): return translations -i18nSupybot = None def PluginInternationalization(name='supybot'): # This is a proxy that prevents having several objects for the same plugin if name in i18nClasses: @@ -326,40 +325,6 @@ class _PluginInternationalization: name in self._l10nFunctions: return self._l10nFunctions[name] - def internationalizeFunction(self, name): - """Decorates functions and internationalize their code. - - Only useful for Supybot core functions""" - if self.name != 'supybot': - return - class FunctionInternationalizer: - def __init__(self, parent, name): - self._parent = parent - self._name = name - def __call__(self, obj): - obj = InternationalizedFunction(self._parent, self._name, obj) - obj.loadLocale() - return obj - return FunctionInternationalizer(self, name) - -class InternationalizedFunction: - """Proxy for functions that need to be fully localized. - - The localization code is in locales/LOCALE.py""" - def __init__(self, internationalizer, name, function): - self._internationalizer = internationalizer - self._name = name - self._origin = function - internationalizedFunctions.append(self) - def loadLocale(self): - self.__call__ = self._internationalizer.localizeFunction(self._name) - if self.__call__ == None: - self.restore() - def restore(self): - self.__call__ = self._origin - - def __call__(self, *args, **kwargs): - return self._origin(*args, **kwargs) try: class InternationalizedString(str): @@ -375,6 +340,7 @@ except TypeError: know if a string is already localized""" pass + def internationalizeDocstring(obj): """Decorates functions and internationalize their docstring. @@ -391,3 +357,13 @@ def internationalizeDocstring(obj): # attribute '__doc__' of 'type' objects is not writable pass return obj + + +def _install(): + from . import utils + _ = PluginInternationalization() + utils.gen._ = _ + utils.str._ = _ + utils.str._relocalizeFunctions(_) + +_install() diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 4b43ea523..eb82cb563 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -50,6 +50,7 @@ csv.split = split builtins = (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__) + # We use this often enough that we're going to stick it in builtins. def force(x): if callable(x): @@ -58,8 +59,6 @@ def force(x): return x builtins['force'] = force -internationalization = builtins.get('supybotInternationalization', None) - # These imports need to happen below the block above, so things get put into # __builtins__ appropriately. from .gen import * diff --git a/src/utils/gen.py b/src/utils/gen.py index f732d28f9..4e1e1b253 100644 --- a/src/utils/gen.py +++ b/src/utils/gen.py @@ -45,7 +45,9 @@ from . import crypt from .str import format from .file import mktemp from . import minisix -from . import internationalization as _ + +# will be replaced by supybot.i18n.install() +_ = lambda x: x def warn_non_constant_time(f): @functools.wraps(f) diff --git a/src/utils/str.py b/src/utils/str.py index f6a43e58e..c9a86c21e 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -38,20 +38,46 @@ import sys import time import string import textwrap +import functools from . import minisix from .iter import any from .structures import TwoWayDictionary -from . import internationalization as _ -internationalizeFunction = _.internationalizeFunction - try: from charade.universaldetector import UniversalDetector charadeLoaded = True except ImportError: charadeLoaded = False +# will be replaced by supybot.i18n.install() +_ = lambda x: x + +# used by supybot.i18n.reloadLocales() to (re)load the localized function of +# these functions +_localizedFunctions = {} +_defaultFunctions = {} + + +def internationalizeFunction(f): + f_name = f.__name__ + _localizedFunctions[f_name] = f + _defaultFunctions[f_name] = f + + @functools.wraps(f) + def newf(*args, **kwargs): + f = _localizedFunctions[f_name] + assert f is not None, "_localizedFunctions[%s] is None" % f_name + return f(*args, **kwargs) + + return newf + + +def _relocalizeFunctions(localizer): + for f_name in list(_localizedFunctions): + f = localizer.localizeFunction(f_name) or _defaultFunctions[f_name] + _localizedFunctions[f_name] = f + if minisix.PY3: def decode_raw_line(line): #first, try to decode using utf-8 @@ -390,7 +416,7 @@ def matchCase(s1, s2): L[i] = L[i].upper() return ''.join(L) -@internationalizeFunction('pluralize') +@internationalizeFunction def pluralize(s): """Returns the plural of s. Put any exceptions to the general English rule of appending 's' in the plurals dictionary. @@ -413,7 +439,7 @@ def pluralize(s): else: return matchCase(s, s+'s') -@internationalizeFunction('depluralize') +@internationalizeFunction def depluralize(s): """Returns the singular of s.""" consonants = 'bcdfghjklmnpqrstvwxz' @@ -467,7 +493,7 @@ def nItems(n, item, between=None): else: return format('%s %s %s', n, between, item) -@internationalizeFunction('ordinal') +@internationalizeFunction def ordinal(i): """Returns i + the ordinal indicator for the number. @@ -486,7 +512,7 @@ def ordinal(i): ord = 'rd' return '%s%s' % (i, ord) -@internationalizeFunction('be') +@internationalizeFunction def be(i): """Returns the form of the verb 'to be' based on the number i.""" if i == 1: @@ -494,7 +520,7 @@ def be(i): else: return 'are' -@internationalizeFunction('has') +@internationalizeFunction def has(i): """Returns the form of the verb 'to have' based on the number i.""" if i == 1: