mirror of
https://github.com/Mikaela/Limnoria.git
synced 2025-02-06 01:24:05 +01:00
Merge branch 'l10n-fr' into testing
This commit is contained in:
commit
27bb53b560
100
locale/fr.py
Normal file
100
locale/fr.py
Normal file
@ -0,0 +1,100 @@
|
||||
# -*- encoding: utf8 -*-
|
||||
###
|
||||
# Copyright (c) 2010, Valentin Lorentz
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author of this software nor the name of
|
||||
# contributors to this software may be used to endorse or promote products
|
||||
# derived from this software without specific prior written consent.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
###
|
||||
|
||||
"""
|
||||
Supybot utility functions localization in French.
|
||||
"""
|
||||
|
||||
def pluralize(s):
|
||||
"""Returns the plural of s.
|
||||
"""
|
||||
lowered = s.lower()
|
||||
if lowered.endswith('ou') and \
|
||||
lowered in ['bijou', 'caillou', 'chou', 'genou', 'hibou', 'joujou',
|
||||
'pou']:
|
||||
return s + 'x'
|
||||
elif lowered.endwith('al') and \
|
||||
lowered not in ['bal', 'carnaval', 'chacal', 'festival', 'récital',
|
||||
'régal', 'cal', 'étal', 'aval', 'caracal', 'val', 'choral',
|
||||
'corral', 'galgal', 'gayal']:
|
||||
return s[0:-2] + 'aux'
|
||||
elif lowered.endswith('ail') and \
|
||||
lowered not in ['bail', 'corail', 'émail', 'soupirail', 'travail',
|
||||
'ventail', 'vitrail', 'aspirail', 'fermail']:
|
||||
return s[0:-3] + 'aux'
|
||||
elif lowered.endswith('eau'):
|
||||
return s + 'x'
|
||||
elif lowered == 'pare-feu':
|
||||
return s
|
||||
elif lowered.endwith('eu') and \
|
||||
lowered not in ['bleu', 'pneu', 'émeu', 'enfeu']:
|
||||
# Note: when 'lieu' is a fish, it has a 's' ; else, it has a 'x'
|
||||
return s + 'x'
|
||||
else:
|
||||
return s + 's'
|
||||
|
||||
def depluralize(s):
|
||||
"""Returns the singular of s."""
|
||||
lowered = s.lower()
|
||||
if lowered.endswith('aux') and \
|
||||
lowered in ['baux', 'coraux', 'émaux', 'soupiraux', 'travaux',
|
||||
'ventaux', 'vitraux', 'aspiraux', 'fermaux']:
|
||||
return s[0:-3] + 'ail'
|
||||
elif lowered.endswith('aux'):
|
||||
return s[0:-3] + 'al'
|
||||
else:
|
||||
return s[0:-1]
|
||||
|
||||
def ordinal(i):
|
||||
"""Returns i + the ordinal indicator for the number.
|
||||
|
||||
Example: ordinal(3) => '3ème'
|
||||
"""
|
||||
i = int(i)
|
||||
if i == 1:
|
||||
return '1er'
|
||||
else:
|
||||
return '%sème' % i
|
||||
|
||||
def be(i):
|
||||
"""Returns the form of the verb 'être' based on the number i."""
|
||||
# Note: this function is used only for the third person
|
||||
if i == 1:
|
||||
return 'est'
|
||||
else:
|
||||
return 'sont'
|
||||
|
||||
def has(i):
|
||||
"""Returns the form of the verb 'avoir' based on the number i."""
|
||||
# Note: this function is used only for the third person
|
||||
if i == 1:
|
||||
return 'a'
|
||||
else:
|
||||
return 'ont'
|
@ -67,7 +67,7 @@ msgstr ""
|
||||
|
||||
#: plugin.py:95
|
||||
msgid "I have spawned %n; %n %b still currently active: %L."
|
||||
msgstr "J'ai lancé %n ; %n %b sont encore actuellement actifs : %L."
|
||||
msgstr "J'ai lancé %n ; %n %b encore actuellement en vie : %L."
|
||||
|
||||
#: plugin.py:103
|
||||
msgid ""
|
||||
@ -105,7 +105,7 @@ msgstr "Mes enfants ont pris %.2f secondes du temps utilisateur et %.2f secondes
|
||||
|
||||
#: plugin.py:138
|
||||
msgid "I have taken %.2f seconds of user time and %.2f seconds of system time, for a total of %.2f seconds of CPU time. %s"
|
||||
msgstr "J'ai pris %.2f secondes du temps utilisateur et %.2f secondes du temps système, pour un total de %.2f secondes de temps CPU."
|
||||
msgstr "J'ai pris %.2f secondes du temps utilisateur et %.2f secondes du temps système, pour un total de %.2f secondes de temps CPU. %s"
|
||||
|
||||
#: plugin.py:160
|
||||
msgid "Unable to run ps command."
|
||||
|
133
src/i18n.py
133
src/i18n.py
@ -31,7 +31,7 @@
|
||||
Supybot internationalisation and localisation managment.
|
||||
"""
|
||||
|
||||
__all__ = ['PluginInternationalization']
|
||||
__all__ = ['PluginInternationalization', 'internationalizeDocstring']
|
||||
|
||||
import re
|
||||
import sys
|
||||
@ -77,14 +77,29 @@ def getLocalePath(name, localeName, extension):
|
||||
|
||||
i18nClasses = {}
|
||||
internationalizedCommands = {}
|
||||
internationalizedFunctions = [] # No need to know there name
|
||||
|
||||
def reloadLocals():
|
||||
for pluginName in i18nClasses:
|
||||
i18nClasses[pluginName].loadLocale()
|
||||
for commandHash in internationalizedCommands:
|
||||
internationalizeDocstring(internationalizedCommands[commandHash])
|
||||
for function in internationalizedFunctions:
|
||||
function.loadLocale()
|
||||
|
||||
class PluginInternationalization:
|
||||
|
||||
i18nSupybot = None
|
||||
def PluginInternationalization(name='supybot'):
|
||||
# This is a proxy, that prevent Supybot against having more than one
|
||||
# internationalizer
|
||||
global i18nSupybot
|
||||
if name != 'supybot':
|
||||
return _PluginInternationalization(name)
|
||||
elif i18nSupybot == None:
|
||||
i18nSupybot = _PluginInternationalization('supybot')
|
||||
return i18nSupybot
|
||||
|
||||
class _PluginInternationalization:
|
||||
"""Internationalization managment for a plugin."""
|
||||
def __init__(self, name='supybot'):
|
||||
self.name = name
|
||||
@ -96,19 +111,28 @@ class PluginInternationalization:
|
||||
self.loadLocale()
|
||||
|
||||
def loadLocale(self, localeName=None):
|
||||
"""(Re)loads the locale used by this class."""
|
||||
if localeName is None and 'conf' in globals():
|
||||
localeName = conf.supybot.language()
|
||||
elif localeName is None:
|
||||
localeName = 'en'
|
||||
self.currentLocaleName = localeName
|
||||
|
||||
self._loadL10nCode()
|
||||
|
||||
try:
|
||||
translationFile = open(getLocalePath(self.name, localeName, 'po'),
|
||||
'ru') # ru is the mode, not the beginning
|
||||
# of 'russian' ;)
|
||||
self._parse(translationFile)
|
||||
except IOError: # The translation is unavailable
|
||||
self.translations = {}
|
||||
return
|
||||
|
||||
def _parse(self, translationFile):
|
||||
"""A .po files parser.
|
||||
|
||||
Give it a file object."""
|
||||
step = WAITING_FOR_MSGID
|
||||
self.translations = {}
|
||||
for line in translationFile:
|
||||
@ -154,61 +178,92 @@ class PluginInternationalization:
|
||||
self._translate(untranslated, translated)
|
||||
|
||||
def _translate(self, untranslated, translated):
|
||||
self.translations.update({self._parse(untranslated):
|
||||
self._parse(translated)})
|
||||
self.translations.update({self._unescape(untranslated):
|
||||
self._unescape(translated)})
|
||||
|
||||
def _parse(self, string):
|
||||
return str.replace(string, '\\n', '\n') # Replace \\n by \n
|
||||
def _unescape(self, string):
|
||||
return str.replace(string, '\\n', '\n')
|
||||
|
||||
def __call__(self, untranslated, *args):
|
||||
def __call__(self, untranslated):
|
||||
if not 'conf' in globals():
|
||||
if len(args) == 0:
|
||||
return untranslated
|
||||
else:
|
||||
translation = self(untranslated)
|
||||
return translation % args
|
||||
if self.currentLocaleName != conf.supybot.language():
|
||||
# If the locale has been changed
|
||||
reloadLocals()
|
||||
if len(args) == 0:
|
||||
try:
|
||||
return self.translations[untranslated]
|
||||
except KeyError:
|
||||
return untranslated
|
||||
else:
|
||||
try:
|
||||
return self.translations[untranslated] % args
|
||||
except KeyError:
|
||||
return untranslated % args
|
||||
|
||||
def _getL10nCode(self):
|
||||
def _loadL10nCode(self):
|
||||
if self.name != 'supybot':
|
||||
return
|
||||
try:
|
||||
execfile(self._getL10nCodePath())
|
||||
except IOError: # File doesn't exist
|
||||
pass
|
||||
|
||||
functions = locals()
|
||||
functions.pop('self')
|
||||
self._l10nFunctions = functions
|
||||
# Remove old functions and come back to the native language
|
||||
|
||||
def _getL10nCodePath(self):
|
||||
"""Returns the path to the code localization file.
|
||||
|
||||
It contains functions that needs to by fully (code + strings)
|
||||
localized"""
|
||||
if self.name != 'supybot':
|
||||
return
|
||||
return getLocalePath('supybot', self.currentLocaleName, 'py')
|
||||
|
||||
def getPluralizers(self, pluralize, depluralize):
|
||||
# This should be used only by src/utils/str.py
|
||||
try:
|
||||
execfile(self._getL10nCode())
|
||||
except IOError:
|
||||
pass
|
||||
return (pluralize, depluralize)
|
||||
def localizeFunction(self, name):
|
||||
"""Returns the localized version of the function.
|
||||
|
||||
def getOrdinal(self, ordinal):
|
||||
# This should be used only by src/utils/str.py
|
||||
try:
|
||||
execfile(self._getL10nCode())
|
||||
except IOError:
|
||||
pass
|
||||
return ordinal
|
||||
Should be used only by the internationalizedFunction class"""
|
||||
if self.name != 'supybot':
|
||||
return
|
||||
if hasattr(self, '_l10nFunctions') and \
|
||||
self._l10nFunctions.has_key(name):
|
||||
return self._l10nFunctions[name]
|
||||
|
||||
def getBeAndHas(self, be, has):
|
||||
# This should be used only by src/utils/str.py
|
||||
try:
|
||||
execfile(self._getL10nCode())
|
||||
except IOError:
|
||||
pass
|
||||
return (be, has)
|
||||
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 locale/LOCALE.py"""
|
||||
def __init__(self, internationalizer, name, function):
|
||||
self._internationalizer = internationalizer
|
||||
self._name = name
|
||||
self.__call__ = function
|
||||
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 internationalizeDocstring(obj):
|
||||
"""Decorates functions and internationalize their docstring.
|
||||
|
||||
Only useful for commands (commands' docstring is displayed on IRC)"""
|
||||
if sys.modules[obj.__module__].__dict__.has_key('_'):
|
||||
internationalizedCommands.update({hash(obj): obj})
|
||||
obj.__doc__=sys.modules[obj.__module__]._.__call__(obj.__doc__)
|
||||
|
@ -43,7 +43,7 @@ from iter import all, any
|
||||
from structures import TwoWayDictionary
|
||||
|
||||
from supybot.i18n import PluginInternationalization
|
||||
_ = PluginInternationalization()
|
||||
internationalizeFunction=PluginInternationalization().internationalizeFunction
|
||||
|
||||
curry = new.instancemethod
|
||||
chars = string.maketrans('', '')
|
||||
@ -256,6 +256,7 @@ def matchCase(s1, s2):
|
||||
L[i] = L[i].upper()
|
||||
return ''.join(L)
|
||||
|
||||
@internationalizeFunction('pluralize')
|
||||
def pluralize(s):
|
||||
"""Returns the plural of s. Put any exceptions to the general English
|
||||
rule of appending 's' in the plurals dictionary.
|
||||
@ -278,6 +279,7 @@ def pluralize(s):
|
||||
else:
|
||||
return matchCase(s, s+'s')
|
||||
|
||||
@internationalizeFunction('depluralize')
|
||||
def depluralize(s):
|
||||
"""Returns the singular of s."""
|
||||
_depluralizeRegex = re.compile('[%s]ies' % consonants)
|
||||
@ -294,8 +296,6 @@ def depluralize(s):
|
||||
else:
|
||||
return s # Don't know what to do.
|
||||
|
||||
pluralize, depluralize = _.getPluralizers(pluralize, depluralize)
|
||||
|
||||
def nItems(n, item, between=None):
|
||||
"""Works like this:
|
||||
|
||||
@ -332,6 +332,7 @@ def nItems(n, item, between=None):
|
||||
else:
|
||||
return format('%s %s %s', n, between, item)
|
||||
|
||||
@internationalizeFunction('ordinal')
|
||||
def ordinal(i):
|
||||
"""Returns i + the ordinal indicator for the number.
|
||||
|
||||
@ -350,8 +351,7 @@ def ordinal(i):
|
||||
ord = 'rd'
|
||||
return '%s%s' % (i, ord)
|
||||
|
||||
ordinal = _.getOrdinal(ordinal)
|
||||
|
||||
@internationalizeFunction('be')
|
||||
def be(i):
|
||||
"""Returns the form of the verb 'to be' based on the number i."""
|
||||
if i == 1:
|
||||
@ -359,6 +359,7 @@ def be(i):
|
||||
else:
|
||||
return 'are'
|
||||
|
||||
@internationalizeFunction('has')
|
||||
def has(i):
|
||||
"""Returns the form of the verb 'to have' based on the number i."""
|
||||
if i == 1:
|
||||
@ -366,8 +367,6 @@ def has(i):
|
||||
else:
|
||||
return 'have'
|
||||
|
||||
be, has = _.getBeAndHas(be, has)
|
||||
|
||||
def toBool(s):
|
||||
s = s.strip().lower()
|
||||
if s in ('true', 'on', 'enable', 'enabled', '1'):
|
||||
|
Loading…
Reference in New Issue
Block a user