From 50acd3d8d9c1434974a4c79a1b10d7b98c87b007 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sat, 9 Oct 2010 11:36:22 +0200 Subject: [PATCH] Creating the internationalization module and internationalize/localize ChannelStats into French --- plugins/ChannelStats/ChannelStats.pot | 155 ++++++++++++++++++++++++ plugins/ChannelStats/locale/fr.po | 167 ++++++++++++++++++++++++++ plugins/ChannelStats/plugin.py | 117 +++++++++--------- setup.py | 8 ++ src/i18n.py | 129 ++++++++++++++++++++ src/utils/str.py | 8 +- 6 files changed, 526 insertions(+), 58 deletions(-) create mode 100644 plugins/ChannelStats/ChannelStats.pot create mode 100644 plugins/ChannelStats/locale/fr.po create mode 100644 src/i18n.py diff --git a/plugins/ChannelStats/ChannelStats.pot b/plugins/ChannelStats/ChannelStats.pot new file mode 100644 index 000000000..d5f4c7797 --- /dev/null +++ b/plugins/ChannelStats/ChannelStats.pot @@ -0,0 +1,155 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-10-06 16:16+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: plugin.py:252 +msgid "I couldn't find you in my user database." +msgstr "" + +#: plugin.py:265 +msgid "" +"%s has sent %n; a total of %n, %n, %n, and %n; %s of those messages %s. %s " +"has joined %n, parted %n, quit %n, kicked someone %n, been kicked %n, " +"changed the topic %n, and changed the mode %n." +msgstr "" + +#: plugin.py:272 +msgid "character" +msgstr "" + +#: plugin.py:273 plugin.py:355 +msgid "word" +msgstr "" + +#: plugin.py:274 plugin.py:356 +msgid "smiley" +msgstr "" + +#: plugin.py:275 plugin.py:357 +msgid "frown" +msgstr "" + +#: plugin.py:277 plugin.py:358 +msgid "was an ACTION" +msgstr "" + +#: plugin.py:278 plugin.py:359 +msgid "were ACTIONs" +msgstr "" + +#: plugin.py:280 plugin.py:281 plugin.py:282 plugin.py:283 plugin.py:284 +#: plugin.py:285 plugin.py:286 +msgid "time" +msgstr "" + +#: plugin.py:289 +#, python-format +msgid "I have no stats for that %s in %s." +msgstr "" + +#: plugin.py:291 +msgid "" +"[] []\n" +"\n" +" Returns the statistics for on . is only\n" +" necessary if the message isn't sent on the channel itself. If " +"\n" +" isn't given, it defaults to the user sending the command.\n" +" " +msgstr "" + +#: plugin.py:305 +msgid "" +"There's really no reason why you should have underscores or brackets in your " +"mathematical expression. Please remove them." +msgstr "" + +#: plugin.py:309 +msgid "You can't use lambda in this command." +msgstr "" + +#: plugin.py:323 +msgid "stat variable" +msgstr "" + +#: plugin.py:335 +msgid "" +"[] \n" +"\n" +" Returns the ranking of users according to the given stat " +"expression.\n" +" Valid variables in the stat expression include 'msgs', 'chars',\n" +" 'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits',\n" +" 'kicks', 'kicked', 'topics', and 'modes'. Any simple mathematical\n" +" expression involving those variables is permitted.\n" +" " +msgstr "" + +#: plugin.py:349 +msgid "" +"On %s there %h been %i messages, containing %i characters, %n, %n, and %n; " +"%i of those messages %s. There have been %n, %n, %n, %n, %n, and %n. There " +"%b currently %n and the channel has peaked at %n." +msgstr "" + +#: plugin.py:360 +msgid "join" +msgstr "" + +#: plugin.py:361 +msgid "part" +msgstr "" + +#: plugin.py:362 +msgid "quit" +msgstr "" + +#: plugin.py:363 +msgid "kick" +msgstr "" + +#: plugin.py:364 +msgid "mode" +msgstr "" + +#: plugin.py:364 plugin.py:365 +msgid "change" +msgstr "" + +#: plugin.py:365 +msgid "topic" +msgstr "" + +#: plugin.py:367 plugin.py:368 +msgid "user" +msgstr "" + +#: plugin.py:371 +#, python-format +msgid "I've never been on %s." +msgstr "" + +#: plugin.py:372 +msgid "" +"[]\n" +"\n" +" Returns the statistics for . is only necessary " +"if\n" +" the message isn't sent on the channel itself.\n" +" " +msgstr "" diff --git a/plugins/ChannelStats/locale/fr.po b/plugins/ChannelStats/locale/fr.po new file mode 100644 index 000000000..d461f5f24 --- /dev/null +++ b/plugins/ChannelStats/locale/fr.po @@ -0,0 +1,167 @@ +# French translations for supybot-i package +# Traductions françaises du paquet supybot-i. +# Copyright (C) 2010 THE supybot-i'S COPYRIGHT HOLDER +# This file is distributed under the same license as the supybot-i package. +# ProgVal , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: supybot-i 18n\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-10-06 16:16+0200\n" +"PO-Revision-Date: 2010-10-06 16:46+0100\n" +"Last-Translator: Valentin 'ProgVal' Lorentz \n" +"Language-Team: French\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: plugin.py:252 +msgid "I couldn't find you in my user database." +msgstr "Je ne peux vous trouver dans ma base de données." + +#: plugin.py:265 +msgid "%s has sent %n; a total of %n, %n, %n, and %n; %s of those messages %s. %s has joined %n, parted %n, quit %n, kicked someone %n, been kicked %n, changed the topic %n, and changed the mode %n." +msgstr "%s a envoyé %n ; un total de %n, %n, %n, et %n ; %s de ces messages %s. %s est arrivé %nfois, est parti %nfois, a quitté %nfois, a kické %nfois, a été kické %nfois, a changé le topic %nfois, et a changé les modes %nfois." + +#: plugin.py:272 +msgid "character" +msgstr "caractère" + +#: plugin.py:273 +#: plugin.py:355 +msgid "word" +msgstr "mot" + +#: plugin.py:274 +#: plugin.py:356 +msgid "smiley" +msgstr "smiley" + +#: plugin.py:275 +#: plugin.py:357 +msgid "frown" +msgstr "sadley" + +#: plugin.py:277 +#: plugin.py:358 +msgid "was an ACTION" +msgstr "était une action" + +#: plugin.py:278 +#: plugin.py:359 +msgid "were ACTIONs" +msgstr "étaient des ACTIONs" + +#: plugin.py:280 +#: plugin.py:281 +#: plugin.py:282 +#: plugin.py:283 +#: plugin.py:284 +#: plugin.py:285 +#: plugin.py:286 +msgid "time" +msgstr " fois" + +#: plugin.py:289 +#, python-format +msgid "I have no stats for that %s in %s." +msgstr "Je n'ai pas de statistiques pour %s sur %s." + +#: plugin.py:291 +msgid "" +"[] []\n" +"\n" +" Returns the statistics for on . is only\n" +" necessary if the message isn't sent on the channel itself. If \n" +" isn't given, it defaults to the user sending the command.\n" +" " +msgstr "" +"[canal>] [nom]\n" +"\n" +" Retourne les statistiques pour sur le . n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même. Si n'est pas donné, il correspond par défaut à l'utilisateur envoyant la commande" + +#: plugin.py:305 +msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them." +msgstr "Il n'y a aucune raison pour que vous ayez des underscores ou des crochets dans vos expressions mathématiques. Veuillez les supprimer." + +#: plugin.py:309 +msgid "You can't use lambda in this command." +msgstr "Vous ne pouvez utiliser lambda dans cette commande." + +#: plugin.py:323 +msgid "stat variable" +msgstr "Variable statistique" + +#: plugin.py:335 +msgid "" +"[] \n" +"\n" +" Returns the ranking of users according to the given stat expression.\n" +" Valid variables in the stat expression include 'msgs', 'chars',\n" +" 'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits',\n" +" 'kicks', 'kicked', 'topics', and 'modes'. Any simple mathematical\n" +" expression involving those variables is permitted.\n" +" " +msgstr "" +"[] \n" +"\n" +"Retourne le rang des utilisateurs en fonction de l'expression de statistiques données. Les variables valides dans l'expression de statistiques sont : 'msgs', 'chars', 'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits', 'kicks', 'kicked', 'topics', et 'modes'. Toute expression mathématiques simple utilisant ces variables est autorisée." + +#: plugin.py:349 +msgid "On %s there %h been %i messages, containing %i characters, %n, %n, and %n; %i of those messages %s. There have been %n, %n, %n, %n, %n, and %n. There %b currently %n and the channel has peaked at %n." +msgstr "Sur %s il y a eu%v %i messages, contenant %i caractères, %n, %n, et %n ; %i de ces messages %s. Il y a eu %n, %n, %n, %n, %n, et %n. Il y a%v actuellement %n et le record du canal est de %n." + +#: plugin.py:360 +msgid "join" +msgstr "arrivée" + +#: plugin.py:361 +msgid "part" +msgstr "départ" + +#: plugin.py:362 +msgid "quit" +msgstr "quit" + +#: plugin.py:363 +msgid "kick" +msgstr "kick" + +#: plugin.py:364 +msgid "mode" +msgstr "mode" + +#: plugin.py:364 +#: plugin.py:365 +msgid "change" +msgstr "changement" + +#: plugin.py:365 +msgid "topic" +msgstr "topic" + +#: plugin.py:367 +#: plugin.py:368 +msgid "user" +msgstr "utilisateur" + +#: plugin.py:371 +#, python-format +msgid "I've never been on %s." +msgstr "Je n'ai jamais été sur %s." + +#: plugin.py:372 +msgid "" +"[]\n" +"\n" +" Returns the statistics for . is only necessary if\n" +" the message isn't sent on the channel itself.\n" +" " +msgstr "" +"[canal]\n" +"\n" +"Retourne les statistiques pour le . n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même." + diff --git a/plugins/ChannelStats/plugin.py b/plugins/ChannelStats/plugin.py index d09129bed..9a91b3056 100644 --- a/plugins/ChannelStats/plugin.py +++ b/plugins/ChannelStats/plugin.py @@ -34,6 +34,7 @@ import types import supybot.log as log import supybot.conf as conf +import supybot.i18n as i18n import supybot.utils as utils import supybot.world as world import supybot.ircdb as ircdb @@ -44,6 +45,8 @@ import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks +_ = i18n.PluginInternationalization('ChannelStats') + class ChannelStat(irclib.IrcCommandDispatcher): _values = ['actions', 'chars', 'frowns', 'joins', 'kicks','modes', 'msgs', 'parts', 'quits', 'smileys', 'topics', 'words', 'users'] @@ -239,12 +242,6 @@ class ChannelStats(callbacks.Plugin): self.db.channels[channel][id].kicked += 1 def stats(self, irc, msg, args, channel, name): - """[] [] - - Returns the statistics for on . is only - necessary if the message isn't sent on the channel itself. If - isn't given, it defaults to the user sending the command. - """ if name and ircutils.strEqual(name, irc.nick): id = 0 elif not name: @@ -252,7 +249,7 @@ class ChannelStats(callbacks.Plugin): id = ircdb.users.getUserId(msg.prefix) name = ircdb.users.getUser(id).name except KeyError: - irc.error('I couldn\'t find you in my user database.') + irc.error(_('I couldn\'t find you in my user database.')) return elif not ircdb.users.hasUser(name): try: @@ -265,53 +262,51 @@ class ChannelStats(callbacks.Plugin): id = ircdb.users.getUserId(name) try: stats = self.db.getUserStats(channel, id) - s = format('%s has sent %n; a total of %n, %n, ' - '%n, and %n; %s of those messages %s' + s = format(_('%s has sent %n; a total of %n, %n, ' + '%n, and %n; %s of those messages %s. ' '%s has joined %n, parted %n, quit %n, ' 'kicked someone %n, been kicked %n, ' 'changed the topic %n, and changed the ' - 'mode %n.', + 'mode %n.'), name, (stats.msgs, 'message'), - (stats.chars, 'character'), - (stats.words, 'word'), - (stats.smileys, 'smiley'), - (stats.frowns, 'frown'), + (stats.chars, _('character')), + (stats.words, _('word')), + (stats.smileys, _('smiley')), + (stats.frowns, _('frown')), stats.actions, - stats.actions == 1 and 'was an ACTION. ' - or 'were ACTIONs. ', + stats.actions == 1 and _('was an ACTION') + or _('were ACTIONs'), name, - (stats.joins, 'time'), - (stats.parts, 'time'), - (stats.quits, 'time'), - (stats.kicks, 'time'), - (stats.kicked, 'time'), - (stats.topics, 'time'), - (stats.modes, 'time')) + (stats.joins, _('time')), + (stats.parts, _('time')), + (stats.quits, _('time')), + (stats.kicks, _('time')), + (stats.kicked, _('time')), + (stats.topics, _('time')), + (stats.modes, _('time'))) irc.reply(s) except KeyError: - irc.error(format('I have no stats for that %s in %s.', + irc.error(format(_('I have no stats for that %s in %s.'), name, channel)) + stats.__doc__ = _("""[] [] + + Returns the statistics for on . is only + necessary if the message isn't sent on the channel itself. If + isn't given, it defaults to the user sending the command. + """) stats = wrap(stats, ['channeldb', additional('something')]) _env = {'__builtins__': types.ModuleType('__builtins__')} _env.update(math.__dict__) def rank(self, irc, msg, args, channel, expr): - """[] - - Returns the ranking of users according to the given stat expression. - Valid variables in the stat expression include 'msgs', 'chars', - 'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits', - 'kicks', 'kicked', 'topics', and 'modes'. Any simple mathematical - expression involving those variables is permitted. - """ # XXX I could do this the right way, and abstract out a safe eval, # or I could just copy/paste from the Math plugin. if expr != expr.translate(utils.str.chars, '_[]'): - irc.error('There\'s really no reason why you should have ' + irc.error(_('There\'s really no reason why you should have ' 'underscores or brackets in your mathematical ' - 'expression. Please remove them.', Raise=True) + 'expression. Please remove them.'), Raise=True) if 'lambda' in expr: - irc.error('You can\'t use lambda in this command.', Raise=True) + irc.error(_('You can\'t use lambda in this command.'), Raise=True) expr = expr.lower() users = [] for ((c, id), stats) in self.db.items(): @@ -325,7 +320,7 @@ class ChannelStats(callbacks.Plugin): except ZeroDivisionError: v = float('inf') except NameError, e: - irc.errorInvalid('stat variable', str(e).split()[1]) + irc.errorInvalid(_('stat variable'), str(e).split()[1]) except Exception, e: irc.error(utils.exnToString(e), Raise=True) if id == 0: @@ -337,40 +332,48 @@ class ChannelStats(callbacks.Plugin): s = utils.str.commaAndify(['#%s %s (%.3g)' % (i+1, u, v) for (i, (v, u)) in enumerate(users)]) irc.reply(s) + rank.__doc__ = _("""[] + + Returns the ranking of users according to the given stat expression. + Valid variables in the stat expression include 'msgs', 'chars', + 'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits', + 'kicks', 'kicked', 'topics', and 'modes'. Any simple mathematical + expression involving those variables is permitted. + """) rank = wrap(rank, ['channeldb', 'text']) def channelstats(self, irc, msg, args, channel): - """[] - - Returns the statistics for . is only necessary if - the message isn't sent on the channel itself. - """ try: stats = self.db.getChannelStats(channel) curUsers = len(irc.state.channels[channel].users) - s = format('On %s there %h been %i messages, containing %i ' + s = format(_('On %s there %h been %i messages, containing %i ' 'characters, %n, %n, and %n; ' '%i of those messages %s. There have been ' '%n, %n, %n, %n, %n, and %n. There %b currently %n ' - 'and the channel has peaked at %n.', + 'and the channel has peaked at %n.'), channel, stats.msgs, stats.msgs, stats.chars, - (stats.words, 'word'), - (stats.smileys, 'smiley'), - (stats.frowns, 'frown'), - stats.actions, stats.actions == 1 and 'was an ACTION' - or 'were ACTIONs', - (stats.joins, 'join'), - (stats.parts, 'part'), - (stats.quits, 'quit'), - (stats.kicks, 'kick'), - (stats.modes, 'mode', 'change'), - (stats.topics, 'topic', 'change'), + (stats.words, _('word')), + (stats.smileys, _('smiley')), + (stats.frowns, _('frown')), + stats.actions, stats.actions == 1 and _('was an ACTION') + or _('were ACTIONs'), + (stats.joins, _('join')), + (stats.parts, _('part')), + (stats.quits, _('quit')), + (stats.kicks, _('kick')), + (stats.modes, _('mode'), _('change')), + (stats.topics, _('topic'), _('change')), curUsers, - (curUsers, 'user'), - (stats.users, 'user')) + (curUsers, _('user')), + (stats.users, _('user'))) irc.reply(s) except KeyError: - irc.error(format('I\'ve never been on %s.', channel)) + irc.error(format(_('I\'ve never been on %s.'), channel)) + channelstats.__doc__ = _("""[] + + Returns the statistics for . is only necessary if + the message isn't sent on the channel itself. + """) channelstats = wrap(channelstats, ['channeldb']) diff --git a/setup.py b/setup.py index 538c4e837..24a5bd7b8 100644 --- a/setup.py +++ b/setup.py @@ -113,8 +113,14 @@ package_dir = {'supybot': 'src', 'plugins/Time/local/dateutil', } +package_data = {} + for plugin in plugins: package_dir['supybot.plugins.' + plugin] = 'plugins/' + plugin + locale_path = 'plugins/' + plugin + '/locale/' + locale_name = 'supybot.plugins.'+plugin + if os.path.exists(locale_path): + package_data.update({locale_name: ['locale/'+s for s in os.listdir(locale_path)]}) version = '0.83.4.1+git' setup( @@ -151,6 +157,8 @@ setup( package_dir=package_dir, + package_data=package_data, + scripts=['scripts/supybot', 'scripts/supybot-test', 'scripts/supybot-botchk', diff --git a/src/i18n.py b/src/i18n.py new file mode 100644 index 000000000..b1a686f92 --- /dev/null +++ b/src/i18n.py @@ -0,0 +1,129 @@ +### +# 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 internationalisation and localisation managment. +""" + +__all__ = ['PluginInternationalization'] + +import sys +import supybot.conf as conf + +WAITING_FOR_MSGID = 1 +IN_MSGID = 2 +WAITING_FOR_MSGSTR = 3 +IN_MSGSTR = 4 + +MSGID = 'msgid "' +MSGSTR = 'msgstr "' + +#registerGlobalValue(supybot, 'language', +# ValidNick('supybot', """Determines the bot's default language. +# Valid values are things like en, fr, de, etc.""")) + +def get_plugin_dir(plugin_name): + filename = sys.modules[plugin_name].__file__ + if filename.endswith("plugin.pyc"): + return filename[0:-len("plugin.pyc")] + elif filename.endswith("plugin.py"): + return filename[0:-len("plugin.py")] + else: + return + +i18nClasses = {} + +class PluginInternationalization: + """Internationalization managment for a plugin.""" + def __init__(self, name='supybot'): + self.name = name + self.load_locale('toto') + i18nClasses.update({name: self}) + + def load_locale(self, locale_name): + directory = get_plugin_dir(self.name) + 'locale/' + try: + translation_file = open('%s%s.po' % (directory, locale_name), 'ru') + except IOError: # The translation is unavailable + self.translations = {} + return + step = WAITING_FOR_MSGID + self.translations = {} + for line in translation_file: + line = line[0:-1] # Remove the ending \n + + if line.startswith(MSGID): + # Don't check if step is WAITING_FOR_MSGID + untranslated = '' + translated = '' + data = line[len(MSGID):-1] + if len(data) == 0: # Multiline mode + step = IN_MSGID + else: + untranslated += data + step = WAITING_FOR_MSGSTR + + + elif step is IN_MSGID and line.startswith('"') and \ + line.endswith('"'): + untranslated += line[1:-1] + elif step is IN_MSGID and untranslated == '': # Empty MSGID + step = WAITING_FOR_MSGID + elif step is IN_MSGID: # the MSGID is finished + step = WAITING_FOR_MSGSTR + + + if step is WAITING_FOR_MSGSTR and line.startswith(MSGSTR): + data = line[len(MSGSTR):-1] + if len(data) == 0: # Multiline mode + step = IN_MSGID + else: + self.translations.update({untranslated: data}) + step = WAITING_FOR_MSGID + + + elif step is IN_MSGSTR and line.startswith('"') and \ + line.endswith('"'): + untranslated += line[1:-1] + elif step is IN_MSGSTR: # the MSGSTR is finished + step = WAITING_FOR_MSGID + if translated == '': + translated = untranslated + self.translations.update({untranslated: translated}) + + def __call__(self, untranslated, *args): + if len(args) == 0: + try: + return self.translations[untranslated] + except KeyError: + return untranslated + else: + translation = self(untranslated) + return translation % args + diff --git a/src/utils/str.py b/src/utils/str.py index c6d801ce7..93f541d2f 100644 --- a/src/utils/str.py +++ b/src/utils/str.py @@ -1,6 +1,7 @@ ### # Copyright (c) 2002-2005, Jeremiah Fincher # Copyright (c) 2008-2009, James Vega +# Copyright (c) 2010, Valentin Lorentz # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -362,7 +363,7 @@ def timestamp(t): t = time.time() return time.ctime(t) -_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrstu%])') +_formatRe = re.compile('%((?:\d+)?\.\d+f|[bfhiLnpqrstuv%])') def format(s, *args, **kwargs): """w00t. @@ -379,6 +380,8 @@ def format(s, *args, **kwargs): n: nItems (takes a 2-tuple of (n, item) or a 3-tuple of (n, between, item)) t: time, formatted (takes an int) u: url, wrapped in braces (this should be configurable at some point) + v: void : takes one or many arguments, but doesn't display it + (useful for translation) """ args = list(args) args.reverse() # For more efficient popping. @@ -429,6 +432,9 @@ def format(s, *args, **kwargs): return timestamp(args.pop()) elif char == 'u': return '<%s>' % args.pop() + elif char == 'v': + args.pop() + return '' elif char == '%': return '%' else: