mirror of
https://github.com/progval/Limnoria.git
synced 2025-04-27 05:21:09 -05:00
utils: Rewrite i18n initialization
The previous implementation was messy and needlessly complicated This simplifies the logic and removes hackiness by making utils/str.py handle internationalization logic itself, instead of bending over backwards to load logic from the parent package at import time.
This commit is contained in:
parent
4a620bf7f0
commit
93370b6f0e
@ -33,10 +33,7 @@ from . import dynamicScope
|
|||||||
|
|
||||||
from . import i18n
|
from . import i18n
|
||||||
|
|
||||||
builtins = (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)
|
|
||||||
builtins['supybotInternationalization'] = i18n.PluginInternationalization()
|
|
||||||
from . import utils
|
from . import utils
|
||||||
del builtins['supybotInternationalization']
|
|
||||||
|
|
||||||
(__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['format'] = utils.str.format
|
(__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)['format'] = utils.str.format
|
||||||
|
|
||||||
|
52
src/i18n.py
52
src/i18n.py
@ -113,7 +113,6 @@ def getLocalePath(name, localeName, extension):
|
|||||||
|
|
||||||
i18nClasses = weakref.WeakValueDictionary()
|
i18nClasses = weakref.WeakValueDictionary()
|
||||||
internationalizedCommands = weakref.WeakValueDictionary()
|
internationalizedCommands = weakref.WeakValueDictionary()
|
||||||
internationalizedFunctions = [] # No need to know their name
|
|
||||||
|
|
||||||
def reloadLocalesIfRequired():
|
def reloadLocalesIfRequired():
|
||||||
global currentLocale
|
global currentLocale
|
||||||
@ -124,12 +123,13 @@ def reloadLocalesIfRequired():
|
|||||||
reloadLocales()
|
reloadLocales()
|
||||||
|
|
||||||
def reloadLocales():
|
def reloadLocales():
|
||||||
|
import supybot.utils as utils
|
||||||
|
|
||||||
for pluginClass in i18nClasses.values():
|
for pluginClass in i18nClasses.values():
|
||||||
pluginClass.loadLocale()
|
pluginClass.loadLocale()
|
||||||
for command in list(internationalizedCommands.values()):
|
for command in list(internationalizedCommands.values()):
|
||||||
internationalizeDocstring(command)
|
internationalizeDocstring(command)
|
||||||
for function in internationalizedFunctions:
|
utils.str._relocalizeFunctions(PluginInternationalization())
|
||||||
function.loadLocale()
|
|
||||||
|
|
||||||
def normalize(string, removeNewline=False):
|
def normalize(string, removeNewline=False):
|
||||||
import supybot.utils as utils
|
import supybot.utils as utils
|
||||||
@ -201,7 +201,6 @@ def parse(translationFile):
|
|||||||
return translations
|
return translations
|
||||||
|
|
||||||
|
|
||||||
i18nSupybot = None
|
|
||||||
def PluginInternationalization(name='supybot'):
|
def PluginInternationalization(name='supybot'):
|
||||||
# This is a proxy that prevents having several objects for the same plugin
|
# This is a proxy that prevents having several objects for the same plugin
|
||||||
if name in i18nClasses:
|
if name in i18nClasses:
|
||||||
@ -326,40 +325,6 @@ class _PluginInternationalization:
|
|||||||
name in self._l10nFunctions:
|
name in self._l10nFunctions:
|
||||||
return self._l10nFunctions[name]
|
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:
|
try:
|
||||||
class InternationalizedString(str):
|
class InternationalizedString(str):
|
||||||
@ -375,6 +340,7 @@ except TypeError:
|
|||||||
know if a string is already localized"""
|
know if a string is already localized"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def internationalizeDocstring(obj):
|
def internationalizeDocstring(obj):
|
||||||
"""Decorates functions and internationalize their docstring.
|
"""Decorates functions and internationalize their docstring.
|
||||||
|
|
||||||
@ -391,3 +357,13 @@ def internationalizeDocstring(obj):
|
|||||||
# attribute '__doc__' of 'type' objects is not writable
|
# attribute '__doc__' of 'type' objects is not writable
|
||||||
pass
|
pass
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def _install():
|
||||||
|
from . import utils
|
||||||
|
_ = PluginInternationalization()
|
||||||
|
utils.gen._ = _
|
||||||
|
utils.str._ = _
|
||||||
|
utils.str._relocalizeFunctions(_)
|
||||||
|
|
||||||
|
_install()
|
||||||
|
@ -50,6 +50,7 @@ csv.split = split
|
|||||||
|
|
||||||
builtins = (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)
|
builtins = (__builtins__ if isinstance(__builtins__, dict) else __builtins__.__dict__)
|
||||||
|
|
||||||
|
|
||||||
# We use this often enough that we're going to stick it in builtins.
|
# We use this often enough that we're going to stick it in builtins.
|
||||||
def force(x):
|
def force(x):
|
||||||
if callable(x):
|
if callable(x):
|
||||||
@ -58,8 +59,6 @@ def force(x):
|
|||||||
return x
|
return x
|
||||||
builtins['force'] = force
|
builtins['force'] = force
|
||||||
|
|
||||||
internationalization = builtins.get('supybotInternationalization', None)
|
|
||||||
|
|
||||||
# These imports need to happen below the block above, so things get put into
|
# These imports need to happen below the block above, so things get put into
|
||||||
# __builtins__ appropriately.
|
# __builtins__ appropriately.
|
||||||
from .gen import *
|
from .gen import *
|
||||||
|
@ -45,7 +45,9 @@ from . import crypt
|
|||||||
from .str import format
|
from .str import format
|
||||||
from .file import mktemp
|
from .file import mktemp
|
||||||
from . import minisix
|
from . import minisix
|
||||||
from . import internationalization as _
|
|
||||||
|
# will be replaced by supybot.i18n.install()
|
||||||
|
_ = lambda x: x
|
||||||
|
|
||||||
def warn_non_constant_time(f):
|
def warn_non_constant_time(f):
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
|
@ -38,20 +38,46 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import string
|
import string
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import functools
|
||||||
|
|
||||||
from . import minisix
|
from . import minisix
|
||||||
from .iter import any
|
from .iter import any
|
||||||
from .structures import TwoWayDictionary
|
from .structures import TwoWayDictionary
|
||||||
|
|
||||||
from . import internationalization as _
|
|
||||||
internationalizeFunction = _.internationalizeFunction
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from charade.universaldetector import UniversalDetector
|
from charade.universaldetector import UniversalDetector
|
||||||
charadeLoaded = True
|
charadeLoaded = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
charadeLoaded = False
|
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:
|
if minisix.PY3:
|
||||||
def decode_raw_line(line):
|
def decode_raw_line(line):
|
||||||
#first, try to decode using utf-8
|
#first, try to decode using utf-8
|
||||||
@ -390,7 +416,7 @@ def matchCase(s1, s2):
|
|||||||
L[i] = L[i].upper()
|
L[i] = L[i].upper()
|
||||||
return ''.join(L)
|
return ''.join(L)
|
||||||
|
|
||||||
@internationalizeFunction('pluralize')
|
@internationalizeFunction
|
||||||
def pluralize(s):
|
def pluralize(s):
|
||||||
"""Returns the plural of s. Put any exceptions to the general English
|
"""Returns the plural of s. Put any exceptions to the general English
|
||||||
rule of appending 's' in the plurals dictionary.
|
rule of appending 's' in the plurals dictionary.
|
||||||
@ -413,7 +439,7 @@ def pluralize(s):
|
|||||||
else:
|
else:
|
||||||
return matchCase(s, s+'s')
|
return matchCase(s, s+'s')
|
||||||
|
|
||||||
@internationalizeFunction('depluralize')
|
@internationalizeFunction
|
||||||
def depluralize(s):
|
def depluralize(s):
|
||||||
"""Returns the singular of s."""
|
"""Returns the singular of s."""
|
||||||
consonants = 'bcdfghjklmnpqrstvwxz'
|
consonants = 'bcdfghjklmnpqrstvwxz'
|
||||||
@ -467,7 +493,7 @@ def nItems(n, item, between=None):
|
|||||||
else:
|
else:
|
||||||
return format('%s %s %s', n, between, item)
|
return format('%s %s %s', n, between, item)
|
||||||
|
|
||||||
@internationalizeFunction('ordinal')
|
@internationalizeFunction
|
||||||
def ordinal(i):
|
def ordinal(i):
|
||||||
"""Returns i + the ordinal indicator for the number.
|
"""Returns i + the ordinal indicator for the number.
|
||||||
|
|
||||||
@ -486,7 +512,7 @@ def ordinal(i):
|
|||||||
ord = 'rd'
|
ord = 'rd'
|
||||||
return '%s%s' % (i, ord)
|
return '%s%s' % (i, ord)
|
||||||
|
|
||||||
@internationalizeFunction('be')
|
@internationalizeFunction
|
||||||
def be(i):
|
def be(i):
|
||||||
"""Returns the form of the verb 'to be' based on the number i."""
|
"""Returns the form of the verb 'to be' based on the number i."""
|
||||||
if i == 1:
|
if i == 1:
|
||||||
@ -494,7 +520,7 @@ def be(i):
|
|||||||
else:
|
else:
|
||||||
return 'are'
|
return 'are'
|
||||||
|
|
||||||
@internationalizeFunction('has')
|
@internationalizeFunction
|
||||||
def has(i):
|
def has(i):
|
||||||
"""Returns the form of the verb 'to have' based on the number i."""
|
"""Returns the form of the verb 'to have' based on the number i."""
|
||||||
if i == 1:
|
if i == 1:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user